package com.kickstarter.viewmodels; import android.support.annotation.NonNull; import android.util.Pair; import com.kickstarter.libs.ActivityViewModel; import com.kickstarter.libs.Environment; import com.kickstarter.libs.KoalaContext; import com.kickstarter.libs.RefTag; import com.kickstarter.libs.utils.NumberUtils; import com.kickstarter.models.Project; import com.kickstarter.models.Update; import com.kickstarter.services.ApiClientType; import com.kickstarter.ui.IntentKey; import com.kickstarter.ui.activities.UpdateActivity; import okhttp3.Request; import rx.Observable; import rx.subjects.BehaviorSubject; import rx.subjects.PublishSubject; import static com.kickstarter.libs.rx.transformers.Transformers.neverError; import static com.kickstarter.libs.rx.transformers.Transformers.takeWhen; public interface UpdateViewModel { interface Inputs { /** Call when an external link has been activated. */ void externalLinkActivated(); /** Call when a project update comments uri request has been made. */ void goToCommentsRequest(Request request); /** Call when a project uri request has been made. */ void goToProjectRequest(Request request); /** Call when a project update uri request has been made. */ void goToUpdateRequest(Request request); /** Call when the share button is clicked. */ void shareIconButtonClicked(); } interface Outputs { /** Emits when we should start the share intent to show the share sheet. */ Observable<Update> startShareIntent(); /** Emits an update to start the comments activity with. */ Observable<Update> startCommentsActivity(); /** Emits a project and a ref tag to start the project activity with. */ Observable<Pair<Project, RefTag>> startProjectActivity(); /** Emits a string to display in the toolbar title. */ Observable<String> updateSequence(); /** Emits a url to load in the web view. */ Observable<String> webViewUrl(); } final class ViewModel extends ActivityViewModel<UpdateActivity> implements Inputs, Outputs { private final ApiClientType client; public ViewModel(final @NonNull Environment environment) { super(environment); this.client = environment.apiClient(); final Observable<Update> initialUpdate = intent() .map(i -> i.getParcelableExtra(IntentKey.UPDATE)) .ofType(Update.class) .take(1); final Observable<Project> project = intent() .map(i -> i.getParcelableExtra(IntentKey.PROJECT)) .ofType(Project.class); final Observable<String> initialUpdateUrl = initialUpdate .map(u -> u.urls().web().update()); final Observable<String> anotherUpdateUrl = this.goToUpdateRequest .map(request -> request.url().toString()); Observable.merge(initialUpdateUrl, anotherUpdateUrl) .distinctUntilChanged() .compose(bindToLifecycle()) .subscribe(this.webViewUrl::onNext); final Observable<Update> anotherUpdate = this.goToUpdateRequest .map(this::projectUpdateParams) .switchMap(pu -> this.client.fetchUpdate(pu.first, pu.second).compose(neverError())) .share(); final Observable<Update> currentUpdate = Observable.merge(initialUpdate, anotherUpdate); currentUpdate .compose(takeWhen(this.shareButtonClicked)) .compose(bindToLifecycle()) .subscribe(this.startShareIntent::onNext); currentUpdate .compose(takeWhen(this.goToCommentsRequest)) .compose(bindToLifecycle()) .subscribe(this.startCommentsActivity::onNext); currentUpdate .map(u -> NumberUtils.format(u.sequence())) .compose(bindToLifecycle()) .subscribe(this.updateSequence::onNext); project .compose(takeWhen(this.goToProjectRequest)) .compose(bindToLifecycle()) .subscribe(p -> this.startProjectActivity.onNext(Pair.create(p, RefTag.update()))); project .compose(takeWhen(this.externalLinkActivated)) .compose(bindToLifecycle()) .subscribe(p -> this.koala.trackOpenedExternalLink(p, KoalaContext.ExternalLink.PROJECT_UPDATE)); } /** * Parses a request for project and update params. * * @param request Comments or update request. * @return Pair of project param string and update param string. */ private @NonNull Pair<String, String> projectUpdateParams(final @NonNull Request request) { // todo: build a Navigation helper for better param extraction final String projectParam = request.url().encodedPathSegments().get(2); final String updateParam = request.url().encodedPathSegments().get(4); return Pair.create(projectParam, updateParam); } private final PublishSubject<Request> externalLinkActivated = PublishSubject.create(); private final PublishSubject<Request> goToCommentsRequest = PublishSubject.create(); private final PublishSubject<Request> goToProjectRequest = PublishSubject.create(); private final PublishSubject<Request> goToUpdateRequest = PublishSubject.create(); private final PublishSubject<Void> shareButtonClicked = PublishSubject.create(); private final PublishSubject<Update> startShareIntent = PublishSubject.create(); private final PublishSubject<Update> startCommentsActivity = PublishSubject.create(); private final PublishSubject<Pair<Project, RefTag>> startProjectActivity = PublishSubject.create(); private final BehaviorSubject<String> updateSequence = BehaviorSubject.create(); private final BehaviorSubject<String> webViewUrl = BehaviorSubject.create(); public final Inputs inputs = this; public final Outputs outputs = this; @Override public void externalLinkActivated() { this.externalLinkActivated.onNext(null); } @Override public void goToCommentsRequest(final @NonNull Request request) { this.goToCommentsRequest.onNext(request); } @Override public void goToProjectRequest(final @NonNull Request request) { this.goToProjectRequest.onNext(request); } @Override public void goToUpdateRequest(final @NonNull Request request) { this.goToUpdateRequest.onNext(request); } @Override public void shareIconButtonClicked() { this.shareButtonClicked.onNext(null); } @Override public Observable<Update> startShareIntent() { return this.startShareIntent; } @Override public @NonNull Observable<Update> startCommentsActivity() { return this.startCommentsActivity; } @Override public @NonNull Observable<Pair<Project, RefTag>> startProjectActivity() { return this.startProjectActivity; } @Override public @NonNull Observable<String> updateSequence() { return this.updateSequence; } @Override public @NonNull Observable<String> webViewUrl() { return this.webViewUrl; } } }