This commit is contained in:
Avently
2024-10-24 21:02:18 +07:00
parent 373a26dc61
commit 223cf428c6
29 changed files with 325 additions and 205 deletions
@@ -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
@@ -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()
@@ -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) {
@@ -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()
@@ -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<Pair<SharedPreference<Boolean>, 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"
@@ -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<Dp>) {
} 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
@@ -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)
})
@@ -463,7 +463,7 @@ fun ChatView(staleChatId: State<String?>, 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<String?>, 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
@@ -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)
@@ -41,11 +41,9 @@ fun BoxScope.SelectedItemsTopToolbar(selectedChatItems: MutableState<Set<Long>?>
)
},
onTitleClick = null,
showSearch = false,
onTop = oneHandUI.value,
onTop = !oneHandUI.value,
onSearchValueChanged = {},
)
Divider(Modifier.align(if (oneHandUI.value) Alignment.TopStart else Alignment.BottomStart))
}
@Composable
@@ -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) {
@@ -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)
}
}
@@ -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)
) {
@@ -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<Animate
}
}
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
Box(Modifier.fillMaxSize()) {
if (oneHandUI.value) {
ChatListWithLoadingScreen(searchText)
ChatListWithLoadingScreen(searchText, listState)
Column(Modifier.align(Alignment.BottomCenter)) {
Divider()
ChatListToolbar(
userPickerState,
listState,
stopped,
setPerformLA,
)
}
} else {
ChatListWithLoadingScreen(searchText)
ChatListWithLoadingScreen(searchText, listState)
Column {
ChatListToolbar(
userPickerState,
listState,
stopped,
setPerformLA,
)
Divider()
}
if (searchText.value.text.isEmpty() && !chatModel.desktopNoUserNoRemote && chatModel.chatRunning.value == true) {
NewChatSheetFloatingButton(oneHandUI, stopped)
@@ -196,9 +201,9 @@ fun ChatListView(chatModel: ChatModel, userPickerState: MutableStateFlow<Animate
}
@Composable
private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFieldValue>) {
private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFieldValue>, 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<AnimatedViewState>, stopped: Boolean, setPerformLA: (Boolean) -> Unit) {
private fun ChatListToolbar(userPickerState: MutableStateFlow<AnimatedViewState>, listState: LazyListState, stopped: Boolean, setPerformLA: (Boolean) -> Unit) {
val serversSummary: MutableState<PresentedServersSummary?> = remember { mutableStateOf(null) }
val barButtons = arrayListOf<@Composable RowScope.() -> Unit>()
val updatingProgress = remember { chatModel.updatingProgress }.value
@@ -319,6 +324,8 @@ private fun ChatListToolbar(userPickerState: MutableStateFlow<AnimatedViewState>
}
}
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<AnimatedViewState>
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<TextFieldValue>, searchShowingSimplexLink: MutableState<Boolean>, searchChatFilteredBySimplexLink: MutableState<String?>) {
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<TextFieldValue>) {
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
private fun BoxScope.ChatList(searchText: MutableState<TextFieldValue>, 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<TextFieldValue>) {
}
}
}
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<TextFieldValue>) {
} }
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<TextFieldValue>) {
}
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()) } }
}
@@ -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,
@@ -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) })
}
@@ -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<Long?, String>? = 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
@@ -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
@@ -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) })
@@ -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({
@@ -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,
)
}
}
@@ -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,
@@ -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)
})
@@ -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)
}
@@ -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,
@@ -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) })
}
},
@@ -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) {
@@ -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) {
@@ -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,