diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt index 3cba89922d..ad3179e3a8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/App.kt @@ -403,7 +403,7 @@ fun DesktopScreen(settingsState: SettingsViewState) { } VerticalDivider(Modifier.padding(start = DEFAULT_START_MODAL_WIDTH * fontSizeSqrtMultiplier)) tryOrShowError("UserPicker", error = {}) { - UserPicker(chatModel, userPickerState) { + UserPicker(chatModel, userPickerState, scaffoldState.drawerState) { scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() } userPickerState.value = AnimatedViewState.GONE } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index 11b1006f41..39d89eeaf8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -252,7 +252,8 @@ fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerf UserPicker( chatModel = chatModel, userPickerState = userPickerState, - contentAlignment = if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart + contentAlignment = if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart, + drawerState = scaffoldState.drawerState ) { scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() } userPickerState.value = AnimatedViewState.GONE diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListView.kt index 886b82de7d..0909e898c0 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListView.kt @@ -100,6 +100,7 @@ fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stoppe showSettings = false, showCancel = true, contentAlignment = if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart, + drawerState = scaffoldState.drawerState, cancelClicked = { chatModel.sharedContent.value = null userPickerState.value = AnimatedViewState.GONE diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt index a609407184..6d9cde39b3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.kt @@ -1,6 +1,8 @@ package chat.simplex.common.views.chatlist import SectionItemView +import SectionView +import TextIconSpaced import androidx.compose.animation.core.* import androidx.compose.foundation.* import androidx.compose.foundation.interaction.MutableInteractionSource @@ -11,14 +13,16 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.* -import androidx.compose.ui.draw.* +import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color import androidx.compose.ui.graphics.graphicsLayer +import androidx.compose.ui.graphics.painter.Painter import androidx.compose.ui.platform.LocalDensity import dev.icerock.moko.resources.compose.painterResource import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.intl.Locale +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.* import chat.simplex.common.model.* import chat.simplex.common.model.ChatController.appPrefs @@ -29,17 +33,180 @@ import chat.simplex.common.views.helpers.* import chat.simplex.common.platform.* import chat.simplex.common.views.CreateProfile import chat.simplex.common.views.remote.* -import chat.simplex.common.views.usersettings.doWithAuth +import chat.simplex.common.views.usersettings.* import chat.simplex.res.MR import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.* import kotlinx.coroutines.flow.* import kotlin.math.roundToInt +@Composable +private fun UserPickerOptionRow(icon: Painter, text: String, click: (() -> Unit)? = null, disabled: Boolean = false) { + SectionItemView(click, disabled = disabled) { + Icon(icon, text, tint = if (disabled) MaterialTheme.colors.secondary else MenuTextColor) + TextIconSpaced() + Text(text, color = if (disabled) MaterialTheme.colors.secondary else MenuTextColor) + } +} + +@Composable +private fun UsersLayout( + chatModel: ChatModel, + onCurrentUserClick: () -> Unit, + onUserClicked: (user: User) -> Unit, + onShowAllProfilesClicked: () -> Unit +) { + val currentUser = remember { chatModel.currentUser }.value + val stopped = chatModel.chatRunning.value == false + + val users by remember { + derivedStateOf { + chatModel.users + .filter { u -> !u.user.hidden && !u.user.activeUser } + .take(3) + } + } + + if (currentUser != null) { + val mainColor = if (stopped) MaterialTheme.colors.secondary else MenuTextColor + + SectionView(contentPadding = PaddingValues(DEFAULT_PADDING)) { + Row { + Column(modifier = Modifier.clickable(onClick = onCurrentUserClick, enabled = !stopped)) { + ProfileImage(size = 54.dp * fontSizeSqrtMultiplier, image = currentUser.image) + Text( + currentUser.displayName, + style = MaterialTheme.typography.caption, + fontWeight = FontWeight.Bold, + color = mainColor, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + Spacer(Modifier.weight(1f)) + users.forEach { u -> + Column( + Modifier.clickable(onClick = { onUserClicked(u.user) }, enabled = !stopped) + ) { + ProfileImage(size = 44.dp * fontSizeSqrtMultiplier, image = u.user.image) + } + } + Column( + modifier = Modifier + .clip(CircleShape) + .border( + BorderStroke(1.dp, mainColor), + shape = CircleShape + ) + .clickable(onClick = onShowAllProfilesClicked, enabled = !stopped) + .size(44.dp * fontSizeSqrtMultiplier), + verticalArrangement = Arrangement.Center, + horizontalAlignment = Alignment.CenterHorizontally + ) { + Icon(painterResource(MR.images.ic_more_horiz), stringResource(MR.strings.your_chat_profiles), tint = mainColor) + } + } + } + } +} + +@Composable +private fun UserPickerUserSectionLayout ( + chatModel: ChatModel, + userPickerState: MutableStateFlow, + drawerState: DrawerState, + remoteHosts: List, + showCustomModal: (@Composable ModalData.(ChatModel, () -> Unit) -> Unit) -> (() -> Unit), + showModalWithSearch: (@Composable (ChatModel, MutableState) -> Unit) -> Unit, + withAuth: (title: String, desc: String, block: () -> Unit) -> Unit, + ) { + val stopped = chatModel.chatRunning.value == false + val profileHidden = rememberSaveable { mutableStateOf(false) } + + UsersLayout( + chatModel = chatModel, + onCurrentUserClick = showCustomModal { m, close -> UserProfileView(m, close) }, + onUserClicked = { user -> + userPickerState.value = AnimatedViewState.HIDING + if (!user.activeUser) { + withBGApi { + controller.showProgressIfNeeded { + ModalManager.closeAllModalsEverywhere() + chatModel.controller.changeActiveUser(user.remoteHostId, user.userId, null) + } + } + } + }, + onShowAllProfilesClicked = { + withAuth( + generalGetString(MR.strings.auth_open_chat_profiles), + generalGetString(MR.strings.auth_log_in_using_credential) + ) { + showModalWithSearch { it, search -> UserProfilesView(it, search, profileHidden, drawerState) } + } + } + ) + + SectionView { + UserPickerOptionRow( + painterResource(MR.images.ic_qr_code), + stringResource(MR.strings.your_simplex_contact_address), + showCustomModal { it, close -> UserAddressView(it, shareViaProfile = it.currentUser.value!!.addressShared, close = close) }, disabled = stopped + ) + UserPickerOptionRow( + painterResource(MR.images.ic_toggle_on), + stringResource(MR.strings.chat_preferences), + click = if (stopped) null else ({ + showCustomModal { m, close -> + PreferencesView(m, m.currentUser.value ?: return@showCustomModal, close) + }() + }), + disabled = stopped + ) + if (appPlatform.isAndroid) { + UseFromDesktopPickerItem { + ModalManager.start.showCustomModal { close -> + ConnectDesktopView(close) + } + userPickerState.value = AnimatedViewState.GONE + } + } else { + if (remoteHosts.isEmpty()) { + UserPickerOptionRow( + painterResource(MR.images.ic_smartphone_300), + generalGetString(MR.strings.link_a_mobile), + { + ModalManager.start.showModal { + ConnectMobileView() + } + } + ) + } + if (chatModel.desktopNoUserNoRemote) { + UserPickerOptionRow( + painterResource(MR.images.ic_manage_accounts), + generalGetString(MR.strings.create_chat_profile), + { + doWithAuth(generalGetString(MR.strings.auth_open_chat_profiles), generalGetString(MR.strings.auth_log_in_using_credential)) { + ModalManager.center.showModalCloseable { close -> + LaunchedEffect(Unit) { + userPickerState.value = AnimatedViewState.HIDING + } + CreateProfile(chat.simplex.common.platform.chatModel, close) + } + } + } + ) + } + } + } +} + @Composable fun UserPicker( chatModel: ChatModel, userPickerState: MutableStateFlow, + drawerState: DrawerState, showSettings: Boolean = true, contentAlignment: Alignment = Alignment.TopStart, showCancel: Boolean = false, @@ -155,7 +322,7 @@ fun UserPicker( ) { Column( Modifier - .height(404.dp) + .height(IntrinsicSize.Min) .then(if (appPlatform.isAndroid) Modifier.fillMaxWidth() else Modifier.width(IntrinsicSize.Min)) .background(MaterialTheme.colors.surface) ) { @@ -182,7 +349,25 @@ fun UserPicker( } } - UsersView() + UserPickerUserSectionLayout( + chatModel = chatModel, + userPickerState = userPickerState, + drawerState = drawerState, + remoteHosts = remoteHosts, + showCustomModal = { modalView -> { ModalManager.start.showCustomModal { close -> modalView(chatModel, close) } } }, + withAuth = ::doWithAuth, + showModalWithSearch = { modalView -> + 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 = { modalView(chatModel, search) }) + } + }, + ) if (remoteHosts.isNotEmpty() && currentRemoteHost != null && chatModel.localUserCreated.value == true) { LocalDevicePickerItem(false) { @@ -204,40 +389,13 @@ fun UserPicker( Divider(Modifier.requiredHeight(1.dp)) } } - if (appPlatform.isAndroid) { - UseFromDesktopPickerItem { - ModalManager.start.showCustomModal { close -> - ConnectDesktopView(close) - } - userPickerState.value = AnimatedViewState.GONE - } - Divider(Modifier.requiredHeight(1.dp)) - } else { - if (remoteHosts.isEmpty()) { - LinkAMobilePickerItem { - ModalManager.start.showModal { - ConnectMobileView() - } - userPickerState.value = AnimatedViewState.GONE - } - Divider(Modifier.requiredHeight(1.dp)) - } - if (chatModel.desktopNoUserNoRemote) { - CreateInitialProfile { - doWithAuth(generalGetString(MR.strings.auth_open_chat_profiles), generalGetString(MR.strings.auth_log_in_using_credential)) { - ModalManager.center.showModalCloseable { close -> - LaunchedEffect(Unit) { - userPickerState.value = AnimatedViewState.HIDING - } - CreateProfile(chat.simplex.common.platform.chatModel, close) - } - } - } - Divider(Modifier.requiredHeight(1.dp)) - } - } if (showSettings) { - SettingsPickerItem(settingsClicked) + Divider(Modifier.padding(DEFAULT_PADDING)) + UserPickerOptionRow( + painterResource(MR.images.ic_settings), + generalGetString(MR.strings.settings_section_title_settings).lowercase().capitalize(Locale.current), + settingsClicked + ) } if (showCancel) { CancelPickerItem(cancelClicked) @@ -406,42 +564,13 @@ fun LocalDeviceRow(active: Boolean) { @Composable private fun UseFromDesktopPickerItem(onClick: () -> Unit) { - SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) { - val text = generalGetString(MR.strings.settings_section_title_use_from_desktop).lowercase().capitalize(Locale.current) - Icon(painterResource(MR.images.ic_desktop), text, Modifier.size(20.dp * fontSizeSqrtMultiplier), tint = MaterialTheme.colors.onBackground) - Spacer(Modifier.width(DEFAULT_PADDING + 6.dp)) - Text(text, color = MenuTextColor) - } -} + val text = generalGetString(MR.strings.settings_section_title_use_from_desktop).lowercase().capitalize(Locale.current) -@Composable -private fun LinkAMobilePickerItem(onClick: () -> Unit) { - SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) { - val text = generalGetString(MR.strings.link_a_mobile) - Icon(painterResource(MR.images.ic_smartphone_300), text, Modifier.size(20.dp * fontSizeSqrtMultiplier), tint = MaterialTheme.colors.onBackground) - Spacer(Modifier.width(DEFAULT_PADDING + 6.dp)) - Text(text, color = MenuTextColor) - } -} - -@Composable -private fun CreateInitialProfile(onClick: () -> Unit) { - SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) { - val text = generalGetString(MR.strings.create_chat_profile) - Icon(painterResource(MR.images.ic_manage_accounts), text, Modifier.size(20.dp * fontSizeSqrtMultiplier), tint = MaterialTheme.colors.onBackground) - Spacer(Modifier.width(DEFAULT_PADDING + 6.dp)) - Text(text, color = MenuTextColor) - } -} - -@Composable -private fun SettingsPickerItem(onClick: () -> Unit) { - SectionItemView(onClick, padding = PaddingValues(start = DEFAULT_PADDING + 7.dp, end = DEFAULT_PADDING), minHeight = 68.dp) { - val text = generalGetString(MR.strings.settings_section_title_settings).lowercase().capitalize(Locale.current) - Icon(painterResource(MR.images.ic_settings), text, Modifier.size(20.dp * fontSizeSqrtMultiplier), tint = MaterialTheme.colors.onBackground) - Spacer(Modifier.width(DEFAULT_PADDING + 6.dp)) - Text(text, color = MenuTextColor) - } + UserPickerOptionRow( + painterResource(MR.images.ic_desktop), + text, + onClick + ) } @Composable diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserProfileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserProfileView.kt index e3636ec9c5..95af1dc76d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserProfileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserProfileView.kt @@ -34,6 +34,8 @@ fun UserProfileView(chatModel: ChatModel, close: () -> Unit) { KeyChangeEffect(u.value?.remoteHostId, u.value?.userId) { close() } + + Log.d(TAG, "here: ${user?.userId}") if (user != null) { var profile by remember { mutableStateOf(user.profile.toProfile()) } UserProfileLayout(