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

View File

@ -27,6 +27,7 @@ import androidx.recyclerview.widget.DividerItemDecoration
import androidx.recyclerview.widget.LinearLayoutManager
import androidx.recyclerview.widget.RecyclerView
import androidx.recyclerview.widget.SimpleItemAnimator
import at.connyduck.calladapter.networkresult.fold
import autodispose2.androidx.lifecycle.AndroidLifecycleScopeProvider.from
import autodispose2.autoDispose
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>) {
api.relationships(ids)
.observeOn(AndroidSchedulers.mainThread())
.autoDispose(from(this))
.subscribe(::onFetchRelationshipsSuccess) { throwable ->
Log.e(TAG, "Fetch failure for relationships of accounts: $ids", throwable)
}
lifecycleScope.launch {
api.relationships(ids)
.fold(::onFetchRelationshipsSuccess) { throwable ->
Log.e(TAG, "Fetch failure for relationships of accounts: $ids", throwable)
}
}
}
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.MutableLiveData
import androidx.lifecycle.ViewModel
import androidx.lifecycle.viewModelScope
import androidx.paging.Pager
import androidx.paging.PagingConfig
import androidx.paging.cachedIn
import androidx.paging.map
import at.connyduck.calladapter.networkresult.fold
import com.keylesspalace.tusky.appstore.BlockEvent
import com.keylesspalace.tusky.appstore.EventHub
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.Loading
import com.keylesspalace.tusky.util.Resource
import com.keylesspalace.tusky.util.RxAwareViewModel
import com.keylesspalace.tusky.util.Success
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.flow.MutableSharedFlow
import kotlinx.coroutines.flow.flatMapLatest
@ -48,7 +47,7 @@ import javax.inject.Inject
class ReportViewModel @Inject constructor(
private val mastodonApi: MastodonApi,
private val eventHub: EventHub
) : RxAwareViewModel() {
) : ViewModel() {
private val navigationMutable = MutableLiveData<Screen?>()
val navigation: LiveData<Screen?> = navigationMutable
@ -128,10 +127,8 @@ class ReportViewModel @Inject constructor(
val ids = listOf(accountId)
muteStateMutable.value = Loading()
blockStateMutable.value = Loading()
mastodonApi.relationships(ids)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
viewModelScope.launch {
mastodonApi.relationships(ids).fold(
{ data ->
updateRelationship(data.getOrNull(0))
},
@ -139,7 +136,7 @@ class ReportViewModel @Inject constructor(
updateRelationship(null)
}
)
.autoDispose()
}
}
private fun updateRelationship(relationship: Relationship?) {
@ -155,21 +152,22 @@ class ReportViewModel @Inject constructor(
fun toggleMute() {
val alreadyMuted = muteStateMutable.value?.data == true
viewModelScope.launch {
try {
val relationship = if (alreadyMuted) {
mastodonApi.unmuteAccount(accountId)
} else {
mastodonApi.muteAccount(accountId)
if (alreadyMuted) {
mastodonApi.unmuteAccount(accountId)
} else {
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()
@ -178,39 +176,33 @@ class ReportViewModel @Inject constructor(
fun toggleBlock() {
val alreadyBlocked = blockStateMutable.value?.data == true
viewModelScope.launch {
try {
val relationship = if (alreadyBlocked) {
mastodonApi.unblockAccount(accountId)
} else {
mastodonApi.blockAccount(accountId)
}
if (alreadyBlocked) {
mastodonApi.unblockAccount(accountId)
} else {
mastodonApi.blockAccount(accountId)
}.fold({ relationship ->
val blocking = relationship.blocking
blockStateMutable.value = Success(blocking)
if (blocking) {
eventHub.dispatch(BlockEvent(accountId))
}
} catch (t: Throwable) {
}, { t ->
blockStateMutable.value = Error(false, t.message)
}
})
}
blockStateMutable.value = Loading()
}
fun doReport() {
reportingStateMutable.value = Loading()
mastodonApi.reportObservable(accountId, selectedIds.toList(), reportNote, if (isRemoteAccount) isRemoteNotify else null)
.subscribeOn(Schedulers.io())
.observeOn(AndroidSchedulers.mainThread())
.subscribe(
{
viewModelScope.launch {
mastodonApi.report(accountId, selectedIds.toList(), reportNote, if (isRemoteAccount) isRemoteNotify else null)
.fold({
reportingStateMutable.value = Success(true)
},
{ error ->
}, { error ->
reportingStateMutable.value = Error(cause = error)
}
)
.autoDispose()
})
}
}
fun checkClickedUrl(url: String?) {

View File

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