From b77c357704fe33744e4d5ae954e8b6d6aee845bd Mon Sep 17 00:00:00 2001 From: Diogo Date: Fri, 6 Sep 2024 10:08:43 +0100 Subject: [PATCH] move bars logic to android --- .../views/chatlist/UserPicker.android.kt | 85 +++++- .../common/views/chatlist/UserPicker.kt | 252 ++++++------------ .../views/chatlist/UserPicker.desktop.kt | 8 +- 3 files changed, 170 insertions(+), 175 deletions(-) diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.android.kt index bb3defb95b..2fc605aa30 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.android.kt @@ -5,22 +5,28 @@ import androidx.compose.animation.* import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.material.* -import androidx.compose.runtime.Composable +import androidx.compose.material.DrawerDefaults.ScrimOpacity +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.graphics.Brush import androidx.compose.ui.graphics.Color import androidx.compose.ui.unit.dp +import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.model.User import chat.simplex.common.model.UserInfo import chat.simplex.common.platform.appPlatform +import chat.simplex.common.platform.platform import chat.simplex.common.ui.theme.* -import chat.simplex.common.views.helpers.fontSizeSqrtMultiplier -import chat.simplex.common.views.helpers.userPickerAnimSpec +import chat.simplex.common.views.helpers.* +import chat.simplex.common.views.onboarding.OnboardingStage import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource +import kotlinx.coroutines.flow.MutableStateFlow +import kotlinx.coroutines.flow.distinctUntilChanged +import kotlinx.coroutines.launch @Composable actual fun UserPickerInactiveUsersSection( @@ -95,10 +101,77 @@ actual fun UserPickerInactiveUsersSection( } @Composable -actual fun UserPickerScaffold(isVisible: Boolean, content: @Composable () -> Unit) { - Box { +actual fun UserPickerScaffold(pickerState: MutableStateFlow, content: @Composable () -> Unit) { + val currentTheme by CurrentColors.collectAsState() + val resultingColor by remember { + derivedStateOf { + if (currentTheme.colors.isLight) currentTheme.colors.onSurface.copy(alpha = ScrimOpacity) else Color.Black.copy(0.64f) + } + } + val animatedColor = remember { + androidx.compose.animation.core.Animatable( + if (pickerState.value.isVisible()) Color.Transparent else resultingColor, + Color.VectorConverter(resultingColor.colorSpace) + ) + } + + LaunchedEffect(Unit) { + launch { + snapshotFlow { ModalManager.start.modalCount.value } + .collect { modalCount -> + val colors = CurrentColors.value.colors + + if (modalCount == 0 && pickerState.value.isVisible()) { + platform.androidSetDrawerStatusAndNavBarColor( + isLight = colors.isLight, + drawerShadingColor = animatedColor, + toolbarOnTop = !appPrefs.oneHandUI.get(), + navBarColor = colors.surface + ) + } + } + } + } + + LaunchedEffect(Unit) { + snapshotFlow { currentTheme } + .distinctUntilChanged() + .collect { + launch { + pickerState.collect { + val newState = it + val colors = CurrentColors.value.colors + val toColor = if (colors.isLight) colors.onSurface.copy(alpha = ScrimOpacity) else Color.Black.copy(0.64f) + + animatedColor.animateTo(if (newState.isVisible()) toColor else Color.Transparent, newChatSheetAnimSpec()) { + if (newState.isVisible()) { + platform.androidSetDrawerStatusAndNavBarColor( + isLight = colors.isLight, + drawerShadingColor = animatedColor, + toolbarOnTop = !appPrefs.oneHandUI.get(), + navBarColor = colors.surface + ) + } else if (newState.isHiding()) { + platform.androidSetDrawerStatusAndNavBarColor( + isLight = colors.isLight, + drawerShadingColor = animatedColor, + toolbarOnTop = !appPrefs.oneHandUI.get(), + navBarColor = (if (appPrefs.oneHandUI.get() && appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete) { + colors.background.mixWith(CurrentColors.value.colors.onBackground, 0.97f) + } else { + colors.background + }) + ) + } + } + } + } + } + } + + Box(if (appPlatform.isAndroid) Modifier.drawBehind { drawRect(animatedColor.value) } else Modifier) { AnimatedVisibility( - visible = isVisible, + visible = pickerState.run { value.isVisible() }, enter = if (appPlatform.isAndroid) { slideInVertically( initialOffsetY = { it }, 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 72fa8d6f11..82d3104a46 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 @@ -317,79 +317,18 @@ fun UserPicker( .sortedBy { it.hostDeviceName } } } - val currentTheme by CurrentColors.collectAsState() val animatedFloat = remember { Animatable(if (newChat.isVisible()) 0f else 1f) } - val resultingColor by remember { - derivedStateOf { - if (currentTheme.colors.isLight) currentTheme.colors.onSurface.copy(alpha = ScrimOpacity) else Color.Black.copy(0.64f) - } - } - val animatedColor = remember { - Animatable( - if (newChat.isVisible()) Color.Transparent else resultingColor, - Color.VectorConverter(resultingColor.colorSpace) - ) - } LaunchedEffect(Unit) { launch { - snapshotFlow { ModalManager.start.modalCount.value } - .collect { modalCount -> - val colors = CurrentColors.value.colors - - if (modalCount == 0 && newChat.isVisible() && appPlatform.isAndroid) { - platform.androidSetDrawerStatusAndNavBarColor( - isLight = colors.isLight, - drawerShadingColor = animatedColor, - toolbarOnTop = !appPrefs.oneHandUI.get(), - navBarColor = colors.surface - ) - } - } - } - } - - LaunchedEffect(Unit) { - snapshotFlow { currentTheme } - .distinctUntilChanged() - .collect { + userPickerState.collect { + newChat = it launch { - userPickerState.collect { - newChat = it - val colors = CurrentColors.value.colors - val toColor = if (colors.isLight) colors.onSurface.copy(alpha = ScrimOpacity) else Color.Black.copy(0.64f) - - animatedColor.animateTo(if (newChat.isVisible()) toColor else Color.Transparent, newChatSheetAnimSpec()) { - if (appPlatform.isAndroid) { - if (newChat.isVisible()) { - platform.androidSetDrawerStatusAndNavBarColor( - isLight = colors.isLight, - drawerShadingColor = animatedColor, - toolbarOnTop = !appPrefs.oneHandUI.get(), - navBarColor = colors.surface - ) - } else if (newChat.isHiding()) { - platform.androidSetDrawerStatusAndNavBarColor( - isLight = colors.isLight, - drawerShadingColor = animatedColor, - toolbarOnTop = !appPrefs.oneHandUI.get(), - navBarColor = (if (appPrefs.oneHandUI.get() && appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete) { - colors.background.mixWith(CurrentColors.value.colors.onBackground, 0.97f) - } else { - colors.background - }) - ) - } - } - } - - launch { - animatedFloat.animateTo(if (newChat.isVisible()) 1f else 0f, newChatSheetAnimSpec()) - if (newChat.isHiding()) userPickerState.value = AnimatedViewState.GONE - } - } + animatedFloat.animateTo(if (newChat.isVisible()) 1f else 0f, newChatSheetAnimSpec()) + if (newChat.isHiding()) userPickerState.value = AnimatedViewState.GONE } } + } } LaunchedEffect(Unit) { @@ -437,117 +376,100 @@ fun UserPicker( } } } - var drawerHeightPx by remember { mutableStateOf(0) } - var offsetY by remember { mutableStateOf(0f) } - Box(if (appPlatform.isAndroid) Modifier.drawBehind { drawRect(animatedColor.value) } else Modifier) { - UserPickerScaffold(isVisible = newChat.isVisible()) { - Box( + UserPickerScaffold(pickerState = userPickerState) { + Box( + Modifier + .fillMaxSize() + .clickable(interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = { userPickerState.value = AnimatedViewState.HIDING }), + contentAlignment = if (appPlatform.isAndroid) Alignment.BottomStart else Alignment.TopStart + ) { + Column( Modifier - .fillMaxSize() - .pointerInput(Unit) { - detectVerticalDragGestures { _, dragAmount -> - offsetY += dragAmount - - if (offsetY > drawerHeightPx * 0.3f) { - userPickerState.value = AnimatedViewState.HIDING - } - } - } - .clickable(interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = { userPickerState.value = AnimatedViewState.HIDING }), - contentAlignment = if (appPlatform.isAndroid) Alignment.BottomStart else Alignment.TopStart + .height(IntrinsicSize.Min) + .then(if (appPlatform.isDesktop) Modifier.width(DEFAULT_START_MODAL_WIDTH * fontSizeSqrtMultiplier) else Modifier) + .shadow(8.dp, clip = true) + .fillMaxWidth() + .background(MaterialTheme.colors.surface) ) { + val currentRemoteHost = remember { chatModel.currentRemoteHost }.value Column( Modifier - .height(IntrinsicSize.Min) - .then(if (appPlatform.isDesktop) Modifier.width(DEFAULT_START_MODAL_WIDTH * fontSizeSqrtMultiplier) else Modifier) - .shadow(8.dp, clip = true) - .fillMaxWidth() - .background(MaterialTheme.colors.surface) - .onGloballyPositioned { coordinates -> - offsetY = 0f - drawerHeightPx = coordinates.size.height - } + .padding(vertical = DEFAULT_PADDING) ) { - val currentRemoteHost = remember { chatModel.currentRemoteHost }.value - Column( - Modifier - .padding(vertical = DEFAULT_PADDING) - ) { - if (remoteHosts.isNotEmpty()) { - val localDeviceActive = currentRemoteHost == null && chatModel.localUserCreated.value == true + if (remoteHosts.isNotEmpty()) { + val localDeviceActive = currentRemoteHost == null && chatModel.localUserCreated.value == true - DevicePickerRow( - localDeviceActive = localDeviceActive, - remoteHosts = remoteHosts, - onRemoteHostClick = { h, connecting -> - userPickerState.value = AnimatedViewState.HIDING - switchToRemoteHost(h, connecting) - }, - onLocalDeviceClick = { - userPickerState.value = AnimatedViewState.HIDING - switchToLocalDevice() - }, - onRemoteHostActionButtonClick = { h -> - userPickerState.value = AnimatedViewState.HIDING - stopRemoteHostAndReloadHosts(h, true) - } - ) + DevicePickerRow( + localDeviceActive = localDeviceActive, + remoteHosts = remoteHosts, + onRemoteHostClick = { h, connecting -> + userPickerState.value = AnimatedViewState.HIDING + switchToRemoteHost(h, connecting) + }, + onLocalDeviceClick = { + userPickerState.value = AnimatedViewState.HIDING + switchToLocalDevice() + }, + onRemoteHostActionButtonClick = { h -> + userPickerState.value = AnimatedViewState.HIDING + stopRemoteHostAndReloadHosts(h, true) + } + ) + } + val showCustomModal: (@Composable() (ModalData.(ChatModel, () -> Unit) -> Unit)) -> () -> Unit = { modalView -> + { + if (appPlatform.isDesktop) { + userPickerState.value = AnimatedViewState.HIDING + } + ModalManager.start.showCustomModal { close -> modalView(chatModel, close) } } - val showCustomModal: (@Composable() (ModalData.(ChatModel, () -> Unit) -> Unit)) -> () -> Unit = { modalView -> - { + } + + ActiveUserSection( + chatModel = chatModel, + userPickerState = userPickerState, + showCustomModal = showCustomModal, + ) + + Divider(Modifier.padding(DEFAULT_PADDING)) + val profileHidden = rememberSaveable { mutableStateOf(false) } + + GlobalSettingsSection( + chatModel = chatModel, + userPickerState = userPickerState, + setPerformLA = setPerformLA, + onUserClicked = { user -> + userPickerState.value = AnimatedViewState.HIDING + if (!user.activeUser) { + withBGApi { + controller.showProgressIfNeeded { + ModalManager.closeAllModalsEverywhere() + chatModel.controller.changeActiveUser(user.remoteHostId, user.userId, null) + } + } + } + }, + onShowAllProfilesClicked = { + doWithAuth( + generalGetString(MR.strings.auth_open_chat_profiles), + generalGetString(MR.strings.auth_log_in_using_credential) + ) { if (appPlatform.isDesktop) { userPickerState.value = AnimatedViewState.HIDING } - ModalManager.start.showCustomModal { close -> modalView(chatModel, close) } + 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 = { UserProfilesView(chatModel, search, profileHidden) }) + } } } - - ActiveUserSection( - chatModel = chatModel, - userPickerState = userPickerState, - showCustomModal = showCustomModal, - ) - - Divider(Modifier.padding(DEFAULT_PADDING)) - val profileHidden = rememberSaveable { mutableStateOf(false) } - - GlobalSettingsSection( - chatModel = chatModel, - userPickerState = userPickerState, - setPerformLA = setPerformLA, - onUserClicked = { user -> - userPickerState.value = AnimatedViewState.HIDING - if (!user.activeUser) { - withBGApi { - controller.showProgressIfNeeded { - ModalManager.closeAllModalsEverywhere() - chatModel.controller.changeActiveUser(user.remoteHostId, user.userId, null) - } - } - } - }, - onShowAllProfilesClicked = { - doWithAuth( - generalGetString(MR.strings.auth_open_chat_profiles), - generalGetString(MR.strings.auth_log_in_using_credential) - ) { - if (appPlatform.isDesktop) { - userPickerState.value = AnimatedViewState.HIDING - } - 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 = { UserProfilesView(chatModel, search, profileHidden) }) - } - } - } - ) - } + ) } } } @@ -695,7 +617,7 @@ expect fun UserPickerInactiveUsersSection( @Composable expect fun UserPickerScaffold( - isVisible: Boolean, + pickerState: MutableStateFlow, content: @Composable () -> Unit ) diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.desktop.kt index 584db44e0c..0edaedbba0 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chatlist/UserPicker.desktop.kt @@ -11,11 +11,11 @@ import chat.simplex.common.model.User import chat.simplex.common.model.UserInfo import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* -import chat.simplex.common.views.helpers.fontSizeSqrtMultiplier -import chat.simplex.common.views.helpers.userPickerAnimSpec +import chat.simplex.common.views.helpers.* import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource +import kotlinx.coroutines.flow.MutableStateFlow @Composable actual fun UserPickerInactiveUsersSection( @@ -62,9 +62,9 @@ actual fun UserPickerInactiveUsersSection( } @Composable -actual fun UserPickerScaffold(isVisible: Boolean, content: @Composable () -> Unit) { +actual fun UserPickerScaffold(pickerState: MutableStateFlow, content: @Composable () -> Unit) { AnimatedVisibility( - visible = isVisible, + visible = pickerState.value.isVisible(), enter = fadeIn(), exit = fadeOut() ) {