Permalink
Browse files

Move out Watched to seperate fragment too

  • Loading branch information...
chrisbanes committed Feb 5, 2019
1 parent ed70478 commit 16310e4115b2ba4d5cd59efeb2d95f7f11fc790e
@@ -176,6 +176,7 @@ dependencies {
implementation Libs.AndroidX.preference
implementation Libs.AndroidX.constraintlayout
implementation Libs.AndroidX.coreKtx
implementation Libs.AndroidX.Fragment.fragmentKtx

implementation Libs.Google.material

@@ -16,11 +16,16 @@

package app.tivi.home.library

import app.tivi.home.library.followed.FollowedBuilder
import app.tivi.home.library.watched.WatchedBuilder
import dagger.Module
import dagger.android.ContributesAndroidInjector

@Module
internal abstract class LibraryBuilder {
@ContributesAndroidInjector
@ContributesAndroidInjector(modules = [
FollowedBuilder::class,
WatchedBuilder::class
])
internal abstract fun libraryFragment(): LibraryFragment
}
@@ -20,10 +20,7 @@ import androidx.annotation.DrawableRes
import androidx.annotation.StringRes
import app.tivi.R

enum class LibraryFilter(
@StringRes val labelResource: Int,
@DrawableRes val iconResource: Int
) {
enum class LibraryFilter(@StringRes val labelResource: Int, @DrawableRes val iconResource: Int) {
FOLLOWED(R.string.library_followed_shows, R.drawable.ic_heart_24dp),
WATCHED(R.string.library_watched, R.drawable.ic_clock_24dp)
}
@@ -23,54 +23,31 @@ import android.view.View
import android.view.ViewGroup
import androidx.browser.customtabs.CustomTabsIntent
import androidx.core.net.toUri
import androidx.fragment.app.commit
import androidx.lifecycle.ViewModelProviders
import app.tivi.R
import app.tivi.data.resultentities.FollowedShowEntryWithShow
import app.tivi.data.resultentities.WatchedShowEntryWithShow
import app.tivi.databinding.FragmentLibraryBinding
import app.tivi.home.HomeActivity
import app.tivi.home.HomeNavigator
import app.tivi.home.HomeNavigatorViewModel
import app.tivi.home.library.followed.FollowedEpoxyController
import app.tivi.home.library.followed.FollowedFragment
import app.tivi.home.library.watched.WatchedFragment
import app.tivi.trakt.TraktAuthState
import app.tivi.ui.ListItemSharedElementHelper
import app.tivi.ui.SpacingItemDecorator
import app.tivi.ui.epoxy.EmptyEpoxyController
import app.tivi.ui.glide.GlideApp
import app.tivi.ui.glide.asGlideTarget
import app.tivi.util.GridToGridTransitioner
import app.tivi.util.TiviDateFormatter
import app.tivi.util.TiviMvRxFragment
import com.airbnb.epoxy.EpoxyController
import com.airbnb.mvrx.fragmentViewModel
import com.airbnb.mvrx.withState
import javax.inject.Inject

class LibraryFragment : TiviMvRxFragment() {

private lateinit var homeNavigator: HomeNavigator

private lateinit var binding: FragmentLibraryBinding

private lateinit var textCreator: LibraryTextCreator

private val viewModel: LibraryViewModel by fragmentViewModel()
@Inject lateinit var libraryViewModelFactory: LibraryViewModel.Factory

@Inject lateinit var dateFormatter: TiviDateFormatter

private val listItemSharedElementHelper by lazy(LazyThreadSafetyMode.NONE) {
ListItemSharedElementHelper(binding.libraryRv) { it.findViewById(R.id.show_poster) }
}

private var controller: EpoxyController = EmptyEpoxyController
set(value) {
if (field != value) {
field = value
binding.libraryRv.setController(value)
}
}

private val filterController = LibraryFiltersEpoxyController(object : LibraryFiltersEpoxyController.Callbacks {
override fun onFilterSelected(filter: LibraryFilter) {
closeFilterPanel()
@@ -95,8 +72,6 @@ class LibraryFragment : TiviMvRxFragment() {
super.onViewCreated(view, savedInstanceState)
postponeEnterTransition()

textCreator = LibraryTextCreator(requireContext())

view.systemUiVisibility = View.SYSTEM_UI_FLAG_LAYOUT_STABLE or View.SYSTEM_UI_FLAG_LAYOUT_FULLSCREEN

view.setOnApplyWindowInsetsListener { _, insets ->
@@ -107,18 +82,12 @@ class LibraryFragment : TiviMvRxFragment() {
insets
}

binding.libraryRv.apply {
addItemDecoration(SpacingItemDecorator(paddingLeft))
}

binding.libraryFiltersRv.adapter = filterController.adapter

binding.libraryToolbar.run {
inflateMenu(R.menu.home_toolbar)
setOnMenuItemClickListener(this@LibraryFragment::onMenuItemClicked)
}

binding.librarySwipeRefresh.setOnRefreshListener(viewModel::refresh)
}

override fun invalidate() {
@@ -133,24 +102,20 @@ class LibraryFragment : TiviMvRxFragment() {

when (state.filter) {
LibraryFilter.WATCHED -> {
val c = (controller as? LibraryWatchedEpoxyController ?: createWatchedController())
if (state.watchedShows != null) {
// PagingEpoxyController does not like being updated before it has a list
c.tmdbImageUrlProvider = state.tmdbImageUrlProvider
c.isEmpty = state.isEmpty
c.submitList(state.watchedShows)
val fragment = childFragmentManager.findFragmentById(R.id.library_content)
if (fragment !is WatchedFragment) {
childFragmentManager.commit {
add(WatchedFragment(), null)
}
}
controller = c
}
LibraryFilter.FOLLOWED -> {
val c = controller as? FollowedEpoxyController ?: createFollowedController()
if (state.followedShows != null) {
// PagingEpoxyController does not like being updated before it has a list
c.tmdbImageUrlProvider = state.tmdbImageUrlProvider
c.isEmpty = state.isEmpty
c.submitList(state.followedShows)
val fragment = childFragmentManager.findFragmentById(R.id.library_content)
if (fragment !is FollowedFragment) {
childFragmentManager.commit {
add(FollowedFragment(), null)
}
}
controller = c
}
}

@@ -181,32 +146,16 @@ class LibraryFragment : TiviMvRxFragment() {
}

internal fun scrollToTop() {
binding.libraryRv.stopScroll()
binding.libraryRv.smoothScrollToPosition(0)
when (val f = childFragmentManager.findFragmentById(R.id.library_content)) {
is WatchedFragment -> f.scrollToTop()
is FollowedFragment -> f.scrollToTop()
}
}

private fun closeFilterPanel() {
binding.libraryMotion.transitionToStart()
}

private fun createWatchedController() = LibraryWatchedEpoxyController(
object : LibraryWatchedEpoxyController.Callbacks {
override fun onItemClicked(item: WatchedShowEntryWithShow) {
viewModel.onItemPostedClicked(homeNavigator, item.show,
listItemSharedElementHelper.createForItem(item, "poster")
)
}
}, textCreator, dateFormatter)

private fun createFollowedController() = FollowedEpoxyController(
object : FollowedEpoxyController.Callbacks {
override fun onItemClicked(item: FollowedShowEntryWithShow) {
viewModel.onItemPostedClicked(homeNavigator, item.show,
listItemSharedElementHelper.createForItem(item, "poster")
)
}
}, textCreator)

private fun onMenuItemClicked(item: MenuItem) = when (item.itemId) {
R.id.home_menu_user_avatar -> {
viewModel.onProfileItemClicked()
@@ -16,75 +16,41 @@

package app.tivi.home.library

import androidx.paging.DataSource
import androidx.paging.PagedList
import androidx.paging.RxPagedListBuilder
import app.tivi.SharedElementHelper
import app.tivi.data.entities.TiviShow
import app.tivi.data.resultentities.EntryWithShow
import app.tivi.home.HomeNavigator
import app.tivi.home.HomeViewModel
import app.tivi.home.library.LibraryFilter.FOLLOWED
import app.tivi.home.library.LibraryFilter.WATCHED
import app.tivi.interactors.SyncFollowedShows
import app.tivi.interactors.UpdateUserDetails
import app.tivi.interactors.UpdateWatchedShows
import app.tivi.interactors.launchInteractor
import app.tivi.tmdb.TmdbManager
import app.tivi.trakt.TraktAuthState
import app.tivi.trakt.TraktManager
import app.tivi.util.AppRxSchedulers
import app.tivi.util.Logger
import app.tivi.util.RxLoadingCounter
import app.tivi.util.TiviMvRxViewModel
import com.airbnb.mvrx.FragmentViewModelContext
import com.airbnb.mvrx.MvRxViewModelFactory
import com.airbnb.mvrx.ViewModelContext
import com.squareup.inject.assisted.Assisted
import com.squareup.inject.assisted.AssistedInject
import io.reactivex.Observable
import io.reactivex.disposables.Disposable
import io.reactivex.rxkotlin.plusAssign
import net.openid.appauth.AuthorizationService
import java.util.concurrent.TimeUnit

class LibraryViewModel @AssistedInject constructor(
@Assisted initialState: LibraryViewState,
private val schedulers: AppRxSchedulers,
private val updateWatchedShows: UpdateWatchedShows,
private val syncFollowedShows: SyncFollowedShows,
schedulers: AppRxSchedulers,
private val traktManager: TraktManager,
tmdbManager: TmdbManager,
private val updateUserDetails: UpdateUserDetails,
private val logger: Logger
private val updateUserDetails: UpdateUserDetails
) : TiviMvRxViewModel<LibraryViewState>(initialState), HomeViewModel {
private val loadingState = RxLoadingCounter()

private var refreshDisposable: Disposable? = null

init {
setState {
copy(allowedFilters = LibraryFilter.values().asList(), filter = DEFAULT_FILTER)
}

loadingState.observable.execute {
copy(isLoading = it() ?: false)
}

tmdbManager.imageProviderObservable
.delay(50, TimeUnit.MILLISECONDS, schedulers.io)
.execute { copy(tmdbImageUrlProvider = it() ?: tmdbImageUrlProvider) }

dataSourceToObservable(updateWatchedShows.dataSourceFactory())
.execute {
copy(watchedShows = it())
}

dataSourceToObservable(syncFollowedShows.dataSourceFactory())
.execute {
copy(followedShows = it())
}

updateUserDetails.setParams(UpdateUserDetails.Params("me"))
updateUserDetails.observe()
.toObservable()
@@ -95,55 +61,9 @@ class LibraryViewModel @AssistedInject constructor(
if (it == TraktAuthState.LOGGED_IN) {
scope.launchInteractor(updateUserDetails, UpdateUserDetails.ExecuteParams(false))
}
}.execute {
copy(authState = it() ?: TraktAuthState.LOGGED_OUT)
}
.execute { copy(authState = it() ?: TraktAuthState.LOGGED_OUT) }
}

private fun <T : EntryWithShow<*>> dataSourceToObservable(f: DataSource.Factory<Int, T>): Observable<PagedList<T>> {
return RxPagedListBuilder(f, PAGING_CONFIG)
.setBoundaryCallback(object : PagedList.BoundaryCallback<T>() {
override fun onZeroItemsLoaded() = setState { copy(isEmpty = true) }
override fun onItemAtEndLoaded(itemAtEnd: T) = setState { copy(isEmpty = false) }
override fun onItemAtFrontLoaded(itemAtFront: T) = setState { copy(isEmpty = false) }
})
.setFetchScheduler(schedulers.io)
.setNotifyScheduler(schedulers.main)
.buildObservable()
}

fun refresh() {
refreshDisposable?.let {
it.dispose()
disposables.remove(it)
}
refreshDisposable = null

disposables += traktManager.state
.filter { it == TraktAuthState.LOGGED_IN }
.firstOrError()
.subscribe({ refreshFilter() }, logger::e)
.also { refreshDisposable = it }
}

private fun refreshFilter() {
withState {
when (it.filter) {
FOLLOWED -> {
loadingState.addLoader()
scope.launchInteractor(syncFollowedShows, SyncFollowedShows.ExecuteParams(false))
.invokeOnCompletion {
loadingState.removeLoader()
}
}
WATCHED -> {
loadingState.addLoader()
scope.launchInteractor(updateWatchedShows, UpdateWatchedShows.ExecuteParams(false))
.invokeOnCompletion {
loadingState.removeLoader()
}
}
}
}
}

fun onFilterSelected(filter: LibraryFilter) {
@@ -152,10 +72,6 @@ class LibraryViewModel @AssistedInject constructor(
}
}

fun onItemPostedClicked(navigator: HomeNavigator, show: TiviShow, sharedElements: SharedElementHelper? = null) {
navigator.showShowDetails(show, sharedElements)
}

override fun onProfileItemClicked() {
// TODO
}
@@ -173,11 +89,6 @@ class LibraryViewModel @AssistedInject constructor(

companion object : MvRxViewModelFactory<LibraryViewModel, LibraryViewState> {
private val DEFAULT_FILTER = FOLLOWED
private val PAGING_CONFIG = PagedList.Config.Builder()
.setPageSize(60)
.setPrefetchDistance(20)
.setEnablePlaceholders(false)
.build()

override fun create(viewModelContext: ViewModelContext, state: LibraryViewState): LibraryViewModel? {
val fragment: LibraryFragment = (viewModelContext as FragmentViewModelContext).fragment()
@@ -16,10 +16,7 @@

package app.tivi.home.library

import androidx.paging.PagedList
import app.tivi.data.entities.TraktUser
import app.tivi.data.resultentities.FollowedShowEntryWithShow
import app.tivi.data.resultentities.WatchedShowEntryWithShow
import app.tivi.tmdb.TmdbImageUrlProvider
import app.tivi.trakt.TraktAuthState
import com.airbnb.mvrx.MvRxState
@@ -28,10 +25,6 @@ data class LibraryViewState(
val allowedFilters: List<LibraryFilter> = emptyList(),
val filter: LibraryFilter = LibraryFilter.FOLLOWED,
val tmdbImageUrlProvider: TmdbImageUrlProvider = TmdbImageUrlProvider(),
val isLoading: Boolean = false,
val isEmpty: Boolean = true,
val followedShows: PagedList<FollowedShowEntryWithShow>? = null,
val watchedShows: PagedList<WatchedShowEntryWithShow>? = null,
val user: TraktUser? = null,
val authState: TraktAuthState = TraktAuthState.LOGGED_OUT
) : MvRxState
@@ -58,7 +58,7 @@ class FollowedFragment : TiviMvRxFragment() {
}

override fun onCreateView(inflater: LayoutInflater, container: ViewGroup?, savedInstanceState: Bundle?): View? {
binding = app.tivi.databinding.FragmentLibraryFollowedBinding.inflate(inflater, container, false)
binding = FragmentLibraryFollowedBinding.inflate(inflater, container, false)
binding.setLifecycleOwner(viewLifecycleOwner)
return binding.root
}
Oops, something went wrong.

0 comments on commit 16310e4

Please sign in to comment.