remove Rx from AccountViewModel and ReportViewModel (#3463)

This commit is contained in:
Konrad Pozniak 2023-03-22 22:00:03 +01:00 committed by GitHub
parent 8c519af611
commit 787f88b801
No known key found for this signature in database
GPG key ID: 4AEE18F83AFDEB23
5 changed files with 146 additions and 174 deletions

View file

@ -2,6 +2,7 @@ package com.keylesspalace.tusky.components.account
import android.util.Log import android.util.Log
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.fold
import com.keylesspalace.tusky.appstore.BlockEvent import com.keylesspalace.tusky.appstore.BlockEvent
@ -17,19 +18,17 @@ import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.Error import com.keylesspalace.tusky.util.Error
import com.keylesspalace.tusky.util.Loading import com.keylesspalace.tusky.util.Loading
import com.keylesspalace.tusky.util.Resource import com.keylesspalace.tusky.util.Resource
import com.keylesspalace.tusky.util.RxAwareViewModel
import com.keylesspalace.tusky.util.Success import com.keylesspalace.tusky.util.Success
import io.reactivex.rxjava3.core.Single import kotlinx.coroutines.Job
import io.reactivex.rxjava3.disposables.Disposable import kotlinx.coroutines.delay
import kotlinx.coroutines.launch import kotlinx.coroutines.launch
import java.util.concurrent.TimeUnit
import javax.inject.Inject import javax.inject.Inject
class AccountViewModel @Inject constructor( class AccountViewModel @Inject constructor(
private val mastodonApi: MastodonApi, private val mastodonApi: MastodonApi,
private val eventHub: EventHub, private val eventHub: EventHub,
private val accountManager: AccountManager private val accountManager: AccountManager
) : RxAwareViewModel() { ) : ViewModel() {
val accountData = MutableLiveData<Resource<Account>>() val accountData = MutableLiveData<Resource<Account>>()
val relationshipData = MutableLiveData<Resource<Relationship>>() val relationshipData = MutableLiveData<Resource<Relationship>>()
@ -42,7 +41,7 @@ class AccountViewModel @Inject constructor(
lateinit var accountId: String lateinit var accountId: String
var isSelf = false var isSelf = false
private var noteDisposable: Disposable? = null private var noteUpdateJob: Job? = null
init { init {
viewModelScope.launch { viewModelScope.launch {
@ -59,21 +58,22 @@ class AccountViewModel @Inject constructor(
isDataLoading = true isDataLoading = true
accountData.postValue(Loading()) accountData.postValue(Loading())
mastodonApi.account(accountId) viewModelScope.launch {
.subscribe( mastodonApi.account(accountId)
{ account -> .fold(
accountData.postValue(Success(account)) { account ->
isDataLoading = false accountData.postValue(Success(account))
isRefreshing.postValue(false) isDataLoading = false
}, isRefreshing.postValue(false)
{ t -> },
Log.w(TAG, "failed obtaining account", t) { t ->
accountData.postValue(Error()) Log.w(TAG, "failed obtaining account", t)
isDataLoading = false accountData.postValue(Error(cause = t))
isRefreshing.postValue(false) isDataLoading = false
} isRefreshing.postValue(false)
) }
.autoDispose() )
}
} }
} }
@ -81,17 +81,18 @@ class AccountViewModel @Inject constructor(
if (relationshipData.value == null || reload) { if (relationshipData.value == null || reload) {
relationshipData.postValue(Loading()) relationshipData.postValue(Loading())
mastodonApi.relationships(listOf(accountId)) viewModelScope.launch {
.subscribe( mastodonApi.relationships(listOf(accountId))
{ relationships -> .fold(
relationshipData.postValue(Success(relationships[0])) { relationships ->
}, relationshipData.postValue(Success(relationships[0]))
{ t -> },
Log.w(TAG, "failed obtaining relationships", t) { t ->
relationshipData.postValue(Error()) Log.w(TAG, "failed obtaining relationships", t)
} relationshipData.postValue(Error(cause = t))
) }
.autoDispose() )
}
} }
} }
@ -212,75 +213,71 @@ class AccountViewModel @Inject constructor(
relationshipData.postValue(Loading(newRelation)) relationshipData.postValue(Loading(newRelation))
} }
try { val relationshipCall = when (relationshipAction) {
val relationship = when (relationshipAction) { RelationShipAction.FOLLOW -> mastodonApi.followAccount(
RelationShipAction.FOLLOW -> mastodonApi.followAccount( accountId,
accountId, showReblogs = parameter ?: true
showReblogs = parameter ?: true )
) RelationShipAction.UNFOLLOW -> mastodonApi.unfollowAccount(accountId)
RelationShipAction.UNFOLLOW -> mastodonApi.unfollowAccount(accountId) RelationShipAction.BLOCK -> mastodonApi.blockAccount(accountId)
RelationShipAction.BLOCK -> mastodonApi.blockAccount(accountId) RelationShipAction.UNBLOCK -> mastodonApi.unblockAccount(accountId)
RelationShipAction.UNBLOCK -> mastodonApi.unblockAccount(accountId) RelationShipAction.MUTE -> mastodonApi.muteAccount(
RelationShipAction.MUTE -> mastodonApi.muteAccount( accountId,
accountId, parameter ?: true,
parameter ?: true, duration
duration )
) RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(accountId)
RelationShipAction.UNMUTE -> mastodonApi.unmuteAccount(accountId) RelationShipAction.SUBSCRIBE -> {
RelationShipAction.SUBSCRIBE -> { if (isMastodon) {
if (isMastodon) { mastodonApi.followAccount(accountId, notify = true)
mastodonApi.followAccount(accountId, notify = true) } else {
} else { mastodonApi.subscribeAccount(accountId)
mastodonApi.subscribeAccount(accountId)
}
}
RelationShipAction.UNSUBSCRIBE -> {
if (isMastodon) {
mastodonApi.followAccount(accountId, notify = false)
} else {
mastodonApi.unsubscribeAccount(accountId)
}
} }
} }
RelationShipAction.UNSUBSCRIBE -> {
relationshipData.postValue(Success(relationship)) if (isMastodon) {
mastodonApi.followAccount(accountId, notify = false)
when (relationshipAction) { } else {
RelationShipAction.UNFOLLOW -> eventHub.dispatch(UnfollowEvent(accountId)) mastodonApi.unsubscribeAccount(accountId)
RelationShipAction.BLOCK -> eventHub.dispatch(BlockEvent(accountId))
RelationShipAction.MUTE -> eventHub.dispatch(MuteEvent(accountId))
else -> {
} }
} }
} catch (_: Throwable) {
relationshipData.postValue(Error(relation))
} }
relationshipCall.fold(
{ relationship ->
relationshipData.postValue(Success(relationship))
when (relationshipAction) {
RelationShipAction.UNFOLLOW -> eventHub.dispatch(UnfollowEvent(accountId))
RelationShipAction.BLOCK -> eventHub.dispatch(BlockEvent(accountId))
RelationShipAction.MUTE -> eventHub.dispatch(MuteEvent(accountId))
else -> { }
}
},
{ t ->
Log.w(TAG, "failed loading relationship", t)
relationshipData.postValue(Error(relation, cause = t))
}
)
} }
fun noteChanged(newNote: String) { fun noteChanged(newNote: String) {
noteSaved.postValue(false) noteSaved.postValue(false)
noteDisposable?.dispose() noteUpdateJob?.cancel()
noteDisposable = Single.timer(1500, TimeUnit.MILLISECONDS) noteUpdateJob = viewModelScope.launch {
.flatMap { delay(1500)
mastodonApi.updateAccountNote(accountId, newNote) mastodonApi.updateAccountNote(accountId, newNote)
} .fold(
.doOnSuccess { {
noteSaved.postValue(true) noteSaved.postValue(true)
} delay(4000)
.delay(4, TimeUnit.SECONDS) noteSaved.postValue(false)
.subscribe( },
{ { t ->
noteSaved.postValue(false) Log.w(TAG, "Error updating note", t)
}, }
{ )
Log.e(TAG, "Error updating note", it) }
}
)
}
override fun onCleared() {
super.onCleared()
noteDisposable?.dispose()
} }
fun refresh() { fun refresh() {

View file

@ -27,6 +27,7 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator import androidx.recyclerview.widget.SimpleItemAnimator
import at.connyduck.calladapter.networkresult.fold
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from
import autodispose2.autoDispose import autodispose2.autoDispose
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -348,12 +349,12 @@ class AccountListFragment : Fragment(R.layout.fragment_account_list), AccountAct
} }
private fun fetchRelationships(ids: List<String>) { private fun fetchRelationships(ids: List<String>) {
api.relationships(ids) lifecycleScope.launch {
.observeOn(AndroidSchedulers.mainThread()) api.relationships(ids)
.autoDispose(from(this)) .fold(::onFetchRelationshipsSuccess) { throwable ->
.subscribe(::onFetchRelationshipsSuccess) { throwable -> Log.e(TAG, "Fetch failure for relationships of accounts: $ids", throwable)
Log.e(TAG, "Fetch failure for relationships of accounts: $ids", throwable) }
} }
} }
private fun onFetchRelationshipsSuccess(relationships: List<Relationship>) { private fun onFetchRelationshipsSuccess(relationships: List<Relationship>) {

View file

@ -17,11 +17,13 @@ package com.keylesspalace.tusky.components.report
import androidx.lifecycle.LiveData import androidx.lifecycle.LiveData
import androidx.lifecycle.MutableLiveData import androidx.lifecycle.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope import androidx.lifecycle.viewModelScope
import androidx.paging.Pager import androidx.paging.Pager
import androidx.paging.PagingConfig import androidx.paging.PagingConfig
import androidx.paging.cachedIn import androidx.paging.cachedIn
import androidx.paging.map import androidx.paging.map
import at.connyduck.calladapter.networkresult.fold
import com.keylesspalace.tusky.appstore.BlockEvent import com.keylesspalace.tusky.appstore.BlockEvent
import com.keylesspalace.tusky.appstore.EventHub import com.keylesspalace.tusky.appstore.EventHub
import com.keylesspalace.tusky.appstore.MuteEvent import com.keylesspalace.tusky.appstore.MuteEvent
@ -33,11 +35,8 @@ import com.keylesspalace.tusky.network.MastodonApi
import com.keylesspalace.tusky.util.Error import com.keylesspalace.tusky.util.Error
import com.keylesspalace.tusky.util.Loading import com.keylesspalace.tusky.util.Loading
import com.keylesspalace.tusky.util.Resource import com.keylesspalace.tusky.util.Resource
import com.keylesspalace.tusky.util.RxAwareViewModel
import com.keylesspalace.tusky.util.Success import com.keylesspalace.tusky.util.Success
import com.keylesspalace.tusky.util.toViewData import com.keylesspalace.tusky.util.toViewData
import io.reactivex.rxjava3.android.schedulers.AndroidSchedulers
import io.reactivex.rxjava3.schedulers.Schedulers
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
import kotlinx.coroutines.flow.MutableSharedFlow import kotlinx.coroutines.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest import kotlinx.coroutines.flow.flatMapLatest
@ -48,7 +47,7 @@ import javax.inject.Inject
class ReportViewModel @Inject constructor( class ReportViewModel @Inject constructor(
private val mastodonApi: MastodonApi, private val mastodonApi: MastodonApi,
private val eventHub: EventHub private val eventHub: EventHub
) : RxAwareViewModel() { ) : ViewModel() {
private val navigationMutable = MutableLiveData<Screen?>() private val navigationMutable = MutableLiveData<Screen?>()
val navigation: LiveData<Screen?> = navigationMutable val navigation: LiveData<Screen?> = navigationMutable
@ -128,10 +127,8 @@ class ReportViewModel @Inject constructor(
val ids = listOf(accountId) val ids = listOf(accountId)
muteStateMutable.value = Loading() muteStateMutable.value = Loading()
blockStateMutable.value = Loading() blockStateMutable.value = Loading()
mastodonApi.relationships(ids) viewModelScope.launch {
.subscribeOn(Schedulers.io()) mastodonApi.relationships(ids).fold(
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{ data -> { data ->
updateRelationship(data.getOrNull(0)) updateRelationship(data.getOrNull(0))
}, },
@ -139,7 +136,7 @@ class ReportViewModel @Inject constructor(
updateRelationship(null) updateRelationship(null)
} }
) )
.autoDispose() }
} }
private fun updateRelationship(relationship: Relationship?) { private fun updateRelationship(relationship: Relationship?) {
@ -155,21 +152,22 @@ class ReportViewModel @Inject constructor(
fun toggleMute() { fun toggleMute() {
val alreadyMuted = muteStateMutable.value?.data == true val alreadyMuted = muteStateMutable.value?.data == true
viewModelScope.launch { viewModelScope.launch {
try { if (alreadyMuted) {
val relationship = if (alreadyMuted) { mastodonApi.unmuteAccount(accountId)
mastodonApi.unmuteAccount(accountId) } else {
} else { mastodonApi.muteAccount(accountId)
mastodonApi.muteAccount(accountId) }.fold(
{ relationship ->
val muting = relationship.muting
muteStateMutable.value = Success(muting)
if (muting) {
eventHub.dispatch(MuteEvent(accountId))
}
},
{ t ->
muteStateMutable.value = Error(false, t.message)
} }
)
val muting = relationship.muting
muteStateMutable.value = Success(muting)
if (muting) {
eventHub.dispatch(MuteEvent(accountId))
}
} catch (t: Throwable) {
muteStateMutable.value = Error(false, t.message)
}
} }
muteStateMutable.value = Loading() muteStateMutable.value = Loading()
@ -178,39 +176,33 @@ class ReportViewModel @Inject constructor(
fun toggleBlock() { fun toggleBlock() {
val alreadyBlocked = blockStateMutable.value?.data == true val alreadyBlocked = blockStateMutable.value?.data == true
viewModelScope.launch { viewModelScope.launch {
try { if (alreadyBlocked) {
val relationship = if (alreadyBlocked) { mastodonApi.unblockAccount(accountId)
mastodonApi.unblockAccount(accountId) } else {
} else { mastodonApi.blockAccount(accountId)
mastodonApi.blockAccount(accountId) }.fold({ relationship ->
}
val blocking = relationship.blocking val blocking = relationship.blocking
blockStateMutable.value = Success(blocking) blockStateMutable.value = Success(blocking)
if (blocking) { if (blocking) {
eventHub.dispatch(BlockEvent(accountId)) eventHub.dispatch(BlockEvent(accountId))
} }
} catch (t: Throwable) { }, { t ->
blockStateMutable.value = Error(false, t.message) blockStateMutable.value = Error(false, t.message)
} })
} }
blockStateMutable.value = Loading() blockStateMutable.value = Loading()
} }
fun doReport() { fun doReport() {
reportingStateMutable.value = Loading() reportingStateMutable.value = Loading()
mastodonApi.reportObservable(accountId, selectedIds.toList(), reportNote, if (isRemoteAccount) isRemoteNotify else null) viewModelScope.launch {
.subscribeOn(Schedulers.io()) mastodonApi.report(accountId, selectedIds.toList(), reportNote, if (isRemoteAccount) isRemoteNotify else null)
.observeOn(AndroidSchedulers.mainThread()) .fold({
.subscribe(
{
reportingStateMutable.value = Success(true) reportingStateMutable.value = Success(true)
}, }, { error ->
{ error ->
reportingStateMutable.value = Error(cause = error) reportingStateMutable.value = Error(cause = error)
} })
) }
.autoDispose()
} }
fun checkClickedUrl(url: String?) { fun checkClickedUrl(url: String?) {

View file

@ -344,9 +344,9 @@ interface MastodonApi {
): NetworkResult<List<TimelineAccount>> ): NetworkResult<List<TimelineAccount>>
@GET("api/v1/accounts/{id}") @GET("api/v1/accounts/{id}")
fun account( suspend fun account(
@Path("id") accountId: String @Path("id") accountId: String
): Single<Account> ): NetworkResult<Account>
/** /**
* Method to fetch statuses for the specified account. * Method to fetch statuses for the specified account.
@ -386,22 +386,22 @@ interface MastodonApi {
@Path("id") accountId: String, @Path("id") accountId: String,
@Field("reblogs") showReblogs: Boolean? = null, @Field("reblogs") showReblogs: Boolean? = null,
@Field("notify") notify: Boolean? = null @Field("notify") notify: Boolean? = null
): Relationship ): NetworkResult<Relationship>
@POST("api/v1/accounts/{id}/unfollow") @POST("api/v1/accounts/{id}/unfollow")
suspend fun unfollowAccount( suspend fun unfollowAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Relationship ): NetworkResult<Relationship>
@POST("api/v1/accounts/{id}/block") @POST("api/v1/accounts/{id}/block")
suspend fun blockAccount( suspend fun blockAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Relationship ): NetworkResult<Relationship>
@POST("api/v1/accounts/{id}/unblock") @POST("api/v1/accounts/{id}/unblock")
suspend fun unblockAccount( suspend fun unblockAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Relationship ): NetworkResult<Relationship>
@FormUrlEncoded @FormUrlEncoded
@POST("api/v1/accounts/{id}/mute") @POST("api/v1/accounts/{id}/mute")
@ -409,27 +409,27 @@ interface MastodonApi {
@Path("id") accountId: String, @Path("id") accountId: String,
@Field("notifications") notifications: Boolean? = null, @Field("notifications") notifications: Boolean? = null,
@Field("duration") duration: Int? = null @Field("duration") duration: Int? = null
): Relationship ): NetworkResult<Relationship>
@POST("api/v1/accounts/{id}/unmute") @POST("api/v1/accounts/{id}/unmute")
suspend fun unmuteAccount( suspend fun unmuteAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Relationship ): NetworkResult<Relationship>
@GET("api/v1/accounts/relationships") @GET("api/v1/accounts/relationships")
fun relationships( suspend fun relationships(
@Query("id[]") accountIds: List<String> @Query("id[]") accountIds: List<String>
): Single<List<Relationship>> ): NetworkResult<List<Relationship>>
@POST("api/v1/pleroma/accounts/{id}/subscribe") @POST("api/v1/pleroma/accounts/{id}/subscribe")
suspend fun subscribeAccount( suspend fun subscribeAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Relationship ): NetworkResult<Relationship>
@POST("api/v1/pleroma/accounts/{id}/unsubscribe") @POST("api/v1/pleroma/accounts/{id}/unsubscribe")
suspend fun unsubscribeAccount( suspend fun unsubscribeAccount(
@Path("id") accountId: String @Path("id") accountId: String
): Relationship ): NetworkResult<Relationship>
@GET("api/v1/blocks") @GET("api/v1/blocks")
suspend fun blocks( suspend fun blocks(
@ -677,12 +677,12 @@ interface MastodonApi {
@FormUrlEncoded @FormUrlEncoded
@POST("api/v1/reports") @POST("api/v1/reports")
fun reportObservable( fun report(
@Field("account_id") accountId: String, @Field("account_id") accountId: String,
@Field("status_ids[]") statusIds: List<String>, @Field("status_ids[]") statusIds: List<String>,
@Field("comment") comment: String, @Field("comment") comment: String,
@Field("forward") isNotifyRemote: Boolean? @Field("forward") isNotifyRemote: Boolean?
): Single<ResponseBody> ): NetworkResult<Unit>
@GET("api/v1/accounts/{id}/statuses") @GET("api/v1/accounts/{id}/statuses")
fun accountStatusesObservable( fun accountStatusesObservable(
@ -721,10 +721,10 @@ interface MastodonApi {
@FormUrlEncoded @FormUrlEncoded
@POST("api/v1/accounts/{id}/note") @POST("api/v1/accounts/{id}/note")
fun updateAccountNote( suspend fun updateAccountNote(
@Path("id") accountId: String, @Path("id") accountId: String,
@Field("comment") note: String @Field("comment") note: String
): Single<Relationship> ): NetworkResult<Relationship>
@FormUrlEncoded @FormUrlEncoded
@POST("api/v1/push/subscription") @POST("api/v1/push/subscription")

View file

@ -1,18 +0,0 @@
package com.keylesspalace.tusky.util
import androidx.annotation.CallSuper
import androidx.lifecycle.ViewModel
import io.reactivex.rxjava3.disposables.CompositeDisposable
import io.reactivex.rxjava3.disposables.Disposable
open class RxAwareViewModel : ViewModel() {
private val disposables = CompositeDisposable()
fun Disposable.autoDispose() = disposables.add(this)
@CallSuper
override fun onCleared() {
super.onCleared()
disposables.clear()
}
}