chore: confirm before delete contact #35
@@ -3,11 +3,15 @@ package su.reya.coop.screens
|
|||||||
import androidx.compose.foundation.layout.Arrangement
|
import androidx.compose.foundation.layout.Arrangement
|
||||||
import androidx.compose.foundation.layout.Box
|
import androidx.compose.foundation.layout.Box
|
||||||
import androidx.compose.foundation.layout.Column
|
import androidx.compose.foundation.layout.Column
|
||||||
|
import androidx.compose.foundation.layout.PaddingValues
|
||||||
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.padding
|
import androidx.compose.foundation.layout.padding
|
||||||
|
import androidx.compose.foundation.lazy.LazyColumn
|
||||||
|
import androidx.compose.foundation.lazy.itemsIndexed
|
||||||
import androidx.compose.foundation.text.KeyboardActions
|
import androidx.compose.foundation.text.KeyboardActions
|
||||||
import androidx.compose.foundation.text.KeyboardOptions
|
import androidx.compose.foundation.text.KeyboardOptions
|
||||||
|
import androidx.compose.material3.AlertDialog
|
||||||
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
import androidx.compose.material3.ExperimentalMaterial3ExpressiveApi
|
||||||
import androidx.compose.material3.ExtendedFloatingActionButton
|
import androidx.compose.material3.ExtendedFloatingActionButton
|
||||||
import androidx.compose.material3.Icon
|
import androidx.compose.material3.Icon
|
||||||
@@ -20,6 +24,7 @@ import androidx.compose.material3.Scaffold
|
|||||||
import androidx.compose.material3.SegmentedListItem
|
import androidx.compose.material3.SegmentedListItem
|
||||||
import androidx.compose.material3.SnackbarHost
|
import androidx.compose.material3.SnackbarHost
|
||||||
import androidx.compose.material3.Text
|
import androidx.compose.material3.Text
|
||||||
|
import androidx.compose.material3.TextButton
|
||||||
import androidx.compose.material3.TooltipAnchorPosition
|
import androidx.compose.material3.TooltipAnchorPosition
|
||||||
import androidx.compose.material3.TooltipBox
|
import androidx.compose.material3.TooltipBox
|
||||||
import androidx.compose.material3.TooltipDefaults
|
import androidx.compose.material3.TooltipDefaults
|
||||||
@@ -54,6 +59,7 @@ import kotlinx.coroutines.launch
|
|||||||
import org.jetbrains.compose.resources.painterResource
|
import org.jetbrains.compose.resources.painterResource
|
||||||
import rust.nostr.sdk.Nip05Address
|
import rust.nostr.sdk.Nip05Address
|
||||||
import rust.nostr.sdk.PublicKey
|
import rust.nostr.sdk.PublicKey
|
||||||
|
import su.reya.coop.LocalChatViewModel
|
||||||
import su.reya.coop.LocalNavigator
|
import su.reya.coop.LocalNavigator
|
||||||
import su.reya.coop.LocalNostrViewModel
|
import su.reya.coop.LocalNostrViewModel
|
||||||
import su.reya.coop.LocalSnackbarHostState
|
import su.reya.coop.LocalSnackbarHostState
|
||||||
@@ -67,9 +73,11 @@ fun ContactListScreen() {
|
|||||||
val navigator = LocalNavigator.current
|
val navigator = LocalNavigator.current
|
||||||
val snackbarHostState = LocalSnackbarHostState.current
|
val snackbarHostState = LocalSnackbarHostState.current
|
||||||
val nostrViewModel = LocalNostrViewModel.current
|
val nostrViewModel = LocalNostrViewModel.current
|
||||||
|
val chatViewModel = LocalChatViewModel.current
|
||||||
|
|
||||||
val contactList by nostrViewModel.contactList.collectAsStateWithLifecycle()
|
val contactList by nostrViewModel.contactList.collectAsStateWithLifecycle()
|
||||||
var openAddContactDialog by remember { mutableStateOf(false) }
|
var openAddContactDialog by remember { mutableStateOf(false) }
|
||||||
|
var contactToDelete by remember { mutableStateOf<PublicKey?>(null) }
|
||||||
|
|
||||||
Scaffold(
|
Scaffold(
|
||||||
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
containerColor = MaterialTheme.colorScheme.surfaceContainer,
|
||||||
@@ -128,43 +136,54 @@ fun ContactListScreen() {
|
|||||||
}
|
}
|
||||||
},
|
},
|
||||||
content = { innerPadding ->
|
content = { innerPadding ->
|
||||||
Column(
|
if (contactList.isNotEmpty()) {
|
||||||
modifier = Modifier
|
val contacts = remember(contactList) { contactList.toList() }
|
||||||
.fillMaxWidth()
|
|
||||||
.padding(16.dp)
|
LazyColumn(
|
||||||
.padding(innerPadding),
|
modifier = Modifier
|
||||||
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
|
.fillMaxSize()
|
||||||
) {
|
.padding(top = innerPadding.calculateTopPadding()),
|
||||||
if (contactList.isNotEmpty()) {
|
contentPadding = PaddingValues(16.dp),
|
||||||
contactList.forEachIndexed { index, pubkey ->
|
verticalArrangement = Arrangement.spacedBy(ListItemDefaults.SegmentedGap),
|
||||||
|
) {
|
||||||
|
itemsIndexed(contacts) { index, pubkey ->
|
||||||
ContactListItem(
|
ContactListItem(
|
||||||
pubkey = pubkey,
|
pubkey = pubkey,
|
||||||
index = index,
|
index = index,
|
||||||
total = contactList.size,
|
total = contacts.size,
|
||||||
onClick = {})
|
onClick = {
|
||||||
|
val room = chatViewModel.createChatRoom(listOf(pubkey))
|
||||||
|
navigator.navigate(Screen.Chat(room))
|
||||||
|
},
|
||||||
|
onLongClick = {
|
||||||
|
contactToDelete = pubkey
|
||||||
|
}
|
||||||
|
)
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
Box(
|
} else {
|
||||||
modifier = Modifier.fillMaxSize(),
|
Box(
|
||||||
contentAlignment = Alignment.Center
|
modifier = Modifier
|
||||||
|
.fillMaxSize()
|
||||||
|
.padding(innerPadding),
|
||||||
|
contentAlignment = Alignment.Center
|
||||||
|
) {
|
||||||
|
Column(
|
||||||
|
horizontalAlignment = Alignment.CenterHorizontally,
|
||||||
|
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||||
) {
|
) {
|
||||||
Column(
|
Text(
|
||||||
horizontalAlignment = Alignment.CenterHorizontally,
|
text = "No contacts yet",
|
||||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
style = MaterialTheme.typography.titleLargeEmphasized.copy(
|
||||||
) {
|
fontWeight = FontWeight.SemiBold
|
||||||
Text(
|
),
|
||||||
text = "No contacts yet",
|
color = MaterialTheme.colorScheme.onSurface
|
||||||
style = MaterialTheme.typography.titleLargeEmphasized.copy(
|
)
|
||||||
fontWeight = FontWeight.SemiBold
|
Text(
|
||||||
),
|
text = "Your contacts will appear here",
|
||||||
color = MaterialTheme.colorScheme.onSurface
|
style = MaterialTheme.typography.bodyMedium,
|
||||||
)
|
color = MaterialTheme.colorScheme.outline
|
||||||
Text(
|
)
|
||||||
text = "Your contacts will appear here",
|
|
||||||
style = MaterialTheme.typography.bodyMedium,
|
|
||||||
color = MaterialTheme.colorScheme.outline
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -174,6 +193,29 @@ fun ContactListScreen() {
|
|||||||
if (openAddContactDialog) {
|
if (openAddContactDialog) {
|
||||||
AddContactDialog(onDismissRequest = { openAddContactDialog = false })
|
AddContactDialog(onDismissRequest = { openAddContactDialog = false })
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (contactToDelete != null) {
|
||||||
|
AlertDialog(
|
||||||
|
onDismissRequest = { contactToDelete = null },
|
||||||
|
title = { Text("Delete Contact") },
|
||||||
|
text = { Text("Are you sure you want to remove this contact?") },
|
||||||
|
confirmButton = {
|
||||||
|
TextButton(
|
||||||
|
onClick = {
|
||||||
|
contactToDelete?.let { nostrViewModel.removeContact(it) }
|
||||||
|
contactToDelete = null
|
||||||
|
}
|
||||||
|
) {
|
||||||
|
Text("Delete")
|
||||||
|
}
|
||||||
|
},
|
||||||
|
dismissButton = {
|
||||||
|
TextButton(onClick = { contactToDelete = null }) {
|
||||||
|
Text("Cancel")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
@OptIn(ExperimentalMaterial3ExpressiveApi::class)
|
||||||
@@ -282,6 +324,7 @@ fun ContactListItem(
|
|||||||
index: Int,
|
index: Int,
|
||||||
total: Int = 0,
|
total: Int = 0,
|
||||||
onClick: () -> Unit,
|
onClick: () -> Unit,
|
||||||
|
onLongClick: () -> Unit,
|
||||||
) {
|
) {
|
||||||
val nostrViewModel = LocalNostrViewModel.current
|
val nostrViewModel = LocalNostrViewModel.current
|
||||||
val profileFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
val profileFlow = remember(pubkey) { nostrViewModel.getMetadata(pubkey) }
|
||||||
@@ -289,7 +332,7 @@ fun ContactListItem(
|
|||||||
|
|
||||||
SegmentedListItem(
|
SegmentedListItem(
|
||||||
onClick = onClick,
|
onClick = onClick,
|
||||||
onLongClick = { nostrViewModel.removeContact(pubkey) },
|
onLongClick = onLongClick,
|
||||||
shapes = ListItemDefaults.segmentedShapes(
|
shapes = ListItemDefaults.segmentedShapes(
|
||||||
index = index,
|
index = index,
|
||||||
count = total
|
count = total
|
||||||
@@ -301,11 +344,11 @@ fun ContactListItem(
|
|||||||
size = 36.dp
|
size = 36.dp
|
||||||
)
|
)
|
||||||
},
|
},
|
||||||
supportingContent = { Text(text = pubkey.short()) },
|
supportingContent = { Text(pubkey.short()) },
|
||||||
content = {
|
content = {
|
||||||
Text(
|
Text(
|
||||||
text = profile?.name ?: "No name",
|
text = profile?.name ?: "No name",
|
||||||
style = MaterialTheme.typography.titleMediumEmphasized,
|
style = MaterialTheme.typography.titleMedium,
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
)
|
)
|
||||||
|
|||||||
@@ -130,7 +130,7 @@ class MessageManager(private val nostr: Nostr) {
|
|||||||
val filter = Filter().identifier(giftId.toHex())
|
val filter = Filter().identifier(giftId.toHex())
|
||||||
val event = client?.database()?.query(filter)?.first()
|
val event = client?.database()?.query(filter)?.first()
|
||||||
|
|
||||||
return event?.content()?.let { UnsignedEvent.fromJson(it) }
|
return event?.content()?.let { UnsignedEvent.fromJson(it).ensureId() }
|
||||||
} catch (e: Throwable) {
|
} catch (e: Throwable) {
|
||||||
throw IllegalStateException("Failed to get cached rumor: ${e.message}", e)
|
throw IllegalStateException("Failed to get cached rumor: ${e.message}", e)
|
||||||
}
|
}
|
||||||
|
|||||||
Reference in New Issue
Block a user