/*
* Copyright 2016 Jacek Marchwicki <jacek.marchwicki@gmail.com>
*
* Licensed under the Apache License, Version 2.0 (the "License"); you may not
* use this file except in compliance with the License. You may obtain a copy of
* the License at
*
* http://www.apache.org/licenses/LICENSE-2.0
*
* Unless required by applicable law or agreed to in writing, software
* distributed under the License is distributed on an "AS IS" BASIS, WITHOUT
* WARRANTIES OR CONDITIONS OF ANY KIND, either express or implied. See the
* License for the specific language governing permissions and limitations under
* the License.
*/
package com.appunite.rx.example.ui.main;
import com.appunite.login.CurrentLoggedInUserDao;
import com.appunite.rx.ResponseOrError;
import com.appunite.rx.example.dao.auth.MyCurrentLoggedInUserDao;
import com.appunite.rx.example.dao.posts.PostsDao;
import com.appunite.rx.example.dao.posts.model.Post;
import com.appunite.rx.example.dao.posts.model.PostId;
import com.appunite.rx.example.dao.posts.model.PostsIdsResponse;
import com.appunite.rx.example.dao.posts.model.PostsResponse;
import com.appunite.rx.functions.FunctionsN;
import com.appunite.rx.example.internal.Objects;
import com.appunite.rx.operators.MoreOperators;
import com.jacekmarchwicki.universaladapter.BaseAdapterItem;
import java.util.Arrays;
import java.util.Collections;
import java.util.List;
import javax.annotation.Nonnull;
import javax.annotation.Nullable;
import rx.Observable;
import rx.Observer;
import rx.Scheduler;
import rx.functions.Action1;
import rx.functions.Func1;
import rx.observers.Observers;
import rx.subjects.PublishSubject;
import rx.subjects.Subject;
class MainPresenter {
@Nonnull
private final Observable<String> titleObservable;
@Nonnull
private final Observable<List<BaseAdapterItem>> itemsObservable;
@Nonnull
private final Observable<Throwable> errorObservable;
@Nonnull
private final Observable<Boolean> progressObservable;
@Nonnull
private final Subject<AdapterItem, AdapterItem> openDetailsSubject = PublishSubject.create();
@Nonnull
private final PostsDao postsDao;
@Nonnull
private final MyCurrentLoggedInUserDao currentLoggedInUserDao;
@Nonnull
private final Scheduler uiScheduler;
@Nonnull
private final PublishSubject<Object> clickOnFabSubject = PublishSubject.create();
@Nonnull
private final PublishSubject<Object> clickLogoutSubject = PublishSubject.create();
@Nonnull
private final PublishSubject<Object> clickLoginSubject = PublishSubject.create();
MainPresenter(@Nonnull final PostsDao postsDao,
@Nonnull final MyCurrentLoggedInUserDao currentLoggedInUserDao,
@Nonnull Scheduler uiScheduler) {
this.postsDao = postsDao;
this.currentLoggedInUserDao = currentLoggedInUserDao;
this.uiScheduler = uiScheduler;
// Two solutions - you can choose one
if (true) {
final Observable<ResponseOrError<PostsResponse>> postsObservable = postsDao.postsObservable()
.observeOn(uiScheduler)
.replay(1)
.refCount();
titleObservable = postsObservable
.map(new Func1<ResponseOrError<PostsResponse>, String>() {
@Override
public String call(ResponseOrError<PostsResponse> responseOrError) {
return responseOrError.isError() ? "" : responseOrError.data().title();
}
});
itemsObservable = postsObservable
.map(new Func1<ResponseOrError<PostsResponse>, List<Post>>() {
@Override
public List<Post> call(ResponseOrError<PostsResponse> responseOrError) {
return responseOrError.isError() ? Collections.<Post>emptyList() : responseOrError.data().items();
}
})
.switchMap(new Func1<List<Post>, Observable<? extends List<BaseAdapterItem>>>() {
@Override
public Observable<? extends List<BaseAdapterItem>> call(List<Post> items) {
return Observable.from(items)
.map(new Func1<Post, BaseAdapterItem>() {
@Override
public BaseAdapterItem call(Post post) {
return new AdapterItem(post.id(), post.name());
}
})
.toList();
}
});
errorObservable = ResponseOrError.combineErrorsObservable(Collections.singletonList(
ResponseOrError.transform(postsObservable)))
.distinctUntilChanged();
progressObservable = ResponseOrError.combineProgressObservable(Collections.singletonList(
ResponseOrError.transform(postsObservable)));
} else {
final Observable<ResponseOrError<PostsIdsResponse>> postIds = postsDao.postsIdsObservable()
.observeOn(uiScheduler)
.replay(1)
.refCount();
itemsObservable = postIds
.compose(ResponseOrError.map(new Func1<PostsIdsResponse, List<PostId>>() {
@Override
public List<PostId> call(PostsIdsResponse postsIdsResponse) {
return postsIdsResponse.items();
}
}))
.map(new Func1<ResponseOrError<List<PostId>>, List<PostId>>() {
@Override
public List<PostId> call(ResponseOrError<List<PostId>> responseOrError) {
return responseOrError.isError() ? Collections.<PostId>emptyList() : responseOrError.data();
}
})
.compose(MoreOperators.observableSwitch(new Func1<PostId, Observable<BaseAdapterItem>>() {
@Override
public Observable<BaseAdapterItem> call(final PostId postId) {
return postsDao.postDao(postId.id()).postObservable()
.map(new Func1<ResponseOrError<Post>, BaseAdapterItem>() {
@Override
public BaseAdapterItem call(ResponseOrError<Post> postResponseOrError) {
if (postResponseOrError.isData()) {
final Post post = postResponseOrError.data();
return new AdapterItem(post.id(), post.name());
} else {
return new ErrorAdapterItem(postId.id(), postResponseOrError.error());
}
}
});
}
}))
.onBackpressureLatest()
.observeOn(uiScheduler);
titleObservable = postIds
.map(new Func1<ResponseOrError<PostsIdsResponse>, String>() {
@Override
public String call(ResponseOrError<PostsIdsResponse> responseOrError) {
return responseOrError.isError() ? "" : responseOrError.data().title();
}
});
errorObservable = ResponseOrError.combineErrorsObservable(Collections.singletonList(
ResponseOrError.transform(postIds)))
.distinctUntilChanged();
progressObservable = Observable.combineLatest(Arrays.asList(postIds, itemsObservable), FunctionsN.returnFalse())
.startWith(true);
}
}
@Nonnull
Observable<AdapterItem> openDetailsObservable() {
return openDetailsSubject;
}
@Nonnull
Observable<String> titleObservable() {
return titleObservable;
}
@Nonnull
Observable<List<BaseAdapterItem>> itemsObservable() {
return itemsObservable;
}
@Nonnull
Observable<Throwable> errorObservable() {
return errorObservable;
}
@Nonnull
Observable<Boolean> progressObservable() {
return progressObservable;
}
@Nonnull
Observer<Object> loadMoreObserver() {
return postsDao.loadMoreObserver();
}
@Nonnull
Observer<Object> clickOnFabObserver() {
return clickOnFabSubject;
}
@Nonnull
Observable<Object> startCreatePostActivityObservable() {
return clickOnFabSubject;
}
@Nonnull
Observer<Object> clickLoginObserver() {
return clickLoginSubject;
}
@Nonnull
Observer<Object> clickLogoutObserver() {
return clickLogoutSubject;
}
@Nonnull
Observable<Boolean> loginVisibleObservable() {
return currentLoggedInUserDao.currentLoggedInUserObservable()
.map(new Func1<ResponseOrError<CurrentLoggedInUserDao.LoggedInUserDao>, Boolean>() {
@Override
public Boolean call(ResponseOrError<CurrentLoggedInUserDao.LoggedInUserDao> loggedInUserDaoResponseOrError) {
return loggedInUserDaoResponseOrError.isError()
&& loggedInUserDaoResponseOrError.error() instanceof CurrentLoggedInUserDao.NotAuthorizedException;
}
})
.observeOn(uiScheduler)
.startWith(false);
}
@Nonnull
Observable<Boolean> logoutVisibleObservable() {
return currentLoggedInUserDao.currentLoggedInUserObservable()
.map(new Func1<ResponseOrError<CurrentLoggedInUserDao.LoggedInUserDao>, Boolean>() {
@Override
public Boolean call(ResponseOrError<CurrentLoggedInUserDao.LoggedInUserDao> loggedInUserDaoResponseOrError) {
return loggedInUserDaoResponseOrError.isData();
}
})
.observeOn(uiScheduler)
.startWith(false);
}
@Nonnull
Observable<Object> startFirebaseLoginActivity() {
return clickLoginSubject;
}
@Nonnull
Observable<Object> logoutUserObservable() {
return clickLogoutSubject;
}
public static class ErrorAdapterItem implements BaseAdapterItem {
@Nonnull
private final String id;
@Nonnull
private final Throwable error;
ErrorAdapterItem(@Nonnull String id, @Nonnull Throwable error) {
this.id = id;
this.error = error;
}
@Nonnull
public Throwable error() {
return error;
}
@Override
public long adapterId() {
return id.hashCode();
}
@Override
public boolean matches(@Nonnull BaseAdapterItem item) {
return item instanceof ErrorAdapterItem && Objects.equal(id, ((ErrorAdapterItem)item).id);
}
@Override
public boolean same(@Nonnull BaseAdapterItem item) {
return equals(item);
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof ErrorAdapterItem)) return false;
final ErrorAdapterItem that = (ErrorAdapterItem) o;
return Objects.equal(id, that.id) &&
Objects.equal(error, that.error);
}
@Override
public int hashCode() {
return Objects.hashCode(id, error);
}
}
public class AdapterItem implements BaseAdapterItem {
@Nonnull
private final String id;
@Nullable
private final String text;
AdapterItem(@Nonnull String id,
@Nullable String text) {
this.id = id;
this.text = text;
}
@Nonnull
public String id() {
return id;
}
@Nullable
public String text() {
return text;
}
@Override
public boolean equals(Object o) {
if (this == o) return true;
if (!(o instanceof AdapterItem)) return false;
final AdapterItem that = (AdapterItem) o;
return id.equals(that.id) && !(text != null ? !text.equals(that.text) : that.text != null);
}
@Override
public int hashCode() {
int result = id.hashCode();
result = 31 * result + (text != null ? text.hashCode() : 0);
return result;
}
@Override
public long adapterId() {
return id.hashCode();
}
@Override
public boolean matches(@Nonnull BaseAdapterItem item) {
return item instanceof AdapterItem && Objects.equal(id, ((AdapterItem)item).id);
}
@Override
public boolean same(@Nonnull BaseAdapterItem item) {
return equals(item);
}
@Nonnull
public Observer<Object> clickObserver() {
return Observers.create(new Action1<Object>() {
@Override
public void call(Object o) {
openDetailsSubject.onNext(AdapterItem.this);
}
});
}
}
}