diff --git a/apps/multiplatform/build.gradle.kts b/apps/multiplatform/build.gradle.kts index 4e7ac583a5..94cecd1725 100644 --- a/apps/multiplatform/build.gradle.kts +++ b/apps/multiplatform/build.gradle.kts @@ -55,6 +55,7 @@ allprojects { google() mavenCentral() maven("https://maven.pkg.jetbrains.space/public/p/compose/dev") + maven("https://jitpack.io") } } diff --git a/apps/multiplatform/common/build.gradle.kts b/apps/multiplatform/common/build.gradle.kts index 5b45a2317f..91c41e562a 100644 --- a/apps/multiplatform/common/build.gradle.kts +++ b/apps/multiplatform/common/build.gradle.kts @@ -94,6 +94,7 @@ kotlin { val desktopMain by getting { dependencies { implementation("org.jetbrains.kotlinx:kotlinx-coroutines-swing:1.7.1") + implementation("com.github.Dansoftowner:jSystemThemeDetector:3.6") } } val desktopTest by getting diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Resources.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Resources.android.kt index 2604e0d268..226e97d4dc 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Resources.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/Resources.android.kt @@ -48,4 +48,6 @@ actual fun screenOrientation(): ScreenOrientation = when (mainActivity.get()?.re @Composable actual fun screenWidth(): Dp = LocalConfiguration.current.screenWidthDp.dp +actual fun desktopExpandWindowToWidth(width: Dp) {} + actual fun isRtl(text: CharSequence): Boolean = BidiFormatter.getInstance().isRtl(text) diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/ui/theme/Theme.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/ui/theme/Theme.android.kt new file mode 100644 index 0000000000..0dcbb42322 --- /dev/null +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/ui/theme/Theme.android.kt @@ -0,0 +1,6 @@ +package chat.simplex.common.ui.theme + +import androidx.compose.runtime.Composable + +@Composable +actual fun isSystemInDarkTheme(): Boolean = androidx.compose.foundation.isSystemInDarkTheme() diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.android.kt index 25cbffb651..dc8e9dd541 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.android.kt @@ -35,7 +35,7 @@ actual fun SimpleAndAnimatedImageView( ImageView(imagePainter) { hideKeyboard(view) if (getLoadedFilePath(file) != null) { - ModalManager.shared.showCustomModal(animated = false) { close -> + ModalManager.fullscreen.showCustomModal(animated = false) { close -> ImageFullScreenView(imageProvider, close) } } diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/usersettings/Appearance.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/usersettings/Appearance.android.kt index e03e19858a..e02c011fac 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/usersettings/Appearance.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/usersettings/Appearance.android.kt @@ -69,7 +69,7 @@ actual fun AppearanceView(m: ChatModel, showSettingsModal: (@Composable (ChatMod changeIcon = ::setAppIcon, showSettingsModal = showSettingsModal, editColor = { name, initialColor -> - ModalManager.shared.showModalCloseable { close -> + ModalManager.start.showModalCloseable { close -> ColorEditor(name, initialColor, close) } }, 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 c2ff62442a..dda00fc43c 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 @@ -1,35 +1,46 @@ package chat.simplex.common import androidx.compose.animation.core.Animatable +import androidx.compose.foundation.background +import androidx.compose.foundation.clickable +import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.clipToBounds import androidx.compose.ui.graphics.graphicsLayer import androidx.compose.ui.unit.dp import chat.simplex.common.views.usersettings.SetDeliveryReceiptsView import chat.simplex.common.model.* import chat.simplex.common.platform.* -import chat.simplex.common.ui.theme.DEFAULT_PADDING +import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.SimpleButton import chat.simplex.common.views.SplashView import chat.simplex.common.views.call.ActiveCallView import chat.simplex.common.views.call.IncomingCallAlertView import chat.simplex.common.views.chat.ChatView -import chat.simplex.common.views.chatlist.ChatListView -import chat.simplex.common.views.chatlist.ShareListView +import chat.simplex.common.views.chatlist.* import chat.simplex.common.views.database.DatabaseErrorView import chat.simplex.common.views.helpers.* +import chat.simplex.common.views.localauth.VerticalDivider import chat.simplex.common.views.onboarding.* import chat.simplex.common.views.usersettings.* import chat.simplex.res.MR import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import kotlinx.coroutines.* +import kotlinx.coroutines.flow.MutableStateFlow import kotlinx.coroutines.flow.distinctUntilChanged +data class SettingsViewState( + val userPickerState: MutableStateFlow, + val scaffoldState: ScaffoldState, + val switchingUsers: MutableState +) + @Composable fun AppScreen() { ProvideWindowInsets(windowInsetsAnimationsEnabled = true) { @@ -66,7 +77,7 @@ fun MainScreen() { } LaunchedEffect(chatModel.clearOverlays.value) { if (chatModel.clearOverlays.value) { - ModalManager.shared.closeModals() + ModalManager.closeAllModalsEverywhere() chatModel.clearOverlays.value = false } } @@ -105,61 +116,30 @@ fun MainScreen() { onboarding == OnboardingStage.OnboardingComplete && userCreated -> { Box { showAdvertiseLAAlert = true - BoxWithConstraints { - var currentChatId by rememberSaveable { mutableStateOf(chatModel.chatId.value) } - val offset = remember { Animatable(if (chatModel.chatId.value == null) 0f else maxWidth.value) } - Box( - Modifier - .graphicsLayer { - translationX = -offset.value.dp.toPx() - } - ) { - if (chatModel.setDeliveryReceipts.value) { - SetDeliveryReceiptsView(chatModel) - } else { - val stopped = chatModel.chatRunning.value == false - if (chatModel.sharedContent.value == null) - ChatListView(chatModel, AppLock::setPerformLA, stopped) - else - ShareListView(chatModel, stopped) - } - } - val scope = rememberCoroutineScope() - val onComposed: () -> Unit = { - scope.launch { - offset.animateTo( - if (chatModel.chatId.value == null) 0f else maxWidth.value, - chatListAnimationSpec() - ) - if (offset.value == 0f) { - currentChatId = null - } - } - } - LaunchedEffect(Unit) { - launch { - snapshotFlow { chatModel.chatId.value } - .distinctUntilChanged() - .collect { - if (it != null) currentChatId = it - else onComposed() - } - } - } - Box(Modifier.graphicsLayer { translationX = maxWidth.toPx() - offset.value.dp.toPx() }) Box2@{ - currentChatId?.let { - ChatView(it, chatModel, onComposed) - } - } + val userPickerState by rememberSaveable(stateSaver = AnimatedViewState.saver()) { mutableStateOf(MutableStateFlow(AnimatedViewState.GONE)) } + val scaffoldState = rememberScaffoldState() + val switchingUsers = rememberSaveable { mutableStateOf(false) } + val settingsState = remember { SettingsViewState(userPickerState, scaffoldState, switchingUsers) } + if (appPlatform.isAndroid) { + AndroidScreen(settingsState) + } else { + DesktopScreen(settingsState) } } } - onboarding == OnboardingStage.Step1_SimpleXInfo -> SimpleXInfo(chatModel, onboarding = true) + onboarding == OnboardingStage.Step1_SimpleXInfo -> { + SimpleXInfo(chatModel, onboarding = true) + if (appPlatform.isDesktop) { + ModalManager.fullscreen.showInView() + } + } onboarding == OnboardingStage.Step2_CreateProfile -> CreateProfile(chatModel) {} onboarding == OnboardingStage.Step3_CreateSimpleXAddress -> CreateSimpleXAddress(chatModel) onboarding == OnboardingStage.Step4_SetNotificationsMode -> SetNotificationsMode(chatModel) } - ModalManager.shared.showInView() + if (appPlatform.isAndroid) { + ModalManager.fullscreen.showInView() + } val unauthorized = remember { derivedStateOf { AppLock.userAuthorized.value != true } } if (unauthorized.value && !(chatModel.activeCallViewIsVisible.value && chatModel.showCallView.value)) { @@ -178,7 +158,7 @@ fun MainScreen() { } else if (chatModel.showCallView.value) { ActiveCallView() } - ModalManager.shared.showPasscodeInView() + ModalManager.fullscreen.showPasscodeInView() val invitation = chatModel.activeCallInvitation.value if (invitation != null) IncomingCallAlertView(invitation, chatModel) AlertManager.shared.showInView() @@ -200,6 +180,141 @@ fun MainScreen() { } } +@Composable +fun AndroidScreen(settingsState: SettingsViewState) { + BoxWithConstraints { + var currentChatId by rememberSaveable { mutableStateOf(chatModel.chatId.value) } + val offset = remember { Animatable(if (chatModel.chatId.value == null) 0f else maxWidth.value) } + Box( + Modifier + .graphicsLayer { + translationX = -offset.value.dp.toPx() + } + ) { + StartPartOfScreen(settingsState) + } + val scope = rememberCoroutineScope() + val onComposed: () -> Unit = { + scope.launch { + offset.animateTo( + if (chatModel.chatId.value == null) 0f else maxWidth.value, + chatListAnimationSpec() + ) + if (offset.value == 0f) { + currentChatId = null + } + } + } + LaunchedEffect(Unit) { + launch { + snapshotFlow { chatModel.chatId.value } + .distinctUntilChanged() + .collect { + if (it != null) currentChatId = it + else onComposed() + } + } + } + Box(Modifier.graphicsLayer { translationX = maxWidth.toPx() - offset.value.dp.toPx() }) Box2@{ + currentChatId?.let { + ChatView(it, chatModel, onComposed) + } + } + } +} + +@Composable +fun StartPartOfScreen(settingsState: SettingsViewState) { + if (chatModel.setDeliveryReceipts.value) { + SetDeliveryReceiptsView(chatModel) + } else { + val stopped = chatModel.chatRunning.value == false + if (chatModel.sharedContent.value == null) + ChatListView(chatModel, settingsState, AppLock::setPerformLA, stopped) + else + ShareListView(chatModel, settingsState, stopped) + } +} + +@Composable +fun CenterPartOfScreen() { + val currentChatId by remember { ChatModel.chatId } + LaunchedEffect(Unit) { + snapshotFlow { currentChatId } + .distinctUntilChanged() + .collect { + if (it != null) { + ModalManager.center.closeModals() + } + } + } + when (val id = currentChatId) { + null -> { + if (!rememberUpdatedState(ModalManager.center.hasModalsOpen()).value) { + Box( + Modifier + .fillMaxSize() + .background(MaterialTheme.colors.background), + contentAlignment = Alignment.Center + ) { + Text(stringResource(MR.strings.no_selected_chat)) + } + } else { + ModalManager.center.showInView() + } + } + else -> ChatView(id, chatModel) {} + } +} + +@Composable +fun EndPartOfScreen() { + ModalManager.end.showInView() +} + +@Composable +fun DesktopScreen(settingsState: SettingsViewState) { + Box { + // 56.dp is a size of unused space of settings drawer + Box(Modifier.width(DEFAULT_START_MODAL_WIDTH + 56.dp)) { + StartPartOfScreen(settingsState) + } + Box(Modifier.widthIn(max = DEFAULT_START_MODAL_WIDTH)) { + ModalManager.start.showInView() + } + Row(Modifier.padding(start = DEFAULT_START_MODAL_WIDTH).clipToBounds()) { + Box(Modifier.widthIn(min = DEFAULT_MIN_CENTER_MODAL_WIDTH).weight(1f)) { + CenterPartOfScreen() + } + if (ModalManager.end.hasModalsOpen()) { + VerticalDivider() + } + Box(Modifier.widthIn(max = DEFAULT_END_MODAL_WIDTH).clipToBounds()) { + EndPartOfScreen() + } + } + val (userPickerState, scaffoldState, switchingUsers ) = settingsState + val scope = rememberCoroutineScope() + if (scaffoldState.drawerState.isOpen) { + Box( + Modifier + .fillMaxSize() + .padding(start = DEFAULT_START_MODAL_WIDTH) + .clickable(interactionSource = remember { MutableInteractionSource() }, indication = null, onClick = { + ModalManager.start.closeModals() + scope.launch { settingsState.scaffoldState.drawerState.close() } + }) + ) + } + VerticalDivider(Modifier.padding(start = DEFAULT_START_MODAL_WIDTH)) + UserPicker(chatModel, userPickerState, switchingUsers) { + scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() } + } + ModalManager.fullscreen.showInView() + ModalManager.fullscreen.showPasscodeInView() + } +} + @Composable fun InitializationView() { Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/AppLock.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/AppLock.kt index c66eeefd59..fdc1969411 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/AppLock.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/AppLock.kt @@ -71,7 +71,7 @@ object AppLock { private fun initialEnableLA() { val m = ChatModel val appPrefs = ChatController.appPrefs - appPrefs.laMode.set(LAMode.SYSTEM) + appPrefs.laMode.set(LAMode.default) authenticate( generalGetString(MR.strings.auth_enable_simplex_lock), generalGetString(MR.strings.auth_confirm_credential), @@ -100,7 +100,7 @@ object AppLock { private fun setPasscode() { val appPrefs = ChatController.appPrefs - ModalManager.shared.showCustomModal { close -> + ModalManager.fullscreen.showCustomModal { close -> Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) { SetAppPasscodeView( submit = { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index ea44745b39..3189e1c705 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -437,7 +437,7 @@ object ChatModel { val info = getChat(id)?.chatInfo as? ChatInfo.ContactConnection ?: return if (info.contactConnection.connReqInv == connReqInv.value) { connReqInv.value = null - ModalManager.shared.closeModals() + ModalManager.center.closeModals() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 065003a072..3bfe32c732 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -72,7 +72,7 @@ class AppPreferences { set = fun(action: CallOnLockScreen) { _callOnLockScreen.set(action.name) } ) val performLA = mkBoolPreference(SHARED_PREFS_PERFORM_LA, false) - val laMode = mkEnumPreference(SHARED_PREFS_LA_MODE, LAMode.SYSTEM) { LAMode.values().firstOrNull { it.name == this } } + val laMode = mkEnumPreference(SHARED_PREFS_LA_MODE, LAMode.default) { LAMode.values().firstOrNull { it.name == this } } val laLockDelay = mkIntPreference(SHARED_PREFS_LA_LOCK_DELAY, 30) val laNoticeShown = mkBoolPreference(SHARED_PREFS_LA_NOTICE_SHOWN, false) val webrtcIceServers = mkStrPreference(SHARED_PREFS_WEBRTC_ICE_SERVERS, null) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/other/wheel-picker/FWheelPickerDefault.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/other/wheel-picker/FWheelPickerDefault.kt index ed2533b488..9760e9c9f2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/other/wheel-picker/FWheelPickerDefault.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/other/wheel-picker/FWheelPickerDefault.kt @@ -2,7 +2,7 @@ package com.sd.lib.compose.wheel_picker import androidx.compose.animation.core.animateFloatAsState import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme +import chat.simplex.common.ui.theme.isSystemInDarkTheme import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.runtime.getValue diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt index 6e410cc2c3..ed35392159 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/AppCommon.kt @@ -10,6 +10,9 @@ enum class AppPlatform { val isAndroid: Boolean get() = this == ANDROID + + val isDesktop: Boolean + get() = this == DESKTOP } expect val appPlatform: AppPlatform diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Resources.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Resources.kt index 3d31faaa84..c7799e592b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Resources.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/Resources.kt @@ -28,4 +28,6 @@ expect fun screenOrientation(): ScreenOrientation @Composable expect fun screenWidth(): Dp +expect fun desktopExpandWindowToWidth(width: Dp) + expect fun isRtl(text: CharSequence): Boolean diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt index 3f106bd729..6af3156ca2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/ui/theme/Theme.kt @@ -1,7 +1,6 @@ package chat.simplex.common.ui.theme import androidx.compose.foundation.background -import androidx.compose.foundation.isSystemInDarkTheme import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier @@ -191,6 +190,11 @@ val DEFAULT_PADDING_HALF = DEFAULT_PADDING / 2 val DEFAULT_BOTTOM_PADDING = 48.dp val DEFAULT_BOTTOM_BUTTON_PADDING = 20.dp +val DEFAULT_START_MODAL_WIDTH = 388.dp +val DEFAULT_MIN_CENTER_MODAL_WIDTH = 590.dp +val DEFAULT_END_MODAL_WIDTH = 388.dp +val DEFAULT_MAX_IMAGE_WIDTH = 500.dp + val DarkColorPalette = darkColors( primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files primaryVariant = SimplexBlue, @@ -255,6 +259,15 @@ val CurrentColors: MutableStateFlow = MutableStateFlow @Composable fun isInDarkTheme(): Boolean = !CurrentColors.collectAsState().value.colors.isLight +expect fun isSystemInDarkTheme(): Boolean + +fun reactOnDarkThemeChanges(isDark: Boolean) { + if (ChatController.appPrefs.currentTheme.get() == DefaultTheme.SYSTEM.name && CurrentColors.value.colors.isLight == isDark) { + // Change active colors from light to dark and back based on system theme + ThemeManager.applyTheme(DefaultTheme.SYSTEM.name, isDark) + } +} + @Composable fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) { LaunchedEffect(darkTheme) { @@ -264,10 +277,7 @@ fun SimpleXTheme(darkTheme: Boolean? = null, content: @Composable () -> Unit) { } val systemDark = isSystemInDarkTheme() LaunchedEffect(systemDark) { - if (ChatController.appPrefs.currentTheme.get() == DefaultTheme.SYSTEM.name && CurrentColors.value.colors.isLight == systemDark) { - // Change active colors from light to dark and back based on system theme - ThemeManager.applyTheme(DefaultTheme.SYSTEM.name, systemDark) - } + reactOnDarkThemeChanges(systemDark) } val theme by CurrentColors.collectAsState() MaterialTheme( diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt index b2692d541a..47bb6918dd 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/TerminalView.kt @@ -24,6 +24,12 @@ import chat.simplex.common.platform.* @Composable fun TerminalView(chatModel: ChatModel, close: () -> Unit) { val composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = false)) } + val close = { + close() + if (appPlatform.isDesktop) { + ModalManager.center.closeModals() + } + } BackHandler(onBack = { close() }) @@ -126,7 +132,7 @@ fun TerminalLog(terminalItems: List) { modifier = Modifier .fillMaxWidth() .clickable { - ModalManager.shared.showModal(endButtons = { ShareButton { clipboard.shareText(item.details) } }) { + ModalManager.start.showModal(endButtons = { ShareButton { clipboard.shareText(item.details) } }) { SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState())) { Text(item.details, modifier = Modifier.padding(horizontal = DEFAULT_PADDING).padding(bottom = DEFAULT_PADDING)) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt index ba15f426b0..85ab5c2ea3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt @@ -81,7 +81,7 @@ fun ChatInfoView( setContactAlias(chat.chatInfo.apiId, it, chatModel) }, openPreferences = { - ModalManager.shared.showCustomModal { close -> + ModalManager.end.showCustomModal { close -> val user = chatModel.currentUser.value if (user != null) { ContactPreferencesView(chatModel, user, contact.contactId, close) @@ -136,7 +136,7 @@ fun ChatInfoView( }) }, verifyClicked = { - ModalManager.shared.showModalCloseable { close -> + ModalManager.end.showModalCloseable { close -> remember { derivedStateOf { (chatModel.getContactChat(contact.contactId)?.chatInfo as? ChatInfo.Direct)?.contact } }.value?.let { ct -> VerifyCodeView( ct.displayName, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index 3478f33ef2..84a9d02bfd 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -139,7 +139,8 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { if (chat.chatInfo is ChatInfo.Direct) { val contactInfo = chatModel.controller.apiContactInfo(chat.chatInfo.apiId) val (_, code) = chatModel.controller.apiGetContactCode(chat.chatInfo.apiId) - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.end.closeModals() + ModalManager.end.showModalCloseable(true) { close -> remember { derivedStateOf { (chatModel.getContactChat(chat.chatInfo.apiId)?.chatInfo as? ChatInfo.Direct)?.contact } }.value?.let { ct -> ChatInfoView(chatModel, ct, contactInfo?.first, contactInfo?.second, chat.chatInfo.localAlias, code, close) } @@ -149,7 +150,8 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { val link = chatModel.controller.apiGetGroupLink(chat.chatInfo.groupInfo.groupId) var groupLink = link?.first var groupLinkMemberRole = link?.second - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.end.closeModals() + ModalManager.end.showModalCloseable(true) { close -> GroupChatInfoView(chatModel, groupLink, groupLinkMemberRole, { groupLink = it.first; groupLinkMemberRole = it.second @@ -174,7 +176,8 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { member to null } setGroupMembers(groupInfo, chatModel) - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.end.closeModals() + ModalManager.end.showModalCloseable(true) { close -> remember { derivedStateOf { chatModel.groupMembers.firstOrNull { it.memberId == member.memberId } } }.value?.let { mem -> GroupMemberInfoView(groupInfo, mem, stats, code, chatModel, close, close) } @@ -314,7 +317,8 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { withApi { val ciInfo = chatModel.controller.apiGetChatItemInfo(cInfo.chatType, cInfo.apiId, cItem.id) if (ciInfo != null) { - ModalManager.shared.showModal(endButtons = { ShareButton { + ModalManager.end.closeModals() + ModalManager.end.showModal(endButtons = { ShareButton { clipboard.shareText(itemInfoShareText(cItem, ciInfo, chatModel.controller.appPrefs.developerTools.get())) } }) { ChatItemInfoView(cItem, ciInfo, devTools = chatModel.controller.appPrefs.developerTools.get()) @@ -326,8 +330,9 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) { hideKeyboard(view) withApi { setGroupMembers(groupInfo, chatModel) - ModalManager.shared.showModalCloseable(true) { close -> - AddGroupMembersView(groupInfo, false, chatModel, close) + ModalManager.end.closeModals() + ModalManager.end.showModalCloseable(true) { close -> + AddGroupMembersView(groupInfo, false, chatModel, close) } } }, @@ -462,7 +467,9 @@ fun ChatInfoToolbar( showSearch = false } } - BackHandler(onBack = onBackClicked) + if (appPlatform.isAndroid) { + BackHandler(onBack = onBackClicked) + } val barButtons = arrayListOf<@Composable RowScope.() -> Unit>() val menuItems = arrayListOf<@Composable () -> Unit>() menuItems.add { @@ -520,7 +527,7 @@ fun ChatInfoToolbar( } DefaultTopAppBar( - navigationButton = { NavigationButtonBack(onBackClicked) }, + navigationButton = { if (appPlatform.isAndroid || showSearch) { NavigationButtonBack(onBackClicked) } }, title = { ChatInfoToolbarTitle(chat.chatInfo) }, onTitleClick = info, showSearch = showSearch, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt index 6912ced0e8..143815ddc2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ComposeView.kt @@ -170,7 +170,7 @@ fun ComposeView( val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get() val maxFileSize = getMaxFileSize(FileProtocol.XFTP) val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground) - val textStyle = remember { mutableStateOf(smallFont) } + val textStyle = remember(MaterialTheme.colors.isLight) { mutableStateOf(smallFont) } val processPickedMedia = { uris: List, text: String? -> val content = ArrayList() val imagesPreview = ArrayList() @@ -655,7 +655,11 @@ fun ComposeView( } else { showChooseAttachment } - IconButton(attachmentClicked, enabled = !composeState.value.attachmentDisabled && rememberUpdatedState(chat.userCanSend).value) { + IconButton( + attachmentClicked, + Modifier.padding(bottom = if (appPlatform.isAndroid) 0.dp else 7.dp), + enabled = !composeState.value.attachmentDisabled && rememberUpdatedState(chat.userCanSend).value + ) { Icon( painterResource(MR.images.ic_attach_file_filled_500), contentDescription = stringResource(MR.strings.attach), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SendMsgView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SendMsgView.kt index df76617a23..af8f4520b2 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SendMsgView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SendMsgView.kt @@ -83,7 +83,7 @@ fun SendMsgView( if (showDeleteTextButton.value) { DeleteTextButton(composeState) } - Box(Modifier.align(Alignment.BottomEnd)) { + Box(Modifier.align(Alignment.BottomEnd).padding(bottom = if (appPlatform.isAndroid) 0.dp else 5.dp)) { val sendButtonSize = remember { Animatable(36f) } val sendButtonAlpha = remember { Animatable(1f) } val scope = rememberCoroutineScope() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/VerifyCodeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/VerifyCodeView.kt index bb79fce66c..1b25c52cfe 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/VerifyCodeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/VerifyCodeView.kt @@ -113,7 +113,7 @@ private fun VerifyCodeLayout( } else { if (appPlatform.isAndroid) { SimpleButton(generalGetString(MR.strings.scan_code), painterResource(MR.images.ic_qr_code)) { - ModalManager.shared.showModal { + ModalManager.end.showModal { ScanCodeView(verifyCode) { } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt index 05dbab28a7..e9a9854456 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt @@ -50,7 +50,7 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch allowModifyMembers = allowModifyMembers, searchText, openPreferences = { - ModalManager.shared.showCustomModal { close -> + ModalManager.end.showCustomModal { close -> GroupPreferencesView(chatModel, groupInfo.id, close) } }, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt index 77cfe964c0..4b37badfeb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupChatInfoView.kt @@ -54,7 +54,7 @@ fun GroupChatInfoView(chatModel: ChatModel, groupLink: String?, groupLinkMemberR addMembers = { withApi { setGroupMembers(groupInfo, chatModel) - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.end.showModalCloseable(true) { close -> AddGroupMembersView(groupInfo, false, chatModel, close) } } @@ -73,7 +73,7 @@ fun GroupChatInfoView(chatModel: ChatModel, groupLink: String?, groupLinkMemberR } else { member to null } - ModalManager.shared.showModalCloseable(true) { closeCurrent -> + ModalManager.end.showModalCloseable(true) { closeCurrent -> remember { derivedStateOf { chatModel.groupMembers.firstOrNull { it.memberId == member.memberId } } }.value?.let { mem -> GroupMemberInfoView(groupInfo, mem, stats, code, chatModel, closeCurrent) { closeCurrent() @@ -84,13 +84,13 @@ fun GroupChatInfoView(chatModel: ChatModel, groupLink: String?, groupLinkMemberR } }, editGroupProfile = { - ModalManager.shared.showCustomModal { close -> GroupProfileView(groupInfo, chatModel, close) } + ModalManager.end.showCustomModal { close -> GroupProfileView(groupInfo, chatModel, close) } }, addOrEditWelcomeMessage = { - ModalManager.shared.showCustomModal { close -> GroupWelcomeView(chatModel, groupInfo, close) } + ModalManager.end.showCustomModal { close -> GroupWelcomeView(chatModel, groupInfo, close) } }, openPreferences = { - ModalManager.shared.showCustomModal { close -> + ModalManager.end.showCustomModal { close -> GroupPreferencesView( chatModel, chat.id, @@ -102,7 +102,7 @@ fun GroupChatInfoView(chatModel: ChatModel, groupLink: String?, groupLinkMemberR clearChat = { clearChatDialog(chat.chatInfo, chatModel, close) }, leaveGroup = { leaveGroupDialog(groupInfo, chatModel, close) }, manageGroupLink = { - ModalManager.shared.showModal { GroupLinkView(chatModel, groupInfo, groupLink, groupLinkMemberRole, onGroupLinkUpdated) } + ModalManager.end.showModal { GroupLinkView(chatModel, groupInfo, groupLink, groupLinkMemberRole, onGroupLinkUpdated) } } ) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt index b712a629c1..bfe9aff82b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt @@ -149,7 +149,7 @@ fun GroupMemberInfoView( }) }, verifyClicked = { - ModalManager.shared.showModalCloseable { close -> + ModalManager.end.showModalCloseable { close -> remember { derivedStateOf { chatModel.groupMembers.firstOrNull { it.memberId == member.memberId } } }.value?.let { mem -> VerifyCodeView( mem.displayName, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.kt index db772d9dc6..6430a23db8 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.kt @@ -20,6 +20,7 @@ import androidx.compose.ui.unit.dp import chat.simplex.common.views.helpers.* import chat.simplex.common.model.* import chat.simplex.common.platform.* +import chat.simplex.common.ui.theme.DEFAULT_MAX_IMAGE_WIDTH import chat.simplex.res.MR import dev.icerock.moko.resources.StringResource import java.io.File @@ -85,7 +86,7 @@ fun CIImageView( @Composable fun imageViewFullWidth(): Dp { val approximatePadding = 100.dp - return with(LocalDensity.current) { minOf(1000.dp, LocalWindowWidth() - approximatePadding) } + return with(LocalDensity.current) { minOf(DEFAULT_MAX_IMAGE_WIDTH, LocalWindowWidth() - approximatePadding) } } @Composable @@ -93,10 +94,10 @@ fun CIImageView( Image( imageBitmap, contentDescription = stringResource(MR.strings.image_descr), - // .width(1000.dp) is a hack for image to increase IntrinsicSize of FramedItemView + // .width(DEFAULT_MAX_IMAGE_WIDTH) is a hack for image to increase IntrinsicSize of FramedItemView // if text is short and take all available width if text is long modifier = Modifier - .width(if (imageBitmap.width * 0.97 <= imageBitmap.height) imageViewFullWidth() * 0.75f else 1000.dp) + .width(if (imageBitmap.width * 0.97 <= imageBitmap.height) imageViewFullWidth() * 0.75f else DEFAULT_MAX_IMAGE_WIDTH) .combinedClickable( onLongClick = { showMenu.value = true }, onClick = onClick @@ -110,10 +111,10 @@ fun CIImageView( Image( painter, contentDescription = stringResource(MR.strings.image_descr), - // .width(1000.dp) is a hack for image to increase IntrinsicSize of FramedItemView + // .width(DEFAULT_MAX_IMAGE_WIDTH) is a hack for image to increase IntrinsicSize of FramedItemView // if text is short and take all available width if text is long modifier = Modifier - .width(if (painter.intrinsicSize.width * 0.97 <= painter.intrinsicSize.height) imageViewFullWidth() * 0.75f else 1000.dp) + .width(if (painter.intrinsicSize.width * 0.97 <= painter.intrinsicSize.height) imageViewFullWidth() * 0.75f else DEFAULT_MAX_IMAGE_WIDTH) .combinedClickable( onLongClick = { showMenu.value = true }, onClick = onClick diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIInvalidJSONView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIInvalidJSONView.kt index 92baa6a971..21c27fa34e 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIInvalidJSONView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIInvalidJSONView.kt @@ -21,7 +21,11 @@ import chat.simplex.res.MR @Composable fun CIInvalidJSONView(json: String) { Row(Modifier - .clickable { ModalManager.shared.showModal(true) { InvalidJSONView(json) } } + .clickable { + ModalManager.center.closeModals() + ModalManager.end.closeModals() + ModalManager.center.showModal(true) { InvalidJSONView(json) } + } .padding(horizontal = 10.dp, vertical = 6.dp) ) { Text(stringResource(MR.strings.invalid_data), color = Color.Red, fontStyle = FontStyle.Italic) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVIdeoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVIdeoView.kt index 650c525695..22d89e767c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVIdeoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/item/CIVIdeoView.kt @@ -44,7 +44,7 @@ fun CIVideoView( val view = LocalMultiplatformView() VideoView(uri, file, preview, duration * 1000L, showMenu, onClick = { hideKeyboard(view) - ModalManager.shared.showCustomModal(animated = false) { close -> + ModalManager.fullscreen.showCustomModal(animated = false) { close -> ImageFullScreenView(imageProvider, close) } }) @@ -113,7 +113,7 @@ private fun VideoView(uri: URI, file: CIFile, defaultPreview: ImageBitmap, defau } Box { val windowWidth = LocalWindowWidth() - val width = remember(preview) { if (preview.width * 0.97 <= preview.height) videoViewFullWidth(windowWidth) * 0.75f else 1000.dp } + val width = remember(preview) { if (preview.width * 0.97 <= preview.height) videoViewFullWidth(windowWidth) * 0.75f else DEFAULT_MAX_IMAGE_WIDTH } PlayerView( player, width, @@ -202,7 +202,7 @@ private fun DurationProgress(file: CIFile, playing: MutableState, durat @Composable private fun ImageView(preview: ImageBitmap, showMenu: MutableState, onClick: () -> Unit) { val windowWidth = LocalWindowWidth() - val width = remember(preview) { if (preview.width * 0.97 <= preview.height) videoViewFullWidth(windowWidth) * 0.75f else 1000.dp } + val width = remember(preview) { if (preview.width * 0.97 <= preview.height) videoViewFullWidth(windowWidth) * 0.75f else DEFAULT_MAX_IMAGE_WIDTH } Image( preview, contentDescription = stringResource(MR.strings.video_descr), @@ -311,5 +311,5 @@ private fun receiveFileIfValidSize(file: CIFile, receiveFile: (Long) -> Unit) { private fun videoViewFullWidth(windowWidth: Dp): Dp { val approximatePadding = 100.dp - return minOf(1000.dp, windowWidth - approximatePadding) + return minOf(DEFAULT_MAX_IMAGE_WIDTH, windowWidth - approximatePadding) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index de2d67b48c..73cb007898 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -25,6 +25,7 @@ import chat.simplex.common.views.chat.item.ItemAction import chat.simplex.common.views.helpers.* import chat.simplex.common.views.newchat.ContactConnectionInfoView import chat.simplex.common.model.* +import chat.simplex.common.platform.appPlatform import chat.simplex.common.platform.ntfManager import chat.simplex.res.MR import kotlinx.coroutines.delay @@ -73,7 +74,9 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) { ChatListNavLinkLayout( chatLinkPreview = { ContactConnectionView(chat.chatInfo.contactConnection) }, click = { - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.center.closeModals() + ModalManager.end.closeModals() + ModalManager.center.showModalCloseable(true, showClose = appPlatform.isAndroid) { close -> ContactConnectionInfoView(chatModel, chat.chatInfo.contactConnection.connReqInv, chat.chatInfo.contactConnection, false, close) } }, @@ -87,7 +90,8 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) { InvalidDataView() }, click = { - ModalManager.shared.showModal(true) { InvalidJSONView(chat.chatInfo.json) } + ModalManager.end.closeModals() + ModalManager.center.showModal(true) { InvalidJSONView(chat.chatInfo.json) } }, dropdownMenuItems = null, showMenu, @@ -342,7 +346,9 @@ fun ContactConnectionMenuItems(chatInfo: ChatInfo.ContactConnection, chatModel: stringResource(MR.strings.set_contact_name), painterResource(MR.images.ic_edit), onClick = { - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.center.closeModals() + ModalManager.end.closeModals() + ModalManager.center.showModalCloseable(true, showClose = appPlatform.isAndroid) { close -> ContactConnectionInfoView(chatModel, chatInfo.contactConnection.connReqInv, chatInfo.contactConnection, true, close) } showMenu.value = false 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 9c372020fc..ec5d0071c0 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 @@ -14,10 +14,9 @@ import androidx.compose.ui.graphics.* import androidx.compose.ui.platform.LocalUriHandler import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource -import androidx.compose.ui.text.capitalize import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.text.intl.Locale import androidx.compose.ui.unit.* +import chat.simplex.common.SettingsViewState import chat.simplex.common.model.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* @@ -34,9 +33,8 @@ import kotlinx.coroutines.launch import java.net.URI @Composable -fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped: Boolean) { +fun ChatListView(chatModel: ChatModel, settingsState: SettingsViewState, setPerformLA: (Boolean) -> Unit, stopped: Boolean) { val newChatSheetState by rememberSaveable(stateSaver = AnimatedViewState.saver()) { mutableStateOf(MutableStateFlow(AnimatedViewState.GONE)) } - val userPickerState by rememberSaveable(stateSaver = AnimatedViewState.saver()) { mutableStateOf(MutableStateFlow(AnimatedViewState.GONE)) } val showNewChatSheet = { newChatSheetState.value = AnimatedViewState.VISIBLE } @@ -47,7 +45,7 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped: LaunchedEffect(Unit) { if (shouldShowWhatsNew(chatModel)) { delay(1000L) - ModalManager.shared.showCustomModal { close -> WhatsNewView(close = close) } + ModalManager.center.showCustomModal { close -> WhatsNewView(close = close) } } } LaunchedEffect(chatModel.clearOverlays.value) { @@ -60,13 +58,13 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped: connectIfOpenedViaUri(url, chatModel) } } + val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp var searchInList by rememberSaveable { mutableStateOf("") } - val scaffoldState = rememberScaffoldState() val scope = rememberCoroutineScope() - val switchingUsers = rememberSaveable { mutableStateOf(false) } - Scaffold(topBar = { ChatListToolbar(chatModel, scaffoldState.drawerState, userPickerState, stopped) { searchInList = it.trim() } }, + val (userPickerState, scaffoldState, switchingUsers ) = settingsState + Scaffold(topBar = { Box(Modifier.padding(end = endPadding)) { ChatListToolbar(chatModel, scaffoldState.drawerState, userPickerState, stopped) { searchInList = it.trim() } } }, scaffoldState = scaffoldState, - drawerContent = { SettingsView(chatModel, setPerformLA) }, + drawerContent = { SettingsView(chatModel, setPerformLA, scaffoldState.drawerState) }, drawerScrimColor = MaterialTheme.colors.onSurface.copy(alpha = if (isInDarkTheme()) 0.16f else 0.32f), floatingActionButton = { if (searchInList.isEmpty()) { @@ -76,7 +74,7 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped: if (newChatSheetState.value.isVisible()) hideNewChatSheet(true) else showNewChatSheet() } }, - Modifier.padding(end = DEFAULT_PADDING - 16.dp, bottom = DEFAULT_PADDING - 16.dp), + Modifier.padding(end = DEFAULT_PADDING - 16.dp + endPadding, bottom = DEFAULT_PADDING - 16.dp), elevation = FloatingActionButtonDefaults.elevation( defaultElevation = 0.dp, pressedElevation = 0.dp, @@ -91,7 +89,7 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped: } } ) { - Box(Modifier.padding(it)) { + Box(Modifier.padding(it).padding(end = endPadding)) { Column( modifier = Modifier .fillMaxSize() @@ -112,8 +110,10 @@ fun ChatListView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, stopped: if (searchInList.isEmpty()) { NewChatSheet(chatModel, newChatSheetState, stopped, hideNewChatSheet) } - UserPicker(chatModel, userPickerState, switchingUsers) { - scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() } + if (appPlatform.isAndroid) { + UserPicker(chatModel, userPickerState, switchingUsers) { + scope.launch { if (scaffoldState.drawerState.isOpen) scaffoldState.drawerState.close() else scaffoldState.drawerState.open() } + } } if (switchingUsers.value) { Box( 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 b3daa610e8..2d0ed7eb53 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 @@ -13,20 +13,24 @@ import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.unit.dp +import chat.simplex.common.SettingsViewState import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* import chat.simplex.common.model.Chat import chat.simplex.common.model.ChatModel import chat.simplex.common.platform.BackHandler +import chat.simplex.common.platform.appPlatform import chat.simplex.res.MR import kotlinx.coroutines.flow.MutableStateFlow @Composable -fun ShareListView(chatModel: ChatModel, stopped: Boolean) { +fun ShareListView(chatModel: ChatModel, settingsState: SettingsViewState, stopped: Boolean) { var searchInList by rememberSaveable { mutableStateOf("") } - val userPickerState by rememberSaveable(stateSaver = AnimatedViewState.saver()) { mutableStateOf(MutableStateFlow(AnimatedViewState.GONE)) } - val switchingUsers = rememberSaveable { mutableStateOf(false) } + val (userPickerState, scaffoldState, switchingUsers) = settingsState + val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp Scaffold( + Modifier.padding(end = endPadding), + scaffoldState = scaffoldState, topBar = { Column { ShareListToolbar(chatModel, userPickerState, stopped) { searchInList = it.trim() } } }, ) { Box(Modifier.padding(it)) { @@ -42,9 +46,11 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) { } } } - UserPicker(chatModel, userPickerState, switchingUsers, showSettings = false, showCancel = true, cancelClicked = { - chatModel.sharedContent.value = null - }) + if (appPlatform.isAndroid) { + UserPicker(chatModel, userPickerState, switchingUsers, showSettings = false, showCancel = true, cancelClicked = { + chatModel.sharedContent.value = null + }) + } } @Composable 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 e8fbb66635..5381ebc283 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 @@ -41,6 +41,11 @@ fun UserPicker( ) { val scope = rememberCoroutineScope() var newChat by remember { mutableStateOf(userPickerState.value) } + if (newChat.isVisible()) { + BackHandler { + userPickerState.value = AnimatedViewState.HIDING + } + } val users by remember { derivedStateOf { chatModel.users @@ -121,6 +126,7 @@ fun UserPicker( delay(500) switchingUsers.value = true } + ModalManager.closeAllModalsEverywhere() chatModel.controller.changeActiveUser(u.user.userId, null) job.cancel() switchingUsers.value = false diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/ChatArchiveView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/ChatArchiveView.kt index 3963c69ae3..8738db2df1 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/ChatArchiveView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/ChatArchiveView.kt @@ -83,7 +83,7 @@ private fun deleteArchiveAlert(m: ChatModel, archivePath: String) { if (fileDeleted) { m.controller.appPrefs.chatArchiveName.set(null) m.controller.appPrefs.chatArchiveTime.set(null) - ModalManager.shared.closeModal() + ModalManager.start.closeModal() } else { Log.e(TAG, "deleteArchiveAlert delete() error") } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt index 32d2379f75..8c26011e92 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/database/DatabaseView.kt @@ -357,11 +357,11 @@ private fun startChat(m: ChatModel, runChat: MutableState, chatLastSta } if (m.chatDbStatus.value !is DBMigrationResult.OK) { /** Hide current view and show [DatabaseErrorView] */ - ModalManager.shared.closeModals() + ModalManager.closeAllModalsEverywhere() return@withApi } if (m.currentUser.value == null) { - ModalManager.shared.closeModals() + ModalManager.closeAllModalsEverywhere() return@withApi } else { m.controller.apiStartChat() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/CloseSheetBar.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/CloseSheetBar.kt index 0877cb42e9..848b21eb44 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/CloseSheetBar.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/CloseSheetBar.kt @@ -16,7 +16,7 @@ import androidx.compose.ui.unit.dp import chat.simplex.common.ui.theme.* @Composable -fun CloseSheetBar(close: (() -> Unit)?, endButtons: @Composable RowScope.() -> Unit = {}) { +fun CloseSheetBar(close: (() -> Unit)?, showClose: Boolean = true, endButtons: @Composable RowScope.() -> Unit = {}) { Column( Modifier .fillMaxWidth() @@ -32,7 +32,11 @@ fun CloseSheetBar(close: (() -> Unit)?, endButtons: @Composable RowScope.() -> U horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically ) { - NavigationButtonBack(onButtonClicked = close) + if (showClose) { + NavigationButtonBack(onButtonClicked = close) + } else { + Spacer(Modifier) + } Row { endButtons() } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/LocalAuthentication.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/LocalAuthentication.kt index b6b512e03a..8e6c9fffc1 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/LocalAuthentication.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/LocalAuthentication.kt @@ -45,7 +45,7 @@ fun authenticateWithPasscode( completed: (LAResult) -> Unit ) { val password = DatabaseUtils.ksAppPassword.get() ?: return completed(LAResult.Unavailable(generalGetString(MR.strings.la_no_app_password))) - ModalManager.shared.showPasscodeCustomModal { close -> + ModalManager.fullscreen.showPasscodeCustomModal { close -> BackHandler { close() completed(LAResult.Error(generalGetString(MR.strings.authentication_cancelled))) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt index 598ceaab8c..ec3ee8ecea 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/ModalView.kt @@ -8,43 +8,51 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.Color +import chat.simplex.common.model.ChatModel import chat.simplex.common.platform.* -import chat.simplex.common.ui.theme.themedBackground +import chat.simplex.common.ui.theme.* import java.util.concurrent.atomic.AtomicBoolean @Composable fun ModalView( close: () -> Unit, + showClose: Boolean = true, background: Color = MaterialTheme.colors.background, modifier: Modifier = Modifier, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable () -> Unit, ) { - BackHandler(onBack = close) + if (showClose) { + BackHandler(onBack = close) + } Surface(Modifier.fillMaxSize()) { Column(if (background != MaterialTheme.colors.background) Modifier.background(background) else Modifier.themedBackground()) { - CloseSheetBar(close, endButtons) + CloseSheetBar(close, showClose, endButtons) Box(modifier) { content() } } } } -class ModalManager { +enum class ModalPlacement { + START, CENTER, END, FULLSCREEN +} + +class ModalManager(private val placement: ModalPlacement? = null) { private val modalViews = arrayListOf Unit) -> Unit)>>() private val modalCount = mutableStateOf(0) private val toRemove = mutableSetOf() private var oldViewChanging = AtomicBoolean(false) private var passcodeView: MutableState<(@Composable (close: () -> Unit) -> Unit)?> = mutableStateOf(null) - fun showModal(settings: Boolean = false, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable () -> Unit) { + fun showModal(settings: Boolean = false, showClose: Boolean = true, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable () -> Unit) { showCustomModal { close -> - ModalView(close, endButtons = endButtons, content = content) + ModalView(close, showClose = showClose, endButtons = endButtons, content = content) } } - fun showModalCloseable(settings: Boolean = false, content: @Composable (close: () -> Unit) -> Unit) { + fun showModalCloseable(settings: Boolean = false, showClose: Boolean = true, content: @Composable (close: () -> Unit) -> Unit) { showCustomModal { close -> - ModalView(close, content = { content(close) }) + ModalView(close, showClose = showClose, content = { content(close) }) } } @@ -55,8 +63,17 @@ class ModalManager { if (toRemove.isNotEmpty()) { runAtomically { toRemove.removeIf { elem -> modalViews.removeAt(elem); true } } } - modalViews.add(animated to modal) + // Make animated appearance only on Android (everytime) and on Desktop (when it's on the start part of the screen or modals > 0) + // to prevent unneeded animation on different situations + val anim = if (appPlatform.isAndroid) animated else animated && (modalCount.value > 0 || placement == ModalPlacement.START) + modalViews.add(anim to modal) modalCount.value = modalViews.size - toRemove.size + + if (placement == ModalPlacement.CENTER) { + ChatModel.chatId.value = null + } else if (placement == ModalPlacement.END) { + desktopExpandWindowToWidth(DEFAULT_START_MODAL_WIDTH + DEFAULT_MIN_CENTER_MODAL_WIDTH + DEFAULT_END_MODAL_WIDTH) + } } fun showPasscodeCustomModal(modal: @Composable (close: () -> Unit) -> Unit) { @@ -146,6 +163,17 @@ private fun animationSpec() = tween(durationMillis = 250, easing = FastOu // private fun animationSpecFromEnd() = tween(durationMillis = 100, easing = FastOutSlowInEasing) companion object { - val shared = ModalManager() + private val shared = ModalManager() + val start = if (appPlatform.isAndroid) shared else ModalManager(ModalPlacement.START) + val center = if (appPlatform.isAndroid) shared else ModalManager(ModalPlacement.CENTER) + val end = if (appPlatform.isAndroid) shared else ModalManager(ModalPlacement.END) + val fullscreen = if (appPlatform.isAndroid) shared else ModalManager(ModalPlacement.FULLSCREEN) + + fun closeAllModalsEverywhere() { + start.closeModals() + center.closeModals() + end.closeModals() + fullscreen.closeModals() + } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt index 0719c5da37..756e605dc3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/localauth/LocalAuthView.kt @@ -70,7 +70,7 @@ private fun deleteStorageAndRestart(m: ChatModel, password: String, completed: ( if (createdUser != null) { m.controller.startChat(createdUser) } - ModalManager.shared.closeModals() + ModalManager.fullscreen.closeModals() AlertManager.shared.hideAlert() completed(LAResult.Success) } catch (e: Exception) { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddContactView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddContactView.kt index e7de6154ea..2ee2145259 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddContactView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddContactView.kt @@ -30,7 +30,7 @@ fun AddContactView(connReqInvitation: String, connIncognito: Boolean) { connIncognito = connIncognito, share = { clipboard.shareText(connReqInvitation) }, learnMore = { - ModalManager.shared.showModal { + ModalManager.center.showModal { Column( Modifier .fillMaxHeight() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddGroupView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddGroupView.kt index c89e0e43fb..3ab5ef9b2b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddGroupView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddGroupView.kt @@ -45,7 +45,7 @@ fun AddGroupView(chatModel: ChatModel, close: () -> Unit) { chatModel.chatId.value = groupInfo.id setGroupMembers(groupInfo, chatModel) close.invoke() - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.end.showModalCloseable(true) { close -> AddGroupMembersView(groupInfo, true, chatModel, close) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ContactConnectionInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ContactConnectionInfoView.kt index 07d937823b..2b971282b3 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ContactConnectionInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ContactConnectionInfoView.kt @@ -42,7 +42,7 @@ fun ContactConnectionInfoView( **/ DisposableEffect(Unit) { onDispose { - if (!ModalManager.shared.hasModalsOpen()) { + if (!ModalManager.center.hasModalsOpen()) { chatModel.connReqInv.value = null } } @@ -57,7 +57,7 @@ fun ContactConnectionInfoView( onLocalAliasChanged = { setContactAlias(contactConnection, it, chatModel) }, share = { if (connReqInvitation != null) clipboard.shareText(connReqInvitation) }, learnMore = { - ModalManager.shared.showModal { + ModalManager.center.showModal { Column( Modifier .fillMaxHeight() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/CreateLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/CreateLinkView.kt index 3d6cfb2f91..53f7bdcc73 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/CreateLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/CreateLinkView.kt @@ -35,7 +35,7 @@ fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) { **/ DisposableEffect(Unit) { onDispose { - if (!ModalManager.shared.hasModalsOpen()) { + if (!ModalManager.center.hasModalsOpen()) { m.connReqInv.value = null } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt index a51d070f85..171465d86d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatSheet.kt @@ -40,15 +40,18 @@ fun NewChatSheet(chatModel: ChatModel, newChatSheetState: StateFlow ConnectViaLinkView(chatModel, close) } + ModalManager.center.closeModals() + ModalManager.center.showModalCloseable { close -> ConnectViaLinkView(chatModel, close) } }, createGroup = { closeNewChatSheet(false) - ModalManager.shared.showCustomModal { close -> AddGroupView(chatModel, close) } + ModalManager.center.closeModals() + ModalManager.center.showCustomModal { close -> AddGroupView(chatModel, close) } }, closeNewChatSheet, ) @@ -93,10 +96,12 @@ private fun NewChatSheetLayout( } } } + val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp val maxWidth = with(LocalDensity.current) { screenWidth() * density } Column( Modifier .fillMaxSize() + .padding(end = endPadding) .offset { IntOffset(if (newChat.isGone()) -maxWidth.value.roundToInt() else 0, 0) } .clickable(interactionSource = remember { MutableInteractionSource() }, indication = null) { closeNewChatSheet(true) } .drawBehind { drawRect(animatedColor.value) }, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/QRCode.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/QRCode.kt index 9882caa55d..5e43a34197 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/QRCode.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/QRCode.kt @@ -4,9 +4,11 @@ import androidx.compose.desktop.ui.tooling.preview.Preview import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.runtime.* +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.graphics.* import androidx.compose.ui.platform.* +import androidx.compose.ui.unit.dp import dev.icerock.moko.resources.compose.stringResource import boofcv.alg.drawing.FiducialImageEngine import boofcv.alg.fiducial.qrcode.* @@ -25,7 +27,7 @@ fun QRCode( ) { val scope = rememberCoroutineScope() - BoxWithConstraints { + BoxWithConstraints(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) { val maxWidthInPx = with(LocalDensity.current) { maxWidth.roundToPx() } val qr = remember(maxWidthInPx, connReq, tintColor, withLogo) { qrCodeBitmap(connReq, maxWidthInPx).replaceColor(Color.Black.toArgb(), tintColor.toArgb()) @@ -34,7 +36,9 @@ fun QRCode( Image( bitmap = qr, contentDescription = stringResource(MR.strings.image_descr_qr_code), - modifier + Modifier + .widthIn(max = 500.dp) + .then(modifier) .clickable { scope.launch { val image = qrCodeBitmap(connReq, 1024).replaceColor(Color.Black.toArgb(), tintColor.toArgb()) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/HowItWorks.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/HowItWorks.kt index a506c8daff..7785180926 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/HowItWorks.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/HowItWorks.kt @@ -42,7 +42,7 @@ fun HowItWorks(user: User?, onboardingStage: MutableState? = n if (onboardingStage != null) { Box(Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING), contentAlignment = Alignment.Center) { - OnboardingActionButton(user, onboardingStage, onclick = { ModalManager.shared.closeModal() }) + OnboardingActionButton(user, onboardingStage, onclick = { ModalManager.fullscreen.closeModal() }) } Spacer(Modifier.fillMaxHeight().weight(1f)) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SimpleXInfo.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SimpleXInfo.kt index ddb1e81ec4..1e69d92c22 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SimpleXInfo.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/SimpleXInfo.kt @@ -26,7 +26,7 @@ fun SimpleXInfo(chatModel: ChatModel, onboarding: Boolean = true) { SimpleXInfoLayout( user = chatModel.currentUser.value, onboardingStage = if (onboarding) chatModel.onboardingStage else null, - showModal = { modalView -> { ModalManager.shared.showModal { modalView(chatModel) } } }, + showModal = { modalView -> { if (onboarding) ModalManager.fullscreen.showModal { modalView(chatModel) } else ModalManager.start.showModal { modalView(chatModel) } } }, ) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt index e1e35ace8d..f9c85ca56d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/Appearance.kt @@ -41,7 +41,7 @@ object AppearanceScope { ) { val currentTheme by CurrentColors.collectAsState() SectionView(stringResource(MR.strings.settings_section_title_themes)) { - val darkTheme = isSystemInDarkTheme() + val darkTheme = chat.simplex.common.ui.theme.isSystemInDarkTheme() val state = remember { derivedStateOf { currentTheme.name } } ThemeSelector(state) { ThemeManager.applyTheme(it, darkTheme) @@ -226,7 +226,7 @@ object AppearanceScope { @Composable private fun ThemeSelector(state: State, onSelected: (String) -> Unit) { - val darkTheme = isSystemInDarkTheme() + val darkTheme = chat.simplex.common.ui.theme.isSystemInDarkTheme() val values by remember { mutableStateOf(ThemeManager.allThemes(darkTheme).map { it.second.name to it.third }) } ExposedDropDownSettingRow( generalGetString(MR.strings.theme), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/NotificationsSettingsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/NotificationsSettingsView.kt index 6c59bb1508..a3da6c6c90 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/NotificationsSettingsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/NotificationsSettingsView.kt @@ -32,7 +32,7 @@ fun NotificationsSettingsView( notificationsMode = remember { chatModel.controller.appPrefs.notificationsMode.state }, notificationPreviewMode = chatModel.notificationPreviewMode, showPage = { page -> - ModalManager.shared.showModalCloseable(true) { + ModalManager.start.showModalCloseable(true) { when (page) { CurrentPage.NOTIFICATIONS_MODE -> NotificationsModeView(chatModel.controller.appPrefs.notificationsMode.state) { changeNotificationsMode(it, chatModel) } CurrentPage.NOTIFICATION_PREVIEW_MODE -> NotificationPreviewView(chatModel.notificationPreviewMode, onNotificationPreviewModeSelected) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt index 0b00df1c43..9c7c4e25c5 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/PrivacySettings.kt @@ -278,7 +278,7 @@ fun SimplexLockView( } } LAMode.PASSCODE -> { - ModalManager.shared.showCustomModal { close -> + ModalManager.fullscreen.showCustomModal { close -> Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) { SetAppPasscodeView( submit = { @@ -306,7 +306,7 @@ fun SimplexLockView( is LAResult.Failed -> { /* Can be called multiple times on every failure */ } LAResult.Success -> { if (!selfDestruct.get()) { - ModalManager.shared.showCustomModal { close -> + ModalManager.fullscreen.showCustomModal { close -> EnableSelfDestruct(selfDestruct, close) } } else { @@ -322,7 +322,7 @@ fun SimplexLockView( authenticate(generalGetString(MR.strings.la_current_app_passcode), generalGetString(MR.strings.la_change_app_passcode)) { laResult -> when (laResult) { LAResult.Success -> { - ModalManager.shared.showCustomModal { close -> + ModalManager.fullscreen.showCustomModal { close -> Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) { SetAppPasscodeView( submit = { @@ -345,7 +345,7 @@ fun SimplexLockView( authenticate(generalGetString(MR.strings.la_current_app_passcode), generalGetString(MR.strings.change_self_destruct_passcode)) { laResult -> when (laResult) { LAResult.Success -> { - ModalManager.shared.showCustomModal { close -> + ModalManager.fullscreen.showCustomModal { close -> Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) { SetAppPasscodeView( passcodeKeychain = ksSelfDestructPassword, @@ -380,7 +380,7 @@ fun SimplexLockView( setPerformLA(true) } LAMode.PASSCODE -> { - ModalManager.shared.showCustomModal { close -> + ModalManager.fullscreen.showCustomModal { close -> Surface(Modifier.fillMaxSize(), color = MaterialTheme.colors.background) { SetAppPasscodeView( submit = { @@ -427,7 +427,7 @@ fun SimplexLockView( SectionDividerSpaced() SectionView(stringResource(MR.strings.self_destruct_passcode).uppercase()) { val openInfo = { - ModalManager.shared.showModal { + ModalManager.start.showModal { SelfDestructInfoView() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/ProtocolServersView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/ProtocolServersView.kt index a6ee09f749..92246b72fb 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/ProtocolServersView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/ProtocolServersView.kt @@ -62,7 +62,7 @@ fun ProtocolServersView(m: ChatModel, serverProtocol: ServerProtocol, close: () } fun showServer(server: ServerCfg) { - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.start.showModalCloseable(true) { close -> var old by remember { mutableStateOf(server) } val index = servers.indexOf(old) ProtocolServerView( @@ -117,7 +117,7 @@ fun ProtocolServersView(m: ChatModel, serverProtocol: ServerProtocol, close: () if (appPlatform.isAndroid) { SectionItemView({ AlertManager.shared.hideAlert() - ModalManager.shared.showModalCloseable { close -> + ModalManager.start.showModalCloseable { close -> ScanProtocolServer { close() servers = servers + it diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetDeliveryReceiptsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetDeliveryReceiptsView.kt index 1189c8bba6..089ec7713f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetDeliveryReceiptsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SetDeliveryReceiptsView.kt @@ -11,10 +11,10 @@ import androidx.compose.ui.Modifier import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import androidx.compose.ui.text.style.TextAlign +import androidx.compose.ui.unit.dp import chat.simplex.common.model.ChatModel +import chat.simplex.common.platform.* import chat.simplex.res.MR -import chat.simplex.common.platform.TAG -import chat.simplex.common.platform.Log import chat.simplex.common.ui.theme.DEFAULT_PADDING import chat.simplex.common.views.helpers.* @@ -73,8 +73,9 @@ private fun SetDeliveryReceiptsLayout( skip: () -> Unit, userCount: Int, ) { + val endPadding = if (appPlatform.isDesktop) 56.dp else 0.dp Column( - Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(top = DEFAULT_PADDING), + Modifier.fillMaxSize().verticalScroll(rememberScrollState()).padding(top = DEFAULT_PADDING, end = endPadding), horizontalAlignment = Alignment.CenterHorizontally, ) { AppBarTitle(stringResource(MR.strings.delivery_receipts_title)) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt index 3226dc85be..fda24031aa 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt @@ -23,16 +23,17 @@ import androidx.compose.ui.text.font.FontWeight import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.* import chat.simplex.common.model.* -import chat.simplex.common.platform.appVersionInfo +import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.database.DatabaseView import chat.simplex.common.views.helpers.* import chat.simplex.common.views.onboarding.SimpleXInfo import chat.simplex.common.views.onboarding.WhatsNewView import chat.simplex.res.MR +import kotlinx.coroutines.launch @Composable -fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) { +fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, drawerState: DrawerState) { val user = chatModel.currentUser.value val stopped = chatModel.chatRunning.value == false @@ -48,10 +49,10 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) { chatModel.controller.appPrefs.incognito, user.displayName, setPerformLA = setPerformLA, - showModal = { modalView -> { ModalManager.shared.showModal { modalView(chatModel) } } }, - showSettingsModal = { modalView -> { ModalManager.shared.showModal(true) { modalView(chatModel) } } }, + showModal = { modalView -> { ModalManager.start.showModal { modalView(chatModel) } } }, + showSettingsModal = { modalView -> { ModalManager.start.showModal(true) { modalView(chatModel) } } }, showSettingsModalWithSearch = { modalView -> - ModalManager.shared.showCustomModal { close -> + ModalManager.start.showCustomModal { close -> val search = rememberSaveable { mutableStateOf("") } ModalView( { close() }, @@ -61,12 +62,12 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) { content = { modalView(chatModel, search) }) } }, - showCustomModal = { modalView -> { ModalManager.shared.showCustomModal { close -> modalView(chatModel, close) } } }, + showCustomModal = { modalView -> { ModalManager.start.showCustomModal { close -> modalView(chatModel, close) } } }, showVersion = { withApi { val info = chatModel.controller.apiGetVersion() if (info != null) { - ModalManager.shared.showModal { VersionInfoView(info) } + ModalManager.start.showModal { VersionInfoView(info) } } } }, @@ -75,7 +76,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) { block() } else { var autoShow = true - ModalManager.shared.showModalCloseable { close -> + ModalManager.fullscreen.showModalCloseable { close -> val onFinishAuth = { success: Boolean -> if (success) { close() @@ -104,6 +105,7 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit) { } } }, + drawerState = drawerState, ) } } @@ -125,15 +127,25 @@ fun SettingsLayout( showSettingsModalWithSearch: (@Composable (ChatModel, MutableState) -> Unit) -> Unit, showCustomModal: (@Composable (ChatModel, () -> Unit) -> Unit) -> (() -> Unit), showVersion: () -> Unit, - withAuth: (title: String, desc: String, block: () -> Unit) -> Unit + withAuth: (title: String, desc: String, block: () -> Unit) -> Unit, + drawerState: DrawerState, ) { + val scope = rememberCoroutineScope() + val closeSettings: () -> Unit = { scope.launch { drawerState.close() } } + if (drawerState.isOpen) { + BackHandler { + closeSettings() + } + } val theme = CurrentColors.collectAsState() val uriHandler = LocalUriHandler.current - Box(Modifier.fillMaxSize().verticalScroll(rememberScrollState()).themedBackground(theme.value.base)) { + Box(Modifier.fillMaxSize()) { Column( Modifier .fillMaxSize() - .padding(top = DEFAULT_PADDING) + .verticalScroll(rememberScrollState()) + .themedBackground(theme.value.base) + .padding(top = if (appPlatform.isAndroid) DEFAULT_PADDING else DEFAULT_PADDING * 3) ) { AppBarTitle(stringResource(MR.strings.your_settings)) @@ -178,6 +190,17 @@ fun SettingsLayout( SettingsSectionApp(showSettingsModal, showCustomModal, showVersion, withAuth) SectionBottomSpacer() } + if (appPlatform.isDesktop) { + Box( + Modifier + .fillMaxWidth() + .background(MaterialTheme.colors.background) + .background(if (isInDarkTheme()) ToolbarDark else ToolbarLight) + .padding(start = 4.dp, top = 8.dp) + ) { + NavigationButtonBack(closeSettings) + } + } } } @@ -510,6 +533,7 @@ fun PreviewSettingsLayout() { showCustomModal = { {} }, showVersion = {}, withAuth = { _, _, _ -> }, + drawerState = DrawerState(DrawerValue.Closed), ) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt index d907e0fff5..63f06a2aec 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserAddressView.kt @@ -85,7 +85,7 @@ fun UserAddressView( } }, learnMore = { - ModalManager.shared.showModal { + ModalManager.start.showModal { Column( Modifier .fillMaxHeight() diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserProfilesView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserProfilesView.kt index b788787854..ac3a68fc49 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserProfilesView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/UserProfilesView.kt @@ -30,6 +30,7 @@ import chat.simplex.common.views.database.PassphraseField import chat.simplex.common.views.helpers.* import chat.simplex.common.views.onboarding.CreateProfile import chat.simplex.common.model.* +import chat.simplex.common.platform.appPlatform import chat.simplex.res.MR import dev.icerock.moko.resources.StringResource import kotlinx.coroutines.delay @@ -47,11 +48,15 @@ fun UserProfilesView(m: ChatModel, search: MutableState, profileHidden: showHiddenProfilesNotice = m.controller.appPrefs.showHiddenProfilesNotice, visibleUsersCount = visibleUsersCount(m), addUser = { - ModalManager.shared.showModalCloseable { close -> + ModalManager.center.showModalCloseable { close -> CreateProfile(m, close) } }, activateUser = { user -> + if (appPlatform.isDesktop) { + ModalManager.center.closeModals() + ModalManager.end.closeModals() + } withBGApi { m.controller.changeActiveUser(user.userId, userViewPassword(user, searchTextOrPassword.value.trim())) } @@ -99,7 +104,7 @@ fun UserProfilesView(m: ChatModel, search: MutableState, profileHidden: }, unhideUser = { user -> if (passwordEntryRequired(user, searchTextOrPassword.value)) { - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.start.showModalCloseable(true) { close -> ProfileActionView(UserProfileAction.UNHIDE, user) { pwd -> withBGApi { setUserPrivacy(m) { m.controller.apiUnhideUser(user.userId, pwd) } @@ -122,7 +127,7 @@ fun UserProfilesView(m: ChatModel, search: MutableState, profileHidden: withBGApi { setUserPrivacy(m) { m.controller.apiUnmuteUser(user.userId) } } }, showHiddenProfile = { user -> - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.start.showModalCloseable(true) { close -> HiddenProfileView(m, user) { profileHidden.value = true withBGApi { @@ -328,7 +333,7 @@ private fun passwordEntryRequired(user: User, searchTextOrPassword: String): Boo private fun removeUser(m: ChatModel, user: User, users: List, delSMPQueues: Boolean, searchTextOrPassword: String) { if (passwordEntryRequired(user, searchTextOrPassword)) { - ModalManager.shared.showModalCloseable(true) { close -> + ModalManager.start.showModalCloseable(true) { close -> ProfileActionView(UserProfileAction.DELETE, user) { pwd -> withBGApi { doRemoveUser(m, user, users, delSMPQueues, pwd) diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 32e8c4ce88..ac7451c389 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -267,6 +267,9 @@ You have no chats No filtered chats + + No selected chat + Share message… Share media… diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt index 4c5cfb2526..936942d980 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/DesktopApp.kt @@ -24,7 +24,8 @@ import java.io.File val simplexWindowState = SimplexWindowState() fun showApp() = application { - val windowState = rememberWindowState(placement = WindowPlacement.Floating) + val windowState = rememberWindowState(placement = WindowPlacement.Floating, width = 1366.dp, height = 768.dp) + simplexWindowState.windowState = windowState Window(state = windowState, onCloseRequest = ::exitApplication, onKeyEvent = { if (it.key == Key.Escape && it.type == KeyEventType.KeyUp) { simplexWindowState.backstack.lastOrNull()?.invoke() != null @@ -108,6 +109,7 @@ fun showApp() = application { } class SimplexWindowState { + lateinit var windowState: WindowState val backstack = mutableStateListOf<() -> Unit>() val openDialog = DialogState() val openMultipleDialog = DialogState>() diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/PlatformTextField.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/PlatformTextField.desktop.kt index 7148f8217e..ea806c0622 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/PlatformTextField.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/PlatformTextField.desktop.kt @@ -38,7 +38,7 @@ actual fun PlatformTextField( val cs = composeState.value val focusRequester = remember { FocusRequester() } val keyboard = LocalSoftwareKeyboardController.current - val padding = PaddingValues(12.dp, 7.dp, 45.dp, 0.dp) + val padding = PaddingValues(12.dp, 12.dp, 45.dp, 0.dp) LaunchedEffect(cs.contextItem) { if (cs.contextItem !is ComposeContextItem.QuotedItem) return@LaunchedEffect // In replying state @@ -74,14 +74,10 @@ actual fun PlatformTextField( CompositionLocalProvider( LocalLayoutDirection provides if (isRtl) LayoutDirection.Rtl else LocalLayoutDirection.current ) { - Box( - Modifier - .weight(1f) - .padding(horizontal = 12.dp) - .padding(top = 4.dp) - .padding(bottom = 6.dp) - ) { + Column(Modifier.weight(1f).padding(start = 12.dp, end = 32.dp)) { + Spacer(Modifier.height(8.dp)) innerTextField() + Spacer(Modifier.height(10.dp)) } } } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Resources.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Resources.desktop.kt index 1bedcb6f44..3a76808b0c 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Resources.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/Resources.desktop.kt @@ -4,8 +4,8 @@ import androidx.compose.runtime.* import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight -import androidx.compose.ui.unit.Dp -import androidx.compose.ui.unit.dp +import androidx.compose.ui.unit.* +import chat.simplex.common.simplexWindowState import com.russhwolf.settings.* import dev.icerock.moko.resources.StringResource import dev.icerock.moko.resources.desc.desc @@ -49,6 +49,11 @@ actual fun screenWidth(): Dp { return width*/ }// LALAL java.awt.Desktop.getDesktop() +actual fun desktopExpandWindowToWidth(width: Dp) { + if (simplexWindowState.windowState.size.width >= width) return + simplexWindowState.windowState.size = simplexWindowState.windowState.size.copy(width = width) +} + actual fun isRtl(text: CharSequence): Boolean { if (text.isEmpty()) return false return text.any { char -> diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/ui/theme/Theme.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/ui/theme/Theme.desktop.kt new file mode 100644 index 0000000000..d7da2fa123 --- /dev/null +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/ui/theme/Theme.desktop.kt @@ -0,0 +1,10 @@ +package chat.simplex.common.ui.theme + +import com.jthemedetecor.OsThemeDetector + +private val detector: OsThemeDetector = OsThemeDetector.getDetector() + .apply { + registerListener(::reactOnDarkThemeChanges) + } + +actual fun isSystemInDarkTheme(): Boolean = detector.isDark diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.desktop.kt index a9c7f5f0ae..214946b1c9 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/chat/item/CIImageView.desktop.kt @@ -20,7 +20,7 @@ actual fun SimpleAndAnimatedImageView( // LALAL make it animated too ImageView(imageBitmap.toAwtImage().toPainter()) { if (getLoadedFilePath(file) != null) { - ModalManager.shared.showCustomModal(animated = false) { close -> + ModalManager.fullscreen.showCustomModal(animated = false) { close -> ImageFullScreenView(imageProvider, close) } } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/usersettings/Appearance.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/usersettings/Appearance.desktop.kt index 851cf137dd..1a8ad010e3 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/usersettings/Appearance.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/usersettings/Appearance.desktop.kt @@ -29,7 +29,7 @@ actual fun AppearanceView(m: ChatModel, showSettingsModal: (@Composable (ChatMod m.controller.appPrefs.systemDarkTheme, showSettingsModal = showSettingsModal, editColor = { name, initialColor -> - ModalManager.shared.showModalCloseable { close -> + ModalManager.start.showModalCloseable { close -> ColorEditor(name, initialColor, close) } },