Skip to content
Open
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
1 change: 0 additions & 1 deletion anilist/build.gradle
Original file line number Diff line number Diff line change
Expand Up @@ -42,7 +42,6 @@ android {
}

dependencies {

implementation "org.jetbrains.kotlin:kotlin-stdlib:$kotlin_version"
implementation 'androidx.core:core-ktx:1.5.0'
implementation 'androidx.appcompat:appcompat:1.3.0'
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,7 +2,7 @@ package com.huchihaitachi.anilist.di.moule

import com.huchihaitachi.anilist.di.scope.AnilistScope
import com.huchihaitachi.anilist.presentation.AnilistViewState
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.NOT_LOADING
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.VIEW_CONTENT
import com.huchihaitachi.anilist.presentation.AnilistViewState.PageState
import dagger.Module
import dagger.Provides
Expand All @@ -14,7 +14,7 @@ interface AnilistModule {
@AnilistScope
@Provides
fun provideAnilistState(): AnilistViewState = AnilistViewState(
NOT_LOADING,
VIEW_CONTENT,
null,
PageState(currentPage = 0, hasNextPage = true),
null
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -2,9 +2,7 @@ package com.huchihaitachi.anilist.presentation

import android.os.Bundle
import android.view.LayoutInflater
import android.view.MotionEvent
import android.view.View
import android.view.View.OnTouchListener
import android.view.ViewGroup
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
Expand All @@ -17,7 +15,7 @@ import com.huchihaitachi.anilist.R
import com.huchihaitachi.anilist.databinding.ControllerAnilistBinding
import com.huchihaitachi.anilist.di.AnilistSubcomponentProvider
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.PAGE
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.RELOAD
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.REFRESH
import com.huchihaitachi.anilist.presentation.animeList.AnimeEpoxyController
import com.huchihaitachi.base.domain.localized
import com.huchihaitachi.base.domain.stringRes
Expand All @@ -36,11 +34,11 @@ class AnilistController : Controller(), AnilistView {
private val binding: ControllerAnilistBinding
get() = _binding!!
private val _loadAnimePage: PublishSubject<Unit> = PublishSubject.create()
override val loadAnimePage: Observable<Unit>
override val loadPage: Observable<Unit>
get() = _loadAnimePage
private val _reload: PublishSubject<Unit> = PublishSubject.create()
override val reload: Observable<Unit>
get() = _reload
private val _refresh: PublishSubject<Unit> = PublishSubject.create()
override val refresh: Observable<Unit>
get() = _refresh
private val _showDetails: PublishSubject<Int> = PublishSubject.create()
override val showDetails: Observable<Int>
get() = _showDetails
Expand All @@ -55,7 +53,7 @@ class AnilistController : Controller(), AnilistView {
binding.animeListL.pageLoadingPb.visible = state.loading == PAGE
binding.animeListL.totalFooterTv.visible = state.loading != PAGE
binding.animeListL.totalFooterTv.text = resources?.getString(R.string.total, state.pageState?.anime?.size)
binding.animeListL.animeSrl.isRefreshing = state.loading == RELOAD
binding.animeListL.animeSrl.isRefreshing = state.loading == REFRESH
binding.animeListL.errorFooterTv.apply {
visible = state.error != null
state.error?.let { text = it }
Expand Down Expand Up @@ -102,7 +100,7 @@ class AnilistController : Controller(), AnilistView {

private fun setupSwipeToRefresh() {
binding.animeListL.animeSrl.setOnRefreshListener {
_reload.onNext(Unit)
_refresh.onNext(Unit)
}
}

Expand Down
Original file line number Diff line number Diff line change
@@ -1,12 +1,12 @@
package com.huchihaitachi.anilist.presentation

import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.NOT_LOADING
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.VIEW_CONTENT
import com.huchihaitachi.anilist.presentation.AnilistViewState.PageState
import com.huchihaitachi.domain.Anime

data class AnilistPartialState (
val loading: LoadingType = NOT_LOADING,
val loading: LoadingType = VIEW_CONTENT,
val details: Anime? = null,
val pageState: PageState? = null,
val error: String? = null,
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,9 @@ package com.huchihaitachi.anilist.presentation
import com.apollographql.apollo.exception.ApolloNetworkException
import com.huchihaitachi.anilist.R
import com.huchihaitachi.anilist.di.scope.AnilistScope
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.NOT_LOADING
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.VIEW_CONTENT
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.PAGE
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.RELOAD
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.REFRESH
import com.huchihaitachi.anilist.presentation.AnilistViewState.PageState
import com.huchihaitachi.base.BasePresenter
import com.huchihaitachi.base.RxSchedulers
Expand All @@ -26,73 +26,75 @@ class AnilistPresenter @Inject constructor(
private val refreshPageUseCase: RefreshPageUseCase,
private val loadAnimeUseCase: LoadAnimeUseCase,
private val getStringResourceUseCase: GetStringResourceUseCase,
anilistViewState: AnilistViewState,
rxSchedulers: RxSchedulers,
) : BasePresenter<AnilistView, AnilistViewState>(anilistViewState, rxSchedulers) {
initialViewState: AnilistViewState,
rxSchedulers: RxSchedulers
) : BasePresenter<AnilistView, AnilistViewState>(initialViewState, rxSchedulers) {

override fun bindIntents() {
view?.let { view ->
// load page
val loadPageIntent = view.loadAnimePage
val loadPageIntent = view.loadPage
.observeOn(rxSchedulers.io)
.filter { _ ->
state.pageState?.hasNextPage == true
&& state.loading != RELOAD
&& state.loading != REFRESH
&& state.loading != PAGE
&& state.loadingEnabled
}
.flatMap { _ -> loadPage(state.pageState?.currentPage!! + 1) }
//reload
val reloadIntent = view.reload
//refresh
val refreshIntent = view.refresh
.observeOn(rxSchedulers.io)
.filter { _ -> state.loading != RELOAD }
.filter { _ -> state.loading != REFRESH }
.flatMap { unit -> refreshPage() }
//details
val detailsIntent = view.showDetails
.observeOn(rxSchedulers.io)
.switchMap { id ->
loadAnimeUseCase(id)
.toObservable()
.map { details ->
AnilistPartialState(
details = details
)
}
.map { details -> state.copy(details = details) }
.onErrorReturn { throwable ->
AnilistPartialState(
state.copy(
error = when (throwable) {
is ApolloNetworkException -> getStringResourceUseCase(R.string.no_connection)
else -> throwable.message
}
)
}
}
//hide details
val hideDetailsIntent = view.hideDetails
.filter { state.loading == NOT_LOADING }
.map {
AnilistPartialState(error = state.error)
}
val intents = Observable.merge(loadPageIntent, reloadIntent, detailsIntent, hideDetailsIntent)
intents.scan(state, ::animeStateReducer)
.subscribe { s ->
state = s
}
.filter { state.loading == VIEW_CONTENT }
.map { state.copy(details = null) }
Observable.merge(loadPageIntent, refreshIntent, detailsIntent, hideDetailsIntent)
.subscribe { s -> state = s }
.let(disposables::add)
}
}

private fun loadPage(pageNum: Int): Observable<AnilistPartialState> =
private fun loadPage(pageNum: Int): Observable<AnilistViewState> =
loadPageUseCase(pageNum, PER_PAGE)
.toObservable()
.map { page ->
AnilistPartialState(
pageState = PageState(page.anime, page.currentPage, page.hasNextPage)
state.copy(
loading = VIEW_CONTENT,
pageState = PageState(
mutableListOf<Anime>().apply {
state.pageState?.anime?.let(::addAll)
page.anime?.let(::addAll)
},
page.currentPage,
page.hasNextPage
),
loadingEnabled = page.hasNextPage ?: true,
backoff = 0
)
}
.startWith(AnilistPartialState(loading = PAGE))
.startWith(state.copy(loading = PAGE))
.onErrorResumeNext(::loadPageErrorHandler)

private fun loadPageErrorHandler(throwable: Throwable): ObservableSource<AnilistPartialState> =
private fun loadPageErrorHandler(throwable: Throwable): ObservableSource<AnilistViewState> =
when (throwable) {
is ApolloNetworkException ->
Observable.timer(
Expand All @@ -101,82 +103,49 @@ class AnilistPresenter @Inject constructor(
} else {
MAX_BACKOFF
},
TimeUnit.MILLISECONDS
TimeUnit.MILLISECONDS,
rxSchedulers.computation
)
.map { AnilistPartialState() }
.map { state.copy(
error = null,
loadingEnabled = true
) }
.startWith(
AnilistPartialState(
state.copy(
loading = VIEW_CONTENT,
error = getStringResourceUseCase(R.string.no_connection),
loadingEnabled = false,
backoff = state.backoff + 1
)
)
else -> Observable.just(AnilistPartialState(error = throwable.message))
else -> Observable.just(state.copy(
loading = VIEW_CONTENT,
error = throwable.message)
)
}

private fun refreshPage(): Observable<AnilistPartialState> =
private fun refreshPage(): Observable<AnilistViewState> =
refreshPageUseCase(PER_PAGE)
.toObservable()
.map { page ->
AnilistPartialState(
pageState = PageState(page.anime, page.currentPage, page.hasNextPage)
state.copy(
loading = VIEW_CONTENT,
pageState = PageState(page.anime, page.currentPage, page.hasNextPage),
loadingEnabled = page.hasNextPage ?: true,
backoff = 0
)
}
.startWith(AnilistPartialState(loading = RELOAD))
.startWith(state.copy(loading = REFRESH))
.onErrorReturn { throwable ->
AnilistPartialState(
state.copy(
loading = VIEW_CONTENT,
error = when(throwable) {
is ApolloNetworkException -> getStringResourceUseCase(R.string.no_connection)
else -> throwable.message
}
)
}

private fun animeStateReducer(previousState: AnilistViewState, changes: AnilistPartialState) =
if (changes.error == null) {
when (previousState.loading) {
PAGE -> previousState.copy(
changes.loading,
changes.details,
changes.pageState?.copy(
mutableListOf<Anime>().apply {
previousState.pageState?.anime?.let(::addAll)
changes.pageState.anime?.let(::addAll)
}
),
changes.error,
changes.loadingEnabled,
changes.backoff
)
RELOAD ->
previousState.copy(
changes.loading,
changes.details,
changes.pageState?.copy(),
changes.error,
changes.loadingEnabled,
changes.backoff
)
NOT_LOADING -> previousState.copy(
changes.loading,
changes.details,
previousState.pageState?.copy(),
changes.error,
changes.loadingEnabled,
previousState.backoff
)
}
} else {
previousState.copy(
changes.loading,
changes.details,
previousState.pageState?.copy(),
changes.error,
changes.loadingEnabled,
changes.backoff
)
}

companion object {
const val PER_PAGE = 8
const val MAX_BACKOFF = 16000L
Expand Down
Original file line number Diff line number Diff line change
@@ -1,13 +1,11 @@
package com.huchihaitachi.anilist.presentation

import com.huchihaitachi.base.BaseView
import com.huchihaitachi.domain.Anime
import io.reactivex.Observable
import io.reactivex.subjects.PublishSubject

interface AnilistView: BaseView<AnilistViewState> {
val loadAnimePage: Observable<Unit>
val reload: Observable<Unit>
val loadPage: Observable<Unit>
val refresh: Observable<Unit>
val showDetails: Observable<Int>
val hideDetails: Observable<Unit>
}
Original file line number Diff line number Diff line change
@@ -1,11 +1,11 @@
package com.huchihaitachi.anilist.presentation

import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.NOT_LOADING
import com.huchihaitachi.anilist.presentation.AnilistViewState.LoadingType.VIEW_CONTENT
import com.huchihaitachi.base.BaseViewState
import com.huchihaitachi.domain.Anime

data class AnilistViewState(
val loading: LoadingType = NOT_LOADING,
val loading: LoadingType = VIEW_CONTENT,
val details: Anime? = null,
val pageState: PageState? = null,
val error: String? = null,
Expand All @@ -21,7 +21,7 @@ data class AnilistViewState(

enum class LoadingType {
PAGE,
RELOAD,
NOT_LOADING
REFRESH,
VIEW_CONTENT
}
}
Loading