refactor: Convert account relationship API calls to use ApiResult (#1109)

This commit is contained in:
Nik Clayton 2024-11-18 17:34:52 +01:00 committed by GitHub
parent 88561afcca
commit 654a81a136
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
5 changed files with 120 additions and 118 deletions

View File

@ -18,7 +18,8 @@ import app.pachli.util.Error
import app.pachli.util.Loading import app.pachli.util.Loading
import app.pachli.util.Resource import app.pachli.util.Resource
import app.pachli.util.Success import app.pachli.util.Success
import at.connyduck.calladapter.networkresult.fold import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.onSuccess
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.Job import kotlinx.coroutines.Job
@ -71,22 +72,21 @@ class AccountViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
mastodonApi.account(accountId) mastodonApi.account(accountId)
.fold( .onSuccess { result ->
{ account -> val account = result.body
domain = getDomain(account.url) domain = getDomain(account.url)
accountData.postValue(Success(account)) accountData.postValue(Success(account))
isDataLoading = false isDataLoading = false
isRefreshing.postValue(false) isRefreshing.postValue(false)
isFromOwnDomain = domain == activeAccount.domain isFromOwnDomain = domain == activeAccount.domain
}, }
{ t -> .onFailure { t ->
Timber.w(t, "failed obtaining account") Timber.w("failed obtaining account: %s", t)
accountData.postValue(Error(cause = t)) accountData.postValue(Error(cause = t.throwable))
isDataLoading = false isDataLoading = false
isRefreshing.postValue(false) isRefreshing.postValue(false)
}, }
)
} }
} }
} }
@ -97,15 +97,14 @@ class AccountViewModel @Inject constructor(
viewModelScope.launch { viewModelScope.launch {
mastodonApi.relationships(listOf(accountId)) mastodonApi.relationships(listOf(accountId))
.fold( .onSuccess { response ->
{ relationships -> val relationships = response.body
relationshipData.postValue(if (relationships.isNotEmpty()) Success(relationships[0]) else Error()) relationshipData.postValue(if (relationships.isNotEmpty()) Success(relationships[0]) else Error())
}, }
{ t -> .onFailure { t ->
Timber.w(t, "failed obtaining relationships") Timber.w("failed obtaining relationships: %s", t)
relationshipData.postValue(Error(cause = t)) relationshipData.postValue(Error(cause = t.throwable))
}, }
)
} }
} }
} }
@ -149,28 +148,32 @@ class AccountViewModel @Inject constructor(
fun blockDomain(instance: String) { fun blockDomain(instance: String) {
viewModelScope.launch { viewModelScope.launch {
mastodonApi.blockDomain(instance).fold({ mastodonApi.blockDomain(instance)
eventHub.dispatch(DomainMuteEvent(instance)) .onSuccess {
val relation = relationshipData.value?.data eventHub.dispatch(DomainMuteEvent(instance))
if (relation != null) { val relation = relationshipData.value?.data
relationshipData.postValue(Success(relation.copy(blockingDomain = true))) if (relation != null) {
relationshipData.postValue(Success(relation.copy(blockingDomain = true)))
}
}
.onFailure { e ->
Timber.e("Error muting %s: %s", instance, e)
} }
}, { e ->
Timber.e(e, "Error muting %s", instance)
})
} }
} }
fun unblockDomain(instance: String) { fun unblockDomain(instance: String) {
viewModelScope.launch { viewModelScope.launch {
mastodonApi.unblockDomain(instance).fold({ mastodonApi.unblockDomain(instance)
val relation = relationshipData.value?.data .onSuccess {
if (relation != null) { val relation = relationshipData.value?.data
relationshipData.postValue(Success(relation.copy(blockingDomain = false))) if (relation != null) {
relationshipData.postValue(Success(relation.copy(blockingDomain = false)))
}
}
.onFailure { e ->
Timber.e("Error unmuting %s: %s", instance, e)
} }
}, { e ->
Timber.e(e, "Error unmuting %s", instance)
})
} }
} }
@ -258,9 +261,9 @@ class AccountViewModel @Inject constructor(
} }
} }
relationshipCall.fold( relationshipCall
{ relationship -> .onSuccess { response ->
relationshipData.postValue(Success(relationship)) relationshipData.postValue(Success(response.body))
when (relationshipAction) { when (relationshipAction) {
RelationShipAction.UNFOLLOW -> eventHub.dispatch(UnfollowEvent(accountId)) RelationShipAction.UNFOLLOW -> eventHub.dispatch(UnfollowEvent(accountId))
@ -268,12 +271,11 @@ class AccountViewModel @Inject constructor(
RelationShipAction.MUTE -> eventHub.dispatch(MuteEvent(accountId)) RelationShipAction.MUTE -> eventHub.dispatch(MuteEvent(accountId))
else -> { } else -> { }
} }
}, }
{ t -> .onFailure { e ->
Timber.w(t, "failed loading relationship") Timber.w("failed loading relationship: %s", e)
relationshipData.postValue(Error(relation, cause = t)) relationshipData.postValue(Error(relation, cause = e.throwable))
}, }
)
} }
fun noteChanged(newNote: String) { fun noteChanged(newNote: String) {
@ -282,16 +284,14 @@ class AccountViewModel @Inject constructor(
noteUpdateJob = viewModelScope.launch { noteUpdateJob = viewModelScope.launch {
delay(1500) delay(1500)
mastodonApi.updateAccountNote(accountId, newNote) mastodonApi.updateAccountNote(accountId, newNote)
.fold( .onSuccess {
{ noteSaved.postValue(true)
noteSaved.postValue(true) delay(4000)
delay(4000) noteSaved.postValue(false)
noteSaved.postValue(false) }
}, .onFailure { e ->
{ t -> Timber.w("Error updating note: %s", e)
Timber.w(t, "Error updating note") }
},
)
} }
} }

View File

@ -63,6 +63,8 @@ import app.pachli.interfaces.AppBarLayoutHost
import app.pachli.view.EndlessOnScrollListener import app.pachli.view.EndlessOnScrollListener
import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.fold
import com.github.michaelbull.result.getOrElse import com.github.michaelbull.result.getOrElse
import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.onSuccess
import com.google.android.material.color.MaterialColors import com.google.android.material.color.MaterialColors
import com.google.android.material.divider.MaterialDividerItemDecoration import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
@ -229,11 +231,9 @@ class AccountListFragment :
api.blockAccount(id) api.blockAccount(id)
} else { } else {
api.unblockAccount(id) api.unblockAccount(id)
}.fold({ }
onBlockSuccess(block, id, position) .onSuccess { onBlockSuccess(block, id, position) }
}, { .onFailure { onBlockFailure(block, id, it.throwable) }
onBlockFailure(block, id, it)
})
} }
} }
@ -379,8 +379,9 @@ class AccountListFragment :
private fun fetchRelationships(ids: List<String>) { private fun fetchRelationships(ids: List<String>) {
lifecycleScope.launch { lifecycleScope.launch {
api.relationships(ids) api.relationships(ids)
.fold(::onFetchRelationshipsSuccess) { throwable -> .onSuccess { onFetchRelationshipsSuccess(it.body) }
Timber.e(throwable, "Fetch failure for relationships of accounts: %s", ids) .onFailure { throwable ->
Timber.e("Fetch failure for relationships of accounts: %s: %s", ids, throwable)
} }
} }
} }

View File

@ -17,7 +17,8 @@ import app.pachli.core.network.retrofit.MastodonApi
import app.pachli.core.ui.BackgroundMessage import app.pachli.core.ui.BackgroundMessage
import app.pachli.databinding.FragmentInstanceListBinding import app.pachli.databinding.FragmentInstanceListBinding
import app.pachli.view.EndlessOnScrollListener import app.pachli.view.EndlessOnScrollListener
import at.connyduck.calladapter.networkresult.fold import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.onSuccess
import com.google.android.material.divider.MaterialDividerItemDecoration import com.google.android.material.divider.MaterialDividerItemDecoration
import com.google.android.material.snackbar.Snackbar import com.google.android.material.snackbar.Snackbar
import dagger.hilt.android.AndroidEntryPoint import dagger.hilt.android.AndroidEntryPoint
@ -68,22 +69,22 @@ class InstanceListFragment :
override fun mute(mute: Boolean, instance: String, position: Int) { override fun mute(mute: Boolean, instance: String, position: Int) {
viewLifecycleOwner.lifecycleScope.launch { viewLifecycleOwner.lifecycleScope.launch {
if (mute) { if (mute) {
api.blockDomain(instance).fold({ api.blockDomain(instance)
adapter.addItem(instance) .onSuccess { adapter.addItem(instance) }
}, { e -> .onFailure { Timber.e(it.throwable, "Error muting domain %s", instance) }
Timber.e(e, "Error muting domain %s", instance)
})
} else { } else {
api.unblockDomain(instance).fold({ api.unblockDomain(instance)
adapter.removeItem(position) .onSuccess {
Snackbar.make(binding.recyclerView, getString(R.string.confirmation_domain_unmuted, instance), Snackbar.LENGTH_LONG) adapter.removeItem(position)
.setAction(R.string.action_undo) { Snackbar.make(binding.recyclerView, getString(R.string.confirmation_domain_unmuted, instance), Snackbar.LENGTH_LONG)
mute(true, instance, position) .setAction(R.string.action_undo) {
} mute(true, instance, position)
.show() }
}, { e -> .show()
Timber.e(e, "Error unmuting domain %s", instance) }
}) .onFailure { e ->
Timber.e(e.throwable, "Error unmuting domain %s", instance)
}
} }
} }
} }

View File

@ -39,6 +39,8 @@ import app.pachli.util.Resource
import app.pachli.util.Success import app.pachli.util.Success
import app.pachli.viewdata.StatusViewData import app.pachli.viewdata.StatusViewData
import at.connyduck.calladapter.networkresult.fold import at.connyduck.calladapter.networkresult.fold
import com.github.michaelbull.result.onFailure
import com.github.michaelbull.result.onSuccess
import dagger.hilt.android.lifecycle.HiltViewModel import dagger.hilt.android.lifecycle.HiltViewModel
import javax.inject.Inject import javax.inject.Inject
import kotlinx.coroutines.channels.BufferOverflow import kotlinx.coroutines.channels.BufferOverflow
@ -135,14 +137,9 @@ class ReportViewModel @Inject constructor(
muteStateMutable.value = Loading() muteStateMutable.value = Loading()
blockStateMutable.value = Loading() blockStateMutable.value = Loading()
viewModelScope.launch { viewModelScope.launch {
mastodonApi.relationships(ids).fold( mastodonApi.relationships(ids)
{ data -> .onSuccess { updateRelationship(it.body.firstOrNull()) }
updateRelationship(data.getOrNull(0)) .onFailure { updateRelationship(null) }
},
{
updateRelationship(null)
},
)
} }
} }
@ -163,18 +160,18 @@ class ReportViewModel @Inject constructor(
mastodonApi.unmuteAccount(accountId) mastodonApi.unmuteAccount(accountId)
} else { } else {
mastodonApi.muteAccount(accountId) mastodonApi.muteAccount(accountId)
}.fold( }
{ relationship -> .onSuccess { response ->
val relationship = response.body
val muting = relationship.muting val muting = relationship.muting
muteStateMutable.value = Success(muting) muteStateMutable.value = Success(muting)
if (muting) { if (muting) {
eventHub.dispatch(MuteEvent(accountId)) eventHub.dispatch(MuteEvent(accountId))
} }
}, }
{ t -> .onFailure { t ->
muteStateMutable.value = Error(false, t.message) muteStateMutable.value = Error(false, t.throwable.message)
}, }
)
} }
muteStateMutable.value = Loading() muteStateMutable.value = Loading()
@ -187,15 +184,18 @@ class ReportViewModel @Inject constructor(
mastodonApi.unblockAccount(accountId) mastodonApi.unblockAccount(accountId)
} else { } else {
mastodonApi.blockAccount(accountId) mastodonApi.blockAccount(accountId)
}.fold({ relationship -> }
val blocking = relationship.blocking .onSuccess { response ->
blockStateMutable.value = Success(blocking) val relationship = response.body
if (blocking) { val blocking = relationship.blocking
eventHub.dispatch(BlockEvent(accountId)) blockStateMutable.value = Success(blocking)
if (blocking) {
eventHub.dispatch(BlockEvent(accountId))
}
}
.onFailure { t ->
blockStateMutable.value = Error(false, t.throwable.message)
} }
}, { t ->
blockStateMutable.value = Error(false, t.message)
})
} }
blockStateMutable.value = Loading() blockStateMutable.value = Loading()
} }

View File

@ -381,7 +381,7 @@ interface MastodonApi {
@GET("api/v1/accounts/{id}") @GET("api/v1/accounts/{id}")
suspend fun account( suspend fun account(
@Path("id") accountId: String, @Path("id") accountId: String,
): NetworkResult<Account> ): ApiResult<Account>
/** /**
* Method to fetch statuses for the specified account. * Method to fetch statuses for the specified account.
@ -425,22 +425,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,
): NetworkResult<Relationship> ): ApiResult<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,
): NetworkResult<Relationship> ): ApiResult<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,
): NetworkResult<Relationship> ): ApiResult<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,
): NetworkResult<Relationship> ): ApiResult<Relationship>
@FormUrlEncoded @FormUrlEncoded
@POST("api/v1/accounts/{id}/mute") @POST("api/v1/accounts/{id}/mute")
@ -448,27 +448,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,
): NetworkResult<Relationship> ): ApiResult<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,
): NetworkResult<Relationship> ): ApiResult<Relationship>
@GET("api/v1/accounts/relationships") @GET("api/v1/accounts/relationships")
suspend fun relationships( suspend fun relationships(
@Query("id[]") accountIds: List<String>, @Query("id[]") accountIds: List<String>,
): NetworkResult<List<Relationship>> ): ApiResult<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,
): NetworkResult<Relationship> ): ApiResult<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,
): NetworkResult<Relationship> ): ApiResult<Relationship>
@GET("api/v1/blocks") @GET("api/v1/blocks")
suspend fun blocks( suspend fun blocks(
@ -491,12 +491,12 @@ interface MastodonApi {
@POST("api/v1/domain_blocks") @POST("api/v1/domain_blocks")
suspend fun blockDomain( suspend fun blockDomain(
@Field("domain") domain: String, @Field("domain") domain: String,
): NetworkResult<Unit> ): ApiResult<Unit>
@FormUrlEncoded @FormUrlEncoded
// @DELETE doesn't support fields // @DELETE doesn't support fields
@HTTP(method = "DELETE", path = "api/v1/domain_blocks", hasBody = true) @HTTP(method = "DELETE", path = "api/v1/domain_blocks", hasBody = true)
suspend fun unblockDomain(@Field("domain") domain: String): NetworkResult<Unit> suspend fun unblockDomain(@Field("domain") domain: String): ApiResult<Unit>
@GET("api/v1/favourites") @GET("api/v1/favourites")
suspend fun favourites( suspend fun favourites(
@ -744,7 +744,7 @@ interface MastodonApi {
suspend fun updateAccountNote( suspend fun updateAccountNote(
@Path("id") accountId: String, @Path("id") accountId: String,
@Field("comment") note: String, @Field("comment") note: String,
): NetworkResult<Relationship> ): ApiResult<Relationship>
@FormUrlEncoded @FormUrlEncoded
@POST("api/v1/push/subscription") @POST("api/v1/push/subscription")