SharedItems: show loading state when initially loading a tab, and clear remaining TODOs

Co-authored-by: Tim Krüger <t@timkrueger.me>
Signed-off-by: Álvaro Brey <alvaro.brey@nextcloud.com>
This commit is contained in:
Álvaro Brey 2022-05-20 16:31:11 +02:00 committed by Tim Krüger
parent 5310ed3f3d
commit 05340fc466
No known key found for this signature in database
GPG key ID: FECE3A7222C52A4E
2 changed files with 112 additions and 104 deletions

View file

@ -85,13 +85,13 @@ class SharedItemsActivity : AppCompatActivity() {
viewModel = ViewModelProvider(this, viewModelFactory)[SharedItemsViewModel::class.java]
viewModel.viewState.observe(this) { state ->
handleEmptyView(state)
handleEmptyLoadingView(state)
when (state) {
is SharedItemsViewModel.LoadedState -> {
val sharedMediaItems = state.items
Log.d(TAG, "Items received: $sharedMediaItems")
val showGrid = viewModel.currentItemType == SharedItemType.MEDIA
val showGrid = state.selectedType == SharedItemType.MEDIA
val layoutManager = if (showGrid) {
GridLayoutManager(this, SPAN_COUNT)
} else {
@ -104,7 +104,9 @@ class SharedItemsActivity : AppCompatActivity() {
binding.imageRecycler.adapter = adapter
binding.imageRecycler.layoutManager = layoutManager
}
is SharedItemsViewModel.TabsLoadedState -> initTabs(state.types)
is SharedItemsViewModel.TypesLoadedState -> {
initTabs(state.types)
}
else -> {
// noop
}
@ -120,25 +122,28 @@ class SharedItemsActivity : AppCompatActivity() {
}
})
viewModel.initialize(userEntity, roomToken, SharedItemType.MEDIA)
viewModel.initialize(userEntity, roomToken)
}
private fun handleEmptyView(state: SharedItemsViewModel.ViewState?) {
when (state) {
SharedItemsViewModel.NoSharedItemsState -> {
binding.emptyContainer.emptyListViewHeadline.text = getString(R.string.nc_shared_items_empty)
binding.emptyContainer.emptyListView.visibility = View.VISIBLE
binding.sharedItemsTabs.visibility = View.GONE
}
else -> {
binding.emptyContainer.emptyListView.visibility = View.GONE
binding.sharedItemsTabs.visibility = View.VISIBLE
}
private fun handleEmptyLoadingView(state: SharedItemsViewModel.ViewState?) {
binding.emptyContainer.emptyListViewHeadline.text = when (state) {
SharedItemsViewModel.NoSharedItemsState -> getString(R.string.nc_shared_items_description)
else -> getString(R.string.file_list_loading)
}
binding.emptyContainer.emptyListView.visibility = when (state) {
SharedItemsViewModel.NoSharedItemsState, is SharedItemsViewModel.LoadingItemsState -> View.VISIBLE
else -> View.GONE
}
binding.sharedItemsTabs.visibility = when (state) {
SharedItemsViewModel.NoSharedItemsState -> View.GONE
else -> View.VISIBLE
}
}
private fun initTabs(sharedItemTypes: Set<SharedItemType>) {
binding.sharedItemsTabs.removeAllTabs()
if (sharedItemTypes.contains(SharedItemType.MEDIA)) {
val tabMedia: TabLayout.Tab = binding.sharedItemsTabs.newTab()
tabMedia.tag = SharedItemType.MEDIA
@ -190,15 +195,13 @@ class SharedItemsActivity : AppCompatActivity() {
binding.sharedItemsTabs.addOnTabSelectedListener(object : TabLayout.OnTabSelectedListener {
override fun onTabSelected(tab: TabLayout.Tab) {
viewModel.loadItems(tab.tag as SharedItemType)
viewModel.initialLoadItems(tab.tag as SharedItemType)
}
override fun onTabUnselected(tab: TabLayout.Tab) = Unit
override fun onTabReselected(tab: TabLayout.Tab) = Unit
})
viewModel.loadItems(SharedItemType.MEDIA)
}
override fun onOptionsItemSelected(item: MenuItem): Boolean {

View file

@ -42,90 +42,29 @@ class SharedItemsViewModel @Inject constructor(
ViewModel() {
private lateinit var repositoryParameters: SharedItemsRepository.Parameters
private lateinit var _currentItemType: SharedItemType
val currentItemType: SharedItemType
get() = _currentItemType
// items
sealed interface ViewState
object InitialState : ViewState
object NoSharedItemsState : ViewState
open class TabsLoadedState(val types: Set<SharedItemType>) : ViewState
class LoadedState(types: Set<SharedItemType>, val items: SharedMediaItems) : TabsLoadedState(types)
open class TypesLoadedState(val types: Set<SharedItemType>, val selectedType: SharedItemType) : ViewState
class LoadingItemsState(types: Set<SharedItemType>, selectedType: SharedItemType) :
TypesLoadedState(types, selectedType)
class LoadedState(types: Set<SharedItemType>, selectedType: SharedItemType, val items: SharedMediaItems) :
TypesLoadedState(types, selectedType)
private val _viewState: MutableLiveData<ViewState> = MutableLiveData(InitialState)
val viewState: LiveData<ViewState>
get() = _viewState
fun loadNextItems() {
when (val currentState = _viewState.value) {
is LoadedState -> {
val currentSharedItems = currentState.items
if (currentSharedItems.moreItemsExisting) {
repository.media(repositoryParameters, _currentItemType, currentSharedItems.lastSeenId)
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(observer(_currentItemType, false))
}
}
else -> return
}
}
fun loadItems(type: SharedItemType) {
_currentItemType = type
repository.media(repositoryParameters, type)?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(observer(type, true))
}
private fun observer(type: SharedItemType, initModel: Boolean): Observer<SharedMediaItems> {
return object : Observer<SharedMediaItems> {
var newSharedItems: SharedMediaItems? = null
override fun onSubscribe(d: Disposable) = Unit
override fun onNext(response: SharedMediaItems) {
newSharedItems = response
}
override fun onError(e: Throwable) {
Log.d(TAG, "An error occurred: $e")
}
override fun onComplete() {
val items = newSharedItems!!
// todo replace initmodel with tabsloadedstate
if (initModel) {
setCurrentState(items)
} else {
val state = this@SharedItemsViewModel._viewState.value as LoadedState
val oldItems = state.items.items
val newItems =
SharedMediaItems(
oldItems + newSharedItems!!.items,
newSharedItems!!.lastSeenId,
newSharedItems!!.moreItemsExisting
)
setCurrentState(newItems)
}
}
private fun setCurrentState(items: SharedMediaItems) {
when (val state = this@SharedItemsViewModel._viewState.value) {
is TabsLoadedState -> {
this@SharedItemsViewModel._viewState.value = LoadedState(
state.types,
items
)
}
else -> return
}
}
}
fun initialize(userEntity: UserEntity, roomToken: String) {
repositoryParameters = SharedItemsRepository.Parameters(
userEntity.userId,
userEntity.token,
userEntity.baseUrl,
roomToken
)
loadAvailableTypes()
}
private fun loadAvailableTypes() {
@ -150,22 +89,88 @@ class SharedItemsViewModel @Inject constructor(
if (newTypes.isNullOrEmpty()) {
this@SharedItemsViewModel._viewState.value = NoSharedItemsState
} else {
this@SharedItemsViewModel._viewState.value = TabsLoadedState(newTypes)
val selectedType = chooseInitialType(newTypes)
this@SharedItemsViewModel._viewState.value =
TypesLoadedState(newTypes, selectedType)
initialLoadItems(selectedType)
}
}
})
}
// TODO cleanup
fun initialize(userEntity: UserEntity, roomToken: String, initialType: SharedItemType) {
repositoryParameters = SharedItemsRepository.Parameters(
userEntity.userId,
userEntity.token,
userEntity.baseUrl,
roomToken
)
_currentItemType = initialType
loadAvailableTypes()
private fun chooseInitialType(newTypes: Set<SharedItemType>): SharedItemType = when {
newTypes.contains(SharedItemType.MEDIA) -> SharedItemType.MEDIA
else -> newTypes.toList().first()
}
fun initialLoadItems(type: SharedItemType) {
val state = _viewState.value
if (state is TypesLoadedState) {
_viewState.value = LoadingItemsState(state.types, type)
repository.media(repositoryParameters, type)?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(SharedMediaItemsObserver())
}
}
fun loadNextItems() {
when (val currentState = _viewState.value) {
is LoadedState -> {
val currentSharedItems = currentState.items
if (currentSharedItems.moreItemsExisting) {
repository.media(repositoryParameters, currentState.selectedType, currentSharedItems.lastSeenId)
?.subscribeOn(Schedulers.io())
?.observeOn(AndroidSchedulers.mainThread())
?.subscribe(SharedMediaItemsObserver())
}
}
else -> return
}
}
inner class SharedMediaItemsObserver : Observer<SharedMediaItems> {
var newSharedItems: SharedMediaItems? = null
override fun onSubscribe(d: Disposable) = Unit
override fun onNext(response: SharedMediaItems) {
newSharedItems = response
}
override fun onError(e: Throwable) {
Log.d(TAG, "An error occurred: $e")
}
override fun onComplete() {
val items = newSharedItems!!
val state = this@SharedItemsViewModel._viewState.value
if (state is LoadedState) {
val oldItems = state.items.items
val newItems =
SharedMediaItems(
oldItems + newSharedItems!!.items,
newSharedItems!!.lastSeenId,
newSharedItems!!.moreItemsExisting
)
setCurrentState(newItems)
} else {
setCurrentState(items)
}
}
private fun setCurrentState(items: SharedMediaItems) {
when (val state = this@SharedItemsViewModel._viewState.value) {
is TypesLoadedState -> {
this@SharedItemsViewModel._viewState.value = LoadedState(
state.types,
state.selectedType,
items
)
}
else -> return
}
}
}
companion object {