refactor: Convert account relationship API calls to use ApiResult (#1109)
This commit is contained in:
parent
88561afcca
commit
654a81a136
|
@ -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")
|
}
|
||||||
},
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -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)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
|
@ -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()
|
||||||
}
|
}
|
||||||
|
|
|
@ -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")
|
||||||
|
|
Loading…
Reference in New Issue