package com.kickstarter.viewmodels;
import android.support.annotation.NonNull;
import android.util.Pair;
import com.kickstarter.libs.ActivityViewModel;
import com.kickstarter.libs.CurrentUserType;
import com.kickstarter.libs.Environment;
import com.kickstarter.libs.utils.ObjectUtils;
import com.kickstarter.models.Backing;
import com.kickstarter.models.Message;
import com.kickstarter.models.MessageThread;
import com.kickstarter.models.Project;
import com.kickstarter.services.ApiClientType;
import com.kickstarter.services.apiresponses.ErrorEnvelope;
import com.kickstarter.services.apiresponses.MessageThreadEnvelope;
import com.kickstarter.ui.IntentKey;
import com.kickstarter.ui.activities.MessagesActivity;
import java.util.List;
import rx.Notification;
import rx.Observable;
import rx.subjects.BehaviorSubject;
import rx.subjects.PublishSubject;
import static com.kickstarter.libs.rx.transformers.Transformers.combineLatestPair;
import static com.kickstarter.libs.rx.transformers.Transformers.errors;
import static com.kickstarter.libs.rx.transformers.Transformers.takeWhen;
import static com.kickstarter.libs.rx.transformers.Transformers.values;
public interface MessagesViewModel {
interface Inputs {
/** Call when the message edit text changes. */
void messageEditTextChanged(String messageBody);
/** Call when the send message button has been clicked. */
void sendMessageButtonClicked();
}
interface Outputs {
/** Emits the backing and project to populate the backing info header. */
Observable<Pair<Backing, Project>> backingAndProject();
/** Emits a boolean that determines if the backing info view should be hidden. */
Observable<Boolean> backingInfoViewHidden();
/** Emits a list of messages to be displayed. */
Observable<List<Message>> messages();
/** Emits the participant name to be displayed. */
Observable<String> participantNameTextViewText();
/** Emits the project name to be displayed. */
Observable<String> projectNameTextViewText();
/** Emits a string to set the message edit text to. */
Observable<String> setMessageEditText();
/** Emits a string to display in the message error toast. */
Observable<String> showMessageErrorToast();
}
final class ViewModel extends ActivityViewModel<MessagesActivity> implements Inputs, Outputs {
private final ApiClientType client;
private final CurrentUserType currentUser;
public ViewModel(final @NonNull Environment environment) {
super(environment);
this.client = environment.apiClient();
this.currentUser = environment.currentUser();
final Observable<MessageThread> messageThread = intent()
.take(1)
.map(i -> i.getParcelableExtra(IntentKey.MESSAGE_THREAD))
.ofType(MessageThread.class);
final Observable<Notification<Message>> messageNotification = messageThread
.compose(combineLatestPair(this.messageEditTextChanged))
.compose(takeWhen(this.sendMessageButtonClicked))
.switchMap(threadAndBody ->
this.client.sendMessageToThread(threadAndBody.first, threadAndBody.second)
.materialize()
)
.share();
final Observable<Message> messageSent = messageNotification.compose(values());
this.setMessageEditText = messageSent.map(__ -> "");
final Observable<Notification<MessageThreadEnvelope>> envelopeNotification = Observable.merge(
messageThread,
messageThread.compose(takeWhen(messageSent))
)
.switchMap(thread -> this.client.fetchMessagesForThread(thread).materialize())
.share();
final Observable<MessageThreadEnvelope> messageThreadEnvelope = envelopeNotification.compose(values());
final Observable<Notification<Backing>> backingNotification = Observable.combineLatest(
messageThread,
this.currentUser.observable(),
Pair::create
)
.filter(mu -> mu.first.backing() == null)
.switchMap(mu ->
mu.first.project().isBacking()
? this.client.fetchProjectBacking(mu.first.project(), mu.second).materialize()
: this.client.fetchProjectBacking(mu.first.project(), mu.first.participant()).materialize()
)
.share();
final Observable<Backing> backing = Observable.merge(
messageThread.map(MessageThread::backing),
backingNotification.compose(values())
)
.filter(ObjectUtils::isNotNull);
Observable.merge(
messageThread.map(MessageThread::backing).map(ObjectUtils::isNull),
backing.map(ObjectUtils::isNull)
)
.distinctUntilChanged()
.compose(bindToLifecycle())
.subscribe(this.backingInfoViewHidden::onNext);
Observable.combineLatest(
backing,
messageThread.map(MessageThread::project),
Pair::create
)
.compose(bindToLifecycle())
.subscribe(this.backingAndProject::onNext);
messageThread
.map(thread -> thread.project().creator().name())
.compose(bindToLifecycle())
.subscribe(this.participantNameTextViewText::onNext);
messageThreadEnvelope
.map(MessageThreadEnvelope::messages)
.compose(bindToLifecycle())
.subscribe(this.messages::onNext);
messageNotification
.compose(errors())
.map(ErrorEnvelope::fromThrowable)
.map(ErrorEnvelope::errorMessage)
.subscribe(this.showMessageErrorToast::onNext);
messageThread
.map(thread -> thread.project().name())
.compose(bindToLifecycle())
.subscribe(this.projectNameTextViewText::onNext);
}
private final PublishSubject<String> messageEditTextChanged = PublishSubject.create();
private final PublishSubject<Void> sendMessageButtonClicked = PublishSubject.create();
private final BehaviorSubject<Pair<Backing, Project>> backingAndProject = BehaviorSubject.create();
private final BehaviorSubject<Boolean> backingInfoViewHidden = BehaviorSubject.create();
private final BehaviorSubject<List<Message>> messages = BehaviorSubject.create();
private final BehaviorSubject<String> participantNameTextViewText = BehaviorSubject.create();
private final BehaviorSubject<String> projectNameTextViewText = BehaviorSubject.create();
private final PublishSubject<String> showMessageErrorToast = PublishSubject.create();
private final Observable<String> setMessageEditText;
public final Inputs inputs = this;
public final Outputs outputs = this;
@Override public void messageEditTextChanged(final @NonNull String messageBody) {
this.messageEditTextChanged.onNext(messageBody);
}
@Override public void sendMessageButtonClicked() {
this.sendMessageButtonClicked.onNext(null);
}
@Override public @NonNull Observable<Pair<Backing, Project>> backingAndProject() {
return this.backingAndProject;
}
@Override public @NonNull Observable<Boolean> backingInfoViewHidden() {
return this.backingInfoViewHidden;
}
@Override public @NonNull Observable<String> participantNameTextViewText() {
return this.participantNameTextViewText;
}
@Override public @NonNull Observable<List<Message>> messages() {
return this.messages;
}
@Override public @NonNull Observable<String> projectNameTextViewText() {
return this.projectNameTextViewText;
}
@Override public @NonNull Observable<String> showMessageErrorToast() {
return this.showMessageErrorToast;
}
@Override public @NonNull Observable<String> setMessageEditText() {
return this.setMessageEditText;
}
}
}