Refactor FollowSetsManagementDialog UI to correctly adapt to follow set state. Small adjustment in the NostrUserListFeedViewModel.

This commit is contained in:
KotlinGeekDev
2025-09-22 15:41:17 +01:00
parent 9f5301d30a
commit 1e18edfc02
2 changed files with 131 additions and 72 deletions

View File

@@ -57,7 +57,7 @@ class NostrUserListFeedViewModel(
private val _feedContent = MutableStateFlow<FollowSetState>(FollowSetState.Loading) private val _feedContent = MutableStateFlow<FollowSetState>(FollowSetState.Loading)
val feedContent = _feedContent.asStateFlow() val feedContent = _feedContent.asStateFlow()
private fun refresh() { fun refresh() {
viewModelScope.launch(Dispatchers.IO) { viewModelScope.launch(Dispatchers.IO) {
refreshSuspended() refreshSuspended()
} }
@@ -105,7 +105,7 @@ class NostrUserListFeedViewModel(
} }
} catch (e: Exception) { } catch (e: Exception) {
Log.e( Log.e(
"NostrUserListFeedViewModel", this.javaClass.simpleName,
"refreshSuspended: Error loading or refreshing feed -> ${e.message}", "refreshSuspended: Error loading or refreshing feed -> ${e.message}",
) )
_feedContent.update { FollowSetState.FeedError(e.message.toString()) } _feedContent.update { FollowSetState.FeedError(e.message.toString()) }

View File

@@ -29,6 +29,7 @@ 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.consumeWindowInsets import androidx.compose.foundation.layout.consumeWindowInsets
import androidx.compose.foundation.layout.fillMaxHeight
import androidx.compose.foundation.layout.fillMaxSize import androidx.compose.foundation.layout.fillMaxSize
import androidx.compose.foundation.layout.fillMaxWidth import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.imePadding import androidx.compose.foundation.layout.imePadding
@@ -43,6 +44,7 @@ import androidx.compose.material.icons.filled.Add
import androidx.compose.material.icons.filled.Delete import androidx.compose.material.icons.filled.Delete
import androidx.compose.material3.AlertDialogDefaults import androidx.compose.material3.AlertDialogDefaults
import androidx.compose.material3.ButtonDefaults import androidx.compose.material3.ButtonDefaults
import androidx.compose.material3.CircularProgressIndicator
import androidx.compose.material3.ExperimentalMaterial3Api import androidx.compose.material3.ExperimentalMaterial3Api
import androidx.compose.material3.FilterChip import androidx.compose.material3.FilterChip
import androidx.compose.material3.HorizontalDivider import androidx.compose.material3.HorizontalDivider
@@ -80,6 +82,7 @@ import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.FollowSetState
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.ListVisibility import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.ListVisibility
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NewListCreationDialog import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NewListCreationDialog
import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NostrUserListFeedViewModel import com.vitorpamplona.amethyst.ui.screen.loggedIn.lists.NostrUserListFeedViewModel
import com.vitorpamplona.amethyst.ui.stringRes
import com.vitorpamplona.amethyst.ui.theme.ButtonBorder import com.vitorpamplona.amethyst.ui.theme.ButtonBorder
import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer import com.vitorpamplona.amethyst.ui.theme.StdHorzSpacer
import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer import com.vitorpamplona.amethyst.ui.theme.StdVertSpacer
@@ -95,66 +98,66 @@ fun FollowSetsManagementDialog(
val followSetsState by followSetsViewModel.feedContent.collectAsState() val followSetsState by followSetsViewModel.feedContent.collectAsState()
val userInfo by remember { derivedStateOf { LocalCache.getOrCreateUser(userHex) } } val userInfo by remember { derivedStateOf { LocalCache.getOrCreateUser(userHex) } }
when (followSetsState) { Scaffold(
is FollowSetState.Loaded -> { modifier =
val lists = (followSetsState as FollowSetState.Loaded).feed Modifier
.fillMaxSize()
Scaffold( .recalculateWindowInsets(),
modifier = containerColor = AlertDialogDefaults.containerColor,
Modifier topBar = {
.fillMaxSize() TopAppBar(
.recalculateWindowInsets(), title = {
containerColor = AlertDialogDefaults.containerColor, Column {
topBar = { Text(
TopAppBar( text = "Your Lists",
title = { fontWeight = FontWeight.SemiBold,
Column { )
Text(
text = "Your Lists",
fontWeight = FontWeight.SemiBold,
)
// HorizontalDivider() // HorizontalDivider()
}
},
navigationIcon = {
IconButton(
onClick = { navigator.popBack() },
) {
ArrowBackIcon()
}
},
colors =
TopAppBarDefaults
.topAppBarColors(
containerColor = AlertDialogDefaults.containerColor,
),
)
},
floatingActionButton = {
OutlinedButton(
onClick = {
navigator.popBack()
},
shape = ButtonBorder,
colors = ButtonDefaults.filledTonalButtonColors(),
elevation = ButtonDefaults.elevatedButtonElevation(defaultElevation = 6.0.dp),
) {
Text(text = "Cancel", fontWeight = FontWeight.SemiBold)
} }
}, },
) { contentPadding -> navigationIcon = {
Column( IconButton(
modifier = onClick = { navigator.popBack() },
Modifier ) {
.verticalScroll(rememberScrollState()) ArrowBackIcon()
.padding( }
start = 10.dp, },
end = 10.dp, colors =
top = contentPadding.calculateTopPadding(), TopAppBarDefaults
bottom = contentPadding.calculateBottomPadding(), .topAppBarColors(
).consumeWindowInsets(contentPadding) containerColor = AlertDialogDefaults.containerColor,
.imePadding(), ),
) { )
},
floatingActionButton = {
OutlinedButton(
onClick = {
navigator.popBack()
},
shape = ButtonBorder,
colors = ButtonDefaults.filledTonalButtonColors(),
elevation = ButtonDefaults.elevatedButtonElevation(defaultElevation = 6.0.dp),
) {
Text(text = "Cancel", fontWeight = FontWeight.SemiBold)
}
},
) { contentPadding ->
Column(
modifier =
Modifier
.verticalScroll(rememberScrollState())
.padding(
start = 10.dp,
end = 10.dp,
top = contentPadding.calculateTopPadding(),
bottom = contentPadding.calculateBottomPadding(),
).consumeWindowInsets(contentPadding)
.imePadding(),
) {
when (followSetsState) {
is FollowSetState.Loaded -> {
val lists = (followSetsState as FollowSetState.Loaded).feed
lists.forEachIndexed { index, list -> lists.forEachIndexed { index, list ->
Spacer(StdVertSpacer) Spacer(StdVertSpacer)
FollowSetItem( FollowSetItem(
@@ -191,22 +194,78 @@ fun FollowSetsManagementDialog(
}, },
) )
} }
FollowSetsCreationMenu( }
userName = userInfo.toBestDisplayName(),
onSetCreate = { setName, setIsPrivate, description -> FollowSetState.Empty -> {
followSetsViewModel.addFollowSet( EmptyOrNoneFound { followSetsViewModel.refresh() }
setName = setName, }
setDescription = description,
isListPrivate = setIsPrivate, is FollowSetState.FeedError -> {
optionalFirstMemberHex = userHex, val errorMsg = (followSetsState as FollowSetState.FeedError).errorMessage
account = account, ErrorMessage(errorMsg) { followSetsViewModel.refresh() }
) }
},
) FollowSetState.Loading -> {
Loading()
} }
} }
if (followSetsState != FollowSetState.Loading) {
FollowSetsCreationMenu(
userName = userInfo.toBestDisplayName(),
onSetCreate = { setName, setIsPrivate, description ->
followSetsViewModel.addFollowSet(
setName = setName,
setDescription = description,
isListPrivate = setIsPrivate,
optionalFirstMemberHex = userHex,
account = account,
)
},
)
}
} }
else -> {} }
}
@Composable
private fun Loading() {
Column(
Modifier.fillMaxWidth().fillMaxHeight(0.5f),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
CircularProgressIndicator()
Text(stringRes(R.string.loading_feed))
}
}
@Composable
private fun EmptyOrNoneFound(onRefresh: () -> Unit) {
Column(
Modifier.fillMaxWidth().fillMaxHeight(0.5f),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text("No follow sets were found, or you don't have any follow sets. Tap below to refresh, or use the menu to create one.")
Spacer(modifier = StdVertSpacer)
OutlinedButton(onClick = onRefresh) { Text(text = stringRes(R.string.refresh)) }
}
}
@Composable
private fun ErrorMessage(
errorMsg: String,
onRefresh: () -> Unit,
) {
Column(
Modifier.fillMaxWidth().fillMaxHeight(0.5f),
horizontalAlignment = Alignment.CenterHorizontally,
verticalArrangement = Arrangement.Center,
) {
Text("There was a problem while fetching: $errorMsg")
Spacer(modifier = StdVertSpacer)
OutlinedButton(onClick = onRefresh) { Text(text = stringRes(R.string.refresh)) }
} }
} }