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:
parent
4ddcfd8fa9
commit
8de2f2d8bd
|
|
@ -57,6 +57,10 @@ class DiscoverServersListViewModel @Inject constructor(
|
||||||
private val _loadedInviteData = MutableStateFlow<Invite?>(null)
|
private val _loadedInviteData = MutableStateFlow<Invite?>(null)
|
||||||
val loadedInviteData: StateFlow<Invite?> = _loadedInviteData.asStateFlow()
|
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
|
// Initialize by loading servers
|
||||||
init {
|
init {
|
||||||
loadServers()
|
loadServers()
|
||||||
|
|
@ -113,6 +117,23 @@ class DiscoverServersListViewModel @Inject constructor(
|
||||||
}
|
}
|
||||||
}.flowOn(Dispatchers.IO)
|
}.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
|
* Load server data first, then show the dialog with the loaded data
|
||||||
*/
|
*/
|
||||||
|
|
@ -126,13 +147,16 @@ class DiscoverServersListViewModel @Inject constructor(
|
||||||
val result = fetchInviteByCode(inviteCode)
|
val result = fetchInviteByCode(inviteCode)
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
_loadedInviteData.value = result.value
|
_loadedInviteData.value = result.value
|
||||||
// Only set the selected invite code after data is loaded
|
|
||||||
_selectedServerInviteCode.value = inviteCode
|
|
||||||
} else {
|
} 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) {
|
} 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 {
|
} finally {
|
||||||
_processingServerId.value = null
|
_processingServerId.value = null
|
||||||
}
|
}
|
||||||
|
|
@ -144,6 +168,7 @@ class DiscoverServersListViewModel @Inject constructor(
|
||||||
*/
|
*/
|
||||||
fun clearLoadedData() {
|
fun clearLoadedData() {
|
||||||
_loadedInviteData.value = null
|
_loadedInviteData.value = null
|
||||||
|
_loadedError.value = null
|
||||||
_selectedServerInviteCode.value = null
|
_selectedServerInviteCode.value = null
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
@ -1,6 +1,5 @@
|
||||||
package chat.revolt.composables.screens.chat.discover
|
package chat.revolt.composables.screens.chat.discover
|
||||||
|
|
||||||
import android.widget.Space
|
|
||||||
import androidx.compose.foundation.Image
|
import androidx.compose.foundation.Image
|
||||||
import androidx.compose.foundation.border
|
import androidx.compose.foundation.border
|
||||||
import androidx.compose.foundation.layout.Arrangement
|
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.Row
|
||||||
import androidx.compose.foundation.layout.Spacer
|
import androidx.compose.foundation.layout.Spacer
|
||||||
import androidx.compose.foundation.layout.fillMaxWidth
|
import androidx.compose.foundation.layout.fillMaxWidth
|
||||||
import androidx.compose.foundation.layout.padding
|
|
||||||
import androidx.compose.foundation.layout.height
|
import androidx.compose.foundation.layout.height
|
||||||
|
import androidx.compose.foundation.layout.padding
|
||||||
import androidx.compose.foundation.layout.size
|
import androidx.compose.foundation.layout.size
|
||||||
import androidx.compose.foundation.layout.width
|
import androidx.compose.foundation.layout.width
|
||||||
import androidx.compose.foundation.shape.CircleShape
|
import androidx.compose.foundation.shape.CircleShape
|
||||||
|
|
@ -25,7 +24,6 @@ import androidx.compose.runtime.Composable
|
||||||
import androidx.compose.ui.Alignment
|
import androidx.compose.ui.Alignment
|
||||||
import androidx.compose.ui.Modifier
|
import androidx.compose.ui.Modifier
|
||||||
import androidx.compose.ui.draw.clip
|
import androidx.compose.ui.draw.clip
|
||||||
import androidx.compose.ui.graphics.Color
|
|
||||||
import androidx.compose.ui.graphics.ColorFilter
|
import androidx.compose.ui.graphics.ColorFilter
|
||||||
import androidx.compose.ui.res.painterResource
|
import androidx.compose.ui.res.painterResource
|
||||||
import androidx.compose.ui.res.stringResource
|
import androidx.compose.ui.res.stringResource
|
||||||
|
|
@ -49,35 +47,6 @@ fun ServerInviteDialog(
|
||||||
onJoinClick: () -> Unit,
|
onJoinClick: () -> Unit,
|
||||||
onDismiss: () -> 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) {
|
if (error != null) {
|
||||||
AlertDialog(
|
AlertDialog(
|
||||||
|
|
@ -132,9 +101,24 @@ fun ServerInviteDialog(
|
||||||
) {
|
) {
|
||||||
Button(
|
Button(
|
||||||
onClick = onJoinClick,
|
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(
|
TextButton(
|
||||||
onClick = onDismiss,
|
onClick = onDismiss,
|
||||||
|
|
|
||||||
|
|
@ -29,18 +29,20 @@ fun ServerInviteHandler(
|
||||||
var showDialog by remember { mutableStateOf(true) }
|
var showDialog by remember { mutableStateOf(true) }
|
||||||
var error by remember { mutableStateOf<RevoltError?>(null) }
|
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 loadedInviteData by viewModel.loadedInviteData.collectAsState()
|
||||||
|
val loadedError by viewModel.loadedError.collectAsState()
|
||||||
|
|
||||||
if (showDialog && loadedInviteData != null) {
|
if (showDialog) {
|
||||||
ServerInviteDialog(
|
ServerInviteDialog(
|
||||||
isLoading = isJoining,
|
isLoading = isJoining,
|
||||||
invite = loadedInviteData,
|
invite = loadedInviteData,
|
||||||
error = error,
|
error = error ?: loadedError,
|
||||||
onJoinClick = {
|
onJoinClick = {
|
||||||
isJoining = true
|
isJoining = true
|
||||||
scope.launch {
|
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
|
isJoining = false
|
||||||
if (result.ok) {
|
if (result.ok) {
|
||||||
showDialog = false
|
showDialog = false
|
||||||
|
|
|
||||||
|
|
@ -553,6 +553,7 @@
|
||||||
<string name="invite_error_invalid_invite">Could not find an invite with the specified code.</string>
|
<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_banned">You are banned from this server.</string>
|
||||||
<string name="invite_error_unknown">An unknown error occurred.</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_image">%1$s</string>
|
||||||
<string name="media_viewer_title_video">%1$s</string>
|
<string name="media_viewer_title_video">%1$s</string>
|
||||||
|
|
|
||||||
Loading…
Reference in New Issue