Fixed UI for ConnectViaLink, InviteSomeone, NowYouCan

This commit is contained in:
hayk888997
2026-03-26 02:23:56 +04:00
parent 6bf7f6ba61
commit 18f66f6894
10 changed files with 220 additions and 246 deletions

View File

@@ -31,7 +31,7 @@ kotlin {
}
val commonMain by getting {
resources.srcDir(rootProject.rootDir.resolve("../../../simplex-chat-art/multiplatform"))
resources.srcDir(rootProject.rootDir.resolve("../../../simplex-chat-art/multiplatform/light"))
dependencies {
api(compose.runtime)
api(compose.foundation)

View File

@@ -14,7 +14,8 @@ import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.draw.clipToBounds
import androidx.compose.ui.draw.clip
import androidx.compose.ui.graphics.Shape
import androidx.compose.ui.platform.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.viewinterop.AndroidView
@@ -44,6 +45,7 @@ import java.util.concurrent.*
actual fun QRCodeScanner(
showQRCodeScanner: MutableState<Boolean>,
padding: PaddingValues,
clipShape: Shape,
onBarcode: suspend (String) -> Boolean
) {
val context = LocalContext.current
@@ -66,7 +68,7 @@ actual fun QRCodeScanner(
val cameraPermissionState = rememberPermissionState(permission = Manifest.permission.CAMERA)
val modifier = Modifier
.padding(padding)
.clipToBounds()
.clip(clipShape)
.widthIn(max = 400.dp)
.aspectRatio(1f)
val showScanner = remember { showQRCodeScanner }

View File

@@ -609,6 +609,7 @@ fun themedBackgroundBrush(): Brush = Brush.linearGradient(
)
val DEFAULT_PADDING = 20.dp
val DEFAULT_BIG_PADDING = 48.dp
val DEFAULT_ONBOARDING_HORIZONTAL_PADDING = 25.dp
val DEFAULT_SPACE_AFTER_ICON = 4.dp
val DEFAULT_PADDING_HALF = DEFAULT_PADDING / 2

View File

@@ -147,29 +147,46 @@ 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, listState)
Column(Modifier.align(Alignment.BottomCenter)) {
ChatListToolbar(
userPickerState,
listState,
stopped,
setPerformLA,
)
}
} else {
ChatListWithLoadingScreen(searchText, listState)
Column {
ChatListToolbar(
userPickerState,
listState,
stopped,
setPerformLA,
)
}
if (searchText.value.text.isEmpty() && !chatModel.desktopNoUserNoRemote && chatModel.chatRunning.value == true) {
NewChatSheetFloatingButton(oneHandUI, stopped)
val connectSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
val connectSheetScope = rememberCoroutineScope()
val onConnectClick: () -> Unit = { connectSheetScope.launch { connectSheetState.show() } }
ModalBottomSheetLayout(
scrimColor = Color.Black.copy(alpha = 0.12F),
sheetState = connectSheetState,
sheetShape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp),
sheetBackgroundColor = MaterialTheme.colors.secondaryVariant,
sheetContent = {
ModalData().ConnectViewLinkOrQrModal(
rhId = chatModel.currentRemoteHost.value?.remoteHostId,
close = { connectSheetScope.launch { connectSheetState.hide() } }
)
}
) {
Box(Modifier.fillMaxSize()) {
if (oneHandUI.value) {
ChatListWithLoadingScreen(searchText, listState, onConnectClick)
Column(Modifier.align(Alignment.BottomCenter)) {
ChatListToolbar(
userPickerState,
listState,
stopped,
setPerformLA,
)
}
} else {
ChatListWithLoadingScreen(searchText, listState, onConnectClick)
Column {
ChatListToolbar(
userPickerState,
listState,
stopped,
setPerformLA,
)
}
if (searchText.value.text.isEmpty() && !chatModel.desktopNoUserNoRemote && chatModel.chatRunning.value == true) {
NewChatSheetFloatingButton(oneHandUI, stopped)
}
}
}
}
@@ -288,11 +305,11 @@ private fun AddressCreationCard() {
}
@Composable
private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFieldValue>, listState: LazyListState) {
private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFieldValue>, listState: LazyListState, onConnectClick: () -> Unit) {
if (!chatModel.desktopNoUserNoRemote) {
ChatList(searchText = searchText, listState)
EmptyChatListView(onConnectClick)
return
}
EmptyChatListView()
if (chatModel.chats.value.isEmpty() && !chatModel.switchingUsersAndHosts.value && !chatModel.desktopNoUserNoRemote) {
if (chatModel.chatRunning.value == null) {
@@ -302,7 +319,7 @@ private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFiel
color = MaterialTheme.colors.secondary
)
} else {
EmptyChatListView()
EmptyChatListView(onConnectClick)
}
}
}
@@ -427,7 +444,7 @@ private fun ChatListToolbar(userPickerState: MutableStateFlow<AnimatedViewState>
.size(33.dp * fontSizeSqrtMultiplier)
) {
Icon(
painterResource(MR.images.ic_edit_filled),
painterResource(MR.images.ic_add),
stringResource(MR.strings.add_contact_or_create_group),
Modifier.size(sp16),
tint = Color.White

View File

@@ -2,12 +2,16 @@ package chat.simplex.common.views.invitation_redesign
import SectionBottomSpacer
import SectionItemView
import SectionSpacer
import SectionView
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.runtime.saveable.rememberSaveable
@@ -39,55 +43,56 @@ fun ModalData.ConnectViewLinkOrQrModal(rhId: Long?, close: () -> Unit) {
connectProgressManager.cancelConnectProgress()
}
}
Column(
Modifier
.fillMaxWidth()
.wrapContentHeight()
.verticalScroll(rememberScrollState()),
horizontalAlignment = Alignment.CenterHorizontally
) {
Spacer(Modifier.height(24.dp))
ModalView(close) {
ColumnWithScrollBar(
Modifier.fillMaxWidth().background(MaterialTheme.colors.background),
horizontalAlignment = Alignment.CenterHorizontally
Image(
painterResource(MR.images.ic_invitation_connect_link),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier.size(160.dp)
)
Spacer(Modifier.height(24.dp))
SectionView(
title = stringResource(MR.strings.paste_the_link_you_received).uppercase(),
headerBottomPadding = 0.dp
) {
Spacer(Modifier.height(24.dp))
ConnectPasteLinkView(rhId, pastedLink, showQRCodeScanner, close)
}
Image(
painterResource(MR.images.ic_invitation_connect_link),
contentDescription = null,
contentScale = ContentScale.Fit,
modifier = Modifier
.size(200.dp)
.padding(horizontal = DEFAULT_PADDING)
)
if (appPlatform.isAndroid) {
Spacer(Modifier.height(10.dp))
Spacer(Modifier.height(24.dp))
SectionView(stringResource(MR.strings.paste_the_link_you_received).uppercase(), headerBottomPadding = 5.dp) {
ConnectPasteLinkView(rhId, pastedLink, showQRCodeScanner, close)
}
if (appPlatform.isAndroid) {
Spacer(Modifier.height(10.dp))
SectionView(stringResource(MR.strings.or_scan_qr_code).uppercase(), headerBottomPadding = 5.dp) {
Box(
Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING)
.clip(RoundedCornerShape(24.dp))
) {
QRCodeScanner(showQRCodeScanner) { text ->
val linkVerified = strIsSimplexLink(text)
if (!linkVerified) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.invalid_qr_code),
text = generalGetString(MR.strings.code_you_scanned_is_not_simplex_link_qr_code)
)
}
connectFromScanner(rhId, text, close)
}
SectionView(
title = stringResource(MR.strings.or_scan_qr_code).uppercase(),
headerBottomPadding = 0.dp
) {
QRCodeScanner(
showQRCodeScanner = showQRCodeScanner,
padding = PaddingValues(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF),
clipShape = RoundedCornerShape(12.dp)
) { text ->
val linkVerified = strIsSimplexLink(text)
if (!linkVerified) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.invalid_qr_code),
text = generalGetString(MR.strings.code_you_scanned_is_not_simplex_link_qr_code)
)
}
connectFromScanner(rhId, text, close)
}
}
SectionBottomSpacer()
}
SectionBottomSpacer()
SectionSpacer()
}
}
@@ -113,27 +118,19 @@ private fun ConnectPasteLinkView(rhId: Long?, pastedLink: MutableState<String>,
}) {
Box(
Modifier
.weight(1f)
.background(MaterialTheme.colors.background)
.padding(8.dp),
.fillMaxWidth()
.clip(RoundedCornerShape(12.dp))
.background(MaterialTheme.colors.background),
contentAlignment = Alignment.Center
) {
Box(
Modifier
.fillMaxWidth()
.clip(RoundedCornerShape(16.dp))
.background(MaterialTheme.colors.surface),
contentAlignment = Alignment.Center
) {
Text(
stringResource(MR.strings.tap_to_paste_link),
modifier = Modifier.padding(vertical = 16.dp),
color = MaterialTheme.colors.secondary,
fontSize = 18.sp
)
if (connectProgressManager.showConnectProgress != null) {
CIFileViewScope.progressIndicator(sizeMultiplier = 0.6f)
}
Text(
stringResource(MR.strings.tap_to_paste_link),
modifier = Modifier.padding(vertical = 12.dp),
color = Color.LightGray,
fontSize = 16.sp
)
if (connectProgressManager.showConnectProgress != null) {
CIFileViewScope.progressIndicator(sizeMultiplier = 0.6f)
}
}
}

View File

@@ -0,0 +1,72 @@
package chat.simplex.common.views.invitation_redesign
import androidx.compose.foundation.Image
import androidx.compose.foundation.layout.Column
import androidx.compose.foundation.layout.Row
import androidx.compose.foundation.layout.Spacer
import androidx.compose.foundation.layout.fillMaxWidth
import androidx.compose.foundation.layout.height
import androidx.compose.foundation.layout.padding
import androidx.compose.foundation.layout.size
import androidx.compose.foundation.layout.width
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.Icon
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.material.Text
import androidx.compose.runtime.Composable
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import chat.simplex.common.ui.theme.DEFAULT_PADDING_HALF
import chat.simplex.common.ui.theme.appColors
import dev.icerock.moko.resources.ImageResource
import dev.icerock.moko.resources.compose.painterResource
@Composable
fun InvitationCardView(
modifier: Modifier = Modifier,
mainImageResource: ImageResource,
iconResource: ImageResource,
title: String,
description: String? = null
) {
Surface(
shape = RoundedCornerShape(18.dp),
color = MaterialTheme.appColors.sentMessage,
modifier = modifier
) {
Column(
Modifier.fillMaxWidth().padding(bottom = DEFAULT_PADDING_HALF),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painterResource(mainImageResource),
contentDescription = null,
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painterResource(iconResource),
contentDescription = null,
modifier = Modifier.size(24.dp),
tint = MaterialTheme.colors.primary
)
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
Text(
text = title,
style = MaterialTheme.typography.h6.copy(fontWeight = FontWeight.Medium),
)
}
description?.let {
Text(
text = description,
style = MaterialTheme.typography.body2.copy(fontWeight = FontWeight.Medium),
color = MaterialTheme.colors.secondary
)
}
}
}
}

View File

@@ -40,57 +40,29 @@ fun InviteSomeoneWithPicturesView(close: () -> Unit) {
@Composable
fun InviteSomeoneWithPicturesContent() {
Surface(
shape = RoundedCornerShape(18.dp),
color = MaterialTheme.appColors.sentMessage,
InvitationCardView(
mainImageResource = MR.images.ic_invitation_card_one_time_link,
iconResource = MR.images.ic_repeat_one,
title = stringResource(MR.strings.create_private_1_time_link),
description = stringResource(MR.strings.contact_can_use_link_or_scan_qr),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING)
.clickable {
ModalManager.start.showModalCloseable { close ->
OneTimeLinkView(rhId = chatModel.currentRemoteHost.value?.remoteHostId, close = close)
}
ModalManager.start.showModalCloseable { close ->
OneTimeLinkView(rhId = chatModel.currentRemoteHost.value?.remoteHostId, close = close)
}
) {
Image(
painterResource(MR.images.ic_invitation_card_one_time_link),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
}
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
Row(
Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painterResource(MR.images.ic_repeat_one),
contentDescription = null,
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colors.secondary
)
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
Column {
Text(
stringResource(MR.strings.create_private_1_time_link),
style = MaterialTheme.typography.h3.copy(fontWeight = FontWeight.Bold),
)
Text(
stringResource(MR.strings.contact_can_use_link_or_scan_qr),
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.secondary
)
}
}
)
Spacer(Modifier.height(DEFAULT_PADDING * 1.5f))
Spacer(Modifier.height(DEFAULT_PADDING))
Surface(
shape = RoundedCornerShape(18.dp),
color = MaterialTheme.appColors.sentMessage,
InvitationCardView(
mainImageResource = MR.images.ic_invitation_card_public_address,
iconResource = MR.images.ic_qr_code,
title = stringResource(MR.strings.create_public_simplex_address),
description = stringResource(MR.strings.public_link_for_social_media_email_or_website),
modifier = Modifier
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING)
@@ -100,40 +72,7 @@ fun InviteSomeoneWithPicturesContent() {
OneTimeLinkView(rhId = chatModel.currentRemoteHost.value?.remoteHostId, close = close)
}
}
) {
Image(
painterResource(MR.images.ic_invitation_card_public_address),
contentDescription = null,
contentScale = ContentScale.FillWidth,
modifier = Modifier.fillMaxWidth()
)
}
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
Row(
Modifier.fillMaxWidth().padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = 24.dp),
verticalAlignment = Alignment.CenterVertically
) {
Icon(
painterResource(MR.images.ic_qr_code),
contentDescription = null,
modifier = Modifier.size(20.dp),
tint = MaterialTheme.colors.secondary
)
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
Column {
Text(
stringResource(MR.strings.create_public_simplex_address),
style = MaterialTheme.typography.h3.copy(fontWeight = FontWeight.Bold),
)
Text(
stringResource(MR.strings.public_link_for_social_media_email_or_website),
style = MaterialTheme.typography.body2,
color = MaterialTheme.colors.secondary
)
}
}
)
}
@Preview

View File

@@ -2,29 +2,24 @@ package chat.simplex.common.views.invitation_redesign
import SectionBottomSpacer
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.Image
import androidx.compose.foundation.background
import androidx.compose.foundation.clickable
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.unit.dp
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.newchat.*
import chat.simplex.res.MR
@Composable
fun BoxScope.EmptyChatListView() {
fun EmptyChatListView(onConnectClick: () -> Unit) {
var showInviteSomeone by remember { mutableStateOf(false) }
if (showInviteSomeone) {
@@ -39,11 +34,12 @@ fun BoxScope.EmptyChatListView() {
Row(Modifier.fillMaxWidth()) {
NavigationButtonBack(onButtonClicked = { showInviteSomeone = false })
}
Spacer(Modifier.height(DEFAULT_BIG_PADDING))
Text(
stringResource(MR.strings.invite_someone),
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Bold),
)
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
Spacer(Modifier.height(DEFAULT_PADDING))
if (SHOW_PICTURES) {
InviteSomeoneWithPicturesContent()
} else {
@@ -52,12 +48,11 @@ fun BoxScope.EmptyChatListView() {
SectionBottomSpacer()
}
} else {
val closeAll = { ModalManager.start.closeModals() }
Column(
Modifier
.align(Alignment.Center)
.fillMaxWidth()
.padding(horizontal = DEFAULT_PADDING),
.padding(horizontal = DEFAULT_PADDING)
.padding(top = AppBarHeight + DEFAULT_BIG_PADDING),
horizontalAlignment = Alignment.CenterHorizontally
) {
Text(
@@ -65,73 +60,20 @@ fun BoxScope.EmptyChatListView() {
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Bold),
)
Spacer(Modifier.height(DEFAULT_PADDING))
Surface(
shape = RoundedCornerShape(18.dp),
color = MaterialTheme.appColors.sentMessage,
modifier = Modifier.fillMaxWidth().clickable {
showInviteSomeone = true
}
) {
Column(
Modifier.fillMaxWidth().padding(DEFAULT_PADDING),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painterResource(MR.images.ic_invitation_card_invite_someone),
contentDescription = null,
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painterResource(MR.images.ic_repeat_one),
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = MaterialTheme.colors.secondary
)
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
Text(
stringResource(MR.strings.invite_someone_to_chat),
style = MaterialTheme.typography.body1.copy(fontWeight = FontWeight.Medium),
)
}
}
}
InvitationCardView(
mainImageResource = MR.images.ic_invitation_card_invite_someone,
iconResource = MR.images.ic_repeat_one,
title = stringResource(MR.strings.invite_someone_to_chat),
modifier = Modifier.fillMaxWidth().clickable { showInviteSomeone = true }
)
Spacer(Modifier.height(DEFAULT_PADDING))
Surface(
shape = RoundedCornerShape(18.dp),
color = MaterialTheme.appColors.sentMessage,
modifier = Modifier.fillMaxWidth().clickable {
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll)
}
}
) {
Column(
Modifier.fillMaxWidth().padding(DEFAULT_PADDING),
horizontalAlignment = Alignment.CenterHorizontally
) {
Image(
painterResource(MR.images.ic_invitation_card_one_time_link),
contentDescription = null,
modifier = Modifier.fillMaxWidth()
)
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
Row(verticalAlignment = Alignment.CenterVertically) {
Icon(
painterResource(MR.images.ic_qr_code),
contentDescription = null,
modifier = Modifier.size(18.dp),
tint = MaterialTheme.colors.secondary
)
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
Text(
stringResource(MR.strings.connect_via_link_or_qr),
style = MaterialTheme.typography.body1.copy(fontWeight = FontWeight.Medium),
)
}
}
}
InvitationCardView(
mainImageResource = MR.images.ic_invitation_connected_link_qr,
iconResource = MR.images.ic_qr_code,
title = stringResource(MR.strings.connect_via_link_or_qr),
modifier = Modifier.fillMaxWidth().clickable { onConnectClick() }
)
}
}
}
@@ -141,7 +83,7 @@ fun BoxScope.EmptyChatListView() {
fun PreviewEmptyChatListView() {
SimpleXTheme {
Box(Modifier.fillMaxSize()) {
EmptyChatListView()
EmptyChatListView(onConnectClick = {})
}
}
}

View File

@@ -2,7 +2,8 @@ package chat.simplex.common.views.newchat
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.graphics.RectangleShape
import androidx.compose.ui.graphics.Shape
import chat.simplex.common.ui.theme.DEFAULT_PADDING
import chat.simplex.common.ui.theme.DEFAULT_PADDING_HALF
@@ -10,5 +11,6 @@ import chat.simplex.common.ui.theme.DEFAULT_PADDING_HALF
expect fun QRCodeScanner(
showQRCodeScanner: MutableState<Boolean> = remember { mutableStateOf(true) },
padding: PaddingValues = PaddingValues(horizontal = DEFAULT_PADDING * 2f, vertical = DEFAULT_PADDING_HALF),
clipShape: Shape = RectangleShape,
onBarcode: suspend (String) -> Boolean
)

View File

@@ -2,11 +2,13 @@ package chat.simplex.common.views.newchat
import androidx.compose.foundation.layout.PaddingValues
import androidx.compose.runtime.*
import androidx.compose.ui.graphics.Shape
@Composable
actual fun QRCodeScanner(
showQRCodeScanner: MutableState<Boolean>,
padding: PaddingValues,
clipShape: Shape,
onBarcode: suspend (String) -> Boolean
) {
//LALAL