feat(discover): Enhance server invite handling and error management

- Added a new state flow for loaded errors in `DiscoverServersListViewModel` to manage server loading errors.
- Implemented `joinServerWithoutProcessingIndicator` method to join servers without affecting the loading state in the UI.
- Updated `ServerInviteDialog` to display a loading indicator while joining a server and improved error handling.
- Modified `ServerInviteHandler` to utilize the new error state and streamline the dialog's behavior.
- Added a new string resource for the joining state message to enhance user feedback.
This commit is contained in:
AbronStudio 2025-08-05 16:30:32 +03:30
parent 4ddcfd8fa9
commit 8de2f2d8bd
4 changed files with 54 additions and 42 deletions

View File

@ -57,6 +57,10 @@ class DiscoverServersListViewModel @Inject constructor(
private val _loadedInviteData = MutableStateFlow<Invite?>(null)
val loadedInviteData: StateFlow<Invite?> = _loadedInviteData.asStateFlow()
// Error that occurred during loading server data
private val _loadedError = MutableStateFlow<RevoltError?>(null)
val loadedError: StateFlow<RevoltError?> = _loadedError.asStateFlow()
// Initialize by loading servers
init {
loadServers()
@ -113,6 +117,23 @@ class DiscoverServersListViewModel @Inject constructor(
}
}.flowOn(Dispatchers.IO)
/**
* Join server without showing loading in the server list
* This is used when joining from the dialog where we only want the button to show loading
*/
fun joinServerWithoutProcessingIndicator(inviteCode: String): Flow<RsResult<InviteJoined, RevoltError>> = flow {
// Don't set _processingServerId or _isLoading here
_error.value = null
try {
val result = joinInviteByCode(inviteCode)
emit(result)
} catch (e: Exception) {
_error.value = e.message ?: "Unknown error occurred"
emit(RsResult.err(RevoltError("Unknown")))
}
}.flowOn(Dispatchers.IO)
/**
* Load server data first, then show the dialog with the loaded data
*/
@ -126,13 +147,16 @@ class DiscoverServersListViewModel @Inject constructor(
val result = fetchInviteByCode(inviteCode)
if (result.ok) {
_loadedInviteData.value = result.value
// Only set the selected invite code after data is loaded
_selectedServerInviteCode.value = inviteCode
} else {
_error.value = result.error?.type ?: "Unknown error occurred"
// Store the error in loadedError
_loadedError.value = result.error
}
// Set the selected invite code to show the dialog in both success and error cases
_selectedServerInviteCode.value = inviteCode
} catch (e: Exception) {
_error.value = e.message ?: "Unknown error occurred"
// Handle exception by creating a generic error
_loadedError.value = RevoltError("Unknown")
_selectedServerInviteCode.value = inviteCode
} finally {
_processingServerId.value = null
}
@ -144,6 +168,7 @@ class DiscoverServersListViewModel @Inject constructor(
*/
fun clearLoadedData() {
_loadedInviteData.value = null
_loadedError.value = null
_selectedServerInviteCode.value = null
}
}

View File

@ -1,6 +1,5 @@
package chat.revolt.composables.screens.chat.discover
import android.widget.Space
import androidx.compose.foundation.Image
import androidx.compose.foundation.border
import androidx.compose.foundation.layout.Arrangement
@ -9,8 +8,8 @@ import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.CircleShape
@ -25,7 +24,6 @@ import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.ColorFilter
import androidx.compose.ui.res.painterResource
import androidx.compose.ui.res.stringResource
@ -49,35 +47,6 @@ fun ServerInviteDialog(
onJoinClick: () -> Unit,
onDismiss: () -> Unit
) {
if (isLoading) {
AlertDialog(
onDismissRequest = onDismiss,
title = {
Text(
text = stringResource(id = R.string.loading),
textAlign = TextAlign.Center,
modifier = Modifier.fillMaxWidth()
)
},
text = {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier.fillMaxWidth()
) {
CircularProgressIndicator(
modifier = Modifier.size(48.dp)
)
}
},
confirmButton = {},
dismissButton = {
TextButton(onClick = onDismiss) {
Text(text = stringResource(id = R.string.invite_cancel))
}
}
)
return
}
if (error != null) {
AlertDialog(
@ -132,9 +101,24 @@ fun ServerInviteDialog(
) {
Button(
onClick = onJoinClick,
modifier = Modifier.fillMaxWidth()
modifier = Modifier.fillMaxWidth(),
enabled = !isLoading
) {
Text(text = stringResource(id = R.string.invite_join))
if (isLoading) {
Row(
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
CircularProgressIndicator(
modifier = Modifier.size(18.dp),
strokeWidth = 2.dp
)
Spacer(modifier = Modifier.width(8.dp))
Text(text = stringResource(id = R.string.joining))
}
} else {
Text(text = stringResource(id = R.string.invite_join))
}
}
TextButton(
onClick = onDismiss,

View File

@ -29,18 +29,20 @@ fun ServerInviteHandler(
var showDialog by remember { mutableStateOf(true) }
var error by remember { mutableStateOf<RevoltError?>(null) }
// Get the pre-loaded invite data
// Get the pre-loaded invite data and error
val loadedInviteData by viewModel.loadedInviteData.collectAsState()
val loadedError by viewModel.loadedError.collectAsState()
if (showDialog && loadedInviteData != null) {
if (showDialog) {
ServerInviteDialog(
isLoading = isJoining,
invite = loadedInviteData,
error = error,
error = error ?: loadedError,
onJoinClick = {
isJoining = true
scope.launch {
viewModel.joinServer(inviteCode).collectLatest { result ->
// Don't set the processing server ID here to avoid showing loading in the list
viewModel.joinServerWithoutProcessingIndicator(inviteCode).collectLatest { result ->
isJoining = false
if (result.ok) {
showDialog = false

View File

@ -553,6 +553,7 @@
<string name="invite_error_invalid_invite">Could not find an invite with the specified code.</string>
<string name="invite_error_banned">You are banned from this server.</string>
<string name="invite_error_unknown">An unknown error occurred.</string>
<string name="joining">Joining…</string>
<string name="media_viewer_title_image">%1$s</string>
<string name="media_viewer_title_video">%1$s</string>