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 73c920b940..d4b77274ba 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 @@ -6,11 +6,11 @@ import android.content.Context import android.content.SharedPreferences import android.content.res.Configuration import android.text.BidiFormatter +import androidx.compose.foundation.layout.* import androidx.compose.runtime.Composable import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.asImageBitmap -import androidx.compose.ui.platform.LocalConfiguration -import androidx.compose.ui.platform.LocalContext +import androidx.compose.ui.platform.* import androidx.compose.ui.text.font.Font import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.text.font.FontWeight @@ -50,7 +50,11 @@ actual fun windowOrientation(): WindowOrientation = when (mainActivity.get()?.re } @Composable -actual fun windowWidth(): Dp = LocalConfiguration.current.screenWidthDp.dp +actual fun windowWidth(): Dp { + val direction = LocalLayoutDirection.current + val cutout = WindowInsets.displayCutout.only(WindowInsetsSides.Horizontal).asPaddingValues() + return LocalConfiguration.current.screenWidthDp.dp - cutout.calculateStartPadding(direction) - cutout.calculateEndPadding(direction) +} @Composable actual fun windowHeight(): Dp = LocalConfiguration.current.screenHeightDp.dp diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/ScrollableColumn.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/ScrollableColumn.android.kt index ba936520e5..bb03a6b385 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/ScrollableColumn.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/platform/ScrollableColumn.android.kt @@ -9,7 +9,9 @@ import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.input.nestedscroll.nestedScroll import androidx.compose.ui.unit.Dp +import androidx.compose.ui.unit.dp import chat.simplex.common.model.ChatController.appPrefs +import chat.simplex.common.ui.theme.DEFAULT_PADDING import chat.simplex.common.views.chatlist.NavigationBarBackground import chat.simplex.common.views.helpers.* import kotlinx.coroutines.flow.filter @@ -101,9 +103,9 @@ actual fun ColumnWithScrollBar( }, verticalArrangement, horizontalAlignment ) { if (oneHandUI.value) { - Spacer(Modifier.windowInsetsTopHeight(WindowInsets.statusBars)) + Spacer(Modifier.padding(top = DEFAULT_PADDING + 5.dp).windowInsetsTopHeight(WindowInsets.statusBars)) content() - Spacer(Modifier.padding(bottom = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding() + AppBarHeight * fontSizeSqrtMultiplier)) + Spacer(Modifier.navigationBarsPadding().padding(bottom = AppBarHeight * fontSizeSqrtMultiplier)) } else { Spacer(Modifier.statusBarsPadding().padding(top = AppBarHeight * fontSizeSqrtMultiplier)) content() diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt index f3a9be6132..9b68a2bde7 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/call/CallView.android.kt @@ -47,6 +47,7 @@ import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* import chat.simplex.common.views.helpers.* +import chat.simplex.common.views.onboarding.OnboardingStage import chat.simplex.res.MR import com.google.accompanist.permissions.* import dev.icerock.moko.resources.StringResource @@ -328,11 +329,14 @@ private fun ActiveCallOverlayLayout( flipCamera: () -> Unit ) { Column { - CloseSheetBar({ chatModel.activeCallViewIsCollapsed.value = true }, true, tintColor = Color(0xFFFFFFD8)) { - if (call.hasVideo) { - Text(call.contact.chatViewName, Modifier.fillMaxWidth().padding(end = DEFAULT_PADDING), color = Color(0xFFFFFFD8), style = MaterialTheme.typography.h2, overflow = TextOverflow.Ellipsis, maxLines = 1) - } - } + CallAppBar( + title = { + if (call.hasVideo) { + Text(call.contact.chatViewName, Modifier.offset(x = (-4).dp).padding(end = DEFAULT_PADDING), color = Color(0xFFFFFFD8), style = MaterialTheme.typography.h2, overflow = TextOverflow.Ellipsis, maxLines = 1) + } + }, + onBack = { chatModel.activeCallViewIsCollapsed.value = true } + ) Column(Modifier.padding(horizontal = DEFAULT_PADDING)) { @Composable fun SelectSoundDevice(size: Dp) { 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 0b532a264d..acead644b1 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 @@ -120,9 +120,6 @@ fun AppearanceScope.AppearanceLayout( SectionDividerSpaced() BarsAlphaSection(MR.strings.appearance_in_app_bars_alpha, appPrefs.inAppBarsAlpha, 0.95f) - SectionDividerSpaced(maxTopPadding = true) - BarsAlphaSection(MR.strings.appearance_nav_bar_alpha, appPrefs.navBarAlpha, 0.6f) - SectionDividerSpaced(maxTopPadding = true) ProfileImageSection() 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 a0d0814cc8..37349a8ba7 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 @@ -224,7 +224,6 @@ class AppPreferences { val fontScale = mkFloatPreference(SHARED_PREFS_FONT_SCALE, 1f) val densityScale = mkFloatPreference(SHARED_PREFS_DENSITY_SCALE, 1f) val inAppBarsAlpha = mkFloatPreference(SHARED_PREFS_IN_APP_BARS_ALPHA, 0.95f) - val navBarAlpha = mkFloatPreference(SHARED_PREFS_NAV_BAR_ALPHA, 0.6f) val whatsNewVersion = mkStrPreference(SHARED_PREFS_WHATS_NEW_VERSION, null) val lastMigratedVersionCode = mkIntPreference(SHARED_PREFS_LAST_MIGRATED_VERSION_CODE, 0) @@ -246,7 +245,7 @@ class AppPreferences { val iosCallKitEnabled = mkBoolPreference(SHARED_PREFS_IOS_CALL_KIT_ENABLED, true) val iosCallKitCallsInRecents = mkBoolPreference(SHARED_PREFS_IOS_CALL_KIT_CALLS_IN_RECENTS, false) - val oneHandUI = mkBoolPreference(SHARED_PREFS_ONE_HAND_UI, appPlatform.isAndroid) + val oneHandUI = mkBoolPreference(SHARED_PREFS_ONE_HAND_UI, true) val hintPreferences: List, Boolean>> = listOf( laNoticeShown to false, @@ -431,7 +430,6 @@ class AppPreferences { private const val SHARED_PREFS_FONT_SCALE = "FontScale" private const val SHARED_PREFS_DENSITY_SCALE = "DensityScale" private const val SHARED_PREFS_IN_APP_BARS_ALPHA = "InAppBarsAlpha" - private const val SHARED_PREFS_NAV_BAR_ALPHA = "NavBarAlpha" private const val SHARED_PREFS_WHATS_NEW_VERSION = "WhatsNewVersion" private const val SHARED_PREFS_LAST_MIGRATED_VERSION_CODE = "LastMigratedVersionCode" private const val SHARED_PREFS_CUSTOM_DISAPPEARING_MESSAGE_TIME = "CustomDisappearingMessageTime" 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 413d7a0e94..4a782e46ea 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 @@ -79,6 +79,7 @@ fun TerminalLayout( fun onMessageChange(s: String) { composeState.value = composeState.value.copy(message = s) } + val oneHandUI = remember { appPrefs.oneHandUI.state } Box(Modifier.fillMaxSize()) { val composeViewHeight = remember { mutableStateOf(0.dp) } AdaptingBottomPaddingLayout(Modifier, CONSOLE_COMPOSE_LAYOUT_ID, composeViewHeight) { @@ -88,8 +89,10 @@ fun TerminalLayout( .layoutId(CONSOLE_COMPOSE_LAYOUT_ID) .align(Alignment.BottomCenter) .navigationBarsPadding() + .consumeWindowInsets(PaddingValues(bottom = if (oneHandUI.value) AppBarHeight * fontSizeSqrtMultiplier else 0.dp)) .imePadding() - .background(MaterialTheme.colors.background.copy(remember { appPrefs.inAppBarsAlpha.state }.value)) + .padding(bottom = if (oneHandUI.value) AppBarHeight * fontSizeSqrtMultiplier else 0.dp) + .background(MaterialTheme.colors.background) ) { Divider() Box(Modifier.padding(horizontal = 8.dp)) { @@ -119,8 +122,9 @@ fun TerminalLayout( } } } - val oneHandUI = remember { appPrefs.oneHandUI.state } - NavigationBarBackground(true, oneHandUI.value) + if (!oneHandUI.value) { + NavigationBarBackground(true, oneHandUI.value) + } } } @@ -176,7 +180,7 @@ fun TerminalLog(floating: Boolean, composeViewHeight: State) { } else { ModalManager.start } - modalPlace.showModal(endButtons = { ShareButton { clipboard.shareText(item.details) } }) { + modalPlace.showModal(endButtons = listOf { ShareButton { clipboard.shareText(item.details) } }) { ColumnWithScrollBar { SelectionContainer { val details = item.details diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt index 0741eeef91..7868c541ae 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/WelcomeView.kt @@ -118,13 +118,16 @@ fun CreateFirstProfile(chatModel: ChatModel, close: () -> Unit) { .themedBackground(), horizontalAlignment = Alignment.CenterHorizontally ) { - CloseSheetBar(close = { - if (chatModel.users.none { !it.user.hidden }) { - appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) - } else { - close() - } - }) + DefaultTopAppBar( + navigationButton = { NavigationButtonBack(onButtonClicked = { + if (chatModel.users.none { !it.user.hidden }) { + appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) + } else { + close() + } + } ) }, + onTop = true + ) BackHandler(onBack = { appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) }) 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 dda04dd574..8c38f6987d 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 @@ -463,7 +463,7 @@ fun ChatView(staleChatId: State, onComposed: suspend (chatId: String) - withBGApi { var initialCiInfo = loadChatItemInfo() ?: return@withBGApi ModalManager.end.closeModals() - ModalManager.end.showModalCloseable(endButtons = { + ModalManager.end.showModalCloseable(endButtons = listOf { ShareButton { clipboard.shareText(itemInfoShareText(chatModel, cItem, initialCiInfo, chatModel.controller.appPrefs.developerTools.get())) } @@ -548,7 +548,7 @@ fun ChatView(staleChatId: State, onComposed: suspend (chatId: String) - CompositionLocalProvider( LocalAppBarHandler provides handler ) { - ModalView(close, showClose = appPlatform.isAndroid, endButtons = { ShareButton { clipboard.shareText(chatInfo.json) } }, content = { + ModalView(close, showClose = appPlatform.isAndroid, endButtons = listOf { ShareButton { clipboard.shareText(chatInfo.json) } }, content = { InvalidJSONView(chatInfo.json) }) LaunchedEffect(chatInfo.id) { @@ -678,18 +678,24 @@ fun ChatLayout( } } val oneHandUI = remember { appPrefs.oneHandUI.state } - Box(Modifier - .layoutId(CHAT_COMPOSE_LAYOUT_ID) - .align(Alignment.BottomCenter) - .imePadding() - .then(if (oneHandUI.value) Modifier.padding(bottom = AppBarHeight * fontSizeSqrtMultiplier) else Modifier.navigationBarsPadding()) + Box( + Modifier + .layoutId(CHAT_COMPOSE_LAYOUT_ID) + .align(Alignment.BottomCenter) + .imePadding() + .navigationBarsPadding() + .then(if (oneHandUI.value) Modifier.padding(bottom = AppBarHeight * fontSizeSqrtMultiplier) else Modifier) ) { composeView() } } val oneHandUI = remember { appPrefs.oneHandUI.state } - NavigationBarBackground(true, oneHandUI.value) - Box(if (oneHandUI.value) Modifier.align(Alignment.BottomStart).navigationBarsPadding() else Modifier) { + if (oneHandUI.value) { + StatusBarBackground() + } else { + NavigationBarBackground(true, oneHandUI.value) + } + Box(if (oneHandUI.value) Modifier.align(Alignment.BottomStart).imePadding() else Modifier) { if (selectedChatItems.value == null) { if (chatInfo != null) { ChatInfoToolbar(chatInfo, back, info, startCall, endCall, addMembers, openGroupLink, changeNtfsState, onSearchValueChanged, showSearch) @@ -862,22 +868,29 @@ fun BoxScope.ChatInfoToolbar( title = { ChatInfoToolbarTitle(chatInfo) }, onTitleClick = if (chatInfo is ChatInfo.Local) null else info, showSearch = showSearch.value, - onTop = oneHandUI.value, + onTop = !oneHandUI.value, onSearchValueChanged = onSearchValueChanged, buttons = barButtons ) Box(Modifier.fillMaxWidth().wrapContentSize(Alignment.TopEnd)) { val density = LocalDensity.current val width = remember { mutableStateOf(250.dp) } + val height = remember { mutableStateOf(0.dp) } DefaultDropdownMenu( showMenu, - modifier = Modifier.onSizeChanged { with(density) { width.value = it.width.toDp().coerceAtLeast(250.dp) } }, - offset = DpOffset(-width.value, AppBarHeight) + modifier = Modifier.onSizeChanged { with(density) { + width.value = it.width.toDp().coerceAtLeast(250.dp) + if (oneHandUI.value) height.value = it.height.toDp() + } }, + offset = DpOffset(-width.value, if (oneHandUI.value) -height.value else AppBarHeight) ) { - menuItems.forEach { it() } + if (oneHandUI.value) { + menuItems.asReversed().forEach { it() } + } else { + menuItems.forEach { it() } + } } } - Divider(Modifier.background(MaterialTheme.colors.background).align(if (oneHandUI.value) Alignment.TopStart else Alignment.BottomStart)) } @Composable 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 89bb35b605..498d2bd5c5 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 @@ -13,6 +13,7 @@ import androidx.compose.ui.Modifier import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.ImageBitmap import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.text.font.FontStyle import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource @@ -897,7 +898,7 @@ fun ComposeView( } } } - Column(Modifier.background(MaterialTheme.colors.background.copy(remember { appPrefs.inAppBarsAlpha.state }.value))) { + Box(Modifier.background(MaterialTheme.colors.background).onSizeChanged { println("LALAL SIZE0 $it") }) { Divider() Row(Modifier.padding(end = 8.dp), verticalAlignment = Alignment.Bottom) { val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on(chat.chatInfo.groupInfo.membership) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SelectableChatItemToolbars.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SelectableChatItemToolbars.kt index 6c421e7e04..a6f65feab4 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SelectableChatItemToolbars.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/SelectableChatItemToolbars.kt @@ -41,11 +41,9 @@ fun BoxScope.SelectedItemsTopToolbar(selectedChatItems: MutableState?> ) }, onTitleClick = null, - showSearch = false, - onTop = oneHandUI.value, + onTop = !oneHandUI.value, onSearchValueChanged = {}, ) - Divider(Modifier.align(if (oneHandUI.value) Alignment.TopStart else Alignment.BottomStart)) } @Composable 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 76c4fc4a62..2e2dd56c80 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 @@ -14,6 +14,7 @@ import androidx.compose.ui.draw.alpha import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.* import androidx.compose.ui.graphics.painter.Painter +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.semantics.Role import androidx.compose.ui.text.TextStyle import androidx.compose.ui.unit.* @@ -60,7 +61,8 @@ fun SendMsgView( ) { val showCustomDisappearingMessageDialog = remember { mutableStateOf(false) } - Box(Modifier.padding(vertical = if (appPlatform.isAndroid) 8.dp else 6.dp)) { + val padding = if (appPlatform.isAndroid) PaddingValues(vertical = 8.dp) else PaddingValues(top = 3.dp, bottom = 4.dp) + Box(Modifier.padding(padding)) { val cs = composeState.value var progressByTimeout by rememberSaveable { mutableStateOf(false) } LaunchedEffect(composeState.value.inProgress) { 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 a7f2c384ae..6b28150128 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 @@ -285,9 +285,15 @@ fun ModalData.GroupChatInfoLayout( } } Box { + val oneHandUI = remember { appPrefs.oneHandUI.state } LazyColumnWithScrollBar( Modifier .fillMaxWidth(), + contentPadding = if (oneHandUI.value) { + PaddingValues(top = WindowInsets.statusBars.asPaddingValues().calculateTopPadding() + DEFAULT_PADDING + 5.dp, bottom = WindowInsets.navigationBars.asPaddingValues().calculateBottomPadding()) + } else { + PaddingValues() + }, state = listState ) { item { @@ -402,7 +408,6 @@ fun ModalData.GroupChatInfoLayout( Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) } } - val oneHandUI = remember { appPrefs.oneHandUI.state } NavigationBarBackground(oneHandUI.value, oneHandUI.value) } } 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 d297d5ef76..ede483ed06 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 @@ -23,7 +23,7 @@ fun CIInvalidJSONView(json: String) { .clickable { ModalManager.center.closeModals() ModalManager.end.closeModals() - ModalManager.center.showModal(true, endButtons = { ShareButton { clipboard.shareText(json) } }) { InvalidJSONView(json) } + ModalManager.center.showModal(true, endButtons = listOf { ShareButton { clipboard.shareText(json) } }) { InvalidJSONView(json) } } .padding(horizontal = 10.dp, vertical = 6.dp) ) { 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 4fa8298505..585112b14f 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 @@ -12,8 +12,12 @@ import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable import androidx.compose.ui.* import androidx.compose.ui.draw.clip +import androidx.compose.ui.draw.drawBehind import androidx.compose.ui.focus.* +import androidx.compose.ui.geometry.Offset +import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.* +import androidx.compose.ui.layout.onSizeChanged import androidx.compose.ui.text.font.FontStyle import androidx.compose.ui.platform.* import androidx.compose.ui.text.TextRange @@ -149,26 +153,27 @@ fun ChatListView(chatModel: ChatModel, userPickerState: MutableStateFlow) { +private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState, listState: LazyListState) { if (!chatModel.desktopNoUserNoRemote) { - ChatList(searchText = searchText) + ChatList(searchText = searchText, listState) } if (chatModel.chats.value.isEmpty() && !chatModel.switchingUsersAndHosts.value && !chatModel.desktopNoUserNoRemote) { Text( @@ -252,7 +257,7 @@ private fun ConnectButton(text: String, onClick: () -> Unit) { } @Composable -private fun ChatListToolbar(userPickerState: MutableStateFlow, stopped: Boolean, setPerformLA: (Boolean) -> Unit) { +private fun ChatListToolbar(userPickerState: MutableStateFlow, listState: LazyListState, stopped: Boolean, setPerformLA: (Boolean) -> Unit) { val serversSummary: MutableState = remember { mutableStateOf(null) } val barButtons = arrayListOf<@Composable RowScope.() -> Unit>() val updatingProgress = remember { chatModel.updatingProgress }.value @@ -319,6 +324,8 @@ private fun ChatListToolbar(userPickerState: MutableStateFlow } } val clipboard = LocalClipboardManager.current + val scope = rememberCoroutineScope() + val canScrollToZero = remember { derivedStateOf { listState.firstVisibleItemIndex != 0 || listState.firstVisibleItemScrollOffset != 0 } } DefaultTopAppBar( navigationButton = { if (chatModel.users.isEmpty() && !chatModel.desktopNoUserNoRemote) { @@ -347,27 +354,25 @@ private fun ChatListToolbar(userPickerState: MutableStateFlow SubscriptionStatusIndicator( click = { ModalManager.start.closeModals() + val summary = serversSummary.value ModalManager.start.showModalCloseable( - endButtons = { - val summary = serversSummary.value - if (summary != null) { + endButtons = if (summary != null) { + listOf { ShareButton { val json = Json { prettyPrint = true } - val text = json.encodeToString(PresentedServersSummary.serializer(), summary) clipboard.shareText(text) } } - } + } else emptyList() ) { ServersSummaryView(chatModel.currentRemoteHost.value, serversSummary) } } ) } }, - onTitleClick = null, - showSearch = false, + onTitleClick = if (canScrollToZero.value) { { scrollToBottom(scope, listState) } } else null, onTop = !oneHandUI.value, onSearchValueChanged = {}, buttons = barButtons @@ -489,7 +494,7 @@ fun connectIfOpenedViaUri(rhId: Long?, uri: String, chatModel: ChatModel) { @Composable private fun ChatListSearchBar(listState: LazyListState, searchText: MutableState, searchShowingSimplexLink: MutableState, searchChatFilteredBySimplexLink: MutableState) { Box { - Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().heightIn(min = AppBarHeight * fontSizeSqrtMultiplier)) { + Row(verticalAlignment = Alignment.CenterVertically, modifier = Modifier.fillMaxWidth().onSizeChanged { println("LALAL SIZE-1 $it") }) { val focusRequester = remember { FocusRequester() } var focused by remember { mutableStateOf(false) } Icon( @@ -593,19 +598,18 @@ enum class ScrollDirection { @Composable fun BoxScope.StatusBarBackground() { if (appPlatform.isAndroid) { - val finalColor = MaterialTheme.colors.background.copy(remember { appPrefs.inAppBarsAlpha.state }.value) + val finalColor = MaterialTheme.colors.background.copy(0.88f) Box(Modifier.fillMaxWidth().windowInsetsTopHeight(WindowInsets.statusBars).background(finalColor)) } } @Composable fun BoxScope.NavigationBarBackground(appBarOnBottom: Boolean = false, mixedColor: Boolean) { - val keyboardState = getKeyboardState() - if (appPlatform.isAndroid && keyboardState.value == KeyboardState.Closed) { + if (appPlatform.isAndroid) { val barPadding = WindowInsets.navigationBars.asPaddingValues() val paddingBottom = barPadding.calculateBottomPadding() val color = if (mixedColor) MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.97f) else MaterialTheme.colors.background - val finalColor = color.copy(remember(appBarOnBottom) { if (appBarOnBottom) appPrefs.inAppBarsAlpha.state else appPrefs.navBarAlpha.state }.value) + val finalColor = color.copy(if (appBarOnBottom) remember { appPrefs.inAppBarsAlpha.state }.value else 0.6f) Box(Modifier.align(Alignment.BottomStart).height(paddingBottom).fillMaxWidth().background(finalColor)) } } @@ -616,14 +620,13 @@ fun BoxScope.NavigationBarBackground(modifier: Modifier, color: Color = Material if (appPlatform.isAndroid && keyboardState.value == KeyboardState.Closed) { val barPadding = WindowInsets.navigationBars.asPaddingValues() val paddingBottom = barPadding.calculateBottomPadding() - val finalColor = color.copy(remember { appPrefs.navBarAlpha.state }.value) + val finalColor = color.copy(0.6f) Box(modifier.align(Alignment.BottomStart).height(paddingBottom).fillMaxWidth().background(finalColor)) } } @Composable -private fun BoxScope.ChatList(searchText: MutableState) { - val listState = rememberLazyListState(lazyListState.first, lazyListState.second) +private fun BoxScope.ChatList(searchText: MutableState, listState: LazyListState) { var scrollDirection by remember { mutableStateOf(ScrollDirection.Idle) } var previousIndex by remember { mutableStateOf(0) } var previousScrollOffset by remember { mutableStateOf(0) } @@ -698,7 +701,7 @@ private fun BoxScope.ChatList(searchText: MutableState) { } } } - if (appPlatform.isAndroid && !oneHandUICardShown.value && chats.count() > 1) { + if (!oneHandUICardShown.value && chats.size > 1) { item { ToggleChatListCard() } @@ -709,7 +712,7 @@ private fun BoxScope.ChatList(searchText: MutableState) { } } ChatListNavLinkView(chat, nextChatSelected) } - if (appPlatform.isAndroid && !oneHandUICardShown.value && chats.count() <= 1) { + if (!oneHandUICardShown.value && chats.size <= 1) { item { ToggleChatListCard() } @@ -725,8 +728,9 @@ private fun BoxScope.ChatList(searchText: MutableState) { } if (oneHandUI.value) { StatusBarBackground() + } else { + NavigationBarBackground(oneHandUI.value, true) } - NavigationBarBackground(oneHandUI.value, true) } fun filteredChats( @@ -771,3 +775,7 @@ private fun filtered(chat: Chat): Boolean = (chat.chatInfo.chatSettings?.favorite ?: false) || chat.chatStats.unreadChat || (chat.chatInfo.ntfsEnabled && chat.chatStats.unreadCount > 0) + +fun scrollToBottom(scope: CoroutineScope, listState: LazyListState) { + scope.launch { try { listState.animateScrollToItem(0) } catch (e: Exception) { Log.e(TAG, e.stackTraceToString()) } } +} 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 ffe3ddcc71..25419cdc85 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 @@ -27,16 +27,12 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) { contentColor = LocalContentColor.current, topBar = { if (!oneHandUI.value) { - Column { - ShareListToolbar(chatModel, stopped) { searchInList = it.trim() } - Divider() - } + ShareListToolbar(chatModel, stopped) { searchInList = it.trim() } } }, bottomBar = { if (oneHandUI.value) { - Column(Modifier.imePadding()) { - Divider() + Box(Modifier.imePadding()) { ShareListToolbar(chatModel, stopped) { searchInList = it.trim() } } } @@ -83,8 +79,9 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) { } if (oneHandUI.value) { StatusBarBackground() + } else { + NavigationBarBackground(oneHandUI.value, true) } - NavigationBarBackground(oneHandUI.value, true) } } } @@ -122,9 +119,9 @@ private fun ShareListToolbar(chatModel: ChatModel, stopped: Boolean, onSearchVal val search = rememberSaveable { mutableStateOf("") } ModalView( { close() }, - endButtons = { - SearchTextField(Modifier.fillMaxWidth(), placeholder = stringResource(MR.strings.search_verb), alwaysVisible = true) { search.value = it } - }, + showSearch = true, + searchAlwaysVisible = true, + onSearchValueChanged = { search.value = it }, content = { ActiveProfilePicker( search = search, 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 730183648d..b79230cca5 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 @@ -277,8 +277,10 @@ fun UserPicker( val profileHidden = rememberSaveable { mutableStateOf(false) } ModalView( { close() }, - endButtons = { - SearchTextField(Modifier.fillMaxWidth(), placeholder = stringResource(MR.strings.search_verb), alwaysVisible = true) { search.value = it } + showSearch = true, + searchAlwaysVisible = true, + onSearchValueChanged = { + search.value = it }, content = { UserProfilesView(chatModel, search, profileHidden) }) } 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 92d2d7dd8e..6b3e094057 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 @@ -31,7 +31,7 @@ fun CloseSheetBar( barPaddingValues: PaddingValues = PaddingValues(horizontal = AppBarHorizontalPadding), endButtons: @Composable RowScope.() -> Unit = {} ) { - val background = MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.97f) + /*val background = MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.97f) val prefAlpha = remember { appPrefs.inAppBarsAlpha.state }.value val themeBackgroundMix = background.copy(prefAlpha) val handler = LocalAppBarHandler.current @@ -44,7 +44,7 @@ fun CloseSheetBar( modifier = Modifier .fillMaxWidth() .then(if (oneHandUI.value) Modifier.navigationBarsPadding() else Modifier) - .clickable(interactionSource = interactionSource, indication = null) { /* receive clicks to not allow to click through */ } + .clickable(interactionSource = interactionSource, indication = null) { *//* receive clicks to not allow to click through *//* } .heightIn(min = AppBarHeight * fontSizeSqrtMultiplier) .drawWithCache { val backgroundColor = if (connection != null) themeBackgroundMix.copy(alpha = if (oneHandUI.value) prefAlpha else topTitleAlpha(false, connection)) else Color.Transparent @@ -94,22 +94,7 @@ fun CloseSheetBar( } ) CloseBarDivider(connection) - } -} - -@Composable -private fun BoxScope.CloseBarDivider(connection: CollapsingAppBarNestedScrollConnection?) { - if (connection != null) { - val oneHandUI = remember { appPrefs.oneHandUI.state } - val prefAlpha = appPrefs.inAppBarsAlpha.get() - Divider( - Modifier - .align(if (oneHandUI.value) Alignment.TopStart else Alignment.BottomStart) - .graphicsLayer { - alpha = if (oneHandUI.value) prefAlpha else topTitleAlpha(false, connection) - } - ) - } + }*/ } @Composable @@ -196,10 +181,6 @@ fun AppBarTitle(title: String, hostDevice: Pair? = null, withPad } } -private fun topTitleAlpha(text: Boolean, connection: CollapsingAppBarNestedScrollConnection) = - if (connection.appBarOffset.absoluteValue < AppBarHandler.appBarMaxHeightPx / 3) 0f - else ((-connection.appBarOffset * 1.5f) / (AppBarHandler.appBarMaxHeightPx)).coerceIn(0f, if (text) 1f else appPrefs.inAppBarsAlpha.get()) - private fun bottomTitleAlpha(connection: CollapsingAppBarNestedScrollConnection?) = if ((connection?.appBarOffset ?: 0f).absoluteValue < AppBarHandler.appBarMaxHeightPx / 3) 1f else ((AppBarHandler.appBarMaxHeightPx) + (connection?.appBarOffset ?: 0f) / 1.5f).coerceAtLeast(0f) / AppBarHandler.appBarMaxHeightPx diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultTopAppBar.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultTopAppBar.kt index 5dc657ec88..462c054e9b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultTopAppBar.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/DefaultTopAppBar.kt @@ -3,27 +3,34 @@ package chat.simplex.common.views.helpers import androidx.compose.foundation.* import androidx.compose.foundation.layout.* import androidx.compose.material.* -import androidx.compose.runtime.Composable -import androidx.compose.runtime.remember +import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier +import androidx.compose.ui.draw.drawWithCache +import androidx.compose.ui.geometry.Size import androidx.compose.ui.graphics.* +import androidx.compose.ui.text.font.FontWeight +import androidx.compose.ui.text.style.TextOverflow import androidx.compose.ui.unit.Dp import dev.icerock.moko.resources.compose.painterResource import dev.icerock.moko.resources.compose.stringResource import androidx.compose.ui.unit.dp import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.ui.theme.* +import chat.simplex.common.views.chatlist.NavigationBarBackground import chat.simplex.res.MR +import kotlin.math.absoluteValue @Composable fun DefaultTopAppBar( navigationButton: (@Composable RowScope.() -> Unit)? = null, - title: (@Composable () -> Unit)?, + title: (@Composable () -> Unit)? = null, + fixedTitleText: String? = null, onTitleClick: (() -> Unit)? = null, - showSearch: Boolean, onTop: Boolean, - onSearchValueChanged: (String) -> Unit, + showSearch: Boolean = false, + searchAlwaysVisible: Boolean = false, + onSearchValueChanged: (String) -> Unit = {}, buttons: List<@Composable RowScope.() -> Unit> = emptyList(), ) { // If I just disable clickable modifier when don't need it, it will stop passing clicks to search. Replacing the whole modifier @@ -31,21 +38,83 @@ fun DefaultTopAppBar( Modifier.clickable(enabled = onTitleClick != null, onClick = onTitleClick ?: { }) } else Modifier + val background = MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.97f) + val prefAlpha = remember { appPrefs.inAppBarsAlpha.state }.value + val themeBackgroundMix = background.copy(prefAlpha) + val handler = LocalAppBarHandler.current + val connection = LocalAppBarHandler.current?.connection + val titleText = remember(handler?.title?.value, fixedTitleText) { + if (fixedTitleText != null) { + mutableStateOf(fixedTitleText) + } else { + handler?.title ?: mutableStateOf("") + } + } + Box(modifier) { + if (!onTop) { + NavigationBarBackground(true, true) + } + Box( + Modifier + .fillMaxWidth() + .then(if (!onTop) Modifier.navigationBarsPadding() else Modifier) + .heightIn(min = AppBarHeight * fontSizeSqrtMultiplier) + .drawWithCache { + val backgroundColor = if (connection != null && onTop) { + themeBackgroundMix.copy(topTitleAlpha(false, connection)) + } else if (title != null || fixedTitleText != null || connection != null) { + themeBackgroundMix.copy(prefAlpha) + } else { + Color.Transparent + } + onDrawBehind { + drawRect(backgroundColor) + } + } + ) { + TopAppBar( + title = { + if (showSearch) { + SearchTextField(Modifier.fillMaxWidth(), alwaysVisible = searchAlwaysVisible, onValueChange = onSearchValueChanged) + } else if (title != null) { + title() + } else if (titleText.value.isNotEmpty() && connection != null) { + Row( + Modifier + .graphicsLayer { + alpha = if (fixedTitleText != null) prefAlpha else topTitleAlpha(true, connection) + } + ) { + Text( + titleText.value, + fontWeight = FontWeight.SemiBold, + maxLines = 1, + overflow = TextOverflow.Ellipsis + ) + } + } + }, + navigationIcon = navigationButton, + buttons = if (!showSearch) buttons else emptyList(), + centered = !showSearch && (title != null || !onTop), + onTop = onTop, + ) + CloseBarDivider(onTop, connection) + } + } +} + + +@Composable +fun CallAppBar( + title: @Composable () -> Unit, + onBack: () -> Unit +) { TopAppBar( - modifier = modifier, - title = { - if (!showSearch) { - title?.invoke() - } else { - SearchTextField(Modifier.fillMaxWidth(), alwaysVisible = false, onValueChange = onSearchValueChanged) - } - }, - backgroundColor = MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, 0.97f) - .copy(remember { appPrefs.inAppBarsAlpha.state }.value), - navigationIcon = navigationButton, - buttons = if (!showSearch) buttons else emptyList(), - centered = !showSearch, - onTop = onTop, + title, + navigationIcon = { NavigationButtonBack(tintColor = Color(0xFFFFFFD8), onButtonClicked = onBack) }, + centered = false, + onTop = true ) } @@ -87,23 +156,36 @@ fun NavigationButtonMenu(onButtonClicked: () -> Unit) { } } +@Composable +private fun BoxScope.CloseBarDivider(onTop: Boolean, connection: CollapsingAppBarNestedScrollConnection?) { + if (connection != null) { + val prefAlpha = appPrefs.inAppBarsAlpha.get() + Divider( + Modifier + .align(if (onTop) Alignment.BottomStart else Alignment.TopStart) + .graphicsLayer { + alpha = if (!onTop) prefAlpha else topTitleAlpha(false, connection) + } + ) + } else { + Divider(Modifier.align(if (onTop) Alignment.BottomStart else Alignment.TopStart)) + } +} + @Composable private fun TopAppBar( title: @Composable () -> Unit, modifier: Modifier = Modifier, navigationIcon: @Composable (RowScope.() -> Unit)? = null, buttons: List<@Composable RowScope.() -> Unit> = emptyList(), - backgroundColor: Color = MaterialTheme.colors.primarySurface, centered: Boolean, onTop: Boolean, ) { Box( modifier - .fillMaxWidth() - .then(if (!onTop) Modifier.navigationBarsPadding() else Modifier) - .background(backgroundColor) .then(if (onTop) Modifier.statusBarsPadding() else Modifier) .height(AppBarHeight * fontSizeSqrtMultiplier) + .fillMaxWidth() .padding(horizontal = 4.dp), contentAlignment = Alignment.CenterStart, ) { @@ -134,15 +216,18 @@ private fun TopAppBar( start = if (centered) kotlin.math.max(startPadding.value, endPadding.value).dp else startPadding, end = if (centered) kotlin.math.max(startPadding.value, endPadding.value).dp else endPadding ), - contentAlignment = Alignment.Center + contentAlignment = if (centered) Alignment.Center else Alignment.CenterStart ) { title() } } } +private fun topTitleAlpha(text: Boolean, connection: CollapsingAppBarNestedScrollConnection) = + if (connection.appBarOffset.absoluteValue < AppBarHandler.appBarMaxHeightPx / 3) 0f + else ((-connection.appBarOffset * 1.5f) / (AppBarHandler.appBarMaxHeightPx)).coerceIn(0f, if (text) 1f else appPrefs.inAppBarsAlpha.get()) + val AppBarHeight = 56.dp val AppBarHorizontalPadding = 4.dp -val BottomAppBarHeight = 60.dp private val TitleInsetWithoutIcon = DEFAULT_PADDING - AppBarHorizontalPadding -val TitleInsetWithIcon = 72.dp +val TitleInsetWithIcon = 52.dp 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 42007a08bb..36713106bc 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,13 +8,12 @@ import androidx.compose.material.* import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier -import androidx.compose.ui.draw.clip import androidx.compose.ui.graphics.Color -import androidx.compose.ui.graphics.RectangleShape import chat.simplex.common.model.ChatController.appPrefs import chat.simplex.common.model.ChatModel import chat.simplex.common.platform.* import chat.simplex.common.ui.theme.* +import chat.simplex.common.views.chatlist.StatusBarBackground import kotlinx.coroutines.flow.MutableStateFlow import java.util.concurrent.atomic.AtomicBoolean import kotlin.math.min @@ -28,7 +27,10 @@ fun ModalView( enableClose: Boolean = true, background: Color = Color.Unspecified, modifier: Modifier = Modifier, - endButtons: @Composable RowScope.() -> Unit = {}, + showSearch: Boolean = false, + searchAlwaysVisible: Boolean = false, + onSearchValueChanged: (String) -> Unit = {}, + endButtons: List<@Composable RowScope.() -> Unit> = emptyList(), content: @Composable BoxScope.() -> Unit, ) { if (showClose) { @@ -41,8 +43,18 @@ fun ModalView( content() } if (showCloseBar) { - Box(Modifier.align(if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart).then(if (oneHandUI.value) Modifier.imePadding() else Modifier)) { - CloseSheetBar(if (enableClose) close else null, showClose, endButtons = endButtons) + if (oneHandUI.value) { + StatusBarBackground() + } + Box(Modifier.align(if (oneHandUI.value) Alignment.BottomStart else Alignment.TopStart)) { + DefaultTopAppBar( + navigationButton = if (showClose) {{ NavigationButtonBack(onButtonClicked = if (enableClose) close else null) }} else null, + onTop = !oneHandUI.value, + showSearch = showSearch, + searchAlwaysVisible = searchAlwaysVisible, + onSearchValueChanged = onSearchValueChanged, + buttons = endButtons + ) } } } @@ -75,14 +87,14 @@ class ModalManager(private val placement: ModalPlacement? = null) { private var passcodeView: MutableStateFlow<(@Composable (close: () -> Unit) -> Unit)?> = MutableStateFlow(null) private var onTimePasscodeView: MutableStateFlow<(@Composable (close: () -> Unit) -> Unit)?> = MutableStateFlow(null) - fun showModal(settings: Boolean = false, showClose: Boolean = true, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable ModalData.() -> Unit) { + fun showModal(settings: Boolean = false, showClose: Boolean = true, endButtons: List<@Composable RowScope.() -> Unit> = emptyList(), content: @Composable ModalData.() -> Unit) { val data = ModalData() showCustomModal { close -> ModalView(close, showClose = showClose, endButtons = endButtons, content = { data.content() }) } } - fun showModalCloseable(settings: Boolean = false, showClose: Boolean = true, endButtons: @Composable RowScope.() -> Unit = {}, content: @Composable ModalData.(close: () -> Unit) -> Unit) { + fun showModalCloseable(settings: Boolean = false, showClose: Boolean = true, endButtons: List<@Composable RowScope.() -> Unit> = emptyList(), content: @Composable ModalData.(close: () -> Unit) -> Unit) { val data = ModalData() showCustomModal { close -> ModalView(close, showClose = showClose, endButtons = endButtons, content = { data.content(close) }) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt index 60dceab4ad..bb8c56b3bf 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/SearchTextField.kt @@ -2,7 +2,7 @@ package chat.simplex.common.views.helpers import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource -import androidx.compose.foundation.layout.defaultMinSize +import androidx.compose.foundation.layout.* import androidx.compose.foundation.shape.ZeroCornerSize import androidx.compose.foundation.text.* import androidx.compose.material.* @@ -10,6 +10,7 @@ import androidx.compose.material.TextFieldDefaults.indicatorLine import androidx.compose.material.TextFieldDefaults.textFieldWithLabelPadding import androidx.compose.runtime.* import androidx.compose.runtime.saveable.rememberSaveable +import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier import androidx.compose.ui.focus.FocusRequester import androidx.compose.ui.focus.focusRequester @@ -81,15 +82,20 @@ fun SearchTextField( ) val shape = MaterialTheme.shapes.small.copy(bottomEnd = ZeroCornerSize, bottomStart = ZeroCornerSize) val interactionSource = remember { MutableInteractionSource() } + val textStyle = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground) + // sizing is done differently on Android and desktop in order to have the same height of search and compose view on desktop + // see PlatformTextField.desktop + SendMsgView + val padding = if (appPlatform.isAndroid) PaddingValues() else PaddingValues(top = 3.dp, bottom = 4.dp) BasicTextField( value = searchText.value, modifier = modifier .background(colors.backgroundColor(enabled).value, shape) .indicatorLine(enabled, false, interactionSource, colors) .focusRequester(focusRequester) + .padding(padding) .defaultMinSize( minWidth = TextFieldDefaults.MinWidth, - minHeight = TextFieldDefaults.MinHeight + minHeight = if (appPlatform.isAndroid) TextFieldDefaults.MinHeight else 0.dp ), onValueChange = { searchText.value = it @@ -100,18 +106,14 @@ fun SearchTextField( visualTransformation = VisualTransformation.None, keyboardOptions = KeyboardOptions(imeAction = ImeAction.Search), singleLine = true, - textStyle = TextStyle( - color = MaterialTheme.colors.onBackground, - fontWeight = FontWeight.Normal, - fontSize = 15.sp - ), + textStyle = textStyle, interactionSource = interactionSource, decorationBox = @Composable { innerTextField -> TextFieldDefaults.TextFieldDecorationBox( value = searchText.value.text, innerTextField = innerTextField, placeholder = { - Text(placeholder, maxLines = 1, overflow = TextOverflow.Ellipsis) + Text(placeholder, style = textStyle.copy(color = MaterialTheme.colors.secondary), maxLines = 1, overflow = TextOverflow.Ellipsis) }, trailingIcon = if (searchText.value.text.isNotEmpty()) {{ IconButton({ 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 546f089ed0..eee4a3da72 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 @@ -48,10 +48,10 @@ fun ModalData.NewChatSheet(rh: RemoteHostInfo?, close: () -> Unit) { Column(modifier = Modifier.fillMaxSize()) { NewChatSheetLayout( addContact = { - ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ -> NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll) } + ModalManager.start.showModalCloseable(endButtons = listOf { AddContactLearnMoreButton() }) { _ -> NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll) } }, scanPaste = { - ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ -> NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll) } + ModalManager.start.showModalCloseable(endButtons = listOf { AddContactLearnMoreButton() }) { _ -> NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll) } }, createGroup = { ModalManager.start.showCustomModal { close -> AddGroupView(chatModel, chatModel.currentRemoteHost.value, close, closeAll) } @@ -62,10 +62,10 @@ fun ModalData.NewChatSheet(rh: RemoteHostInfo?, close: () -> Unit) { } if (oneHandUI.value) { Column(Modifier.align(Alignment.BottomCenter)) { - CloseSheetBarBottom( - close = close, - showClose = true, - closeBarTitle = generalGetString(MR.strings.new_message) + DefaultTopAppBar( + navigationButton = { NavigationButtonBack(onButtonClicked = close) }, + fixedTitleText = generalGetString(MR.strings.new_message), + onTop = false, ) } } @@ -289,7 +289,7 @@ private fun ModalData.NewChatSheetLayout( ) ) { Divider() - Column(Modifier.consumeWindowInsets(WindowInsets.navigationBars).consumeWindowInsets(PaddingValues(bottom = AppBarHeight))) { + Column(Modifier.consumeWindowInsets(WindowInsets.navigationBars).consumeWindowInsets(PaddingValues(bottom = AppBarHeight * fontSizeSqrtMultiplier))) { ContactsSearchBar( listState = listState, searchText = searchText, @@ -420,8 +420,8 @@ private fun ModalData.NewChatSheetLayout( StatusBarBackground() } else { NonOneHandLazyColumn() + NavigationBarBackground(oneHandUI.value, true) } - NavigationBarBackground(oneHandUI.value, true) } } @@ -719,15 +719,16 @@ private fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats } if (oneHandUI.value) { StatusBarBackground() + } else { + NavigationBarBackground(oneHandUI.value, true) } - NavigationBarBackground(oneHandUI.value, true) } if (oneHandUI.value) { Column(Modifier.align(Alignment.BottomCenter)) { - CloseSheetBarBottom( - close = closeDeletedChats, - showClose = true, - closeBarTitle = generalGetString(MR.strings.deleted_chats), + DefaultTopAppBar( + navigationButton = { NavigationButtonBack(onButtonClicked = closeDeletedChats) }, + fixedTitleText = generalGetString(MR.strings.deleted_chats), + onTop = false, ) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt index a7f9a90552..1c2e5c1af0 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/NewChatView.kt @@ -480,9 +480,9 @@ private fun InviteView(rhId: Long?, connReqInvitation: String, contactConnection val search = rememberSaveable { mutableStateOf("") } ModalView( { close() }, - endButtons = { - SearchTextField(Modifier.fillMaxWidth(), placeholder = stringResource(MR.strings.search_verb), alwaysVisible = true) { search.value = it } - }, + showSearch = true, + searchAlwaysVisible = true, + onSearchValueChanged = { search.value = it }, content = { ActiveProfilePicker( search = search, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/LinkAMobileView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/LinkAMobileView.kt index f0e34218d1..e52db7bc02 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/LinkAMobileView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/onboarding/LinkAMobileView.kt @@ -60,9 +60,10 @@ private fun LinkAMobileLayout( updateDeviceName: (String) -> Unit, ) { Column(Modifier.themedBackground()) { - CloseSheetBar(close = { - appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) - }) + DefaultTopAppBar( + navigationButton = { NavigationButtonBack(onButtonClicked = { appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) } ) }, + onTop = true + ) BackHandler(onBack = { appPrefs.onboardingStage.set(OnboardingStage.Step1_SimpleXInfo) }) 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 c176950902..b1326140b7 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 @@ -31,7 +31,7 @@ import dev.icerock.moko.resources.StringResource @Composable fun SimpleXInfo(chatModel: ChatModel, onboarding: Boolean = true) { if (onboarding) { - ModalView({}, showClose = false, endButtons = { + ModalView({}, showClose = false, endButtons = listOf { IconButton({ ModalManager.fullscreen.showModal { HowItWorks(chatModel.currentUser.value, null) }}) { Icon(painterResource(MR.images.ic_info), null, Modifier.size(28.dp), tint = MaterialTheme.colors.primary) } 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 6475d90102..b9732269cd 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 @@ -116,7 +116,7 @@ object AppearanceScope { val localFontScale = remember { mutableStateOf(appPrefs.fontScale.get()) } SectionView(stringResource(MR.strings.appearance_font_size).uppercase(), contentPadding = PaddingValues(horizontal = DEFAULT_PADDING)) { Row(Modifier.padding(top = 10.dp), verticalAlignment = Alignment.CenterVertically) { - Box(Modifier.size(60.dp) + Box(Modifier.size(50.dp) .background(MaterialTheme.colors.surface, RoundedCornerShape(percent = 22)) .clip(RoundedCornerShape(percent = 22)) .clickable { @@ -130,7 +130,7 @@ object AppearanceScope { Text("Aa", color = if (localFontScale.value == 1f) MaterialTheme.colors.primary else MaterialTheme.colors.onBackground) } } - Spacer(Modifier.width(10.dp)) + Spacer(Modifier.width(15.dp)) // Text("${(localFontScale.value * 100).roundToInt()}%", Modifier.width(70.dp), textAlign = TextAlign.Center, fontSize = 12.sp) if (appPlatform.isAndroid) { Slider( @@ -176,27 +176,27 @@ object AppearanceScope { val prefValue = remember { pref.state }.value SectionView(stringResource(sectionTitle).uppercase(), contentPadding = PaddingValues(horizontal = DEFAULT_PADDING)) { Row(Modifier.padding(top = 10.dp), verticalAlignment = Alignment.CenterVertically) { - Box(Modifier.size(60.dp) + Box(Modifier.size(50.dp) .background(MaterialTheme.colors.surface, RoundedCornerShape(percent = 22)) .clip(RoundedCornerShape(percent = 22)) .clickable { pref.set(defaultValue) }, contentAlignment = Alignment.Center) { - Text(String.format(Locale.US, "%.2f", prefValue), + Text(String.format(Locale.US, "%.2f", 1 - prefValue), color = if (prefValue == defaultValue) MaterialTheme.colors.primary else MaterialTheme.colors.onBackground, fontSize = 12.sp, maxLines = 1 ) } - Spacer(Modifier.width(10.dp)) + Spacer(Modifier.width(15.dp)) Slider( - prefValue, + 1 - prefValue, valueRange = 0f..1f, - steps = 101, + steps = 21, onValueChange = { - val diff = it % 0.01f - pref.set(String.format(Locale.US, "%.2f", it + (if (diff >= 0.005f) -diff + 0.01f else -diff)).toFloatOrNull() ?: 1f) + val diff = it % 0.05f + pref.set(1f - (String.format(Locale.US, "%.2f", it + (if (diff >= 0.025f) -diff + 0.05f else -diff)).toFloatOrNull() ?: 1f)) }, colors = SliderDefaults.colors( activeTickColor = Color.Transparent, 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 d59451384b..0b03094aca 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 @@ -52,9 +52,9 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, close: ( val search = rememberSaveable { mutableStateOf("") } ModalView( { close() }, - endButtons = { - SearchTextField(Modifier.fillMaxWidth(), placeholder = stringResource(MR.strings.search_verb), alwaysVisible = true) { search.value = it } - }, + showSearch = true, + searchAlwaysVisible = true, + onSearchValueChanged = { search.value = it }, content = { modalView(chatModel, search) }) } }, 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 e37e99f3e9..e7bcf4802a 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 @@ -1,10 +1,12 @@ package chat.simplex.common.platform +import androidx.compose.foundation.background import androidx.compose.foundation.interaction.MutableInteractionSource import androidx.compose.foundation.layout.* import androidx.compose.foundation.text.BasicTextField import androidx.compose.foundation.text.KeyboardOptions import androidx.compose.material.* +import androidx.compose.material.TextFieldDefaults.textFieldWithLabelPadding import androidx.compose.runtime.* import androidx.compose.ui.Alignment import androidx.compose.ui.Modifier @@ -113,7 +115,9 @@ actual fun PlatformTextField( autoCorrectEnabled = true ), modifier = Modifier - .padding(vertical = 4.dp) + .padding(start = startPadding, end = endPadding) + .offset(y = (-5).dp) + .fillMaxWidth() .focusRequester(focusRequester) .onPreviewKeyEvent { if ((it.key == Key.Enter || it.key == Key.NumPadEnter) && it.type == KeyEventType.KeyDown) { @@ -177,30 +181,24 @@ actual fun PlatformTextField( }, cursorBrush = SolidColor(MaterialTheme.colors.secondary), decorationBox = { innerTextField -> - Row(verticalAlignment = Alignment.Bottom) { CompositionLocalProvider( LocalLayoutDirection provides if (isRtlByCharacters) LayoutDirection.Rtl else LocalLayoutDirection.current ) { - Column(Modifier.weight(1f).padding(start = startPadding, end = endPadding)) { - Spacer(Modifier.height(8.dp)) - TextFieldDefaults.TextFieldDecorationBox( - value = textFieldValue.text, - innerTextField = innerTextField, - placeholder = { Text(placeholder, style = textStyle.value.copy(color = MaterialTheme.colors.secondary)) }, - singleLine = false, - enabled = true, - isError = false, - trailingIcon = null, - interactionSource = remember { MutableInteractionSource() }, - contentPadding = PaddingValues(), - visualTransformation = VisualTransformation.None, - colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Unspecified) - ) - Spacer(Modifier.height(10.dp)) - } + TextFieldDefaults.TextFieldDecorationBox( + value = textFieldValue.text, + innerTextField = innerTextField, + placeholder = { Text(placeholder, style = textStyle.value.copy(color = MaterialTheme.colors.secondary)) }, + singleLine = false, + enabled = true, + isError = false, + trailingIcon = null, + interactionSource = remember { MutableInteractionSource() }, + contentPadding = textFieldWithLabelPadding(start = 0.dp, end = 0.dp), + visualTransformation = VisualTransformation.None, + colors = TextFieldDefaults.textFieldColors(backgroundColor = Color.Unspecified) + ) } - } - }, + } ) showDeleteTextButton.value = cs.message.split("\n").size >= 4 && !cs.inProgress if (composeState.value.preview is ComposePreview.VoicePreview) { diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/ScrollableColumn.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/ScrollableColumn.desktop.kt index 71e4adaaa8..a6044afb9d 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/ScrollableColumn.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/platform/ScrollableColumn.desktop.kt @@ -18,6 +18,8 @@ import androidx.compose.ui.input.pointer.* import androidx.compose.ui.unit.Dp import androidx.compose.ui.unit.dp import chat.simplex.common.model.ChatController.appPrefs +import chat.simplex.common.ui.theme.DEFAULT_PADDING +import chat.simplex.common.ui.theme.DEFAULT_PADDING_HALF import chat.simplex.common.views.helpers.* import kotlinx.coroutines.* import kotlinx.coroutines.flow.filter @@ -166,8 +168,8 @@ actual fun ColumnWithScrollBar( modifier.verticalScroll(state).then(scrollModifier) }, verticalArrangement, horizontalAlignment) { - if (connection != null && !oneHandUI.value) { - Spacer(Modifier.padding(padding)) + if (connection != null) { + Spacer(if (oneHandUI.value) Modifier.padding(top = DEFAULT_PADDING + 5.dp) else Modifier.padding(padding)) } content() if (connection != null && oneHandUI.value) { 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 ba45923048..b3a6653e35 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 @@ -88,7 +88,7 @@ fun DensityScaleSection() { val localDensityScale = remember { mutableStateOf(appPrefs.densityScale.get()) } SectionView(stringResource(MR.strings.appearance_zoom).uppercase(), contentPadding = PaddingValues(horizontal = DEFAULT_PADDING)) { Row(Modifier.padding(top = 10.dp), verticalAlignment = Alignment.CenterVertically) { - Box(Modifier.size(60.dp) + Box(Modifier.size(50.dp) .background(MaterialTheme.colors.surface, RoundedCornerShape(percent = 22)) .clip(RoundedCornerShape(percent = 22)) .clickable { @@ -106,7 +106,7 @@ fun DensityScaleSection() { ) } } - Spacer(Modifier.width(10.dp)) + Spacer(Modifier.width(15.dp)) Slider( localDensityScale.value, valueRange = 1f..2f,