package io.nextop.demo.flip; import io.nextop.*; import rx.android.schedulers.AndroidSchedulers; import rx.functions.Action0; import rx.functions.Action1; import rx.functions.Func2; import java.util.List; import java.util.Map; import java.util.concurrent.TimeUnit; public class FeedViewModelManager extends ThinViewModelManager<FeedViewModel> { FeedViewModelManager(Nextop nextop) { super(nextop); } // CONTROLLER void addFlip(Id feedId, final Id flipId) { Message update = Message.newBuilder() .setRoute("PUT http://" + Flip.REMOTE + "/flip/$flip-id") .set("flip-id", flipId) .build(); nextop.send(update); update(feedId, new Func2<FeedViewModel, RxState, FeedViewModel>() { @Override public FeedViewModel call(FeedViewModel feedVm, RxState state) { feedVm.add(new FeedViewModel.FlipState(flipId, -1L)); return feedVm; } }); } void loadBack(final Id feedId) { // update: set load back pending; then if not already pending before, load N more ids less than the last id in the view model, update again updateComplete(feedId, new Func2<FeedViewModel, RxState, FeedViewModel>() { @Override public FeedViewModel call(FeedViewModel feedVm, RxState state) { Message message = Message.newBuilder() .setRoute("GET http://" + Flip.REMOTE + "/feed") .set("before", feedVm.getMinUpdateIndex()) .build(); state.bind(nextop.send(message) .doOnNext(new Action1<Message>() { @Override public void call(final Message message) { add(feedId, message); } })).subscribe(); return feedVm; } }); } private void add(final Id feedId, final Message results) { final List<WireValue> values = results.getContent().asList(); if (!values.isEmpty()) { update(feedId, new Func2<FeedViewModel, RxState, FeedViewModel>() { @Override public FeedViewModel call(FeedViewModel feedVm, RxState state) { for (WireValue value : values) { Map<WireValue, WireValue> m = value.asMap(); // FIXME asId Id flipId = Id.valueOf(m.get(WireValue.of("flip_id")).asString()); long updateIndex = m.get(WireValue.of("most_recent_update_index")).asLong(); feedVm.add(new FeedViewModel.FlipState(flipId, updateIndex)); } return feedVm; } }); } } // VMM @Override protected void startUpdates(final FeedViewModel feedVm, final RxState state) { // 1. sync the state. on sync, mark it as syncd // 2. poll for incremental updates. incremental updates take the previous known update id (given from the state sync), // and return a list of changes (id) that should be moved or added to the top // 3. changes update vm Message sync = Message.newBuilder() .setRoute("GET http://" + Flip.REMOTE + "/feed") .build(); state.bind(nextop.send(sync)) .doOnNext(new Action1<Message>() { @Override public void call(Message message) { add(feedVm.id, message); state.sync(); } }).doOnCompleted(new Action0() { @Override public void call() { // start the poller Action0 poller = new Action0() { @Override public void call() { Message poll = Message.newBuilder() .setRoute("GET http://" + Flip.REMOTE + "/feed") .set("after", feedVm.getMaxUpdateIndex()) .build(); state.bind(nextop.send(poll)) .doOnNext(new Action1<Message>() { @Override public void call(final Message message) { add(feedVm.id, message); } }).subscribe(); } }; state.bind(AndroidSchedulers.mainThread().createWorker().schedulePeriodically(poller, pollTimeoutMs, pollTimeoutMs, TimeUnit.MILLISECONDS)); } }).subscribe(); } @Override protected FeedViewModel create(Id id) { return new FeedViewModel(id); } }