mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-09-02 23:34:40 +00:00
more features and better performance
This commit is contained in:
@@ -285,28 +285,28 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
@Suppress("DEPRECATION")
|
||||
val windowInsetController = ViewCompat.getWindowInsetsController(window.decorView)
|
||||
// Blend status bar color to the animated color
|
||||
val colors = CurrentColors.value.colors
|
||||
val baseBackgroundColor = if (toolbarOnTop) colors.background.mixWith(colors.onBackground, 0.97f) else colors.background
|
||||
var statusBar = baseBackgroundColor.mixWith(drawerShadingColor.copy(1f), 1 - drawerShadingColor.alpha).toArgb()
|
||||
// val colors = CurrentColors.value.colors
|
||||
// val baseBackgroundColor = if (toolbarOnTop) colors.background.mixWith(colors.onBackground, 0.97f) else colors.background
|
||||
// var statusBar = baseBackgroundColor.mixWith(drawerShadingColor.copy(1f), 1 - drawerShadingColor.alpha).toArgb()
|
||||
var statusBarLight = isLight
|
||||
|
||||
// SimplexGreen while in call
|
||||
//
|
||||
// // SimplexGreen while in call
|
||||
if (window.statusBarColor == SimplexGreen.toArgb()) {
|
||||
statusBarColorAfterCall.intValue = statusBar
|
||||
statusBar = SimplexGreen.toArgb()
|
||||
// statusBarColorAfterCall.intValue = statusBar
|
||||
// statusBar = SimplexGreen.toArgb()
|
||||
statusBarLight = false
|
||||
}
|
||||
window.statusBarColor = statusBar
|
||||
val navBar = navBarColor.toArgb()
|
||||
// window.statusBarColor = statusBar
|
||||
// val navBar = navBarColor.toArgb()
|
||||
if (windowInsetController?.isAppearanceLightStatusBars != statusBarLight) {
|
||||
windowInsetController?.isAppearanceLightStatusBars = statusBarLight
|
||||
}
|
||||
if (window.navigationBarColor != navBar) {
|
||||
window.navigationBarColor = navBar
|
||||
}
|
||||
if (windowInsetController?.isAppearanceLightNavigationBars != isLight) {
|
||||
windowInsetController?.isAppearanceLightNavigationBars = isLight
|
||||
}
|
||||
// if (window.navigationBarColor != navBar) {
|
||||
// window.navigationBarColor = navBar
|
||||
// }
|
||||
// if (windowInsetController?.isAppearanceLightNavigationBars != isLight) {
|
||||
// windowInsetController?.isAppearanceLightNavigationBars = isLight
|
||||
// }
|
||||
}
|
||||
|
||||
override fun androidSetStatusAndNavBarColors(isLight: Boolean, backgroundColor: Color, hasTop: Boolean, hasBottom: Boolean) {
|
||||
|
||||
@@ -64,7 +64,6 @@ kotlin {
|
||||
implementation("androidx.activity:activity-compose:1.9.1")
|
||||
val workVersion = "2.9.1"
|
||||
implementation("androidx.work:work-runtime-ktx:$workVersion")
|
||||
implementation("com.google.accompanist:accompanist-insets:0.30.1")
|
||||
|
||||
// Video support
|
||||
implementation("com.google.android.exoplayer:exoplayer:2.19.1")
|
||||
|
||||
-12
@@ -3,20 +3,8 @@ package chat.simplex.common.platform
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import java.io.File
|
||||
|
||||
actual fun Modifier.navigationBarsWithImePadding(): Modifier = navigationBarsWithImePadding()
|
||||
|
||||
@Composable
|
||||
actual fun ProvideWindowInsets(
|
||||
consumeWindowInsets: Boolean,
|
||||
windowInsetsAnimationsEnabled: Boolean,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
com.google.accompanist.insets.ProvideWindowInsets(content = content)
|
||||
}
|
||||
|
||||
@Composable
|
||||
actual fun Modifier.desktopOnExternalDrag(
|
||||
enabled: Boolean,
|
||||
|
||||
+12
-15
@@ -7,10 +7,9 @@ import androidx.compose.foundation.lazy.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.graphicsLayer
|
||||
import androidx.compose.ui.input.nestedscroll.nestedScroll
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlin.math.absoluteValue
|
||||
@@ -40,19 +39,15 @@ actual fun LazyColumnWithScrollBar(
|
||||
}
|
||||
}
|
||||
}
|
||||
val direction = LocalLayoutDirection.current
|
||||
val paddingEnd = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction)
|
||||
if (connection != null) {
|
||||
LazyColumn(modifier.nestedScroll(connection), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled) {
|
||||
LazyColumn(modifier.nestedScroll(connection).padding(end = paddingEnd), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled) {
|
||||
content()
|
||||
item {
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
LazyColumn(modifier, state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled) {
|
||||
LazyColumn(modifier.padding(end = paddingEnd), state, contentPadding, reverseLayout, verticalArrangement, horizontalAlignment, flingBehavior, userScrollEnabled) {
|
||||
content()
|
||||
item {
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -66,6 +61,7 @@ actual fun ColumnWithScrollBar(
|
||||
maxIntrinsicSize: Boolean,
|
||||
content: @Composable() (ColumnScope.() -> Unit)
|
||||
) {
|
||||
val modifier = modifier.imePadding()
|
||||
val state = state ?: LocalAppBarHandler.current?.scrollState ?: rememberScrollState()
|
||||
val connection = LocalAppBarHandler.current?.connection
|
||||
LaunchedEffect(Unit) {
|
||||
@@ -78,21 +74,22 @@ actual fun ColumnWithScrollBar(
|
||||
}
|
||||
}
|
||||
}
|
||||
val direction = LocalLayoutDirection.current
|
||||
if (connection != null) {
|
||||
Column(
|
||||
if (maxIntrinsicSize) {
|
||||
modifier.nestedScroll(connection).verticalScroll(state).height(IntrinsicSize.Max)
|
||||
modifier.nestedScroll(connection).verticalScroll(state).height(IntrinsicSize.Max).padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction))
|
||||
} else {
|
||||
modifier.nestedScroll(connection).verticalScroll(state)
|
||||
modifier.nestedScroll(connection).verticalScroll(state).padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction))
|
||||
}, verticalArrangement, horizontalAlignment) {
|
||||
content()
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
|
||||
}
|
||||
} else {
|
||||
Column(if (maxIntrinsicSize) {
|
||||
modifier.verticalScroll(state).height(IntrinsicSize.Max)
|
||||
modifier.verticalScroll(state).height(IntrinsicSize.Max).padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction))
|
||||
} else {
|
||||
modifier.verticalScroll(state)
|
||||
modifier.verticalScroll(state).padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction))
|
||||
}, verticalArrangement, horizontalAlignment) {
|
||||
content()
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
|
||||
|
||||
-9
@@ -14,15 +14,6 @@ import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import java.io.File
|
||||
|
||||
expect fun Modifier.navigationBarsWithImePadding(): Modifier
|
||||
|
||||
@Composable
|
||||
expect fun ProvideWindowInsets(
|
||||
consumeWindowInsets: Boolean = true,
|
||||
windowInsetsAnimationsEnabled: Boolean = true,
|
||||
content: @Composable () -> Unit
|
||||
)
|
||||
|
||||
@Composable
|
||||
expect fun Modifier.desktopOnExternalDrag(
|
||||
enabled: Boolean = true,
|
||||
|
||||
+2
-4
@@ -78,7 +78,6 @@ fun TerminalLayout(
|
||||
composeState.value = composeState.value.copy(message = s)
|
||||
}
|
||||
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
Scaffold(
|
||||
topBar = { CloseSheetBar(close) },
|
||||
bottomBar = {
|
||||
@@ -112,7 +111,7 @@ fun TerminalLayout(
|
||||
}
|
||||
},
|
||||
contentColor = LocalContentColor.current,
|
||||
modifier = Modifier.navigationBarsWithImePadding()
|
||||
modifier = Modifier.navigationBarsPadding().imePadding()
|
||||
) { contentPadding ->
|
||||
Surface(
|
||||
modifier = Modifier
|
||||
@@ -124,7 +123,6 @@ fun TerminalLayout(
|
||||
TerminalLog(floating)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -172,7 +170,7 @@ fun TerminalLog(floating: Boolean) {
|
||||
ModalManager.start
|
||||
}
|
||||
modalPlace.showModal(endButtons = { ShareButton { clipboard.shareText(item.details) } }) {
|
||||
SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState())) {
|
||||
SelectionContainer(modifier = Modifier.verticalScroll(rememberScrollState()).navigationBarsPadding()) {
|
||||
val details = item.details
|
||||
.let {
|
||||
if (it.length < 100_000) it
|
||||
|
||||
-6
@@ -40,8 +40,6 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
val scrollState = rememberScrollState()
|
||||
val keyboardState by getKeyboardState()
|
||||
var savedKeyboardState by remember { mutableStateOf(keyboardState) }
|
||||
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -102,7 +100,6 @@ fun CreateProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -115,7 +112,6 @@ fun CreateFirstProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
CompositionLocalProvider(
|
||||
LocalAppBarHandler provides handler
|
||||
) {
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.fillMaxSize()
|
||||
@@ -178,7 +174,6 @@ fun CreateFirstProfile(chatModel: ChatModel, close: () -> Unit) {
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun createProfileInNoProfileSetup(displayName: String, close: () -> Unit) {
|
||||
@@ -255,7 +250,6 @@ fun ProfileNameField(name: MutableState<String>, placeholder: String = "", isVal
|
||||
val modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.heightIn(min = 50.dp)
|
||||
.navigationBarsWithImePadding()
|
||||
.onFocusChanged { focused = it.isFocused }
|
||||
Column(
|
||||
Modifier
|
||||
|
||||
+125
-158
@@ -107,6 +107,7 @@ fun ChatView(staleChatId: State<String?>, onComposed: suspend (chatId: String) -
|
||||
chatModel.chats.value.firstOrNull { chat -> chat.chatInfo.id == activeChatInfo.value?.id }?.chatStats?.unreadCount ?: 0
|
||||
}
|
||||
}
|
||||
println("LALAL TOP")
|
||||
val clipboard = LocalClipboardManager.current
|
||||
when (chatInfo) {
|
||||
is ChatInfo.Direct, is ChatInfo.Group, is ChatInfo.Local -> {
|
||||
@@ -634,6 +635,7 @@ fun ChatLayout(
|
||||
showViaProxy: Boolean,
|
||||
showSearch: MutableState<Boolean>
|
||||
) {
|
||||
println("LALAL CHATLAYOUT TOP")
|
||||
val scope = rememberCoroutineScope()
|
||||
val attachmentDisabled = remember { derivedStateOf { composeState.value.attachmentDisabled } }
|
||||
Box(
|
||||
@@ -656,66 +658,62 @@ fun ChatLayout(
|
||||
},
|
||||
)
|
||||
) {
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
sheetElevation = 0.dp,
|
||||
sheetContent = {
|
||||
ChooseAttachmentView(
|
||||
attachmentOption,
|
||||
hide = { scope.launch { attachmentBottomSheetState.hide() } }
|
||||
)
|
||||
},
|
||||
sheetState = attachmentBottomSheetState,
|
||||
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
|
||||
) {
|
||||
val floatingButton: MutableState<@Composable () -> Unit> = remember { mutableStateOf({}) }
|
||||
val setFloatingButton = { button: @Composable () -> Unit ->
|
||||
floatingButton.value = button
|
||||
}
|
||||
println("LALAL CHATLAYOUT TOP 1")
|
||||
|
||||
Scaffold(
|
||||
topBar = {
|
||||
if (selectedChatItems.value == null) {
|
||||
val chatInfo = chatInfo.value
|
||||
if (chatInfo != null) {
|
||||
ChatInfoToolbar(chatInfo, back, info, startCall, endCall, addMembers, openGroupLink, changeNtfsState, onSearchValueChanged, showSearch)
|
||||
}
|
||||
} else {
|
||||
SelectedItemsTopToolbar(selectedChatItems)
|
||||
}
|
||||
},
|
||||
bottomBar = composeView,
|
||||
floatingActionButton = { floatingButton.value() },
|
||||
contentColor = LocalContentColor.current,
|
||||
backgroundColor = Color.Unspecified
|
||||
) { contentPadding ->
|
||||
val wallpaperImage = MaterialTheme.wallpaper.type.image
|
||||
val wallpaperType = MaterialTheme.wallpaper.type
|
||||
val backgroundColor = MaterialTheme.wallpaper.background ?: wallpaperType.defaultBackgroundColor(CurrentColors.value.base, MaterialTheme.colors.background)
|
||||
val tintColor = MaterialTheme.wallpaper.tint ?: wallpaperType.defaultTintColor(CurrentColors.value.base)
|
||||
BoxWithConstraints(Modifier
|
||||
.fillMaxSize()
|
||||
.background(MaterialTheme.colors.background)
|
||||
.then(if (wallpaperImage != null)
|
||||
Modifier.drawWithCache { chatViewBackground(wallpaperImage, wallpaperType, backgroundColor, tintColor) }
|
||||
else
|
||||
Modifier)
|
||||
.padding(contentPadding)
|
||||
) {
|
||||
val remoteHostId = remember { remoteHostId }.value
|
||||
val chatInfo = remember { chatInfo }.value
|
||||
if (chatInfo != null) {
|
||||
ChatItemsList(
|
||||
remoteHostId, chatInfo, unreadCount, composeState, searchValue,
|
||||
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, loadPrevMessages, deleteMessage, deleteMessages,
|
||||
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
|
||||
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
||||
setReaction, showItemDetails, markRead, setFloatingButton, onComposed, developerTools, showViaProxy,
|
||||
)
|
||||
}
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
sheetElevation = 0.dp,
|
||||
sheetContent = {
|
||||
ChooseAttachmentView(
|
||||
attachmentOption,
|
||||
hide = { scope.launch { attachmentBottomSheetState.hide() } }
|
||||
)
|
||||
},
|
||||
sheetState = attachmentBottomSheetState,
|
||||
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
|
||||
) {
|
||||
println("LALAL CHATLAYOUT TOP 2")
|
||||
val wallpaperImage = MaterialTheme.wallpaper.type.image
|
||||
val wallpaperType = MaterialTheme.wallpaper.type
|
||||
val backgroundColor = MaterialTheme.wallpaper.background ?: wallpaperType.defaultBackgroundColor(CurrentColors.value.base, MaterialTheme.colors.background)
|
||||
val tintColor = MaterialTheme.wallpaper.tint ?: wallpaperType.defaultTintColor(CurrentColors.value.base)
|
||||
Column(
|
||||
Modifier
|
||||
.background(MaterialTheme.colors.background)
|
||||
.then(if (wallpaperImage != null)
|
||||
Modifier.drawWithCache { chatViewBackground(wallpaperImage, wallpaperType, backgroundColor, tintColor) }
|
||||
else
|
||||
Modifier)
|
||||
) {
|
||||
if (selectedChatItems.value == null) {
|
||||
val chatInfo = chatInfo.value
|
||||
if (chatInfo != null) {
|
||||
ChatInfoToolbar(chatInfo, back, info, startCall, endCall, addMembers, openGroupLink, changeNtfsState, onSearchValueChanged, showSearch)
|
||||
}
|
||||
} else {
|
||||
SelectedItemsTopToolbar(selectedChatItems)
|
||||
}
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
) {
|
||||
println("LALAL CHATLAYOUT")
|
||||
val remoteHostId = remember { remoteHostId }.value
|
||||
val chatInfo = remember { chatInfo }.value
|
||||
if (chatInfo != null) {
|
||||
ChatItemsList(
|
||||
remoteHostId, chatInfo, unreadCount, composeState, searchValue,
|
||||
useLinkPreviews, linkMode, selectedChatItems, showMemberInfo, loadPrevMessages, deleteMessage, deleteMessages,
|
||||
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature, openDirectChat, forwardItem,
|
||||
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
|
||||
setReaction, showItemDetails, markRead, onComposed, developerTools, showViaProxy,
|
||||
)
|
||||
}
|
||||
}
|
||||
composeView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -882,14 +880,12 @@ fun ChatInfoToolbar(
|
||||
onSearchValueChanged = onSearchValueChanged,
|
||||
buttons = barButtons
|
||||
)
|
||||
|
||||
Divider(Modifier.statusBarsPadding().padding(top = AppBarHeight * fontSizeSqrtMultiplier))
|
||||
|
||||
Box(Modifier.fillMaxWidth().wrapContentSize(Alignment.TopEnd).offset(y = AppBarHeight * fontSizeSqrtMultiplier)) {
|
||||
Box(Modifier.fillMaxWidth().wrapContentSize(Alignment.TopEnd)) {
|
||||
DefaultDropdownMenu(showMenu) {
|
||||
menuItems.forEach { it() }
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -931,7 +927,7 @@ private fun ContactVerifiedShield() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BoxWithConstraintsScope.ChatItemsList(
|
||||
fun BoxScope.ChatItemsList(
|
||||
remoteHostId: Long?,
|
||||
chatInfo: ChatInfo,
|
||||
unreadCount: State<Int>,
|
||||
@@ -960,11 +956,11 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
setReaction: (ChatInfo, ChatItem, Boolean, MsgReaction) -> Unit,
|
||||
showItemDetails: (ChatInfo, ChatItem) -> Unit,
|
||||
markRead: (CC.ItemRange, unreadCountAfter: Int?) -> Unit,
|
||||
setFloatingButton: (@Composable () -> Unit) -> Unit,
|
||||
onComposed: suspend (chatId: String) -> Unit,
|
||||
developerTools: Boolean,
|
||||
showViaProxy: Boolean
|
||||
) {
|
||||
println("LALAL CHATITEMSLIST")
|
||||
val listState = rememberLazyListState()
|
||||
val scope = rememberCoroutineScope()
|
||||
ScrollToBottom(chatInfo.id, listState, chatModel.chatItems)
|
||||
@@ -985,11 +981,11 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
|
||||
Spacer(Modifier.size(8.dp))
|
||||
val reversedChatItems by remember { derivedStateOf { chatModel.chatItems.asReversed() } }
|
||||
val maxHeightRounded = with(LocalDensity.current) { maxHeight.roundToPx() }
|
||||
val maxHeight = remember { derivedStateOf { listState.layoutInfo.viewportSize.height } }
|
||||
val scrollToItem: (Long) -> Unit = { itemId: Long ->
|
||||
val index = reversedChatItems.indexOfFirst { it.id == itemId }
|
||||
if (index != -1) {
|
||||
scope.launch { listState.animateScrollToItem(kotlin.math.min(reversedChatItems.lastIndex, index + 1), -maxHeightRounded) }
|
||||
scope.launch { listState.animateScrollToItem(kotlin.math.min(reversedChatItems.lastIndex, index + 1), -maxHeight.value) }
|
||||
}
|
||||
}
|
||||
// TODO: Having this block on desktop makes ChatItemsList() to recompose twice on chatModel.chatId update instead of once
|
||||
@@ -1010,6 +1006,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
)
|
||||
LazyColumnWithScrollBar(Modifier.align(Alignment.BottomCenter), state = listState, reverseLayout = true) {
|
||||
itemsIndexed(reversedChatItems, key = { _, item -> item.id to item.meta.createdAt.toEpochMilliseconds() }) { i, cItem ->
|
||||
println("LALAL ITEM $i")
|
||||
CompositionLocalProvider(
|
||||
// Makes horizontal and vertical scrolling to coexist nicely.
|
||||
// With default touchSlop when you scroll LazyColumn, you can unintentionally open reply view
|
||||
@@ -1020,7 +1017,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
scope.launch {
|
||||
listState.scrollToItem(
|
||||
kotlin.math.min(reversedChatItems.lastIndex, indexInReversed + 1),
|
||||
-maxHeightRounded
|
||||
-maxHeight.value
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -1264,7 +1261,9 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
}
|
||||
}
|
||||
}
|
||||
FloatingButtons(chatModel.chatItems, unreadCount, remoteHostId, chatInfo, searchValue, markRead, setFloatingButton, listState)
|
||||
println("LALAL REDRAW TOP")
|
||||
|
||||
FloatingButtons(chatModel.chatItems, unreadCount, remoteHostId, chatInfo, searchValue, markRead, listState)
|
||||
|
||||
FloatingDate(
|
||||
Modifier.padding(top = 10.dp).align(Alignment.TopCenter),
|
||||
@@ -1322,87 +1321,61 @@ private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems:
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun BoxWithConstraintsScope.FloatingButtons(
|
||||
fun BoxScope.FloatingButtons(
|
||||
chatItems: State<List<ChatItem>>,
|
||||
unreadCount: State<Int>,
|
||||
remoteHostId: Long?,
|
||||
chatInfo: ChatInfo,
|
||||
searchValue: State<String>,
|
||||
markRead: (CC.ItemRange, unreadCountAfter: Int?) -> Unit,
|
||||
setFloatingButton: (@Composable () -> Unit) -> Unit,
|
||||
listState: LazyListState
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
var firstVisibleIndex by remember { mutableStateOf(listState.firstVisibleItemIndex) }
|
||||
var lastIndexOfVisibleItems by remember { mutableStateOf(listState.layoutInfo.visibleItemsInfo.lastIndex) }
|
||||
var firstItemIsVisible by remember { mutableStateOf(firstVisibleIndex == 0) }
|
||||
|
||||
LaunchedEffect(listState) {
|
||||
snapshotFlow { listState.firstVisibleItemIndex }
|
||||
.distinctUntilChanged()
|
||||
.collect {
|
||||
firstVisibleIndex = it
|
||||
firstItemIsVisible = firstVisibleIndex == 0
|
||||
}
|
||||
val maxHeight = remember { derivedStateOf { listState.layoutInfo.viewportSize.height } }
|
||||
val maxWidth = remember { derivedStateOf { listState.layoutInfo.viewportSize.width } }
|
||||
SideEffect {
|
||||
println("LALAL REDRAW ${maxHeight.value}")
|
||||
}
|
||||
|
||||
LaunchedEffect(listState) {
|
||||
// When both snapshotFlows located in one LaunchedEffect second block will never be called because coroutine is paused on first block
|
||||
// so separate them into two LaunchedEffects
|
||||
snapshotFlow { listState.layoutInfo.visibleItemsInfo.lastIndex }
|
||||
.distinctUntilChanged()
|
||||
.collect {
|
||||
lastIndexOfVisibleItems = it
|
||||
}
|
||||
}
|
||||
val bottomUnreadCount by remember {
|
||||
val bottomUnreadCount = remember {
|
||||
derivedStateOf {
|
||||
if (unreadCount.value == 0) return@derivedStateOf 0
|
||||
val items = chatItems.value
|
||||
val from = items.lastIndex - firstVisibleIndex - lastIndexOfVisibleItems
|
||||
val from = items.lastIndex - listState.firstVisibleItemIndex - listState.layoutInfo.visibleItemsInfo.lastIndex
|
||||
if (items.size <= from || from < 0) return@derivedStateOf 0
|
||||
|
||||
items.subList(from, items.size).count { it.isRcvNew }
|
||||
}
|
||||
}
|
||||
val firstVisibleOffset = (-with(LocalDensity.current) { maxHeight.roundToPx() } * 0.8).toInt()
|
||||
|
||||
LaunchedEffect(bottomUnreadCount, firstItemIsVisible) {
|
||||
val showButtonWithCounter = bottomUnreadCount > 0 && !firstItemIsVisible && searchValue.value.isEmpty()
|
||||
val showButtonWithArrow = !showButtonWithCounter && !firstItemIsVisible
|
||||
setFloatingButton(
|
||||
bottomEndFloatingButton(
|
||||
bottomUnreadCount,
|
||||
showButtonWithCounter,
|
||||
showButtonWithArrow,
|
||||
onClickArrowDown = {
|
||||
scope.launch { listState.animateScrollToItem(0) }
|
||||
},
|
||||
onClickCounter = {
|
||||
scope.launch { listState.animateScrollToItem(kotlin.math.max(0, bottomUnreadCount - 1), firstVisibleOffset) }
|
||||
}
|
||||
))
|
||||
}
|
||||
val showBottomButtonWithCounter = remember { derivedStateOf { bottomUnreadCount.value > 0 && listState.firstVisibleItemIndex != 0 && searchValue.value.isEmpty() } }
|
||||
val showBottomButtonWithArrow = remember { derivedStateOf { !showBottomButtonWithCounter.value && listState.firstVisibleItemIndex != 0 } }
|
||||
BottomEndFloatingButton(
|
||||
bottomUnreadCount,
|
||||
showBottomButtonWithCounter,
|
||||
showBottomButtonWithArrow,
|
||||
onClickArrowDown = {
|
||||
scope.launch { listState.animateScrollToItem(0) }
|
||||
},
|
||||
onClickCounter = {
|
||||
val firstVisibleOffset = (-maxHeight.value * 0.8).toInt()
|
||||
scope.launch { listState.animateScrollToItem(kotlin.math.max(0, bottomUnreadCount.value - 1), firstVisibleOffset) }
|
||||
}
|
||||
)
|
||||
// Don't show top FAB if is in search
|
||||
if (searchValue.value.isNotEmpty()) return
|
||||
val fabSize = 56.dp
|
||||
val topUnreadCount by remember {
|
||||
derivedStateOf { unreadCount.value - bottomUnreadCount }
|
||||
}
|
||||
val showButtonWithCounter = topUnreadCount > 0
|
||||
val height = with(LocalDensity.current) { maxHeight.toPx() }
|
||||
val topUnreadCount = remember { derivedStateOf { unreadCount.value - bottomUnreadCount.value } }
|
||||
val showDropDown = remember { mutableStateOf(false) }
|
||||
|
||||
TopEndFloatingButton(
|
||||
Modifier.padding(end = DEFAULT_PADDING, top = 24.dp).align(Alignment.TopEnd),
|
||||
topUnreadCount,
|
||||
showButtonWithCounter,
|
||||
onClick = { scope.launch { listState.animateScrollBy(height) } },
|
||||
onClick = { scope.launch { listState.animateScrollBy(maxHeight.value.toFloat()) } },
|
||||
onLongClick = { showDropDown.value = true }
|
||||
)
|
||||
|
||||
Box {
|
||||
DefaultDropdownMenu(showDropDown, offset = DpOffset(this@FloatingButtons.maxWidth - DEFAULT_PADDING, 24.dp + fabSize)) {
|
||||
Box(Modifier.offset { IntOffset(maxWidth.value - DEFAULT_PADDING.roundToPx(), (24.dp + fabSize).roundToPx()) }) {
|
||||
DefaultDropdownMenu(showDropDown) {
|
||||
ItemAction(
|
||||
generalGetString(MR.strings.mark_read),
|
||||
painterResource(MR.images.ic_check),
|
||||
@@ -1410,7 +1383,7 @@ fun BoxWithConstraintsScope.FloatingButtons(
|
||||
val minUnreadItemId = chatModel.chats.value.firstOrNull { it.remoteHostId == remoteHostId && it.id == chatInfo.id }?.chatStats?.minUnreadItemId ?: return@ItemAction
|
||||
markRead(
|
||||
CC.ItemRange(minUnreadItemId, chatItems.value[chatItems.value.size - listState.layoutInfo.visibleItemsInfo.lastIndex - 1].id - 1),
|
||||
bottomUnreadCount
|
||||
bottomUnreadCount.value
|
||||
)
|
||||
showDropDown.value = false
|
||||
})
|
||||
@@ -1472,12 +1445,11 @@ fun MemberImage(member: GroupMember) {
|
||||
@Composable
|
||||
private fun TopEndFloatingButton(
|
||||
modifier: Modifier = Modifier,
|
||||
unreadCount: Int,
|
||||
showButtonWithCounter: Boolean,
|
||||
unreadCount: State<Int>,
|
||||
onClick: () -> Unit,
|
||||
onLongClick: () -> Unit
|
||||
) = when {
|
||||
showButtonWithCounter -> {
|
||||
unreadCount.value > 0 -> {
|
||||
val interactionSource = interactionSourceWithDetection(onClick, onLongClick)
|
||||
FloatingActionButton(
|
||||
{}, // no action here
|
||||
@@ -1487,7 +1459,7 @@ private fun TopEndFloatingButton(
|
||||
interactionSource = interactionSource,
|
||||
) {
|
||||
Text(
|
||||
unreadCountStr(unreadCount),
|
||||
unreadCountStr(unreadCount.value),
|
||||
color = MaterialTheme.colors.primary,
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
@@ -1694,48 +1666,43 @@ fun openGroupLink(groupInfo: GroupInfo, rhId: Long?, view: Any? = null, close: (
|
||||
}
|
||||
}
|
||||
|
||||
private fun bottomEndFloatingButton(
|
||||
unreadCount: Int,
|
||||
showButtonWithCounter: Boolean,
|
||||
showButtonWithArrow: Boolean,
|
||||
@Composable
|
||||
private fun BoxScope.BottomEndFloatingButton(
|
||||
unreadCount: State<Int>,
|
||||
showButtonWithCounter: State<Boolean>,
|
||||
showButtonWithArrow: State<Boolean>,
|
||||
onClickArrowDown: () -> Unit,
|
||||
onClickCounter: () -> Unit
|
||||
): @Composable () -> Unit = when {
|
||||
showButtonWithCounter -> {
|
||||
{
|
||||
FloatingActionButton(
|
||||
onClick = onClickCounter,
|
||||
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
||||
modifier = Modifier.size(48.dp),
|
||||
backgroundColor = MaterialTheme.colors.secondaryVariant,
|
||||
) {
|
||||
Text(
|
||||
unreadCountStr(unreadCount),
|
||||
color = MaterialTheme.colors.primary,
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
}
|
||||
) = when {
|
||||
showButtonWithCounter.value -> {
|
||||
FloatingActionButton(
|
||||
onClick = onClickCounter,
|
||||
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
||||
modifier = Modifier.padding(end = DEFAULT_PADDING, bottom = DEFAULT_PADDING).align(Alignment.BottomEnd).size(48.dp),
|
||||
backgroundColor = MaterialTheme.colors.secondaryVariant,
|
||||
) {
|
||||
Text(
|
||||
unreadCountStr(unreadCount.value),
|
||||
color = MaterialTheme.colors.primary,
|
||||
fontSize = 14.sp,
|
||||
)
|
||||
}
|
||||
}
|
||||
showButtonWithArrow -> {
|
||||
{
|
||||
FloatingActionButton(
|
||||
onClick = onClickArrowDown,
|
||||
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
||||
modifier = Modifier.size(48.dp),
|
||||
backgroundColor = MaterialTheme.colors.secondaryVariant,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(MR.images.ic_keyboard_arrow_down),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
showButtonWithArrow.value -> {
|
||||
FloatingActionButton(
|
||||
onClick = onClickArrowDown,
|
||||
elevation = FloatingActionButtonDefaults.elevation(0.dp, 0.dp, 0.dp, 0.dp),
|
||||
modifier = Modifier.padding(end = DEFAULT_PADDING, bottom = DEFAULT_PADDING).align(Alignment.BottomEnd).size(48.dp),
|
||||
backgroundColor = MaterialTheme.colors.secondaryVariant,
|
||||
) {
|
||||
Icon(
|
||||
painter = painterResource(MR.images.ic_keyboard_arrow_down),
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
else -> {
|
||||
{}
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
+1
@@ -1045,6 +1045,7 @@ fun ComposeView(
|
||||
)
|
||||
}
|
||||
Spacer(Modifier.imePadding().navigationBarsPadding())
|
||||
// WindowInsets.ime.asPaddingValues().calculateBottomPadding()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -43,7 +43,7 @@ fun SelectedItemsTopToolbar(selectedChatItems: MutableState<Set<Long>?>) {
|
||||
onTop = true,
|
||||
onSearchValueChanged = {},
|
||||
)
|
||||
Divider(Modifier.padding(top = AppBarHeight * fontSizeSqrtMultiplier))
|
||||
Divider()
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
+1
@@ -397,6 +397,7 @@ fun ModalData.GroupChatInfoLayout(
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.systemBars))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
-3
@@ -82,10 +82,9 @@ fun GroupProfileLayout(
|
||||
}, close)
|
||||
}
|
||||
}
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
modifier = Modifier.navigationBarsWithImePadding(),
|
||||
modifier = Modifier.imePadding(),
|
||||
sheetContent = {
|
||||
GetImageBottomSheet(
|
||||
chosenImage,
|
||||
@@ -177,7 +176,6 @@ fun GroupProfileLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun canUpdateProfile(displayName: String, groupProfile: GroupProfile): Boolean =
|
||||
|
||||
+14
-1
@@ -591,6 +591,11 @@ enum class ScrollDirection {
|
||||
Up, Down, Idle
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun StatusBarBackground() {
|
||||
Box(Modifier.background(MaterialTheme.colors.background).windowInsetsTopHeight(WindowInsets.statusBars))
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChatList(chatModel: ChatModel, searchText: MutableState<TextFieldValue>) {
|
||||
val listState = rememberLazyListState(lazyListState.first, lazyListState.second)
|
||||
@@ -630,8 +635,12 @@ private fun ChatList(chatModel: ChatModel, searchText: MutableState<TextFieldVal
|
||||
val searchShowingSimplexLink = remember { mutableStateOf(false) }
|
||||
val searchChatFilteredBySimplexLink = remember { mutableStateOf<String?>(null) }
|
||||
val chats = filteredChats(showUnreadAndFavorites, searchShowingSimplexLink, searchChatFilteredBySimplexLink, searchText.value.text, allChats.value.toList())
|
||||
Column {
|
||||
if (oneHandUI.value) {
|
||||
StatusBarBackground()
|
||||
}
|
||||
LazyColumnWithScrollBar(
|
||||
Modifier.fillMaxSize(),
|
||||
Modifier.fillMaxSize().then(if (!oneHandUI.value) Modifier.imePadding() else Modifier),
|
||||
listState,
|
||||
reverseLayout = oneHandUI.value
|
||||
) {
|
||||
@@ -679,6 +688,10 @@ private fun ChatList(chatModel: ChatModel, searchText: MutableState<TextFieldVal
|
||||
ToggleChatListCard()
|
||||
}
|
||||
}
|
||||
if (!oneHandUI.value) {
|
||||
item { Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
if (chats.isEmpty() && chatModel.chats.value.isNotEmpty()) {
|
||||
Box(Modifier.fillMaxSize(), contentAlignment = Alignment.Center) {
|
||||
|
||||
+10
-2
@@ -35,7 +35,7 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) {
|
||||
},
|
||||
bottomBar = {
|
||||
if (oneHandUI.value) {
|
||||
Column {
|
||||
Column(Modifier.imePadding()) {
|
||||
Divider()
|
||||
ShareListToolbar(chatModel, stopped) { searchInList = it.trim() }
|
||||
}
|
||||
@@ -73,6 +73,9 @@ fun ShareListView(chatModel: ChatModel, stopped: Boolean) {
|
||||
Column(
|
||||
modifier = Modifier.fillMaxSize()
|
||||
) {
|
||||
if (oneHandUI.value) {
|
||||
StatusBarBackground()
|
||||
}
|
||||
if (chatModel.chats.value.isNotEmpty()) {
|
||||
ShareList(
|
||||
chatModel,
|
||||
@@ -213,7 +216,7 @@ private fun ShareList(
|
||||
}
|
||||
}
|
||||
LazyColumnWithScrollBar(
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
modifier = Modifier.fillMaxSize().then(if (!oneHandUI.value) Modifier.imePadding() else Modifier),
|
||||
reverseLayout = oneHandUI.value
|
||||
) {
|
||||
items(chats) { chat ->
|
||||
@@ -225,5 +228,10 @@ private fun ShareList(
|
||||
hasSimplexLink = hasSimplexLink,
|
||||
)
|
||||
}
|
||||
if (!oneHandUI.value) {
|
||||
item {
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.navigationBars))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -143,6 +143,7 @@ fun UserPicker(
|
||||
.fillMaxWidth()
|
||||
.then(if (newChat.isVisible()) Modifier.shadow(8.dp, clip = true) else Modifier)
|
||||
.background(if (appPlatform.isAndroid) MaterialTheme.colors.background.mixWith(MaterialTheme.colors.onBackground, alpha = 1 - userPickerAlpha()) else MaterialTheme.colors.surface)
|
||||
.navigationBarsPadding()
|
||||
.padding(bottom = USER_PICKER_SECTION_SPACING - DEFAULT_MIN_SECTION_ITEM_PADDING_VERTICAL),
|
||||
pickerState = userPickerState
|
||||
) {
|
||||
|
||||
+2
@@ -382,6 +382,7 @@ private fun drawToBitmap(image: ImageBitmap, imageScale: Float, tint: Color, siz
|
||||
}
|
||||
|
||||
fun CacheDrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperType, background: Color, tint: Color): DrawResult {
|
||||
println("LALAL SIZE $size")
|
||||
val imageScale = if (imageType is WallpaperType.Preset) {
|
||||
(imageType.scale ?: 1f) * imageType.predefinedImageScale
|
||||
} else if (imageType is WallpaperType.Image && imageType.scaleType == WallpaperScaleType.REPEAT) {
|
||||
@@ -396,6 +397,7 @@ fun CacheDrawScope.chatViewBackground(image: ImageBitmap, imageType: WallpaperTy
|
||||
}
|
||||
|
||||
return onDrawBehind {
|
||||
println("LALAL SIZE2 $size")
|
||||
val quality = if (appPlatform.isAndroid) FilterQuality.High else FilterQuality.Low
|
||||
drawRect(background)
|
||||
when (imageType) {
|
||||
|
||||
+3
-1
@@ -12,6 +12,7 @@ import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.ui.draw.*
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.platform.LocalLayoutDirection
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.platform.appPlatform
|
||||
@@ -99,7 +100,8 @@ fun CloseSheetBar(close: (() -> Unit)?, showClose: Boolean = true, tintColor: Co
|
||||
} else {
|
||||
Spacer(Modifier.weight(1f))
|
||||
}
|
||||
Row {
|
||||
val direction = LocalLayoutDirection.current
|
||||
Row(Modifier.padding(end = WindowInsets.navigationBars.asPaddingValues().calculateEndPadding(direction))) {
|
||||
endButtons()
|
||||
}
|
||||
}
|
||||
|
||||
-1
@@ -57,7 +57,6 @@ fun TextEditor(
|
||||
) {
|
||||
val textFieldModifier = modifier
|
||||
.fillMaxWidth()
|
||||
.navigationBarsWithImePadding()
|
||||
.onFocusChanged { focused = it.isFocused }
|
||||
.padding(10.dp)
|
||||
|
||||
|
||||
+1
-3
@@ -84,10 +84,9 @@ fun AddGroupLayout(
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val incognito = remember { mutableStateOf(incognitoPref.get()) }
|
||||
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
modifier = Modifier.navigationBarsWithImePadding(),
|
||||
modifier = Modifier.imePadding(),
|
||||
sheetContent = {
|
||||
GetImageBottomSheet(
|
||||
chosenImage,
|
||||
@@ -170,7 +169,6 @@ fun AddGroupLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun canCreateProfile(displayName: String): Boolean = displayName.trim().isNotEmpty() && isValidDisplayName(displayName.trim())
|
||||
|
||||
+17
-18
@@ -1,9 +1,7 @@
|
||||
package chat.simplex.common.views.newchat
|
||||
|
||||
import SectionDivider
|
||||
import SectionDividerSpaced
|
||||
import SectionItemView
|
||||
import SectionSpacer
|
||||
import SectionView
|
||||
import TextIconSpaced
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
@@ -33,24 +31,22 @@ import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chatlist.ScrollDirection
|
||||
import chat.simplex.common.views.chatlist.StatusBarBackground
|
||||
import chat.simplex.common.views.contacts.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import java.net.URI
|
||||
|
||||
@Composable
|
||||
fun ModalData.NewChatSheet(rh: RemoteHostInfo?, close: () -> Unit) {
|
||||
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
||||
val keyboardState by getKeyboardState()
|
||||
val showToolbarInOneHandUI = remember { derivedStateOf { keyboardState == KeyboardState.Closed && oneHandUI.value } }
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
if (showToolbarInOneHandUI.value) {
|
||||
Column {
|
||||
if (oneHandUI.value) {
|
||||
Column(Modifier.imePadding()) {
|
||||
Divider()
|
||||
CloseSheetBar(
|
||||
close = close,
|
||||
@@ -187,8 +183,12 @@ private fun ModalData.NewChatSheetLayout(
|
||||
derivedStateOf { filterContactTypes(chatModel.chats.value, deletedContactTypes) }
|
||||
}
|
||||
|
||||
Column {
|
||||
if (oneHandUI.value) {
|
||||
StatusBarBackground()
|
||||
}
|
||||
LazyColumnWithScrollBar(
|
||||
Modifier.fillMaxSize(),
|
||||
Modifier.fillMaxSize().then(if (!oneHandUI.value) Modifier.imePadding() else Modifier),
|
||||
listState,
|
||||
reverseLayout = oneHandUI.value
|
||||
) {
|
||||
@@ -242,8 +242,6 @@ private fun ModalData.NewChatSheetLayout(
|
||||
)
|
||||
if (!oneHandUI.value) {
|
||||
Divider()
|
||||
} else {
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,6 +351,7 @@ private fun ModalData.NewChatSheetLayout(
|
||||
ContactListNavLinkView(chat, nextChatSelected, showDeletedChatIcon = true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -556,13 +555,10 @@ private fun contactTypesSearchTargets(baseContactTypes: List<ContactType>, searc
|
||||
@Composable
|
||||
private fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats: () -> Unit, close: () -> Unit) {
|
||||
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
||||
val keyboardState by getKeyboardState()
|
||||
val showToolbarInOneHandUI = remember { derivedStateOf { keyboardState == KeyboardState.Closed && oneHandUI.value } }
|
||||
|
||||
Scaffold(
|
||||
bottomBar = {
|
||||
if (showToolbarInOneHandUI.value) {
|
||||
Column {
|
||||
if (oneHandUI.value) {
|
||||
Column(Modifier.imePadding()) {
|
||||
Divider()
|
||||
CloseSheetBar(
|
||||
close = closeDeletedChats,
|
||||
@@ -592,8 +588,12 @@ private fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats
|
||||
contactChats = allChats
|
||||
)
|
||||
|
||||
Column {
|
||||
if (oneHandUI.value) {
|
||||
StatusBarBackground()
|
||||
}
|
||||
LazyColumnWithScrollBar(
|
||||
Modifier.fillMaxSize(),
|
||||
Modifier.fillMaxSize().then(if (!oneHandUI.value) Modifier.imePadding() else Modifier),
|
||||
contentPadding = contentPadding,
|
||||
reverseLayout = oneHandUI.value,
|
||||
) {
|
||||
@@ -612,8 +612,6 @@ private fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats
|
||||
item {
|
||||
if (!oneHandUI.value) {
|
||||
Divider()
|
||||
} else {
|
||||
Spacer(Modifier.windowInsetsBottomHeight(WindowInsets.ime))
|
||||
}
|
||||
ContactsSearchBar(
|
||||
listState = listState,
|
||||
@@ -647,6 +645,7 @@ private fun ModalData.DeletedContactsView(rh: RemoteHostInfo?, closeDeletedChats
|
||||
ContactListNavLinkView(chat, nextChatSelected, showDeletedChatIcon = false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
+3
@@ -434,6 +434,9 @@ fun ActiveProfilePicker(
|
||||
ProfilePickerUserOption(p)
|
||||
}
|
||||
}
|
||||
item {
|
||||
Spacer(Modifier.imePadding())
|
||||
}
|
||||
}
|
||||
}
|
||||
if (progressByTimeout) {
|
||||
|
||||
+1
-1
@@ -123,7 +123,7 @@ fun WhatsNewView(viaSettings: Boolean = false, close: () -> Unit) {
|
||||
.padding(horizontal = DEFAULT_PADDING),
|
||||
verticalArrangement = Arrangement.spacedBy(DEFAULT_PADDING.times(0.75f))
|
||||
) {
|
||||
AppBarTitle(String.format(generalGetString(MR.strings.new_in_version), v.version), bottomPadding = DEFAULT_PADDING)
|
||||
AppBarTitle(String.format(generalGetString(MR.strings.new_in_version), v.version), withPadding = false, bottomPadding = DEFAULT_PADDING)
|
||||
|
||||
v.features.forEach { feature ->
|
||||
if (feature.show) {
|
||||
|
||||
+1
@@ -912,6 +912,7 @@ object AppearanceScope {
|
||||
ColumnWithScrollBar(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.imePadding()
|
||||
) {
|
||||
AppBarTitle(name.text)
|
||||
|
||||
|
||||
-6
@@ -39,7 +39,6 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, close: (
|
||||
val user = chatModel.currentUser.value
|
||||
val stopped = chatModel.chatRunning.value == false
|
||||
SettingsLayout(
|
||||
profile = user?.profile,
|
||||
stopped,
|
||||
chatModel.chatDbEncrypted.value == true,
|
||||
remember { chatModel.controller.appPrefs.storeDBPassphrase.state }.value,
|
||||
@@ -80,7 +79,6 @@ val simplexTeamUri =
|
||||
|
||||
@Composable
|
||||
fun SettingsLayout(
|
||||
profile: LocalProfile?,
|
||||
stopped: Boolean,
|
||||
encrypted: Boolean,
|
||||
passphraseSaved: Boolean,
|
||||
@@ -94,17 +92,14 @@ fun SettingsLayout(
|
||||
showVersion: () -> Unit,
|
||||
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit,
|
||||
) {
|
||||
val scope = rememberCoroutineScope()
|
||||
val view = LocalMultiplatformView()
|
||||
LaunchedEffect(Unit) {
|
||||
hideKeyboard(view)
|
||||
}
|
||||
val theme = CurrentColors.collectAsState()
|
||||
val uriHandler = LocalUriHandler.current
|
||||
ColumnWithScrollBar(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.themedBackground(theme.value.base)
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.your_settings))
|
||||
|
||||
@@ -488,7 +483,6 @@ private fun runAuth(title: String, desc: String, onFinish: (success: Boolean) ->
|
||||
fun PreviewSettingsLayout() {
|
||||
SimpleXTheme {
|
||||
SettingsLayout(
|
||||
profile = LocalProfile.sampleData,
|
||||
stopped = false,
|
||||
encrypted = false,
|
||||
passphraseSaved = false,
|
||||
|
||||
+1
-5
@@ -71,10 +71,8 @@ fun UserProfileLayout(
|
||||
val keyboardState by getKeyboardState()
|
||||
var savedKeyboardState by remember { mutableStateOf(keyboardState) }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
modifier = Modifier.navigationBarsWithImePadding(),
|
||||
sheetContent = {
|
||||
GetImageBottomSheet(
|
||||
chosenImage,
|
||||
@@ -90,7 +88,6 @@ fun UserProfileLayout(
|
||||
displayName.value == profile.displayName &&
|
||||
fullName.value == profile.fullName &&
|
||||
profile.image == profileImage.value
|
||||
|
||||
val closeWithAlert = {
|
||||
if (dataUnchanged || !canSaveProfile(displayName.value, profile)) {
|
||||
close()
|
||||
@@ -103,7 +100,7 @@ fun UserProfileLayout(
|
||||
Modifier
|
||||
.padding(horizontal = DEFAULT_PADDING),
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.your_current_profile))
|
||||
AppBarTitle(stringResource(MR.strings.your_current_profile), withPadding = false)
|
||||
ReadableText(generalGetString(MR.strings.your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it), TextAlign.Center)
|
||||
Column(
|
||||
Modifier
|
||||
@@ -170,7 +167,6 @@ fun UserProfileLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
|
||||
-11
@@ -8,17 +8,6 @@ import androidx.compose.ui.input.pointer.*
|
||||
import java.io.File
|
||||
import java.net.URI
|
||||
|
||||
actual fun Modifier.navigationBarsWithImePadding(): Modifier = this
|
||||
|
||||
@Composable
|
||||
actual fun ProvideWindowInsets(
|
||||
consumeWindowInsets: Boolean,
|
||||
windowInsetsAnimationsEnabled: Boolean,
|
||||
content: @Composable () -> Unit
|
||||
) {
|
||||
content()
|
||||
}
|
||||
|
||||
@Composable
|
||||
actual fun Modifier.desktopOnExternalDrag(
|
||||
enabled: Boolean,
|
||||
|
||||
Reference in New Issue
Block a user