android: smaller new chat button in 1-hand UI mode, line on the correct side of the bar (#4569)

* android: smaller new chat button in 1-hand UI mode, line on the correct side of the bar (wrong color in 1-hand UI mode)

* remove comment

* desktop: open new chat information buttons on start modal

* remove no longer applicable comment

* desktop, android: move to chat when accepting from chat list and snsReady

* android: keep search visible if keyboard is open on new chat sheet

* android: keep search visibile if keyboard is open on chat list

* android: scroll modal header on new chat sheet

* android: added divider between search and toolbar in one hand ui

* make one hand ui toolbar more extensible by using scafold

* android: remove tiny paddings around one hand ui toolbars

* android: hide toolbar when searching on one hand ui

* avoid passing one hand ui as param everywhere

* make paddings match in new chat sheet action buttons

* flip animation

* refactor and divider fix

* fix padding

* bigger padding

* appPrefs

---------

Co-authored-by: Diogo Cunha <diogofncunha@gmail.com>
Co-authored-by: Avently <7953703+avently@users.noreply.github.com>
This commit is contained in:
Evgeny
2024-08-04 21:37:20 +01:00
committed by GitHub
parent bf697c722a
commit ee7c5d2aac
8 changed files with 195 additions and 175 deletions
@@ -27,6 +27,7 @@ import chat.simplex.common.views.chat.*
import chat.simplex.common.views.chat.group.deleteGroupDialog
import chat.simplex.common.views.chat.group.leaveGroupDialog
import chat.simplex.common.views.chat.item.ItemAction
import chat.simplex.common.views.contacts.onRequestAccepted
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.newchat.*
import chat.simplex.res.MR
@@ -129,7 +130,7 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>, oneHandUI:
ContactRequestView(chat.chatInfo)
}
},
click = { contactRequestAlertDialog(chat.remoteHostId, chat.chatInfo, chatModel) },
click = { contactRequestAlertDialog(chat.remoteHostId, chat.chatInfo, chatModel) { onRequestAccepted(it) } },
dropdownMenuItems = {
tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) {
ContactRequestMenuItems(chat.remoteHostId, chat.chatInfo, chatModel, showMenu)
@@ -43,14 +43,12 @@ import kotlinx.serialization.json.Json
import java.net.URI
import kotlin.time.Duration.Companion.seconds
private fun showNewChatSheet(oneHandUI: State<Boolean>, barTitle: String) {
private fun showNewChatSheet(oneHandUI: State<Boolean>) {
ModalManager.start.closeModals()
ModalManager.end.closeModals()
chatModel.newChatSheetVisible.value = true
ModalManager.start.showModalCloseable(
closeOnTop = !oneHandUI.value,
closeBarTitle = if (oneHandUI.value) barTitle else null,
endButtons = { Spacer(Modifier.minimumInteractiveComponentSize()) }
) { close ->
NewChatSheet(rh = chatModel.currentRemoteHost.value, close)
DisposableEffect(Unit) {
@@ -88,19 +86,21 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf
Scaffold(
topBar = {
if (!oneHandUI.state.value) {
Box(Modifier.padding(end = endPadding)) {
Column(Modifier.padding(end = endPadding)) {
ChatListToolbar(
scaffoldState.drawerState,
userPickerState,
stopped,
oneHandUI
)
Divider()
}
}
},
bottomBar = {
if (oneHandUI.state.value) {
Box(Modifier.padding(end = endPadding)) {
Column(Modifier.padding(end = endPadding)) {
Divider()
ChatListToolbar(
scaffoldState.drawerState,
userPickerState,
@@ -125,7 +125,7 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf
FloatingActionButton(
onClick = {
if (!stopped) {
showNewChatSheet(oneHandUI.state, generalGetString(MR.strings.new_chat))
showNewChatSheet(oneHandUI.state)
}
},
Modifier
@@ -222,14 +222,15 @@ private fun ChatListToolbar(drawerState: DrawerState, userPickerState: MutableSt
IconButton(
onClick = {
if (!stopped) {
showNewChatSheet(oneHandUI.state, generalGetString(MR.strings.new_chat))
showNewChatSheet(oneHandUI.state)
}
},
) {
Box(
contentAlignment = Alignment.Center,
modifier = Modifier
.background(if (!stopped) MaterialTheme.colors.primary else MaterialTheme.colors.secondary, shape = CircleShape)
.padding(DEFAULT_PADDING_HALF)
.size(33.dp * fontSizeSqrtMultiplier)
){
Icon(
painterResource(MR.images.ic_edit_filled),
@@ -327,7 +328,6 @@ private fun ChatListToolbar(drawerState: DrawerState, userPickerState: MutableSt
onSearchValueChanged = {},
buttons = barButtons
)
Divider(Modifier.padding(top = AppBarHeight * fontSizeSqrtMultiplier))
}
@Composable
@@ -551,6 +551,7 @@ private fun ChatList(chatModel: ChatModel, searchText: MutableState<TextFieldVal
var scrollDirection by remember { mutableStateOf(ScrollDirection.Idle) }
var previousIndex by remember { mutableStateOf(0) }
var previousScrollOffset by remember { mutableStateOf(0) }
val keyboardState by getKeyboardState()
LaunchedEffect(listState.firstVisibleItemIndex, listState.firstVisibleItemScrollOffset) {
val currentIndex = listState.firstVisibleItemIndex
@@ -590,7 +591,10 @@ private fun ChatList(chatModel: ChatModel, searchText: MutableState<TextFieldVal
Modifier
.offset {
val y = if (searchText.value.text.isEmpty()) {
if (oneHandUI.state.value && scrollDirection == ScrollDirection.Up) {
if (
(oneHandUI.state.value && scrollDirection == ScrollDirection.Up) ||
(appPlatform.isAndroid && keyboardState == KeyboardState.Opened)
) {
0
} else if (listState.firstVisibleItemIndex == 0) -listState.firstVisibleItemScrollOffset else -1000
} else {
@@ -21,6 +21,7 @@ import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.intl.Locale
import androidx.compose.ui.unit.*
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatController.stopRemoteHostAndReloadHosts
import chat.simplex.common.model.ChatModel.controller
import chat.simplex.common.ui.theme.*
@@ -149,7 +150,7 @@ fun UserPicker(
.padding(bottom = 10.dp, top = 10.dp)
.graphicsLayer {
alpha = animatedFloat.value
translationY = (animatedFloat.value - 1) * xOffset
translationY = (if (appPrefs.oneHandUI.state.value) -1 else 1) * (animatedFloat.value - 1) * xOffset
},
contentAlignment = contentAlignment
) {
@@ -5,6 +5,7 @@ import androidx.compose.ui.graphics.Color
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.model.ChatModel.withChats
import chat.simplex.common.platform.*
import chat.simplex.common.views.chat.*
@@ -16,7 +17,7 @@ import chat.simplex.common.views.newchat.chatContactType
import chat.simplex.res.MR
import kotlinx.coroutines.delay
private fun onRequestAccepted(chat: Chat) {
fun onRequestAccepted(chat: Chat) {
val chatInfo = chat.chatInfo
if (chatInfo is ChatInfo.Direct) {
ModalManager.start.closeModals()
@@ -27,7 +28,8 @@ private fun onRequestAccepted(chat: Chat) {
}
@Composable
fun ContactListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>, oneHandUI: State<Boolean>) {
fun ContactListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>) {
val oneHandUI = remember { appPrefs.oneHandUI.state }
val showMenu = remember { mutableStateOf(false) }
val rhId = chat.remoteHostId
val disabled = chatModel.chatRunning.value == false || chatModel.deletedChats.value.contains(rhId to chat.chatInfo.id)
@@ -20,7 +20,7 @@ import chat.simplex.res.MR
import dev.icerock.moko.resources.compose.painterResource
@Composable
fun CloseSheetBar(close: (() -> Unit)?, showClose: Boolean = true, tintColor: Color = if (close != null) MaterialTheme.colors.primary else MaterialTheme.colors.secondary, arrangement: Arrangement.Vertical = Arrangement.Top, closeBarTitle: String? = null, endButtons: @Composable RowScope.() -> Unit = {}) {
fun CloseSheetBar(close: (() -> Unit)?, showClose: Boolean = true, tintColor: Color = if (close != null) MaterialTheme.colors.primary else MaterialTheme.colors.secondary, arrangement: Arrangement.Vertical = Arrangement.Top, closeBarTitle: String? = null, barPaddingValues: PaddingValues = PaddingValues(horizontal = AppBarHorizontalPadding), endButtons: @Composable RowScope.() -> Unit = {}) {
var rowModifier = Modifier
.fillMaxWidth()
.height(AppBarHeight * fontSizeSqrtMultiplier)
@@ -36,7 +36,7 @@ fun CloseSheetBar(close: (() -> Unit)?, showClose: Boolean = true, tintColor: Co
.heightIn(min = AppBarHeight * fontSizeSqrtMultiplier)
) {
Row(
modifier = Modifier.padding(horizontal = AppBarHorizontalPadding),
modifier = Modifier.padding(barPaddingValues),
content = {
Row(
rowModifier,
@@ -26,7 +26,6 @@ fun ModalView(
background: Color = MaterialTheme.colors.background,
modifier: Modifier = Modifier,
closeOnTop: Boolean = true,
closeBarTitle: String? = null,
endButtons: @Composable RowScope.() -> Unit = {},
content: @Composable () -> Unit,
) {
@@ -38,14 +37,10 @@ fun ModalView(
if (closeOnTop) {
CloseSheetBar(if (enableClose) close else null, showClose, endButtons = endButtons)
}
Box(if (closeOnTop) modifier else modifier.padding(bottom = AppBarHeight * fontSizeSqrtMultiplier)) {
Box(modifier = modifier) {
content()
}
}
if (!closeOnTop) {
CloseSheetBar(if (enableClose) close else null, showClose, endButtons = endButtons, arrangement = Arrangement.Bottom, closeBarTitle = closeBarTitle)
}
}
}
@@ -72,17 +67,17 @@ class ModalManager(private val placement: ModalPlacement? = null) {
// java.lang.IllegalStateException: Reading a state that was created after the snapshot was taken or in a snapshot that has not yet been applied
private var passcodeView: MutableStateFlow<(@Composable (close: () -> Unit) -> Unit)?> = MutableStateFlow(null)
fun showModal(settings: Boolean = false, showClose: Boolean = true, closeOnTop: Boolean = true, closeBarTitle: String? = null,endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable ModalData.() -> Unit) {
fun showModal(settings: Boolean = false, showClose: Boolean = true, closeOnTop: Boolean = true, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable ModalData.() -> Unit) {
val data = ModalData()
showCustomModal { close ->
ModalView(close, showClose = showClose, closeOnTop = closeOnTop, closeBarTitle = closeBarTitle, endButtons = endButtons, content = { data.content() })
ModalView(close, showClose = showClose, closeOnTop = closeOnTop, endButtons = endButtons, content = { data.content() })
}
}
fun showModalCloseable(settings: Boolean = false, showClose: Boolean = true, closeOnTop: Boolean = true, closeBarTitle: String? = null, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable ModalData.(close: () -> Unit) -> Unit) {
fun showModalCloseable(settings: Boolean = false, showClose: Boolean = true, closeOnTop: Boolean = true, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable ModalData.(close: () -> Unit) -> Unit) {
val data = ModalData()
showCustomModal { close ->
ModalView(close, showClose = showClose, endButtons = endButtons, closeOnTop = closeOnTop, closeBarTitle = closeBarTitle, content = { data.content(close) })
ModalView(close, showClose = showClose, endButtons = endButtons, closeOnTop = closeOnTop, content = { data.content(close) })
}
}
@@ -28,6 +28,7 @@ import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.*
import chat.simplex.common.model.*
import chat.simplex.common.model.ChatController.appPrefs
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chatlist.ScrollDirection
@@ -39,45 +40,47 @@ import java.net.URI
@Composable
fun NewChatSheet(rh: RemoteHostInfo?, close: () -> Unit) {
val oneHandUI = remember { chatModel.controller.appPrefs.oneHandUI }
val oneHandUI = remember { appPrefs.oneHandUI.state }
val keyboardState by getKeyboardState()
val showToolbarInOneHandUI = remember { derivedStateOf { keyboardState == KeyboardState.Closed && oneHandUI.value } }
Column(
modifier = Modifier.fillMaxSize()
) {
if (!oneHandUI.state.value) {
Box(contentAlignment = Alignment.Center) {
val bottomPadding = DEFAULT_PADDING
AppBarTitle(
stringResource(MR.strings.new_chat),
hostDevice(rh?.remoteHostId),
bottomPadding = bottomPadding
)
Scaffold(
bottomBar = {
if (showToolbarInOneHandUI.value) {
Column {
Divider()
CloseSheetBar(
close = close,
showClose = true,
endButtons = { Spacer(Modifier.minimumInteractiveComponentSize()) },
arrangement = Arrangement.Bottom,
closeBarTitle = generalGetString(MR.strings.new_chat),
barPaddingValues = PaddingValues(horizontal = 0.dp)
)
}
}
}
) {
Column(
modifier = Modifier.fillMaxSize().padding(it)
) {
val closeAll = { ModalManager.start.closeModals() }
val closeAll = { ModalManager.start.closeModals() }
var modifier = Modifier.fillMaxSize()
if (oneHandUI.state.value) {
modifier = modifier.scale(scaleX = 1f, scaleY = -1f)
}
Column(modifier = modifier) {
NewChatSheetLayout(
addContact = {
ModalManager.start.showModalCloseable { _ -> NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll ) }
},
scanPaste = {
ModalManager.start.showModalCloseable { _ -> NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll) }
},
createGroup = {
ModalManager.start.showCustomModal { close -> AddGroupView(chatModel, chatModel.currentRemoteHost.value, close, closeAll) }
},
rh = rh,
close = close,
oneHandUI = oneHandUI
)
Column(modifier = Modifier.fillMaxSize().then(if (oneHandUI.value) Modifier.scale(scaleX = 1f, scaleY = -1f) else Modifier)) {
NewChatSheetLayout(
addContact = {
ModalManager.start.showModalCloseable { _ -> NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll ) }
},
scanPaste = {
ModalManager.start.showModalCloseable { _ -> NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll) }
},
createGroup = {
ModalManager.start.showCustomModal { close -> AddGroupView(chatModel, chatModel.currentRemoteHost.value, close, closeAll) }
},
rh = rh,
close = close
)
}
}
}
}
@@ -90,7 +93,7 @@ fun chatContactType(chat: Chat): ContactType {
return when (val cInfo = chat.chatInfo) {
is ChatInfo.ContactRequest -> ContactType.REQUEST
is ChatInfo.Direct -> {
val contact = cInfo.contact;
val contact = cInfo.contact
when {
contact.activeConn == null && contact.profile.contactLink != null -> ContactType.CARD
@@ -116,15 +119,15 @@ private fun NewChatSheetLayout(
scanPaste: () -> Unit,
createGroup: () -> Unit,
close: () -> Unit,
oneHandUI: SharedPreference<Boolean>
) {
val oneHandUI = remember { appPrefs.oneHandUI.state }
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
val searchShowingSimplexLink = remember { mutableStateOf(false) }
val searchChatFilteredBySimplexLink = remember { mutableStateOf<String?>(null) }
val showUnreadAndFavorites = remember { ChatController.appPrefs.showUnreadAndFavorites.state }.value
val baseContactTypes = listOf(ContactType.CARD, ContactType.RECENT, ContactType.REQUEST)
val contactTypes by remember(baseContactTypes, searchText.value.text.isEmpty()) {
val baseContactTypes = remember { listOf(ContactType.CARD, ContactType.RECENT, ContactType.REQUEST) }
val contactTypes by remember(searchText.value.text.isEmpty()) {
derivedStateOf { contactTypesSearchTargets(baseContactTypes, searchText.value.text.isEmpty()) }
}
val allChats by remember(chatModel.chats.value, contactTypes) {
@@ -133,6 +136,7 @@ private fun NewChatSheetLayout(
var scrollDirection by remember { mutableStateOf(ScrollDirection.Idle) }
var previousIndex by remember { mutableStateOf(0) }
var previousScrollOffset by remember { mutableStateOf(0) }
val keyboardState by getKeyboardState()
LaunchedEffect(listState.firstVisibleItemIndex, listState.firstVisibleItemScrollOffset) {
val currentIndex = listState.firstVisibleItemIndex
@@ -160,22 +164,32 @@ private fun NewChatSheetLayout(
contactChats = allChats
)
var sectionModifier = Modifier.fillMaxWidth()
if (oneHandUI.state.value) {
sectionModifier = sectionModifier.scale(scaleX = 1f, scaleY = -1f)
}
val sectionModifier = if (oneHandUI.value) Modifier.fillMaxWidth().scale(scaleX = 1f, scaleY = -1f) else Modifier.fillMaxWidth()
LazyColumnWithScrollBar(
Modifier.fillMaxWidth(),
listState
) {
if (!oneHandUI.value) {
item {
Box(contentAlignment = Alignment.Center) {
val bottomPadding = DEFAULT_PADDING
AppBarTitle(
stringResource(MR.strings.new_chat),
hostDevice(rh?.remoteHostId),
bottomPadding = bottomPadding
)
}
}
}
stickyHeader {
Column(
Modifier
.offset {
val y = if (searchText.value.text.isEmpty()) {
if (oneHandUI.state.value && scrollDirection == ScrollDirection.Up) {
if (
(oneHandUI.value && scrollDirection == ScrollDirection.Up) ||
(appPlatform.isAndroid && keyboardState == KeyboardState.Opened)
) {
0
} else if (listState.firstVisibleItemIndex == 0) -listState.firstVisibleItemScrollOffset else -1000
} else {
@@ -185,7 +199,7 @@ private fun NewChatSheetLayout(
}
.background(MaterialTheme.colors.background)
) {
if (!oneHandUI.state.value) {
if (!oneHandUI.value) {
Divider()
}
ContactsSearchBar(
@@ -194,13 +208,12 @@ private fun NewChatSheetLayout(
searchShowingSimplexLink = searchShowingSimplexLink,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
close = close,
oneHandUI = oneHandUI
)
Divider()
}
}
item {
Spacer(Modifier.padding(bottom = DEFAULT_PADDING))
Spacer(Modifier.padding(bottom = 27.dp))
if (searchText.value.text.isEmpty()) {
Row {
@@ -210,21 +223,18 @@ private fun NewChatSheetLayout(
text = stringResource(MR.strings.add_contact_tab),
click = addContact,
extraPadding = true,
oneHandUI = oneHandUI.state
)
NewChatButton(
icon = painterResource(MR.images.ic_qr_code),
text = if (appPlatform.isAndroid) stringResource(MR.strings.scan_paste_link) else stringResource(MR.strings.paste_link),
click = scanPaste,
extraPadding = true,
oneHandUI = oneHandUI.state
)
NewChatButton(
icon = painterResource(MR.images.ic_group),
text = stringResource(MR.strings.create_group_button),
click = createGroup,
extraPadding = true,
oneHandUI = oneHandUI.state
)
}
}
@@ -242,11 +252,9 @@ private fun NewChatSheetLayout(
ModalManager.start.showCustomModal { closeDeletedChats ->
ModalView(
close = closeDeletedChats,
closeOnTop = !oneHandUI.state.value,
closeBarTitle = if (oneHandUI.state.value) generalGetString(MR.strings.deleted_chats) else null,
endButtons = { Spacer(Modifier.minimumInteractiveComponentSize()) }
closeOnTop = !oneHandUI.value,
) {
DeletedContactsView(rh = rh, close = {
DeletedContactsView(rh = rh, closeDeletedChats = closeDeletedChats, close = {
ModalManager.start.closeModals()
})
}
@@ -263,16 +271,16 @@ private fun NewChatSheetLayout(
}
}
}
SectionDividerSpaced()
SectionDividerSpaced(maxBottomPadding = false)
}
}
}
item {
if (filteredContactChats.isNotEmpty() && !oneHandUI.state.value) {
if (filteredContactChats.isNotEmpty() && !oneHandUI.value) {
Text(
stringResource(MR.strings.contact_list_header_title).uppercase(), color = MaterialTheme.colors.secondary, style = MaterialTheme.typography.body2,
modifier = sectionModifier.padding(start = DEFAULT_PADDING, bottom = 5.dp), fontSize = 12.sp
modifier = sectionModifier.padding(start = DEFAULT_PADDING, top = DEFAULT_PADDING_HALF, bottom = DEFAULT_PADDING_HALF), fontSize = 12.sp
)
}
}
@@ -283,7 +291,7 @@ private fun NewChatSheetLayout(
chatModel.chatId.value != null && filteredContactChats.getOrNull(index + 1)?.id == chatModel.chatId.value
}
}
ContactListNavLinkView(chat, nextChatSelected, oneHandUI.state)
ContactListNavLinkView(chat, nextChatSelected)
}
}
@@ -308,8 +316,9 @@ private fun NewChatButton(
iconColor: Color = MaterialTheme.colors.secondary,
disabled: Boolean = false,
extraPadding: Boolean = false,
oneHandUI: State<Boolean>
) {
val oneHandUI = remember { appPrefs.oneHandUI.state }
SectionItemView(click, disabled = disabled) {
Row(modifier = if (oneHandUI.value) Modifier.scale(scaleX = 1f, scaleY = -1f) else Modifier) {
Icon(icon, text, tint = if (disabled) MaterialTheme.colors.secondary else iconColor)
@@ -326,14 +335,10 @@ private fun ContactsSearchBar(
searchShowingSimplexLink: MutableState<Boolean>,
searchChatFilteredBySimplexLink: MutableState<String?>,
close: () -> Unit,
oneHandUI: SharedPreference<Boolean>
) {
var modifier = Modifier.fillMaxWidth();
if (oneHandUI.state.value) {
modifier = modifier.scale(scaleX = 1f, scaleY = -1f)
}
val oneHandUI = remember { appPrefs.oneHandUI.state }
val modifier = if (oneHandUI.value) Modifier.fillMaxWidth().scale(scaleX = 1f, scaleY = -1f) else Modifier.fillMaxWidth()
var focused by remember { mutableStateOf(false) }
Row(verticalAlignment = Alignment.CenterVertically, modifier = modifier) {
@@ -416,7 +421,7 @@ private fun ContactsSearchBar(
@Composable
private fun ToggleFilterButton() {
val pref = remember { ChatController.appPrefs.showUnreadAndFavorites }
val pref = remember { appPrefs.showUnreadAndFavorites }
IconButton(onClick = { pref.set(!pref.get()) }) {
val sp16 = with(LocalDensity.current) { 16.sp.toDp() }
Icon(
@@ -471,7 +476,7 @@ private fun filteredContactChats(
}
private fun filterChat(chat: Chat, searchText: String, showUnreadAndFavorites: Boolean): Boolean {
var meetsPredicate = true;
var meetsPredicate = true
val s = searchText.trim().lowercase()
val cInfo = chat.chatInfo
@@ -485,7 +490,7 @@ private fun filterChat(chat: Chat, searchText: String, showUnreadAndFavorites: B
meetsPredicate = meetsPredicate && (cInfo.chatSettings?.favorite ?: false)
}
return meetsPredicate;
return meetsPredicate
}
private fun viewNameContains(cInfo: ChatInfo, s: String): Boolean =
@@ -512,84 +517,99 @@ private fun contactTypesSearchTargets(baseContactTypes: List<ContactType>, searc
}
@Composable
private fun DeletedContactsView(rh: RemoteHostInfo?, close: () -> Unit) {
val oneHandUI = remember { chatModel.controller.appPrefs.oneHandUI }
private fun DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats: () -> Unit, close: () -> Unit) {
val oneHandUI = remember { appPrefs.oneHandUI.state }
val keyboardState by getKeyboardState()
val showToolbarInOneHandUI = remember { derivedStateOf { keyboardState == KeyboardState.Closed && oneHandUI.value } }
var modifier = Modifier.fillMaxSize()
if (oneHandUI.state.value) {
modifier = modifier.scale(scaleX = 1f, scaleY = -1f)
}
Column(
modifier,
Scaffold(
bottomBar = {
if (showToolbarInOneHandUI.value) {
CloseSheetBar(
close = closeDeletedChats,
showClose = true,
endButtons = { Spacer(Modifier.minimumInteractiveComponentSize()) },
arrangement = Arrangement.Bottom,
closeBarTitle = generalGetString(MR.strings.deleted_chats),
barPaddingValues = PaddingValues(horizontal = 0.dp)
)
Divider()
}
}
) {
if (!oneHandUI.state.value) {
Box(contentAlignment = Alignment.Center) {
val bottomPadding = DEFAULT_PADDING
AppBarTitle(
stringResource(MR.strings.deleted_chats),
hostDevice(rh?.remoteHostId),
bottomPadding = bottomPadding
)
}
}
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
val searchShowingSimplexLink = remember { mutableStateOf(false) }
val searchChatFilteredBySimplexLink = remember { mutableStateOf<String?>(null) }
val showUnreadAndFavorites = remember { ChatController.appPrefs.showUnreadAndFavorites.state }.value
val contactTypes = listOf(ContactType.CHAT_DELETED)
val allChats by remember(chatModel.chats.value, contactTypes) {
derivedStateOf { filterContactTypes(chatModel.chats.value, contactTypes) }
}
val filteredContactChats = filteredContactChats(
showUnreadAndFavorites = showUnreadAndFavorites,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
searchShowingSimplexLink = searchShowingSimplexLink,
searchText = searchText.value.text,
contactChats = allChats
)
LazyColumnWithScrollBar(
Modifier.fillMaxWidth(),
listState
Column(
Modifier
.fillMaxSize()
.padding(it)
.then(if (oneHandUI.value) Modifier.scale(scaleX = 1f, scaleY = -1f) else Modifier),
) {
item {
Divider()
ContactsSearchBar(
listState = listState,
searchText = searchText,
searchShowingSimplexLink = searchShowingSimplexLink,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
close = close,
oneHandUI = oneHandUI
)
Divider()
Spacer(Modifier.padding(bottom = DEFAULT_PADDING))
}
itemsIndexed(filteredContactChats) { index, chat ->
val nextChatSelected = remember(chat.id, filteredContactChats) {
derivedStateOf {
chatModel.chatId.value != null && filteredContactChats.getOrNull(index + 1)?.id == chatModel.chatId.value
}
}
ContactListNavLinkView(chat, nextChatSelected, oneHandUI.state)
}
}
if (filteredContactChats.isEmpty() && allChats.isNotEmpty()) {
Column(Modifier.fillMaxSize().padding(DEFAULT_PADDING)) {
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(
generalGetString(MR.strings.no_filtered_contacts),
color = MaterialTheme.colors.secondary,
modifier = if (oneHandUI.state.value) Modifier.scale(scaleX = 1f, scaleY = -1f) else Modifier
if (!oneHandUI.value) {
Box(contentAlignment = Alignment.Center) {
val bottomPadding = DEFAULT_PADDING
AppBarTitle(
stringResource(MR.strings.deleted_chats),
hostDevice(rh?.remoteHostId),
bottomPadding = bottomPadding
)
}
}
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
val searchShowingSimplexLink = remember { mutableStateOf(false) }
val searchChatFilteredBySimplexLink = remember { mutableStateOf<String?>(null) }
val showUnreadAndFavorites = remember { appPrefs.showUnreadAndFavorites.state }.value
val allChats by remember(chatModel.chats.value) {
derivedStateOf { filterContactTypes(chatModel.chats.value, listOf(ContactType.CHAT_DELETED)) }
}
val filteredContactChats = filteredContactChats(
showUnreadAndFavorites = showUnreadAndFavorites,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
searchShowingSimplexLink = searchShowingSimplexLink,
searchText = searchText.value.text,
contactChats = allChats
)
LazyColumnWithScrollBar(
Modifier.fillMaxWidth(),
listState
) {
item {
if (!oneHandUI.value) {
Divider()
}
ContactsSearchBar(
listState = listState,
searchText = searchText,
searchShowingSimplexLink = searchShowingSimplexLink,
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
close = close,
)
Divider()
Spacer(Modifier.padding(bottom = DEFAULT_PADDING))
}
itemsIndexed(filteredContactChats) { index, chat ->
val nextChatSelected = remember(chat.id, filteredContactChats) {
derivedStateOf {
chatModel.chatId.value != null && filteredContactChats.getOrNull(index + 1)?.id == chatModel.chatId.value
}
}
ContactListNavLinkView(chat, nextChatSelected)
}
}
if (filteredContactChats.isEmpty() && allChats.isNotEmpty()) {
Column(Modifier.fillMaxSize().padding(DEFAULT_PADDING)) {
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Text(
generalGetString(MR.strings.no_filtered_contacts),
color = MaterialTheme.colors.secondary,
modifier = if (oneHandUI.value) Modifier.scale(scaleX = 1f, scaleY = -1f) else Modifier
)
}
}
}
}
}
}
@@ -61,9 +61,8 @@ fun ModalData.NewChatView(rh: RemoteHostInfo?, selection: NewChatOption, showQRC
/** When [AddContactLearnMore] is open, we don't need to drop [ChatModel.showingInvitation].
* Otherwise, it will be called here AFTER [AddContactLearnMore] is launched and will clear the value too soon.
* It will be dropped automatically when connection established or when user goes away from this screen.
* It applies only to Android because on Desktop center space will not be overlapped by [AddContactLearnMore]
**/
if (chatModel.showingInvitation.value != null && (ModalManager.start.openModalCount() == 1 || appPlatform.isDesktop)) {
if (chatModel.showingInvitation.value != null && ModalManager.start.openModalCount() == 1) {
val conn = contactConnection.value
if (chatModel.showingInvitation.value?.connChatUsed == false && conn != null) {
AlertManager.shared.showAlertDialog(
@@ -78,7 +77,7 @@ fun ModalData.NewChatView(rh: RemoteHostInfo?, selection: NewChatOption, showQRC
controller.deleteChat(Chat(remoteHostId = rh?.remoteHostId, chatInfo = chatInfo, chatItems = listOf()))
if (chatModel.chatId.value == chatInfo.id) {
chatModel.chatId.value = null
ModalManager.end.closeModals()
ModalManager.start.closeModals()
}
}
}
@@ -212,8 +211,7 @@ private fun InviteView(rhId: Long?, connReqInvitation: String, contactConnection
Spacer(Modifier.height(10.dp))
val incognito = remember { mutableStateOf(controller.appPrefs.incognito.get()) }
IncognitoToggle(controller.appPrefs.incognito, incognito) {
if (appPlatform.isDesktop) ModalManager.end.closeModals()
ModalManager.end.showModal { IncognitoView() }
ModalManager.start.showModal { IncognitoView() }
}
KeyChangeEffect(incognito.value) {
withBGApi {
@@ -233,8 +231,7 @@ private fun InviteView(rhId: Long?, connReqInvitation: String, contactConnection
private fun AddContactLearnMoreButton() {
IconButton(
{
if (appPlatform.isDesktop) ModalManager.end.closeModals()
ModalManager.end.showModalCloseable { close ->
ModalManager.start.showModalCloseable { close ->
AddContactLearnMore(close)
}
},