fix: instance unban (#887)

This commit is contained in:
Diego Beraldin 2024-05-26 11:09:50 +02:00 committed by GitHub
parent 30379ef681
commit af239aafb4
No known key found for this signature in database
GPG Key ID: B5690EEEBB952194
19 changed files with 160 additions and 94 deletions

View File

@ -4,7 +4,7 @@ import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class BlockSiteForm(
data class BlockInstanceForm(
@SerialName("instance_id") val instanceId: InstanceId,
@SerialName("block") val block: Boolean,
)

View File

@ -0,0 +1,9 @@
package com.github.diegoberaldin.raccoonforlemmy.core.api.dto
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@Serializable
data class BlockInstanceResponse(
@SerialName("blocked") val blocked: Boolean,
)

View File

@ -1,6 +1,7 @@
package com.github.diegoberaldin.raccoonforlemmy.core.api.service
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockSiteForm
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockInstanceForm
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockInstanceResponse
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.GetSiteResponse
import de.jensklingenberg.ktorfit.Response
import de.jensklingenberg.ktorfit.http.Body
@ -21,6 +22,6 @@ interface SiteService {
@Headers("Content-Type: application/json")
suspend fun block(
@Header("Authorization") authHeader: String? = null,
@Body form: BlockSiteForm,
): Response<GetSiteResponse>
@Body form: BlockInstanceForm,
): Response<BlockInstanceResponse>
}

View File

@ -423,24 +423,24 @@ class DefaultCommunityRepositoryTest {
mockk {
every { isSuccessful } returns true
every { body() } returns mockk()
val token = "fake-token"
sut.block(
auth = token,
id = communityId,
blocked = true,
)
coVerify {
communityService.block(
authHeader = token.toAuthHeader(),
withArg { data ->
assertEquals(communityId, data.communityId)
assertTrue(data.block)
},
)
}
}
val token = "fake-token"
sut.block(
auth = token,
id = communityId,
blocked = true,
)
coVerify {
communityService.block(
authHeader = token.toAuthHeader(),
withArg { data ->
assertEquals(communityId, data.communityId)
assertTrue(data.block)
},
)
}
}
@Test

View File

@ -64,7 +64,7 @@ interface CommunityRepository {
id: Long,
blocked: Boolean,
auth: String?,
): Result<Unit>
)
suspend fun banUser(
auth: String?,

View File

@ -217,21 +217,18 @@ internal class DefaultCommunityRepository(
id: Long,
blocked: Boolean,
auth: String?,
): Result<Unit> =
): Unit =
withContext(Dispatchers.IO) {
runCatching {
val data =
BlockCommunityForm(
communityId = id,
block = blocked,
auth = auth.orEmpty(),
)
services.community.block(
authHeader = auth.toAuthHeader(),
form = data,
val data =
BlockCommunityForm(
communityId = id,
block = blocked,
auth = auth.orEmpty(),
)
Unit
}
services.community.block(
authHeader = auth.toAuthHeader(),
form = data,
)
}
override suspend fun banUser(

View File

@ -1,6 +1,6 @@
package com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.repository
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockSiteForm
import com.github.diegoberaldin.raccoonforlemmy.core.api.dto.BlockInstanceForm
import com.github.diegoberaldin.raccoonforlemmy.core.api.provider.ServiceProvider
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.AccountBansModel
import com.github.diegoberaldin.raccoonforlemmy.domain.lemmy.data.AccountSettingsModel
@ -61,20 +61,18 @@ internal class DefaultSiteRepository(
id: Long,
blocked: Boolean,
auth: String?,
): Result<Unit> =
): Unit =
withContext(Dispatchers.IO) {
runCatching {
val data =
BlockSiteForm(
instanceId = id,
block = blocked,
)
services.site.block(
authHeader = auth.toAuthHeader(),
form = data,
val data =
BlockInstanceForm(
instanceId = id,
block = blocked,
)
Unit
}
services.site.block(
authHeader = auth.toAuthHeader(),
form = data,
)
Unit
}
override suspend fun getMetadata(url: String): MetadataModel? =

View File

@ -285,21 +285,19 @@ internal class DefaultUserRepository(
id: Long,
blocked: Boolean,
auth: String?,
): Result<Unit> =
): Unit =
withContext(Dispatchers.IO) {
runCatching {
val data =
BlockPersonForm(
personId = id,
block = blocked,
auth = auth.orEmpty(),
)
services.user.block(
authHeader = auth.toAuthHeader(),
form = data,
val data =
BlockPersonForm(
personId = id,
block = blocked,
auth = auth.orEmpty(),
)
Unit
}
services.user.block(
authHeader = auth.toAuthHeader(),
form = data,
)
Unit
}
override suspend fun getModeratedCommunities(

View File

@ -18,7 +18,7 @@ interface SiteRepository {
id: Long,
blocked: Boolean,
auth: String? = null,
): Result<Unit>
)
suspend fun getMetadata(url: String): MetadataModel?

View File

@ -90,7 +90,7 @@ interface UserRepository {
id: Long,
blocked: Boolean,
auth: String? = null,
): Result<Unit>
)
suspend fun getModeratedCommunities(
auth: String? = null,

View File

@ -394,7 +394,7 @@ class CommunityDetailScreen(
this +=
Option(
OptionId.Block,
LocalXmlStrings.current.communityDetailBlock,
LocalXmlStrings.current.blockActionCommunity,
)
this +=
Option(

View File

@ -595,7 +595,11 @@ class CommunityDetailViewModel(
updateState { it.copy(asyncInProgress = true) }
try {
val auth = identityRepository.authToken.value
communityRepository.block(communityId, true, auth).getOrThrow()
communityRepository.block(
id = communityId,
blocked = true,
auth = auth,
)
emitEffect(CommunityDetailMviModel.Effect.Success)
} catch (e: Throwable) {
emitEffect(CommunityDetailMviModel.Effect.Error(e.message))
@ -612,7 +616,7 @@ class CommunityDetailViewModel(
val community = uiState.value.community
val instanceId = community.instanceId
val auth = identityRepository.authToken.value
siteRepository.block(instanceId, true, auth).getOrThrow()
siteRepository.block(instanceId, true, auth)
emitEffect(CommunityDetailMviModel.Effect.Success)
} catch (e: Throwable) {
emitEffect(CommunityDetailMviModel.Effect.Error(e.message))

View File

@ -40,5 +40,9 @@ interface ManageBanMviModel :
val bannedInstances: List<InstanceModel> = emptyList(),
)
sealed interface Effect
sealed interface Effect {
data object Success : Effect
data class Failure(val message: String?) : Effect
}
}

View File

@ -14,6 +14,7 @@ import androidx.compose.foundation.lazy.rememberLazyListState
import androidx.compose.material.ExperimentalMaterialApi
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.automirrored.filled.ArrowBack
import androidx.compose.material.pullrefresh.PullRefreshIndicator
import androidx.compose.material.pullrefresh.pullRefresh
import androidx.compose.material.pullrefresh.rememberPullRefreshState
import androidx.compose.material3.ExperimentalMaterial3Api
@ -27,9 +28,11 @@ import androidx.compose.material3.TopAppBar
import androidx.compose.material3.TopAppBarDefaults
import androidx.compose.material3.rememberTopAppBarState
import androidx.compose.runtime.Composable
import androidx.compose.runtime.LaunchedEffect
import androidx.compose.runtime.collectAsState
import androidx.compose.runtime.getValue
import androidx.compose.runtime.remember
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.input.nestedscroll.nestedScroll
@ -50,6 +53,8 @@ import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.onClick
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallback
import com.github.diegoberaldin.raccoonforlemmy.core.utils.compose.rememberCallbackArgs
import com.github.diegoberaldin.raccoonforlemmy.unit.manageban.components.InstanceItem
import kotlinx.coroutines.flow.launchIn
import kotlinx.coroutines.flow.onEach
class ManageBanScreen : Screen {
@OptIn(ExperimentalMaterial3Api::class, ExperimentalMaterialApi::class)
@ -64,6 +69,22 @@ class ManageBanScreen : Screen {
val settingsRepository = remember { getSettingsRepository() }
val settings by settingsRepository.currentSettings.collectAsState()
val lazyListState = rememberLazyListState()
val successMessage = LocalXmlStrings.current.messageOperationSuccessful
val errorMessage = LocalXmlStrings.current.messageGenericError
LaunchedEffect(model) {
model.effects.onEach { evt ->
when (evt) {
is ManageBanMviModel.Effect.Failure -> {
snackbarHostState.showSnackbar(evt.message ?: errorMessage)
}
ManageBanMviModel.Effect.Success -> {
snackbarHostState.showSnackbar(successMessage)
}
}
}.launchIn(this)
}
Scaffold(
modifier = Modifier.background(MaterialTheme.colorScheme.background),
@ -200,13 +221,11 @@ class ManageBanScreen : Screen {
)
},
onOptionSelected =
rememberCallbackArgs(model) { optionId ->
rememberCallbackArgs(user) { optionId ->
when (optionId) {
OptionId.Unban -> {
model.reduce(
ManageBanMviModel.Intent.UnblockUser(
user.id,
),
ManageBanMviModel.Intent.UnblockUser(user.id),
)
}
@ -250,13 +269,11 @@ class ManageBanScreen : Screen {
)
},
onOptionSelected =
rememberCallbackArgs(model) { optionId ->
rememberCallbackArgs(community) { optionId ->
when (optionId) {
OptionId.Unban -> {
model.reduce(
ManageBanMviModel.Intent.UnblockCommunity(
community.id,
),
ManageBanMviModel.Intent.UnblockCommunity(community.id),
)
}
@ -298,13 +315,11 @@ class ManageBanScreen : Screen {
)
},
onOptionSelected =
rememberCallbackArgs(model) { optionId ->
rememberCallbackArgs(instance) { optionId ->
when (optionId) {
OptionId.Unban -> {
model.reduce(
ManageBanMviModel.Intent.UnblockInstance(
instance.id,
),
ManageBanMviModel.Intent.UnblockInstance(instance.id),
)
}
@ -317,6 +332,14 @@ class ManageBanScreen : Screen {
}
}
}
PullRefreshIndicator(
refreshing = uiState.refreshing,
state = pullRefreshState,
modifier = Modifier.align(Alignment.TopCenter),
backgroundColor = MaterialTheme.colorScheme.background,
contentColor = MaterialTheme.colorScheme.onBackground,
)
}
}
}

View File

@ -74,7 +74,7 @@ class ManageBanViewModel(
private fun unbanUser(id: Long) {
screenModelScope.launch {
val auth = identityRepository.authToken.value.orEmpty()
runCatching {
try {
userRepository.block(
id = id,
blocked = false,
@ -83,6 +83,9 @@ class ManageBanViewModel(
updateState {
it.copy(bannedUsers = it.bannedUsers.filter { e -> e.id != id })
}
emitEffect(ManageBanMviModel.Effect.Success)
} catch (e: Throwable) {
emitEffect(ManageBanMviModel.Effect.Failure(e.message))
}
}
}
@ -90,7 +93,7 @@ class ManageBanViewModel(
private fun unbanCommunity(id: Long) {
screenModelScope.launch {
val auth = identityRepository.authToken.value.orEmpty()
runCatching {
try {
communityRepository.block(
id = id,
blocked = false,
@ -99,6 +102,9 @@ class ManageBanViewModel(
updateState {
it.copy(bannedCommunities = it.bannedCommunities.filter { e -> e.id != id })
}
emitEffect(ManageBanMviModel.Effect.Success)
} catch (e: Throwable) {
emitEffect(ManageBanMviModel.Effect.Failure(e.message))
}
}
}
@ -106,7 +112,7 @@ class ManageBanViewModel(
private fun unbanInstance(id: Long) {
screenModelScope.launch {
val auth = identityRepository.authToken.value.orEmpty()
runCatching {
try {
siteRepository.block(
id = id,
blocked = false,
@ -115,6 +121,9 @@ class ManageBanViewModel(
updateState {
it.copy(bannedInstances = it.bannedInstances.filter { e -> e.id != id })
}
emitEffect(ManageBanMviModel.Effect.Success)
} catch (e: Throwable) {
emitEffect(ManageBanMviModel.Effect.Failure(e.message))
}
}
}

View File

@ -63,8 +63,9 @@ fun InstanceItem(
)
Text(
modifier = Modifier.weight(1f),
text = name,
style = MaterialTheme.typography.bodySmall,
style = MaterialTheme.typography.bodyLarge,
color = fullColor,
)

View File

@ -517,25 +517,39 @@ class PostListViewModel(
private fun blockUser(userId: Long) {
screenModelScope.launch {
val auth = identityRepository.authToken.value
userRepository.block(userId, true, auth)
runCatching {
val auth = identityRepository.authToken.value
userRepository.block(
id = userId,
blocked = true,
auth = auth,
)
}
}
}
private fun blockCommunity(communityId: Long) {
screenModelScope.launch {
val auth = identityRepository.authToken.value
communityRepository.block(communityId, true, auth)
runCatching {
val auth = identityRepository.authToken.value
communityRepository.block(
id = communityId,
blocked = true,
auth = auth,
)
}
}
}
private fun blockInstance(instanceId: Long) {
screenModelScope.launch {
try {
runCatching {
val auth = identityRepository.authToken.value
siteRepository.block(instanceId, true, auth)
} catch (e: Throwable) {
e.printStackTrace()
siteRepository.block(
id = instanceId,
blocked = true,
auth = auth,
)
}
}
}

View File

@ -278,7 +278,7 @@ class UserDetailScreen(
this +=
Option(
OptionId.Block,
LocalXmlStrings.current.communityDetailBlock,
LocalXmlStrings.current.blockActionUser,
)
this +=
Option(

View File

@ -536,7 +536,11 @@ class UserDetailViewModel(
updateState { it.copy(asyncInProgress = true) }
try {
val auth = identityRepository.authToken.value
userRepository.block(userId, true, auth).getOrThrow()
userRepository.block(
id = userId,
blocked = true,
auth = auth,
)
emitEffect(UserDetailMviModel.Effect.Success)
} catch (e: Throwable) {
emitEffect(UserDetailMviModel.Effect.Error(e.message))
@ -553,7 +557,11 @@ class UserDetailViewModel(
val user = uiState.value.user
val instanceId = user.instanceId
val auth = identityRepository.authToken.value
siteRepository.block(instanceId, true, auth).getOrThrow()
siteRepository.block(
id = instanceId,
blocked = true,
auth = auth,
)
emitEffect(UserDetailMviModel.Effect.Success)
} catch (e: Throwable) {
emitEffect(UserDetailMviModel.Effect.Error(e.message))