profile picker modal for external shares

This commit is contained in:
Diogo
2024-08-30 13:32:44 +01:00
parent b78512031e
commit 46ce478676
3 changed files with 104 additions and 17 deletions
@@ -35,7 +35,7 @@ fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stoppe
topBar = {
if (!oneHandUI.value) {
Column {
ShareListToolbar(chatModel, userPickerState, stopped) { searchInList = it.trim() }
ShareListToolbar(chatModel, stopped) { searchInList = it.trim() }
Divider()
}
}
@@ -44,7 +44,7 @@ fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stoppe
if (oneHandUI.value) {
Column {
Divider()
ShareListToolbar(chatModel, userPickerState, stopped) { searchInList = it.trim() }
ShareListToolbar(chatModel, stopped) { searchInList = it.trim() }
}
}
}
@@ -92,16 +92,6 @@ fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stoppe
}
}
}
if (appPlatform.isAndroid) {
tryOrShowError("UserPicker", error = {}) {
UserPicker(
chatModel,
userPickerState,
contentAlignment = if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart,
drawerState = scaffoldState.drawerState,
)
}
}
}
private fun hasSimplexLink(msg: String): Boolean {
@@ -117,7 +107,7 @@ private fun EmptyList() {
}
@Composable
private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableStateFlow<AnimatedViewState>, stopped: Boolean, onSearchValueChanged: (String) -> Unit) {
private fun ShareListToolbar(chatModel: ChatModel, stopped: Boolean, onSearchValueChanged: (String) -> Unit) {
var showSearch by rememberSaveable { mutableStateOf(false) }
val hideSearchOnBack = { onSearchValueChanged(""); showSearch = false }
if (showSearch) {
@@ -133,7 +123,24 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
.filter { u -> !u.user.activeUser && !u.user.hidden }
.all { u -> u.unreadCount == 0 }
UserProfileButton(chatModel.currentUser.value?.profile?.image, allRead) {
userPickerState.value = AnimatedViewState.VISIBLE
if (appPlatform.isAndroid) {
ModalManager.start.showCustomModal { close ->
val search = rememberSaveable { mutableStateOf("") }
ModalView(
{ close() },
endButtons = {
SearchTextField(Modifier.fillMaxWidth(), placeholder = stringResource(MR.strings.search_verb), alwaysVisible = true) { search.value = it }
},
content = {
ShareListUserPicker(
chatModel = chatModel,
search = search,
close = close,
)
}
)
}
}
}
}
else -> NavigationButtonBack(onButtonClicked = {
@@ -8,11 +8,13 @@ import androidx.compose.foundation.*
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.interaction.collectIsHoveredAsState
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.itemsIndexed
import androidx.compose.foundation.shape.*
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.*
import androidx.compose.ui.draw.alpha
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Color
import androidx.compose.ui.graphics.graphicsLayer
@@ -32,11 +34,11 @@ import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.platform.*
import chat.simplex.common.views.CreateProfile
import chat.simplex.common.views.newchat.*
import chat.simplex.common.views.remote.*
import chat.simplex.common.views.usersettings.*
import chat.simplex.common.views.usersettings.AppearanceScope.ColorModeSwitcher
import chat.simplex.res.MR
import dev.icerock.moko.resources.ImageResource
import dev.icerock.moko.resources.compose.stringResource
import kotlinx.coroutines.*
import kotlinx.coroutines.flow.*
@@ -51,6 +53,84 @@ private fun UserPickerOptionRow(icon: Painter, text: String, click: (() -> Unit)
}
}
@Composable
fun ShareListUserPicker(
chatModel: ChatModel,
search: MutableState<String>,
close: () -> Unit,
) {
val switchingProfile = remember { mutableStateOf(false) }
val searchTextOrPassword = rememberSaveable { search }
val profiles = remember {
chatModel.users.map { it.user }.sortedBy { !it.activeUser }
}
val filteredProfiles by remember {
derivedStateOf { filteredProfiles(profiles, searchTextOrPassword.value) }
}
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
LaunchedEffect(switchingProfile.value) {
progressByTimeout = if (switchingProfile.value) {
delay(500)
switchingProfile.value
} else {
false
}
}
BoxWithConstraints {
Column(
Modifier
.fillMaxSize()
.alpha(if (progressByTimeout) 0.6f else 1f)
) {
LazyColumnWithScrollBar(userScrollEnabled = !switchingProfile.value) {
item {
AppBarTitle(stringResource(MR.strings.select_chat_profile), hostDevice(chatModel.remoteHostId), bottomPadding = DEFAULT_PADDING)
}
itemsIndexed(filteredProfiles) { _, p ->
ProfilePickerOption(
title = p.chatViewName,
disabled = switchingProfile.value || p.activeUser,
image = { ProfileImage(size = 42.dp, image = p.image) },
selected = p.activeUser,
onSelected = {
switchingProfile.value = true
withApi {
try {
controller.changeActiveUser(
rhId = p.remoteHostId,
toUserId = p.userId,
viewPwd = if (p.hidden) searchTextOrPassword.value else null
)
if (chatModel.currentUser.value?.userId != p.userId) {
AlertManager.shared.showAlertMsg(
generalGetString(
MR.strings.switching_profile_error_title
),
String.format(generalGetString(MR.strings.switching_profile_error_message), p.chatViewName)
)
}
close.invoke()
} finally {
switchingProfile.value = false
}
}
}
)
}
}
}
if (progressByTimeout) {
DefaultProgressView("")
}
}
}
@Composable
private fun UsersLayout(
chatModel: ChatModel,
@@ -208,7 +208,7 @@ private fun RetryButton(onClick: () -> Unit) {
}
@Composable
private fun ProfilePickerOption(
fun ProfilePickerOption(
title: String,
selected: Boolean,
disabled: Boolean,
@@ -267,7 +267,7 @@ private fun ProfilePickerOption(
)
}
private fun filteredProfiles(users: List<User>, searchTextOrPassword: String): List<User> {
fun filteredProfiles(users: List<User>, searchTextOrPassword: String): List<User> {
val s = searchTextOrPassword.trim()
val lower = s.lowercase()
return users.filter { u ->