public interface HomeView { public Observable<Boolean> loadFirstPageIntent(); public Observable<Boolean> loadNextPageIntent(); public Observable<Boolean> pullToRefreshIntent(); public Observable<String> loadAllProductsFromCategoryIntent(); public void render(HomeViewState viewState); }
public final class HomeViewState { private final boolean loadingFirstPage; private final Throwable firstPageError; private final List<FeedItem> data; private final boolean loadingNextPage; private final Throwable nextPageError; private final boolean loadingPullToRefresh; private final Throwable pullToRefreshError; // ... ... // ... ... }
public class AdditionalItemsLoadable implements FeedItem { private final int moreItemsAvailableCount; private final String categoryName; private final boolean loading; private final Throwable loadingError; // ... ... // ... ... }
public class HomeFeedLoader { public Observable<List<FeedItem>> loadNewestPage() { ... } public Observable<List<FeedItem>> loadFirstPage() { ... } public Observable<List<FeedItem>> loadNextPage() { ... } public Observable<List<Product>> loadProductsOfCategory(String categoryName) { ... } }
class HomePresenter extends MviBasePresenter<HomeView, HomeViewState> { private final HomeFeedLoader feedLoader; @Override protected void bindIntents() { Observable<HomeViewState> loadFirstPage = intent(HomeView::loadFirstPageIntent) .flatMap(ignored -> feedLoader.loadFirstPage() .map(items -> new HomeViewState(items, false, null) ) .startWith(new HomeViewState(emptyList, true, null) ) .onErrorReturn(error -> new HomeViewState(emptyList, false, error)) subscribeViewState(loadFirstPage, HomeView::render); } }
class HomePresenter extends MviBasePresenter<HomeView, HomeViewState> { private final HomeFeedLoader feedLoader; @Override protected void bindIntents() { Observable<HomeViewState> loadFirstPage = ... ; Observable<HomeViewState> pullToRefresh = intent(HomeView::pullToRefreshIntent) .flatMap(ignored -> feedLoader.loadNewestPage() .map( items -> new HomeViewState(...)) .startWith(new HomeViewState(...)) .onErrorReturn(error -> new HomeViewState(...))); Observable<HomeViewState> allIntents = Observable.merge(loadFirstPage, pullToRefresh); subscribeViewState(allIntents, HomeView::render); } }
public State reduce( State previous, Foo foo ){ State newState; // ... State Foo return newState; }
class HomePresenter extends MviBasePresenter<HomeView, HomeViewState> { private final HomeFeedLoader feedLoader; @Override protected void bindIntents() { Observable<PartialState> loadFirstPage = intent(HomeView::loadFirstPageIntent) .flatMap(ignored -> feedLoader.loadFirstPage() .map(items -> new PartialState.FirstPageData(items) ) .startWith(new PartialState.FirstPageLoading(true) ) .onErrorReturn(error -> new PartialState.FirstPageError(error)) Observable<PartialState> pullToRefresh = intent(HomeView::pullToRefreshIntent) .flatMap(ignored -> feedLoader.loadNewestPage() .map( items -> new PartialState.PullToRefreshData(items) .startWith(new PartialState.PullToRefreshLoading(true))) .onErrorReturn(error -> new PartialState.PullToRefreshError(error))); Observable<PartialState> allIntents = Observable.merge(loadFirstPage, pullToRefresh); HomeViewState initialState = ... ; // Observable<HomeViewState> stateObservable = allIntents.scan(initialState, this::viewStateReducer) subscribeViewState(stateObservable, HomeView::render); } private HomeViewState viewStateReducer(HomeViewState previousState, PartialState changes){ ... } }
private HomeViewState viewStateReducer(HomeViewState previousState, PartialState changes){ if (changes instanceof PartialState.FirstPageLoading) return previousState.toBuilder() .firstPageLoading(true) .firstPageError(null) .build() if (changes instanceof PartialState.FirstPageError) return previousState.builder() .firstPageLoading(false) .firstPageError(((PartialState.FirstPageError) changes).getError()) .build(); if (changes instanceof PartialState.FirstPageLoaded) return previousState.builder() .firstPageLoading(false) .firstPageError(null) .data(((PartialState.FirstPageLoaded) changes).getData()) .build(); if (changes instanceof PartialState.PullToRefreshLoading) return previousState.builder() .pullToRefreshLoading(true) .nextPageError(null) .build(); if (changes instanceof PartialState.PullToRefreshError) return previousState.builder() .pullToRefreshLoading(false) // Hide pull to refresh indicator .pullToRefreshError(((PartialState.PullToRefreshError) changes).getError()) .build(); if (changes instanceof PartialState.PullToRefreshData) { List<FeedItem> data = new ArrayList<>(); data.addAll(((PullToRefreshData) changes).getData()); data.addAll(previousState.getData()); return previousState.builder() .pullToRefreshLoading(false) .pullToRefreshError(null) .data(data) .build(); } throw new IllegalStateException("Don't know how to reduce the partial state " + changes); }
class HomePresenter extends MviBasePresenter<HomeView, HomeViewState> { private final HomeFeedLoader feedLoader; @Override protected void bindIntents() { Observable<PartialState> loadFirstPage = ... ; Observable<PartialState> pullToRefresh = ... ; Observable<PartialState> nextPage = intent(HomeView::loadNextPageIntent) .flatMap(ignored -> feedLoader.loadNextPage() .map(items -> new PartialState.NextPageLoaded(items)) .startWith(new PartialState.NextPageLoading()) .onErrorReturn(PartialState.NexPageLoadingError::new)); Observable<PartialState> loadMoreFromCategory = intent(HomeView::loadAllProductsFromCategoryIntent) .flatMap(categoryName -> feedLoader.loadProductsOfCategory(categoryName) .map( products -> new PartialState.ProductsOfCategoryLoaded(categoryName, products)) .startWith(new PartialState.ProductsOfCategoryLoading(categoryName)) .onErrorReturn(error -> new PartialState.ProductsOfCategoryError(categoryName, error))); Observable<PartialState> allIntents = Observable.merge(loadFirstPage, pullToRefresh, nextPage, loadMoreFromCategory); HomeViewState initialState = ... ; Observable<HomeViewState> stateObservable = allIntents.scan(initialState, this::viewStateReducer) subscribeViewState(stateObservable, HomeView::render); } private HomeViewState viewStateReducer(HomeViewState previousState, PartialState changes){ if (changes instanceof PartialState.NextPageLoading) { return previousState.builder().nextPageLoading(true).nextPageError(null).build(); } if (changes instanceof PartialState.NexPageLoadingError) return previousState.builder() .nextPageLoading(false) .nextPageError(((PartialState.NexPageLoadingError) changes).getError()) .build(); if (changes instanceof PartialState.NextPageLoaded) { List<FeedItem> data = new ArrayList<>(); data.addAll(previousState.getData()); data.addAll(((PartialState.NextPageLoaded) changes).getData()); return previousState.builder().nextPageLoading(false).nextPageError(null).data(data).build(); } if (changes instanceof PartialState.ProductsOfCategoryLoading) { int indexLoadMoreItem = findAdditionalItems(categoryName, previousState.getData()); AdditionalItemsLoadable ail = (AdditionalItemsLoadable) previousState.getData().get(indexLoadMoreItem); AdditionalItemsLoadable itemsThatIndicatesError = ail.builder() .loading(true).error(null).build(); List<FeedItem> data = new ArrayList<>(); data.addAll(previousState.getData()); data.set(indexLoadMoreItem, itemsThatIndicatesError); return previousState.builder().data(data).build(); } if (changes instanceof PartialState.ProductsOfCategoryLoadingError) { int indexLoadMoreItem = findAdditionalItems(categoryName, previousState.getData()); AdditionalItemsLoadable ail = (AdditionalItemsLoadable) previousState.getData().get(indexLoadMoreItem); AdditionalItemsLoadable itemsThatIndicatesError = ail.builder().loading(false).error( ((ProductsOfCategoryLoadingError)changes).getError()).build(); List<FeedItem> data = new ArrayList<>(); data.addAll(previousState.getData()); data.set(indexLoadMoreItem, itemsThatIndicatesError); return previousState.builder().data(data).build(); } if (changes instanceof PartialState.ProductsOfCategoryLoaded) { String categoryName = (ProductsOfCategoryLoaded) changes.getCategoryName(); int indexLoadMoreItem = findAdditionalItems(categoryName, previousState.getData()); int indexOfSectionHeader = findSectionHeader(categoryName, previousState.getData()); List<FeedItem> data = new ArrayList<>(); data.addAll(previousState.getData()); removeItems(data, indexOfSectionHeader, indexLoadMoreItem); // ( ) data.addAll(indexOfSectionHeader + 1,((ProductsOfCategoryLoaded) changes).getData()); return previousState.builder().data(data).build(); } throw new IllegalStateException("Don't know how to reduce the partial state " + changes); } }
Source: https://habr.com/ru/post/348908/
All Articles