diff --git a/composeApp/src/androidMain/composeResources/drawable/ic_report.xml b/composeApp/src/androidMain/composeResources/drawable/ic_report.xml
new file mode 100644
index 0000000..0b85c04
--- /dev/null
+++ b/composeApp/src/androidMain/composeResources/drawable/ic_report.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/composeApp/src/androidMain/composeResources/drawable/ic_request.xml b/composeApp/src/androidMain/composeResources/drawable/ic_request.xml
new file mode 100644
index 0000000..38af524
--- /dev/null
+++ b/composeApp/src/androidMain/composeResources/drawable/ic_request.xml
@@ -0,0 +1,9 @@
+
+
+
diff --git a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt
index d29813d..01b8d72 100644
--- a/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt
+++ b/composeApp/src/androidMain/kotlin/su/reya/coop/screens/HomeScreen.kt
@@ -76,6 +76,7 @@ import androidx.compose.ui.platform.LocalContext
import androidx.compose.ui.text.font.FontStyle
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
+import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.dp
import androidx.core.app.NotificationManagerCompat
import androidx.lifecycle.compose.LifecycleResumeEffect
@@ -84,7 +85,9 @@ import coop.composeapp.generated.resources.Res
import coop.composeapp.generated.resources.ic_close
import coop.composeapp.generated.resources.ic_new_chat
import coop.composeapp.generated.resources.ic_qr
+import coop.composeapp.generated.resources.ic_request
import coop.composeapp.generated.resources.ic_scanner
+import kotlinx.coroutines.flow.flowOf
import kotlinx.coroutines.launch
import org.jetbrains.compose.resources.painterResource
import rust.nostr.sdk.PublicKey
@@ -93,6 +96,7 @@ import su.reya.coop.LocalNostrViewModel
import su.reya.coop.LocalScanResult
import su.reya.coop.LocalSnackbarHostState
import su.reya.coop.Room
+import su.reya.coop.RoomKind
import su.reya.coop.Screen
import su.reya.coop.ago
import su.reya.coop.shared.Avatar
@@ -111,8 +115,14 @@ fun HomeScreen() {
val clipboardManager = LocalClipboard.current
val viewModel = LocalNostrViewModel.current
+
+ val scope = rememberCoroutineScope()
+ val sheetState = rememberModalBottomSheetState(true)
+ val listState = rememberLazyListState()
+ val pullToRefreshState = rememberPullToRefreshState()
+
val currentUser = viewModel.currentUser() ?: return
- val currentUserProfile = viewModel.getMetadata(currentUser) ?: return
+ val currentUserProfile = viewModel.getMetadata(currentUser)
val userProfile by currentUserProfile.collectAsStateWithLifecycle()
val chatRooms by viewModel.chatRooms.collectAsStateWithLifecycle()
@@ -120,11 +130,6 @@ fun HomeScreen() {
val isPartialProcessedGiftWrap by viewModel.isPartialProcessedGiftWrap.collectAsState(initial = false)
val isBannerDismissed by viewModel.isNotificationBannerDismissed.collectAsState()
- val scope = rememberCoroutineScope()
- val sheetState = rememberModalBottomSheetState(true)
- val listState = rememberLazyListState()
- val pullToRefreshState = rememberPullToRefreshState()
-
val expandedFab by remember { derivedStateOf { listState.firstVisibleItemIndex == 0 } }
var showBottomSheet by remember { mutableStateOf(false) }
var isRefreshing by remember { mutableStateOf(false) }
@@ -140,6 +145,11 @@ fun HomeScreen() {
// State will be updated by LifecycleResumeEffect
}
+ // Partition chat rooms into requests and ongoing
+ val (requests, ongoing) = remember(chatRooms) {
+ chatRooms.partition { it.kind == RoomKind.Request }
+ }
+
LifecycleResumeEffect(context) {
isNotificationEnabled = NotificationManagerCompat.from(context).areNotificationsEnabled()
onPauseOrDispose { }
@@ -350,7 +360,11 @@ fun HomeScreen() {
state = listState,
modifier = Modifier.fillMaxSize()
) {
- items(chatRooms.toList(), key = { it.id }) { room ->
+ if (requests.isNotEmpty()) {
+ item { NewRequests(requests) }
+ }
+
+ items(ongoing, key = { it.id }) { room ->
ChatRoom(
room = room,
onClick = { navigator.navigate(Screen.Chat(room.id)) }
@@ -603,6 +617,89 @@ fun HomeScreen() {
}
}
+@Composable
+fun NewRequests(requests: List) {
+ val navigator = LocalNavigator.current
+ val viewModel = LocalNostrViewModel.current
+
+ val total = requests.size
+ val firstRoom = requests.getOrNull(0)
+ val secondRoom = requests.getOrNull(1)
+
+ val firstName by remember(firstRoom) {
+ firstRoom?.displayNameFlow(viewModel) ?: flowOf("")
+ }.collectAsStateWithLifecycle("Loading...")
+
+ val secondName by remember(secondRoom) {
+ secondRoom?.displayNameFlow(viewModel) ?: flowOf("")
+ }.collectAsStateWithLifecycle("")
+
+ val supportingText = when {
+ total == 1 && firstRoom != null -> {
+ val message = firstRoom.lastMessage ?: ""
+ "$firstName: $message"
+ }
+
+ total == 2 -> {
+ "$firstName and $secondName"
+ }
+
+ total > 2 -> {
+ val othersCount = total - 2
+ val othersText = if (othersCount == 1) "1 other" else "$othersCount others"
+ "$firstName, $secondName and $othersText"
+ }
+
+ else -> ""
+ }
+
+ ListItem(
+ modifier = Modifier.clickable {
+ navigator.navigate(Screen.RequestList)
+ },
+ leadingContent = {
+ Box(
+ modifier = Modifier
+ .size(48.dp)
+ .clip(MaterialShapes.Clover4Leaf.toShape()),
+ contentAlignment = Alignment.Center
+ ) {
+ Surface(
+ modifier = Modifier.size(48.dp),
+ color = MaterialTheme.colorScheme.tertiaryContainer,
+ ) {
+ Box(contentAlignment = Alignment.Center) {
+ Icon(
+ painter = painterResource(Res.drawable.ic_request),
+ contentDescription = "Requests",
+ tint = MaterialTheme.colorScheme.onTertiaryFixed
+ )
+ }
+ }
+ }
+ },
+ headlineContent = {
+ Text(
+ text = "Requests",
+ style = MaterialTheme.typography.titleMediumEmphasized
+ )
+ },
+ supportingContent = {
+ if (supportingText.isNotEmpty()) {
+ Text(
+ text = supportingText,
+ style = MaterialTheme.typography.bodyMedium,
+ maxLines = 1,
+ overflow = TextOverflow.Ellipsis
+ )
+ }
+ },
+ colors = ListItemDefaults.colors(
+ containerColor = MaterialTheme.colorScheme.surface
+ )
+ )
+}
+
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
@Composable
fun ChatRoom(room: Room, onClick: () -> Unit) {
@@ -636,7 +733,8 @@ fun ChatRoom(room: Room, onClick: () -> Unit) {
if (!room.lastMessage.isNullOrBlank()) {
Text(
text = room.lastMessage!!,
- style = MaterialTheme.typography.bodyMedium
+ style = MaterialTheme.typography.bodyMedium,
+ overflow = TextOverflow.Ellipsis
)
}
},