I'm trying to create a form, send-only view that has a SignaturePad which once the user clicks on the save button, a save intent gets fired and do some background processing.
I have the following:
Presenter:
@Override
protected void bindIntents() {
Observable<SignatureViewState> observable =
intent(SignatureView::saveSignature)
.switchMap(intent -> Observable.fromCallable(() ->
storage.createFile(intent.getFullPath(), intent.getName(), intent.getImage()))
.subscribeOn(Schedulers.from(threadExecutor)))
.map(SignatureViewState.SuccessState::new)
.cast(SignatureViewState.class)
.startWith(new SignatureViewState.LoadingState())
.onErrorReturn(SignatureViewState.ErrorState::new)
.observeOn(postExecutionThread.getScheduler());
subscribeViewState(observable, SignatureView::render);
}
SignatureFragment:
@Override
public void onViewCreated(View view, @Nullable Bundle savedInstanceState) {
saveButtonClickObservable = RxView.clicks(saveBtn)
.share()
.map(bla -> true);
compositeDisposable.add(saveButtonClickObservable.subscribe());
...
@Override
public Observable<SaveSignatureIntent> saveSignature() {
Observable<SaveSignatureIntent> saveSignatureIntentObservable =
Observable.just(new SaveSignatureIntent(savePath, bookingId + ".png", null));
Observable<SaveSignatureIntent> saveSignatureIntent =
Observable.zip(signatureBitmapObservable, saveSignatureIntentObservable,
(signature, intent) -> new SaveSignatureIntent(intent.fullPath, intent.name, signature));
return saveButtonClickObservable
.flatMap(bla -> saveSignatureIntent);
}
@Override
public void render(SignatureViewState state) {
if(state instanceof SignatureViewState.LoadingState)
renderLoading();
else if (state instanceof SignatureViewState.SuccessState)
renderSuccess();
if(state instanceof SignatureViewState.ErrorState)
renderError();
}
Lastly my view:
public interface SignatureView extends MvpView {
Observable<SignatureFragment.SaveSignatureIntent> saveSignature();
void render(SignatureViewState state);
}
The problem is, once my frag gets created, the .startWith gets fired, without the user clicking the button. After that, if the user clicks on the button, the loading state never gets called (.startwith again) but only the success (or error). What am I missing here ?
Thanks again !
Edit:
signatureBitmapObservable = Observable.fromCallable(() -> signaturePad.getTransparentSignatureBitmap(true))
.subscribeOn(Schedulers.io())
.startWith(bla -> renderLoading());
Another process is getting a transparent Bitmap, but after adding startWith, my callable never gets called. If I take it out, it works like a charm.
This is just a RxJava mistake, not really Mosby related.
put .startWith(new SignatureViewState.LoadingState())
(and maybe .onErrorReturn(SignatureViewState.ErrorState::new)
) in the observable returned from switchMap()
. Like this:
intent(SignatureView::saveSignature)
.switchMap(intent -> Observable.fromCallable(() ->
storage.createFile(intent.getFullPath(), intent.getName(), intent.getImage()))
.subscribeOn(Schedulers.from(threadExecutor))
.map(SignatureViewState.SuccessState::new)
.cast(SignatureViewState.class)
.startWith(new SignatureViewState.LoadingState())
.onErrorReturn(SignatureViewState.ErrorState::new)
) // end of switchMap
.observeOn(postExecutionThread.getScheduler());
The observable returned from switchMap only start once the user clicks on your button (because switchMap only triggers after intent has been fired).
startWith()
means "before you do the real work, emit this first". If you apply startWith() as you did in your original code snipped, obviously it the observable start with loading, but what you really want is "before saving the signature, start with loading state". Therefore startWith()
must be part of the "save signature" observable, and not of the "main" observable per se.