From cdb9d87f4107c919930f23e93dcbc0a5b45f943b Mon Sep 17 00:00:00 2001 From: Konrad Pozniak Date: Thu, 2 Jan 2020 18:50:58 +0100 Subject: [PATCH] use RxAwareViewModel for more ViewModels (#1613) * use RxAwareViewModel for more ViewModels * fix ReportViewModel --- .../conversation/ConversationsViewModel.kt | 18 +-- .../components/report/ReportViewModel.kt | 112 ++++++++---------- .../scheduled/ScheduledTootViewModel.kt | 3 - .../components/search/SearchViewModel.kt | 33 +++--- .../viewmodel/AccountsInListViewModel.kt | 15 +-- .../tusky/viewmodel/ListsViewModel.kt | 17 ++- 6 files changed, 83 insertions(+), 115 deletions(-) diff --git a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt index 2cb9c2d90..3c61de807 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/conversation/ConversationsViewModel.kt @@ -4,15 +4,13 @@ import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations -import androidx.lifecycle.ViewModel import androidx.paging.PagedList import com.keylesspalace.tusky.db.AccountManager import com.keylesspalace.tusky.db.AppDatabase import com.keylesspalace.tusky.network.TimelineCases import com.keylesspalace.tusky.util.Listing import com.keylesspalace.tusky.util.NetworkState -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo +import com.keylesspalace.tusky.util.RxAwareViewModel import io.reactivex.schedulers.Schedulers import javax.inject.Inject @@ -21,7 +19,7 @@ class ConversationsViewModel @Inject constructor( private val timelineCases: TimelineCases, private val database: AppDatabase, private val accountManager: AccountManager -) : ViewModel() { +) : RxAwareViewModel() { private val repoResult = MutableLiveData>() @@ -29,8 +27,6 @@ class ConversationsViewModel @Inject constructor( val networkState: LiveData = Transformations.switchMap(repoResult) { it.networkState } val refreshState: LiveData = Transformations.switchMap(repoResult) { it.refreshState } - private val disposables = CompositeDisposable() - fun load() { val accountId = accountManager.activeAccount?.id ?: return if (repoResult.value == null) { @@ -61,7 +57,7 @@ class ConversationsViewModel @Inject constructor( .doOnError { t -> Log.w("ConversationViewModel", "Failed to favourite conversation", t) } .onErrorReturnItem(0) .subscribe() - .addTo(disposables) + .autoDispose() } } @@ -79,7 +75,7 @@ class ConversationsViewModel @Inject constructor( .subscribeOn(Schedulers.io()) .doOnError { t -> Log.w("ConversationViewModel", "Failed to bookmark conversation", t) } .subscribe() - .addTo(disposables) + .autoDispose() } } @@ -98,7 +94,7 @@ class ConversationsViewModel @Inject constructor( .doOnError { t -> Log.w("ConversationViewModel", "Failed to favourite conversation", t) } .onErrorReturnItem(0) .subscribe() - .addTo(disposables) + .autoDispose() } } @@ -150,8 +146,4 @@ class ConversationsViewModel @Inject constructor( .subscribe() } - override fun onCleared() { - disposables.dispose() - } - } \ No newline at end of file diff --git a/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt index 88b27285e..8b1e5cda3 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/report/ReportViewModel.kt @@ -18,7 +18,6 @@ package com.keylesspalace.tusky.components.report import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations -import androidx.lifecycle.ViewModel import androidx.paging.PagedList import com.keylesspalace.tusky.components.report.adapter.StatusesRepository import com.keylesspalace.tusky.components.report.model.StatusViewState @@ -26,16 +25,13 @@ import com.keylesspalace.tusky.entity.Relationship import com.keylesspalace.tusky.entity.Status import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.* -import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable import io.reactivex.schedulers.Schedulers import javax.inject.Inject class ReportViewModel @Inject constructor( private val mastodonApi: MastodonApi, - private val statusesRepository: StatusesRepository) : ViewModel() { - private val disposables = CompositeDisposable() + private val statusesRepository: StatusesRepository) : RxAwareViewModel() { private val navigationMutable = MutableLiveData() val navigation: LiveData = navigationMutable @@ -87,11 +83,6 @@ class ReportViewModel @Inject constructor( repoResult.value = statusesRepository.getStatuses(accountId, statusId, disposables) } - override fun onCleared() { - super.onCleared() - disposables.clear() - } - fun navigateTo(screen: Screen) { navigationMutable.value = screen } @@ -105,19 +96,19 @@ class ReportViewModel @Inject constructor( val ids = listOf(accountId) muteStateMutable.value = Loading() blockStateMutable.value = Loading() - disposables.add( - mastodonApi.relationshipsObservable(ids) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { data -> - updateRelationship(data.getOrNull(0)) + mastodonApi.relationshipsObservable(ids) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { data -> + updateRelationship(data.getOrNull(0)) - }, - { - updateRelationship(null) - } - )) + }, + { + updateRelationship(null) + } + ) + .autoDispose() } @@ -132,62 +123,61 @@ class ReportViewModel @Inject constructor( } fun toggleMute() { - val single: Single = if (muteStateMutable.value?.data == true) { + if (muteStateMutable.value?.data == true) { mastodonApi.unmuteAccountObservable(accountId) } else { mastodonApi.muteAccountObservable(accountId) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { relationship -> + muteStateMutable.value = Success(relationship?.muting == true) + }, + { error -> + muteStateMutable.value = Error(false, error.message) + } + ).autoDispose() + muteStateMutable.value = Loading() - disposables.add( - single - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { relationship -> - muteStateMutable.value = Success(relationship?.muting == true) - }, - { error -> - muteStateMutable.value = Error(false, error.message) - } - )) } fun toggleBlock() { - val single: Single = if (blockStateMutable.value?.data == true) { + if (blockStateMutable.value?.data == true) { mastodonApi.unblockAccountObservable(accountId) } else { mastodonApi.blockAccountObservable(accountId) } + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { relationship -> + blockStateMutable.value = Success(relationship?.blocking == true) + }, + { error -> + blockStateMutable.value = Error(false, error.message) + } + ) + .autoDispose() + blockStateMutable.value = Loading() - disposables.add( - single - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { relationship -> - blockStateMutable.value = Success(relationship?.blocking == true) - }, - { error -> - blockStateMutable.value = Error(false, error.message) - } - )) } fun doReport() { reportingStateMutable.value = Loading() - disposables.add( - mastodonApi.reportObservable(accountId, selectedIds.toList(), reportNote, if (isRemoteAccount) isRemoteNotify else null) - .subscribeOn(Schedulers.io()) - .observeOn(AndroidSchedulers.mainThread()) - .subscribe( - { - reportingStateMutable.value = Success(true) - }, - { error -> - reportingStateMutable.value = Error(cause = error) - } - ) - ) + mastodonApi.reportObservable(accountId, selectedIds.toList(), reportNote, if (isRemoteAccount) isRemoteNotify else null) + .subscribeOn(Schedulers.io()) + .observeOn(AndroidSchedulers.mainThread()) + .subscribe( + { + reportingStateMutable.value = Success(true) + }, + { error -> + reportingStateMutable.value = Error(cause = error) + } + ) + .autoDispose() + } fun retryStatusLoad() { diff --git a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootViewModel.kt index 4d531a455..3584168ee 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/scheduled/ScheduledTootViewModel.kt @@ -16,7 +16,6 @@ package com.keylesspalace.tusky.components.scheduled import android.util.Log -import androidx.lifecycle.ViewModel import androidx.paging.Config import androidx.paging.toLiveData import com.keylesspalace.tusky.appstore.EventHub @@ -25,8 +24,6 @@ import com.keylesspalace.tusky.entity.ScheduledStatus import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.RxAwareViewModel import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo import javax.inject.Inject class ScheduledTootViewModel @Inject constructor( diff --git a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt index c912bdce2..9ae7095fa 100644 --- a/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/components/search/SearchViewModel.kt @@ -4,7 +4,6 @@ import android.util.Log import androidx.lifecycle.LiveData import androidx.lifecycle.MutableLiveData import androidx.lifecycle.Transformations -import androidx.lifecycle.ViewModel import androidx.paging.PagedList import com.keylesspalace.tusky.components.search.adapter.SearchRepository import com.keylesspalace.tusky.db.AccountEntity @@ -14,18 +13,17 @@ import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.network.TimelineCases import com.keylesspalace.tusky.util.Listing import com.keylesspalace.tusky.util.NetworkState +import com.keylesspalace.tusky.util.RxAwareViewModel import com.keylesspalace.tusky.util.ViewDataUtils import com.keylesspalace.tusky.viewdata.StatusViewData import io.reactivex.Single import io.reactivex.android.schedulers.AndroidSchedulers -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo import javax.inject.Inject class SearchViewModel @Inject constructor( mastodonApi: MastodonApi, private val timelineCases: TimelineCases, - private val accountManager: AccountManager) : ViewModel() { + private val accountManager: AccountManager) : RxAwareViewModel() { var currentQuery: String = "" @@ -37,7 +35,6 @@ class SearchViewModel @Inject constructor( val mediaPreviewEnabled: Boolean get() = activeAccount?.mediaPreviewEnabled ?: false - private val disposables = CompositeDisposable() private val statusesRepository = SearchRepository>(mastodonApi) private val accountsRepository = SearchRepository(mastodonApi) @@ -83,11 +80,6 @@ class SearchViewModel @Inject constructor( } - override fun onCleared() { - super.onCleared() - disposables.clear() - } - fun removeItem(status: Pair) { timelineCases.delete(status.first.id) .subscribe({ @@ -96,7 +88,7 @@ class SearchViewModel @Inject constructor( }, { err -> Log.d(TAG, "Failed to delete status", err) }) - .addTo(disposables) + .autoDispose() } @@ -110,13 +102,13 @@ class SearchViewModel @Inject constructor( } fun reblog(status: Pair, reblog: Boolean) { - disposables.add(timelineCases.reblog(status.first, reblog) + timelineCases.reblog(status.first, reblog) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { setRebloggedForStatus(status, reblog) }, { err -> Log.d(TAG, "Failed to reblog status ${status.first.id}", err) } ) - ) + .autoDispose() } private fun setRebloggedForStatus(status: Pair, reblog: Boolean) { @@ -152,7 +144,7 @@ class SearchViewModel @Inject constructor( fun voteInPoll(status: Pair, choices: MutableList) { val votedPoll = status.first.actionableStatus.poll!!.votedCopy(choices) updateStatus(status, votedPoll) - disposables.add(timelineCases.voteInPoll(status.first, choices) + timelineCases.voteInPoll(status.first, choices) .observeOn(AndroidSchedulers.mainThread()) .subscribe( { newPoll -> updateStatus(status, newPoll) }, @@ -160,7 +152,8 @@ class SearchViewModel @Inject constructor( Log.d(TAG, "Failed to vote in poll: ${status.first.id}", t) } - )) + ) + .autoDispose() } private fun updateStatus(status: Pair, newPoll: Poll) { @@ -182,9 +175,10 @@ class SearchViewModel @Inject constructor( loadedStatuses[idx] = newPair repoResultStatus.value?.refresh?.invoke() } - disposables.add(timelineCases.favourite(status.first, isFavorited) + timelineCases.favourite(status.first, isFavorited) .onErrorReturnItem(status.first) - .subscribe()) + .subscribe() + .autoDispose() } fun bookmark(status: Pair, isBookmarked: Boolean) { @@ -194,9 +188,10 @@ class SearchViewModel @Inject constructor( loadedStatuses[idx] = newPair repoResultStatus.value?.refresh?.invoke() } - disposables.add(timelineCases.favourite(status.first, isBookmarked) + timelineCases.favourite(status.first, isBookmarked) .onErrorReturnItem(status.first) - .subscribe()) + .subscribe() + .autoDispose() } fun getAllAccountsOrderedByActive(): List { diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountsInListViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountsInListViewModel.kt index 58c844d90..1dc412283 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountsInListViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/AccountsInListViewModel.kt @@ -17,26 +17,23 @@ package com.keylesspalace.tusky.viewmodel import android.util.Log -import androidx.lifecycle.ViewModel import com.keylesspalace.tusky.entity.Account import com.keylesspalace.tusky.network.MastodonApi import com.keylesspalace.tusky.util.Either import com.keylesspalace.tusky.util.Either.Left import com.keylesspalace.tusky.util.Either.Right +import com.keylesspalace.tusky.util.RxAwareViewModel import com.keylesspalace.tusky.util.withoutFirstWhich import io.reactivex.Observable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo import io.reactivex.subjects.BehaviorSubject import javax.inject.Inject data class State(val accounts: Either>, val searchResult: List?) -class AccountsInListViewModel @Inject constructor(private val api: MastodonApi) : ViewModel() { +class AccountsInListViewModel @Inject constructor(private val api: MastodonApi) : RxAwareViewModel() { val state: Observable get() = _state private val _state = BehaviorSubject.createDefault(State(Right(listOf()), null)) - private val disposable = CompositeDisposable() fun load(listId: String) { val state = _state.value!! @@ -45,7 +42,7 @@ class AccountsInListViewModel @Inject constructor(private val api: MastodonApi) updateState { copy(accounts = Right(accounts)) } }, { e -> updateState { copy(accounts = Left(e)) } - }).addTo(disposable) + }).autoDispose() } } @@ -59,7 +56,7 @@ class AccountsInListViewModel @Inject constructor(private val api: MastodonApi) Log.i(javaClass.simpleName, "Failed to add account to the list: ${account.username}") }) - .addTo(disposable) + .autoDispose() } fun deleteAccountFromList(listId: String, accountId: String) { @@ -73,7 +70,7 @@ class AccountsInListViewModel @Inject constructor(private val api: MastodonApi) }, { Log.i(javaClass.simpleName, "Failed to remove account from thelist: $accountId") }) - .addTo(disposable) + .autoDispose() } fun search(query: String) { @@ -85,7 +82,7 @@ class AccountsInListViewModel @Inject constructor(private val api: MastodonApi) updateState { copy(searchResult = result) } }, { updateState { copy(searchResult = listOf()) } - }).addTo(disposable) + }).autoDispose() } } diff --git a/app/src/main/java/com/keylesspalace/tusky/viewmodel/ListsViewModel.kt b/app/src/main/java/com/keylesspalace/tusky/viewmodel/ListsViewModel.kt index e6331072c..22f509b59 100644 --- a/app/src/main/java/com/keylesspalace/tusky/viewmodel/ListsViewModel.kt +++ b/app/src/main/java/com/keylesspalace/tusky/viewmodel/ListsViewModel.kt @@ -16,14 +16,12 @@ package com.keylesspalace.tusky.viewmodel -import androidx.lifecycle.ViewModel import com.keylesspalace.tusky.entity.MastoList import com.keylesspalace.tusky.network.MastodonApi -import com.keylesspalace.tusky.util.withoutFirstWhich +import com.keylesspalace.tusky.util.RxAwareViewModel import com.keylesspalace.tusky.util.replacedFirstWhich +import com.keylesspalace.tusky.util.withoutFirstWhich import io.reactivex.Observable -import io.reactivex.disposables.CompositeDisposable -import io.reactivex.rxkotlin.addTo import io.reactivex.subjects.BehaviorSubject import io.reactivex.subjects.PublishSubject import java.io.IOException @@ -31,7 +29,7 @@ import java.net.ConnectException import javax.inject.Inject -internal class ListsViewModel @Inject constructor(private val api: MastodonApi) : ViewModel() { +internal class ListsViewModel @Inject constructor(private val api: MastodonApi) : RxAwareViewModel() { enum class LoadingState { INITIAL, LOADING, LOADED, ERROR_NETWORK, ERROR_OTHER } @@ -46,7 +44,6 @@ internal class ListsViewModel @Inject constructor(private val api: MastodonApi) val events: Observable get() = _events private val _state = BehaviorSubject.createDefault(State(listOf(), LoadingState.INITIAL)) private val _events = PublishSubject.create() - private val disposable = CompositeDisposable() fun retryLoading() { loadIfNeeded() @@ -71,7 +68,7 @@ internal class ListsViewModel @Inject constructor(private val api: MastodonApi) copy(loadingState = if (err is IOException || err is ConnectException) LoadingState.ERROR_NETWORK else LoadingState.ERROR_OTHER) } - }).addTo(disposable) + }).autoDispose() } fun createNewList(listName: String) { @@ -81,7 +78,7 @@ internal class ListsViewModel @Inject constructor(private val api: MastodonApi) } }, { sendEvent(Event.CREATE_ERROR) - }).addTo(disposable) + }).autoDispose() } fun renameList(listId: String, listName: String) { @@ -91,7 +88,7 @@ internal class ListsViewModel @Inject constructor(private val api: MastodonApi) } }, { sendEvent(Event.RENAME_ERROR) - }).addTo(disposable) + }).autoDispose() } fun deleteList(listId: String) { @@ -101,7 +98,7 @@ internal class ListsViewModel @Inject constructor(private val api: MastodonApi) } }, { sendEvent(Event.DELETE_ERROR) - }).addTo(disposable) + }).autoDispose() } private inline fun updateState(crossinline fn: State.() -> State) {