mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-02 03:26:11 +00:00
android, desktop: deleted contacts and requests
This commit is contained in:
+29
@@ -76,6 +76,27 @@ fun ContactListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>, oneHand
|
||||
oneHandUI
|
||||
)
|
||||
}
|
||||
is ChatInfo.ContactConnection -> {
|
||||
ChatListNavLinkLayout(
|
||||
chatLinkPreview = {
|
||||
tryOrShowError("${chat.id}ContactListNavLink", error = { ErrorChatListItem() }) {
|
||||
ContactPreviewView(chat, disabled)
|
||||
}
|
||||
},
|
||||
click = {
|
||||
contactRequestAlertDialog(
|
||||
rhId = chat.remoteHostId,
|
||||
connection = chat.chatInfo.contactConnection
|
||||
)
|
||||
},
|
||||
dropdownMenuItems = {},
|
||||
showMenu,
|
||||
disabled,
|
||||
selectedChat,
|
||||
nextChatSelected,
|
||||
oneHandUI
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -111,4 +132,12 @@ fun DeleteContactAction(chat: Chat, chatModel: ChatModel, showMenu: MutableState
|
||||
},
|
||||
color = Color.Red
|
||||
)
|
||||
}
|
||||
|
||||
fun contactRequestAlertDialog(rhId: Long?, connection: PendingContactConnection) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = "Connection not yet accepted",
|
||||
text = "${connection.displayName} didn't accept your connection request yet.",
|
||||
hostDevice = hostDevice(rhId),
|
||||
)
|
||||
}
|
||||
+31
-5
@@ -9,6 +9,7 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -27,15 +28,31 @@ fun ContactPreviewView(
|
||||
val cInfo = chat.chatInfo
|
||||
|
||||
@Composable
|
||||
fun inactiveIcon() {
|
||||
fun OverlayIcon(painter: Painter, description: String) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_cancel_filled),
|
||||
stringResource(MR.strings.icon_descr_group_inactive),
|
||||
painter,
|
||||
description,
|
||||
Modifier.size(18.dp).background(MaterialTheme.colors.background, CircleShape),
|
||||
tint = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun pendingIcon() {
|
||||
OverlayIcon(
|
||||
painterResource(MR.images.ic_timer),
|
||||
stringResource(MR.strings.icon_descr_pending_contact_connection)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun inactiveIcon() {
|
||||
OverlayIcon(
|
||||
painterResource(MR.images.ic_cancel_filled),
|
||||
stringResource(MR.strings.icon_descr_group_inactive)
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun chatPreviewImageOverlayIcon() {
|
||||
when (cInfo) {
|
||||
@@ -43,7 +60,7 @@ fun ContactPreviewView(
|
||||
if (!cInfo.contact.active) {
|
||||
inactiveIcon()
|
||||
}
|
||||
|
||||
is ChatInfo.ContactConnection -> pendingIcon()
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -78,6 +95,15 @@ fun ContactPreviewView(
|
||||
color = Color.Unspecified
|
||||
)
|
||||
}
|
||||
is ChatInfo.ContactConnection ->
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
Text(
|
||||
cInfo.chatViewName,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
@@ -100,7 +126,7 @@ fun ContactPreviewView(
|
||||
|
||||
Spacer(Modifier.fillMaxWidth().weight(1f))
|
||||
|
||||
if (chat.chatInfo is ChatInfo.ContactRequest) {
|
||||
if (chat.chatInfo is ChatInfo.ContactRequest || chat.chatInfo is ChatInfo.ContactConnection) {
|
||||
Text(
|
||||
text = generalGetString(MR.strings.contact_type_new).uppercase(),
|
||||
color = MaterialTheme.colors.onPrimary,
|
||||
|
||||
+66
-24
@@ -51,6 +51,7 @@ import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.model.ChatType
|
||||
import chat.simplex.common.model.ContactStatus
|
||||
import chat.simplex.common.model.Format
|
||||
import chat.simplex.common.model.RemoteHostInfo
|
||||
import chat.simplex.common.model.SharedPreference
|
||||
import chat.simplex.common.platform.BackHandler
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
@@ -65,6 +66,10 @@ import chat.simplex.common.platform.hideKeyboard
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chatlist.DetailedSMPStatsView
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.newchat.AddGroupView
|
||||
import chat.simplex.common.views.newchat.NewChatOption
|
||||
import chat.simplex.common.views.newchat.NewChatOptions
|
||||
import chat.simplex.common.views.newchat.NewChatView
|
||||
import chat.simplex.common.views.newchat.strHasSingleSimplexLink
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
@@ -73,9 +78,8 @@ import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.launch
|
||||
import java.util.HashSet
|
||||
|
||||
|
||||
enum class ContactType {
|
||||
CARD, REQUEST, RECENT, REMOVED, UNKNOWN
|
||||
CARD, REQUEST_RECEIVED, REQUEST_SENT, RECENT, REMOVED, UNKNOWN
|
||||
}
|
||||
|
||||
private fun contactChats(c: List<Chat>, contactTypes: List<ContactType>): List<Chat> {
|
||||
@@ -84,16 +88,19 @@ private fun contactChats(c: List<Chat>, contactTypes: List<ContactType>): List<C
|
||||
|
||||
private fun getContactType(chat: Chat): ContactType {
|
||||
return when (val cInfo = chat.chatInfo) {
|
||||
is ChatInfo.ContactRequest -> ContactType.REQUEST
|
||||
is ChatInfo.ContactRequest -> ContactType.REQUEST_RECEIVED
|
||||
is ChatInfo.Direct -> {
|
||||
val contact = cInfo.contact;
|
||||
|
||||
when {
|
||||
contact.activeConn == null && contact.profile.contactLink != null -> ContactType.CARD
|
||||
contact.contactStatus == ContactStatus.DeletedByUser || contact.contactStatus == ContactStatus.Deleted -> ContactType.REMOVED
|
||||
contact.chatDeleted -> ContactType.REMOVED
|
||||
else -> ContactType.RECENT
|
||||
}
|
||||
}
|
||||
// TODO: Redo this.
|
||||
is ChatInfo.ContactConnection -> ContactType.REQUEST_SENT
|
||||
else -> ContactType.UNKNOWN
|
||||
}
|
||||
}
|
||||
@@ -111,7 +118,32 @@ val chatsByTypeComparator = Comparator<Chat> { chat1, chat2 ->
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactActionsSection(contactActions: @Composable () -> Unit) {
|
||||
fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, close: () -> Unit) {
|
||||
ModalView(
|
||||
close = close
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxSize(),
|
||||
) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
val bottomPadding = DEFAULT_PADDING
|
||||
AppBarTitle(
|
||||
stringResource(MR.strings.contact_type_deleted),
|
||||
hostDevice(rh?.remoteHostId),
|
||||
bottomPadding = bottomPadding
|
||||
)
|
||||
}
|
||||
|
||||
ContactsLayout(
|
||||
contactActions = {},
|
||||
contactTypes = listOf(ContactType.REMOVED),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ContactActionsSection(contactActions: @Composable () -> Unit, rh: RemoteHostInfo?) {
|
||||
contactActions()
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
@@ -121,7 +153,10 @@ fun ContactActionsSection(contactActions: @Composable () -> Unit) {
|
||||
SectionView {
|
||||
SectionItemView(
|
||||
click = {
|
||||
//
|
||||
ModalManager.start.showCustomModal { close -> DeletedContactsView(
|
||||
rh = rh,
|
||||
close = close)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Icon(
|
||||
@@ -137,7 +172,10 @@ fun ContactActionsSection(contactActions: @Composable () -> Unit) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsView(contactActions: @Composable () -> Unit) {
|
||||
private fun ContactsLayout(
|
||||
contactActions: @Composable () -> Unit,
|
||||
contactTypes: List<ContactType>,
|
||||
contactListTitle: String? = null) {
|
||||
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
|
||||
var searchFocused by remember { mutableStateOf(false) }
|
||||
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(
|
||||
@@ -158,20 +196,28 @@ fun ContactsView(contactActions: @Composable () -> Unit) {
|
||||
}
|
||||
|
||||
if (!searchFocused) {
|
||||
ContactActionsSection(contactActions)
|
||||
contactActions()
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
SectionView(title = stringResource(MR.strings.contact_list_header_title).uppercase(), padding = PaddingValues(DEFAULT_PADDING)) {
|
||||
ContactsList(listState = listState, chatModel = chatModel, searchText = searchText, contactType = ContactType.RECENT)
|
||||
}
|
||||
|
||||
if (searchFocused) {
|
||||
ContactActionsSection(contactActions)
|
||||
SectionView(title = contactListTitle, padding = PaddingValues(DEFAULT_PADDING)) {
|
||||
ContactsList(listState = listState, chatModel = chatModel, searchText = searchText, contactTypes = contactTypes)
|
||||
// TODO: Empty list states.
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsView(
|
||||
contactActions: @Composable () -> Unit,
|
||||
rh: RemoteHostInfo?
|
||||
) {
|
||||
ContactsLayout(
|
||||
contactActions = { ContactActionsSection(contactActions, rh) },
|
||||
contactTypes = listOf(ContactType.CARD, ContactType.RECENT, ContactType.REQUEST_RECEIVED, ContactType.REQUEST_SENT),
|
||||
contactListTitle = stringResource(MR.strings.contact_list_header_title).uppercase()
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContactsSearchBar(listState: LazyListState, searchText: MutableState<TextFieldValue>, focused: Boolean, onFocusChanged: (hasFocus: Boolean) -> Unit) {
|
||||
@@ -245,9 +291,8 @@ fun ToggleFilterButton() {
|
||||
|
||||
private var lazyListState = 0 to 0
|
||||
|
||||
|
||||
@Composable
|
||||
fun ContactsList(listState: LazyListState, chatModel: ChatModel, searchText: MutableState<TextFieldValue>, contactType: ContactType) {
|
||||
fun ContactsList(listState: LazyListState, chatModel: ChatModel, searchText: MutableState<TextFieldValue>, contactTypes: List<ContactType>) {
|
||||
val oneHandUI = remember { chatModel.controller.appPrefs.oneHandUI }
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
@@ -257,7 +302,10 @@ fun ContactsList(listState: LazyListState, chatModel: ChatModel, searchText: Mut
|
||||
}
|
||||
val showUnreadAndFavorites =
|
||||
remember { ChatController.appPrefs.showUnreadAndFavorites.state }.value
|
||||
val allChats = remember { contactChats(chatModel.chats, listOf(ContactType.CARD, ContactType.RECENT, ContactType.REQUEST)) }
|
||||
val allChats = remember { contactChats(
|
||||
chatModel.chats,
|
||||
contactTypes)
|
||||
}
|
||||
|
||||
val filteredContactChats = filteredContactChats(
|
||||
showUnreadAndFavorites = showUnreadAndFavorites,
|
||||
@@ -275,7 +323,7 @@ fun ContactsList(listState: LazyListState, chatModel: ChatModel, searchText: Mut
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LazyColumnWithScrollBar(
|
||||
LazyColumn(
|
||||
Modifier.fillMaxWidth(),
|
||||
listState
|
||||
) {
|
||||
@@ -314,18 +362,12 @@ private fun filteredContactChats(
|
||||
searchText: String,
|
||||
contactChats: List<Chat>
|
||||
): List<Chat> {
|
||||
// TODO: Refresh list after delete.
|
||||
return contactChats
|
||||
.filter { chat -> filterChat(
|
||||
chat = chat,
|
||||
searchText = searchText,
|
||||
showUnreadAndFavorites = showUnreadAndFavorites) }
|
||||
.distinctBy { chat ->
|
||||
when (val cInfo = chat.chatInfo) {
|
||||
is ChatInfo.ContactRequest -> cInfo.contactRequest.contactRequestId
|
||||
is ChatInfo.Direct -> cInfo.contact.contactId
|
||||
else -> cInfo.id
|
||||
}
|
||||
}
|
||||
.sortedWith(chatsByTypeComparator)
|
||||
}
|
||||
|
||||
|
||||
+2
-1
@@ -70,7 +70,8 @@ fun ModalData.NewChatView(rh: RemoteHostInfo?) {
|
||||
ModalManager.center.showCustomModal { close -> AddGroupView(chatModel, chatModel.currentRemoteHost.value, close) }
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
rh = rh
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -2221,4 +2221,5 @@
|
||||
<string name="contact_type_recent">Recent</string>
|
||||
<string name="no_filtered_contacts">No filtered contacts</string>
|
||||
<string name="contact_list_header_title">Your contacts</string>
|
||||
<string name="icon_descr_pending_contact_connection">Pending contact connection</string>
|
||||
</resources>
|
||||
Reference in New Issue
Block a user