Commit b18b3d12 authored by Vladislav Bogdashkin's avatar Vladislav Bogdashkin 🎣

added pull-to-request articles pages

parent 00b2983d
...@@ -15,6 +15,7 @@ import io.reactivex.schedulers.Schedulers ...@@ -15,6 +15,7 @@ import io.reactivex.schedulers.Schedulers
import timber.log.Timber import timber.log.Timber
import timber.log.Timber.e import timber.log.Timber.e
import javax.inject.Inject import javax.inject.Inject
import kotlin.math.floor
/** /**
* Created by Vladislav Bogdashkin on 29.10.2019. * Created by Vladislav Bogdashkin on 29.10.2019.
...@@ -26,15 +27,15 @@ class FeedsContractModule @Inject constructor( ...@@ -26,15 +27,15 @@ class FeedsContractModule @Inject constructor(
private val db: IDb private val db: IDb
): FeedsContract { ): FeedsContract {
override fun fetchFeeds(): Observable<List<FeedModel>> = override fun fetchFeeds(): Observable<List<FeedModel>> =
fetchAllFeeds().doOnError (::e) fetchAllFeeds().doOnError (::e)
override fun fetchFeedObservable(id: Int): Observable<ArticlesPreviewModel> = override fun fetchFeedObservable(id: Int): Observable<ArticlesPreviewModel> =
fetchArticles(id) fetchArticles(id)
override fun fetchFeedObservable(id: Int,pageSize:Int, startIndex:Int)
: Observable<ArticlesPreviewModel> = fetchArticles(id,pageSize,startIndex)
override fun getArticle(id: Int): Observable<ArticlePreviewModel> { override fun getArticle(id: Int): Observable<ArticlePreviewModel> {
TODO("not implemented") //To change body of created functions use File | Settings | File Templates. TODO("not implemented") //To change body of created functions use File | Settings | File Templates.
} }
...@@ -44,14 +45,14 @@ class FeedsContractModule @Inject constructor( ...@@ -44,14 +45,14 @@ class FeedsContractModule @Inject constructor(
} }
private fun fetchArticlessApi(feedId:Int): Observable<List<ArticleEntity>>? { private fun fetchArticlessApi(feedId:Int,pageSize:Int, startIndex:Int): Observable<List<ArticleEntity>>? {
val feed = db.getFeed(feedId)?.firstOrNull() val feed = db.getFeed(feedId)?.firstOrNull()
if (feed == null) { if (feed == null) {
e("Unknown feedId: $feedId - cann't resolve feed_alias!") e("Unknown feedId: $feedId - cann't resolve feed_alias!")
throw CustomApiException.UnknownCustomApiException(-1,null,"Unknown Feed!") throw CustomApiException.UnknownCustomApiException(-1,null,"Unknown Feed!")
} }
else { else {
return api.getArticlesPage(feed.alias, 11, 1) return api.getArticlesPage(feed.alias, pageSize, floor(startIndex.toDouble()/pageSize.toDouble()).toInt())
.doOnNext { Timber.d("raw0 $it") } .doOnNext { Timber.d("raw0 $it") }
.map { it.items } .map { it.items }
.map { fromRaw(it, feedId) } .map { fromRaw(it, feedId) }
...@@ -61,21 +62,21 @@ class FeedsContractModule @Inject constructor( ...@@ -61,21 +62,21 @@ class FeedsContractModule @Inject constructor(
} }
private fun fetchArticlesDb(feedId:Int): Observable<MutableList<ArticleEntity>>? { private fun fetchArticlesDb(feedId:Int,pageSize:Int, startIndex:Int): Observable<MutableList<ArticleEntity>>? {
val feed = db.getFeed(feedId)?.firstOrNull() val feed = db.getFeed(feedId)?.firstOrNull()
if (feed == null) { if (feed == null) {
e("Unknown feedId: $feedId - cann't resolve feed_alias!") e("Unknown feedId: $feedId - cann't resolve feed_alias!")
throw CustomApiException.UnknownCustomApiException(-1, null, "Unknown Feed!") throw CustomApiException.UnknownCustomApiException(-1, null, "Unknown Feed!")
} else return db.fetchArticles(feed.alias, 11, 1) } else return db.fetchArticles(feed.alias, pageSize, startIndex)
.toList() .toList()
.toObservable() .toObservable()
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
} }
private fun fetchArticles(feedId:Int): Observable<ArticlesPreviewModel> = private fun fetchArticles(feedId:Int,pageSize:Int = 10, startIndex:Int = 0): Observable<ArticlesPreviewModel> =
Observable.mergeDelayError( Observable.mergeDelayError(
arrayListOf(fetchArticlessApi(feedId),fetchArticlesDb(feedId)) arrayListOf(fetchArticlessApi(feedId,pageSize,startIndex),fetchArticlesDb(feedId,pageSize,startIndex))
).map { fromEntity(feedId,it) } ).take(1).map { fromEntity(feedId,it) }
private fun fetchFeedsApi() = private fun fetchFeedsApi() =
......
package com.biganto.visual.roompark.di.dagger package com.biganto.visual.roompark.di.dagger
import android.app.Application import android.app.Application
import com.biganto.visual.androidplayer.data.repository.local.ILocalStore
import com.biganto.visual.roompark.base.RoomParkApplication import com.biganto.visual.roompark.base.RoomParkApplication
import com.biganto.visual.roompark.data.local.UserState
import com.biganto.visual.roompark.data.repository.api.IRoomParkApi
import com.biganto.visual.roompark.data.repository.db.IDb
import com.biganto.visual.roompark.data.repository.mapper.fromRaw
import com.biganto.visual.roompark.domain.contract.AuthContract
import com.biganto.visual.roompark.domain.model.AuthInfoModel
import com.biganto.visual.roompark.domain.model.fromEntity
import dagger.Binds import dagger.Binds
import dagger.Module import dagger.Module
import io.reactivex.Completable
import io.reactivex.Observable
import timber.log.Timber
import javax.inject.Inject
/** /**
* Created by Vladislav Bogdashkin on 13.06.2018. * Created by Vladislav Bogdashkin on 13.06.2018.
...@@ -33,7 +21,7 @@ const val ESTATES_CACHE_LIMIT_SECONDS_INACTIVE = 200L ...@@ -33,7 +21,7 @@ const val ESTATES_CACHE_LIMIT_SECONDS_INACTIVE = 200L
const val FILES_CACHE_LIMIT_SIZE = 10000 const val FILES_CACHE_LIMIT_SIZE = 10000
const val FILES_CACHE_LIMIT_SECONDS_INACTIVE = 60L const val FILES_CACHE_LIMIT_SECONDS_INACTIVE = 60L
const val DATABASE_VERSION = 6 const val DATABASE_VERSION = 7
@Module @Module
abstract class AppModule{ abstract class AppModule{
......
...@@ -14,4 +14,9 @@ interface FeedsContract{ ...@@ -14,4 +14,9 @@ interface FeedsContract{
fun fetchFeeds(): Observable<List<FeedModel>> fun fetchFeeds(): Observable<List<FeedModel>>
fun getArticle(id:Int): Observable<ArticlePreviewModel> fun getArticle(id:Int): Observable<ArticlePreviewModel>
fun fetchFeedObservable(id: Int): Observable<ArticlesPreviewModel> fun fetchFeedObservable(id: Int): Observable<ArticlesPreviewModel>
fun fetchFeedObservable(
id: Int,
pageSize: Int,
startIndex: Int
): Observable<ArticlesPreviewModel>
} }
\ No newline at end of file
...@@ -4,6 +4,7 @@ import com.biganto.visual.roompark.domain.model.* ...@@ -4,6 +4,7 @@ import com.biganto.visual.roompark.domain.model.*
import com.biganto.visual.roompark.domain.use_case.FeedUseCase import com.biganto.visual.roompark.domain.use_case.FeedUseCase
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.Single import io.reactivex.Single
import io.reactivex.schedulers.Schedulers
import java.util.* import java.util.*
import javax.inject.Inject import javax.inject.Inject
...@@ -29,7 +30,8 @@ class FeedsInteractor @Inject constructor( ...@@ -29,7 +30,8 @@ class FeedsInteractor @Inject constructor(
// else -> error("unknown feedId") // else -> error("unknown feedId")
// } // }
// ) // )
fun fetchArticles(feedId: Int,pageSize:Int,startIndex:Int): Observable<ArticlesPreviewModel> =
feedsUseCase.fetchArticlesPage(feedId,pageSize,startIndex)
fun fetchAlbums(): Single<List<AlbumPreviewModel>> = Single.just(albumsPreviews) fun fetchAlbums(): Single<List<AlbumPreviewModel>> = Single.just(albumsPreviews)
......
...@@ -16,6 +16,9 @@ class FeedUseCase @Inject constructor( ...@@ -16,6 +16,9 @@ class FeedUseCase @Inject constructor(
fun getArticles(feedId:Int) = fun getArticles(feedId:Int) =
contract.fetchFeedObservable(feedId) contract.fetchFeedObservable(feedId)
fun fetchArticlesPage(feedId:Int,pageSize:Int,startIndex:Int) =
contract.fetchFeedObservable(feedId,pageSize,startIndex)
} }
\ No newline at end of file
package com.biganto.visual.roompark.presentation.screen.feeds package com.biganto.visual.roompark.presentation.screen.feeds
import com.biganto.visual.roompark.conductor.BigantoBaseContract import com.biganto.visual.roompark.conductor.BigantoBaseContract
import com.biganto.visual.roompark.domain.model.FeedModel
import io.reactivex.Observable import io.reactivex.Observable
/** /**
...@@ -9,6 +10,7 @@ import io.reactivex.Observable ...@@ -9,6 +10,7 @@ import io.reactivex.Observable
interface FeedsScreen : BigantoBaseContract<FeedsScreenViewState> { interface FeedsScreen : BigantoBaseContract<FeedsScreenViewState> {
fun feedsTabSelected(): Observable<Int> fun feedsTabSelected(): Observable<Int>
fun requsetsNewArticles(): Observable<Pair<FeedModel, Int>>
} }
......
...@@ -22,6 +22,7 @@ import com.google.android.material.button.MaterialButton ...@@ -22,6 +22,7 @@ import com.google.android.material.button.MaterialButton
import com.google.android.material.tabs.TabLayout import com.google.android.material.tabs.TabLayout
import com.google.android.material.textview.MaterialTextView import com.google.android.material.textview.MaterialTextView
import com.jakewharton.rxbinding3.material.selections import com.jakewharton.rxbinding3.material.selections
import com.jakewharton.rxbinding3.recyclerview.scrollStateChanges
import com.jakewharton.rxbinding3.view.clicks import com.jakewharton.rxbinding3.view.clicks
import io.reactivex.Observable import io.reactivex.Observable
import io.reactivex.android.schedulers.AndroidSchedulers import io.reactivex.android.schedulers.AndroidSchedulers
...@@ -70,6 +71,18 @@ class FeedsScreenController : ...@@ -70,6 +71,18 @@ class FeedsScreenController :
lateinit var feedsBlockView:ViewGroup lateinit var feedsBlockView:ViewGroup
override fun requsetsNewArticles() =
feedsRecyclerView.scrollStateChanges()
.filter { it == RecyclerView.SCROLL_STATE_IDLE}
.map { feedsRecyclerView.layoutManager as LinearLayoutManager }
.map { it.findLastCompletelyVisibleItemPosition() }
.doOnNext { Timber.d("Visible items $it of ${(feedsRecyclerView.adapter?.itemCount?:0) - 3}") }
.filter { it>(feedsRecyclerView.adapter?.itemCount?:0)-2 }
.map { feedsRecyclerView.adapter?.itemCount?:0 }
.map { Pair(storedFeedsList[feedsTabs.selectedTabPosition],it)}
.debounce(120L,TimeUnit.MILLISECONDS)
.observeOn(AndroidSchedulers.mainThread())
private fun bindRecycler(){ private fun bindRecycler(){
feedsRecyclerView.isNestedScrollingEnabled = true feedsRecyclerView.isNestedScrollingEnabled = true
feedsRecyclerView.layoutManager = feedsRecyclerView.layoutManager =
...@@ -120,6 +133,7 @@ class FeedsScreenController : ...@@ -120,6 +133,7 @@ class FeedsScreenController :
is FeedsScreenViewState.Idle -> render(viewState) is FeedsScreenViewState.Idle -> render(viewState)
is FeedsScreenViewState.FeedsPages -> render(viewState) is FeedsScreenViewState.FeedsPages -> render(viewState)
is FeedsScreenViewState.AlbumsPages -> render(viewState) is FeedsScreenViewState.AlbumsPages -> render(viewState)
is FeedsScreenViewState.ArticlesScrollPage -> render(viewState)
is FeedsScreenViewState.CamsList -> render(viewState) is FeedsScreenViewState.CamsList -> render(viewState)
is FeedsScreenViewState.GetFeedArticlesPreview -> render(viewState) is FeedsScreenViewState.GetFeedArticlesPreview -> render(viewState)
is FeedsScreenViewState.RestoreView -> render(viewState) is FeedsScreenViewState.RestoreView -> render(viewState)
...@@ -161,6 +175,10 @@ class FeedsScreenController : ...@@ -161,6 +175,10 @@ class FeedsScreenController :
(devProgressRecyclerView.adapter as AlbumsPreviewAdapter).setItems(viewState.items) (devProgressRecyclerView.adapter as AlbumsPreviewAdapter).setItems(viewState.items)
} }
private fun render(viewState: FeedsScreenViewState.ArticlesScrollPage){
(feedsRecyclerView.adapter as ArticlesPreviewAdapter).addItems(viewState.items)
}
private fun render(viewState: FeedsScreenViewState.GetFeedArticlesPreview){ private fun render(viewState: FeedsScreenViewState.GetFeedArticlesPreview){
(feedsRecyclerView.adapter as ArticlesPreviewAdapter).setItems(viewState.items) (feedsRecyclerView.adapter as ArticlesPreviewAdapter).setItems(viewState.items)
} }
......
...@@ -12,6 +12,7 @@ import javax.inject.Inject ...@@ -12,6 +12,7 @@ import javax.inject.Inject
* Created by Vladislav Bogdashkin on 30.09.2019. * Created by Vladislav Bogdashkin on 30.09.2019.
*/ */
const val FEED_PREVIEW_PAGE_SIZE = 10
class FeedsScreenPresenter @Inject constructor( class FeedsScreenPresenter @Inject constructor(
private val interactor: FeedsInteractor private val interactor: FeedsInteractor
...@@ -62,11 +63,19 @@ class FeedsScreenPresenter @Inject constructor( ...@@ -62,11 +63,19 @@ class FeedsScreenPresenter @Inject constructor(
FeedsScreenViewState.GetFeedArticlesPreview(it.articles.toList()) FeedsScreenViewState.GetFeedArticlesPreview(it.articles.toList())
} }
val getNewArticlesPage = intent(FeedsScreen::requsetsNewArticles)
.flatMap { interactor.fetchArticles(it.first.feedId, FEED_PREVIEW_PAGE_SIZE,it.second) }
.map {
restoreModel.articles = it.articles
FeedsScreenViewState.ArticlesScrollPage(it.articles.toList())
}
val state = restoreStateObservable val state = restoreStateObservable
.mergeWith(fetchFeeds) .mergeWith(fetchFeeds)
.mergeWith(getFeedArticlesPreview) .mergeWith(getFeedArticlesPreview)
.mergeWith(fetchAlbums) .mergeWith(fetchAlbums)
.mergeWith(fetchCams) .mergeWith(fetchCams)
.mergeWith(getNewArticlesPage)
.doOnError{ Timber.e(it)} .doOnError{ Timber.e(it)}
.subscribeOn(Schedulers.io()) .subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread()) .observeOn(AndroidSchedulers.mainThread())
......
...@@ -19,6 +19,7 @@ sealed class FeedsScreenViewState : BigantoBaseViewState() { ...@@ -19,6 +19,7 @@ sealed class FeedsScreenViewState : BigantoBaseViewState() {
class CamsList(val items:List<WebCamModel>) : FeedsScreenViewState() class CamsList(val items:List<WebCamModel>) : FeedsScreenViewState()
class GetFeedArticlesPreview(val items:List<ArticlePreviewModel>) : FeedsScreenViewState() class GetFeedArticlesPreview(val items:List<ArticlePreviewModel>) : FeedsScreenViewState()
class ArticlesScrollPage(val items:List<ArticlePreviewModel>) : FeedsScreenViewState()
class RestoreView(val restore:RestoreModel) : FeedsScreenViewState() class RestoreView(val restore:RestoreModel) : FeedsScreenViewState()
......
...@@ -20,6 +20,8 @@ class AlbumsPreviewAdapter : CommonRecyclerAdapter<AlbumCardViewHolder,AlbumPrev ...@@ -20,6 +20,8 @@ class AlbumsPreviewAdapter : CommonRecyclerAdapter<AlbumCardViewHolder,AlbumPrev
override val vhKlazz = AlbumCardViewHolder::class override val vhKlazz = AlbumCardViewHolder::class
override fun getVhLayout(): Int = R.layout.estate_card_viewholder override fun getVhLayout(): Int = R.layout.estate_card_viewholder
} }
......
...@@ -40,6 +40,11 @@ abstract class CommonRecyclerAdapter<VH:CommonViewHolder<M>,M:Any> : RecyclerVie ...@@ -40,6 +40,11 @@ abstract class CommonRecyclerAdapter<VH:CommonViewHolder<M>,M:Any> : RecyclerVie
notifyDataSetChanged() notifyDataSetChanged()
} }
fun addItems(items:List<M>){
this.list.addAll(list)
notifyItemRangeInserted(list.size-items.size,items.size)
}
protected abstract val vhKlazz : KClass<VH> protected abstract val vhKlazz : KClass<VH>
@LayoutRes @LayoutRes
......
Markdown is supported
0% or
You are about to add 0 people to the discussion. Proceed with caution.
Finish editing this message first!
Please register or to comment