mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-07-28 18:40:09 +00:00
UI improvements and fix navigation
This commit is contained in:
+1
-1
@@ -148,4 +148,4 @@
|
||||
</indentOptions>
|
||||
</codeStyleSettings>
|
||||
</code_scheme>
|
||||
</component>
|
||||
</component>
|
||||
|
||||
+116
-37
@@ -151,45 +151,120 @@ fun ChatListView(chatModel: ChatModel, userPickerState: MutableStateFlow<Animate
|
||||
val connectSheetScope = rememberCoroutineScope()
|
||||
val onConnectClick: () -> Unit = { connectSheetScope.launch { connectSheetState.show() } }
|
||||
|
||||
var showOneTimeLinkSheet by remember { mutableStateOf(false) }
|
||||
val oneTimeLinkSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
|
||||
val oneTimeLinkSheetScope = rememberCoroutineScope()
|
||||
val onOneTimeLinkClick: () -> Unit = {
|
||||
showOneTimeLinkSheet = true
|
||||
oneTimeLinkSheetScope.launch { oneTimeLinkSheetState.show() }
|
||||
}
|
||||
|
||||
LaunchedEffect(oneTimeLinkSheetState.currentValue) {
|
||||
if (oneTimeLinkSheetState.currentValue == ModalBottomSheetValue.Hidden && showOneTimeLinkSheet) {
|
||||
showOneTimeLinkSheet = false
|
||||
}
|
||||
}
|
||||
|
||||
var showConnectBannerNewChatSheet by remember { mutableStateOf(false) }
|
||||
var connectBannerSheetSelection by remember { mutableStateOf(NewChatOption.INVITE) }
|
||||
var connectBannerSheetShowQR by remember { mutableStateOf(false) }
|
||||
val connectBannerSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
|
||||
val connectBannerSheetScope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(connectBannerSheetState.currentValue) {
|
||||
if (connectBannerSheetState.currentValue == ModalBottomSheetValue.Hidden && showConnectBannerNewChatSheet) {
|
||||
showConnectBannerNewChatSheet = false
|
||||
}
|
||||
}
|
||||
|
||||
val onBannerInviteClick: () -> Unit = {
|
||||
connectBannerSheetSelection = NewChatOption.INVITE
|
||||
connectBannerSheetShowQR = false
|
||||
showConnectBannerNewChatSheet = true
|
||||
connectBannerSheetScope.launch { connectBannerSheetState.show() }
|
||||
}
|
||||
val onBannerScanPasteClick: () -> Unit = {
|
||||
connectBannerSheetSelection = NewChatOption.CONNECT
|
||||
connectBannerSheetShowQR = appPlatform.isAndroid
|
||||
showConnectBannerNewChatSheet = true
|
||||
connectBannerSheetScope.launch { connectBannerSheetState.show() }
|
||||
}
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
sheetState = connectSheetState,
|
||||
sheetState = connectBannerSheetState,
|
||||
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() } }
|
||||
)
|
||||
if (showConnectBannerNewChatSheet) {
|
||||
ModalData().NewChatViewSheet(
|
||||
rh = chatModel.currentRemoteHost.value,
|
||||
selection = connectBannerSheetSelection,
|
||||
showQRCodeScanner = connectBannerSheetShowQR,
|
||||
close = { connectBannerSheetScope.launch { connectBannerSheetState.hide() } }
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.height(1.dp))
|
||||
}
|
||||
}
|
||||
) {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
if (oneHandUI.value) {
|
||||
ChatListWithLoadingScreen(searchText, listState, onConnectClick)
|
||||
Column(Modifier.align(Alignment.BottomCenter)) {
|
||||
ChatListToolbar(
|
||||
userPickerState,
|
||||
listState,
|
||||
stopped,
|
||||
setPerformLA,
|
||||
)
|
||||
}
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
sheetState = oneTimeLinkSheetState,
|
||||
sheetShape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp),
|
||||
sheetBackgroundColor = MaterialTheme.colors.secondaryVariant,
|
||||
sheetContent = {
|
||||
if (showOneTimeLinkSheet) {
|
||||
OneTimeLinkBottomSheet(
|
||||
rhId = chatModel.currentRemoteHost.value?.remoteHostId,
|
||||
close = { oneTimeLinkSheetScope.launch { oneTimeLinkSheetState.hide() } }
|
||||
)
|
||||
} 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)
|
||||
Spacer(Modifier.height(1.dp))
|
||||
}
|
||||
}
|
||||
) {
|
||||
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, onOneTimeLinkClick, onBannerInviteClick, onBannerScanPasteClick)
|
||||
Column(Modifier.align(Alignment.BottomCenter)) {
|
||||
ChatListToolbar(
|
||||
userPickerState,
|
||||
listState,
|
||||
stopped,
|
||||
setPerformLA,
|
||||
)
|
||||
}
|
||||
} else {
|
||||
ChatListWithLoadingScreen(searchText, listState, onConnectClick, onOneTimeLinkClick, onBannerInviteClick, onBannerScanPasteClick)
|
||||
Column {
|
||||
ChatListToolbar(
|
||||
userPickerState,
|
||||
listState,
|
||||
stopped,
|
||||
setPerformLA,
|
||||
)
|
||||
}
|
||||
if (searchText.value.text.isEmpty() && !chatModel.desktopNoUserNoRemote && chatModel.chatRunning.value == true) {
|
||||
NewChatSheetFloatingButton(oneHandUI, stopped)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} // closes ModalBottomSheetLayout(connectBannerSheetState)
|
||||
|
||||
if (searchText.value.text.isEmpty()) {
|
||||
if (appPlatform.isDesktop && !oneHandUI.value) {
|
||||
@@ -305,9 +380,13 @@ private fun AddressCreationCard() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFieldValue>, listState: LazyListState, onConnectClick: () -> Unit) {
|
||||
private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFieldValue>, listState: LazyListState, onConnectClick: () -> Unit, onOneTimeLinkClick: () -> Unit, onBannerInviteClick: () -> Unit, onBannerScanPasteClick: () -> Unit) {
|
||||
if (!chatModel.desktopNoUserNoRemote) {
|
||||
EmptyChatListView(onConnectClick)
|
||||
if (chatModel.chats.value.isEmpty()) {
|
||||
EmptyChatListView(onConnectClick = onConnectClick, onOneTimeLinkClick = onOneTimeLinkClick)
|
||||
} else {
|
||||
ChatList(searchText = searchText, listState, onBannerInviteClick, onBannerScanPasteClick)
|
||||
}
|
||||
return
|
||||
}
|
||||
|
||||
@@ -319,7 +398,7 @@ private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFiel
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
} else {
|
||||
EmptyChatListView(onConnectClick)
|
||||
EmptyChatListView(onConnectClick = onConnectClick, onOneTimeLinkClick = onOneTimeLinkClick)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -770,7 +849,7 @@ fun BoxScope.NavigationBarBackground(modifier: Modifier, color: Color = Material
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun BoxScope.ChatList(searchText: MutableState<TextFieldValue>, listState: LazyListState) {
|
||||
private fun BoxScope.ChatList(searchText: MutableState<TextFieldValue>, listState: LazyListState, onBannerInviteClick: () -> Unit, onBannerScanPasteClick: () -> Unit) {
|
||||
var scrollDirection by remember { mutableStateOf(ScrollDirection.Idle) }
|
||||
var previousIndex by remember { mutableStateOf(0) }
|
||||
var previousScrollOffset by remember { mutableStateOf(0) }
|
||||
@@ -859,7 +938,7 @@ private fun BoxScope.ChatList(searchText: MutableState<TextFieldValue>, listStat
|
||||
}
|
||||
if (!oneHandUICardShown.value || !addressCreationCardShown.value || !connectBannerCardShown.value) {
|
||||
item {
|
||||
ChatListFeatureCards()
|
||||
ChatListFeatureCards(onBannerInviteClick, onBannerScanPasteClick)
|
||||
}
|
||||
}
|
||||
if (appPlatform.isAndroid) {
|
||||
@@ -929,7 +1008,7 @@ private fun NoChatsView(searchText: MutableState<TextFieldValue>) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChatListFeatureCards() {
|
||||
private fun ChatListFeatureCards(onBannerInviteClick: () -> Unit, onBannerScanPasteClick: () -> Unit) {
|
||||
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
||||
val oneHandUICardShown = remember { appPrefs.oneHandUICardShown.state }
|
||||
val addressCreationCardShown = remember { appPrefs.addressCreationCardShown.state }
|
||||
@@ -937,14 +1016,14 @@ private fun ChatListFeatureCards() {
|
||||
|
||||
Column(modifier = Modifier.padding(16.dp), verticalArrangement = Arrangement.spacedBy(16.dp)) {
|
||||
if (!connectBannerCardShown.value) {
|
||||
ConnectBannerCard()
|
||||
ConnectBannerCard(onBannerInviteClick, onBannerScanPasteClick)
|
||||
}
|
||||
if (!oneHandUICardShown.value && !oneHandUI.value) {
|
||||
ToggleChatListCard()
|
||||
}
|
||||
if (!addressCreationCardShown.value) {
|
||||
AddressCreationCard()
|
||||
}
|
||||
// if (!addressCreationCardShown.value) {
|
||||
// AddressCreationCard()
|
||||
// }
|
||||
if (!oneHandUICardShown.value && oneHandUI.value) {
|
||||
ToggleChatListCard()
|
||||
}
|
||||
|
||||
+42
-52
@@ -15,50 +15,58 @@ import androidx.compose.ui.unit.dp
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
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.newchat.*
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
fun ConnectBannerCard() {
|
||||
val closeAll = { ModalManager.start.closeModals() }
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Box {
|
||||
fun ConnectBannerCard(onInviteClick: () -> Unit, onScanPasteClick: () -> Unit) {
|
||||
Column {
|
||||
IconButton(
|
||||
onClick = { appPrefs.connectBannerCardShown.set(true) },
|
||||
modifier = Modifier.align(Alignment.End)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_close),
|
||||
stringResource(MR.strings.back),
|
||||
tint = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
) {
|
||||
Column {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_card_invite_someone),
|
||||
contentDescription = stringResource(MR.strings.create_link_or_qr),
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll)
|
||||
}
|
||||
},
|
||||
contentScale = ContentScale.FillWidth
|
||||
)
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_card_one_time_link),
|
||||
contentDescription = stringResource(MR.strings.paste_link_scan),
|
||||
.aspectRatio(1.6f)
|
||||
.clickable { onInviteClick() },
|
||||
) {
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_card_invite_someone),
|
||||
contentDescription = stringResource(MR.strings.create_link_or_qr),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
Box(
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll)
|
||||
}
|
||||
},
|
||||
contentScale = ContentScale.FillWidth
|
||||
)
|
||||
.aspectRatio(1.6f)
|
||||
.clickable { onScanPasteClick() },
|
||||
) {
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_card_one_time_link),
|
||||
contentDescription = stringResource(MR.strings.paste_link_scan),
|
||||
modifier = Modifier.fillMaxSize(),
|
||||
contentScale = ContentScale.Crop
|
||||
)
|
||||
}
|
||||
}
|
||||
Divider(color = MaterialTheme.colors.onSurface.copy(alpha = 0.06f))
|
||||
Row(
|
||||
@@ -68,11 +76,7 @@ fun ConnectBannerCard() {
|
||||
Row(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll)
|
||||
}
|
||||
},
|
||||
.clickable { onInviteClick() },
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
@@ -91,11 +95,7 @@ fun ConnectBannerCard() {
|
||||
Row(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll)
|
||||
}
|
||||
},
|
||||
.clickable { onScanPasteClick() },
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
@@ -113,16 +113,6 @@ fun ConnectBannerCard() {
|
||||
}
|
||||
}
|
||||
}
|
||||
IconButton(
|
||||
onClick = { appPrefs.connectBannerCardShown.set(true) },
|
||||
modifier = Modifier.align(Alignment.TopEnd).padding(4.dp)
|
||||
) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_close),
|
||||
stringResource(MR.strings.back),
|
||||
tint = MaterialTheme.colors.secondary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -131,6 +121,6 @@ fun ConnectBannerCard() {
|
||||
@Composable
|
||||
fun PreviewConnectBannerCard() {
|
||||
SimpleXTheme {
|
||||
ConnectBannerCard()
|
||||
ConnectBannerCard(onInviteClick = {}, onScanPasteClick = {})
|
||||
}
|
||||
}
|
||||
|
||||
+47
-26
@@ -2,44 +2,73 @@ 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.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
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.usersettings.UserAddressView
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.launch
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun InviteSomeoneWithPicturesView(close: () -> Unit) {
|
||||
var showOneTimeLinkSheet by remember { mutableStateOf(false) }
|
||||
val oneTimeLinkSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
|
||||
val sheetScope = rememberCoroutineScope()
|
||||
|
||||
LaunchedEffect(oneTimeLinkSheetState.currentValue) {
|
||||
if (oneTimeLinkSheetState.currentValue == ModalBottomSheetValue.Hidden && showOneTimeLinkSheet) {
|
||||
showOneTimeLinkSheet = false
|
||||
}
|
||||
}
|
||||
|
||||
ModalView(close) {
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxSize().padding(bottom = DEFAULT_BOTTOM_PADDING).background(Color.White),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
sheetState = oneTimeLinkSheetState,
|
||||
sheetShape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp),
|
||||
sheetBackgroundColor = MaterialTheme.colors.secondaryVariant,
|
||||
sheetContent = {
|
||||
if (showOneTimeLinkSheet) {
|
||||
OneTimeLinkBottomSheet(
|
||||
rhId = chatModel.currentRemoteHost.value?.remoteHostId,
|
||||
close = { sheetScope.launch { oneTimeLinkSheetState.hide() } }
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.height(1.dp))
|
||||
}
|
||||
}
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.invite_someone), withPadding = false)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteSomeoneWithPicturesContent()
|
||||
SectionBottomSpacer()
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxSize().padding(bottom = DEFAULT_BOTTOM_PADDING).background(Color.White),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.invite_someone), withPadding = false)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteSomeoneWithPicturesContent(
|
||||
onOneTimeLinkClick = {
|
||||
showOneTimeLinkSheet = true
|
||||
sheetScope.launch { oneTimeLinkSheetState.show() }
|
||||
}
|
||||
)
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InviteSomeoneWithPicturesContent() {
|
||||
fun InviteSomeoneWithPicturesContent(onOneTimeLinkClick: () -> Unit) {
|
||||
|
||||
InvitationCardView(
|
||||
mainImageResource = MR.images.ic_invitation_card_one_time_link,
|
||||
@@ -49,11 +78,7 @@ fun InviteSomeoneWithPicturesContent() {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable { close ->
|
||||
OneTimeLinkView(rhId = chatModel.currentRemoteHost.value?.remoteHostId, close = close)
|
||||
}
|
||||
}
|
||||
.clickable { onOneTimeLinkClick() }
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
@@ -66,15 +91,11 @@ fun InviteSomeoneWithPicturesContent() {
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.clickable {
|
||||
// TODO, Hayk replace with page when data will be available
|
||||
ModalManager.start.showModalCloseable { close ->
|
||||
OneTimeLinkView(rhId = chatModel.currentRemoteHost.value?.remoteHostId, close = close)
|
||||
}
|
||||
}
|
||||
.clickable { onOneTimeLinkClick() }
|
||||
)
|
||||
}
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Preview
|
||||
@Composable
|
||||
private fun PreviewInviteSomeoneView() {
|
||||
@@ -85,7 +106,7 @@ private fun PreviewInviteSomeoneView() {
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.invite_someone), withPadding = false)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteSomeoneWithPicturesContent()
|
||||
InviteSomeoneWithPicturesContent(onOneTimeLinkClick = {})
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
+5
-5
@@ -19,7 +19,7 @@ import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
fun EmptyChatListView(onConnectClick: () -> Unit) {
|
||||
fun EmptyChatListView(onConnectClick: () -> Unit, onOneTimeLinkClick: () -> Unit) {
|
||||
var showInviteSomeone by remember { mutableStateOf(false) }
|
||||
|
||||
if (showInviteSomeone) {
|
||||
@@ -34,14 +34,14 @@ fun EmptyChatListView(onConnectClick: () -> Unit) {
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
NavigationButtonBack(onButtonClicked = { showInviteSomeone = false })
|
||||
}
|
||||
Spacer(Modifier.height(DEFAULT_BIG_PADDING))
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
Text(
|
||||
stringResource(MR.strings.invite_someone),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Bold),
|
||||
)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
if (SHOW_PICTURES) {
|
||||
InviteSomeoneWithPicturesContent()
|
||||
InviteSomeoneWithPicturesContent(onOneTimeLinkClick = onOneTimeLinkClick)
|
||||
} else {
|
||||
InviteSomeoneContent()
|
||||
}
|
||||
@@ -52,7 +52,7 @@ fun EmptyChatListView(onConnectClick: () -> Unit) {
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.padding(top = AppBarHeight + DEFAULT_BIG_PADDING),
|
||||
.padding(top = AppBarHeight),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
@@ -83,7 +83,7 @@ fun EmptyChatListView(onConnectClick: () -> Unit) {
|
||||
fun PreviewEmptyChatListView() {
|
||||
SimpleXTheme {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
EmptyChatListView(onConnectClick = {})
|
||||
EmptyChatListView(onConnectClick = {}, onOneTimeLinkClick = {})
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+138
-2
@@ -4,7 +4,9 @@ import SectionBottomSpacer
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.Image
|
||||
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.runtime.saveable.rememberSaveable
|
||||
@@ -89,6 +91,140 @@ fun OneTimeLinkView(rhId: Long?, close: () -> Unit) {
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OneTimeLinkBottomSheet(rhId: Long?, close: () -> Unit) {
|
||||
val contactConnection: MutableState<PendingContactConnection?> = rememberSaveable(stateSaver = serializableSaver()) { mutableStateOf(chatModel.showingInvitation.value?.conn) }
|
||||
val connLinkInvitation by remember { derivedStateOf { chatModel.showingInvitation.value?.connLink ?: CreatedConnLink("", null) } }
|
||||
val creatingConnReq = rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
if (
|
||||
connLinkInvitation.connFullLink.isEmpty()
|
||||
&& contactConnection.value == null
|
||||
&& !creatingConnReq.value
|
||||
) {
|
||||
creatingConnReq.value = true
|
||||
withBGApi {
|
||||
val (r, alert) = controller.apiAddContact(rhId, incognito = controller.appPrefs.incognito.get())
|
||||
if (r != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateContactConnection(rhId, r.second)
|
||||
chatModel.showingInvitation.value = ShowingInvitation(connId = r.second.id, connLink = r.first, connChatUsed = false, conn = r.second)
|
||||
contactConnection.value = r.second
|
||||
}
|
||||
} else {
|
||||
creatingConnReq.value = false
|
||||
if (alert != null) {
|
||||
alert()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
if (chatModel.showingInvitation.value != null) {
|
||||
val conn = contactConnection.value
|
||||
if (chatModel.showingInvitation.value?.connChatUsed == false && conn != null) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(MR.strings.keep_unused_invitation_question),
|
||||
text = generalGetString(MR.strings.you_can_view_invitation_link_again),
|
||||
confirmText = generalGetString(MR.strings.delete_verb),
|
||||
dismissText = generalGetString(MR.strings.keep_invitation_link),
|
||||
destructive = true,
|
||||
onConfirm = {
|
||||
withBGApi {
|
||||
val chatInfo = ChatInfo.ContactConnection(conn)
|
||||
controller.deleteChat(Chat(remoteHostId = rhId, chatInfo = chatInfo, chatItems = listOf()))
|
||||
if (chatModel.chatId.value == chatInfo.id) {
|
||||
chatModel.chatId.value = null
|
||||
ModalManager.start.closeModals()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
chatModel.showingInvitation.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
OneTimeLinkBottomSheetContent(connLinkInvitation)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OneTimeLinkBottomSheetContent(connLinkInvitation: CreatedConnLink) {
|
||||
val showShortLink = remember { mutableStateOf(true) }
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_one_time_link),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING * 3)
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
Text(
|
||||
stringResource(MR.strings.send_1_time_link_description),
|
||||
style = MaterialTheme.typography.body1,
|
||||
modifier = Modifier.padding(horizontal = DEFAULT_PADDING)
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
if (connLinkInvitation.connFullLink.isNotEmpty()) {
|
||||
OneTimeLinkBar(connLinkInvitation, showShortLink.value)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
Text(
|
||||
stringResource(MR.strings.or_show_qr_code_in_person_or_video_call),
|
||||
style = MaterialTheme.typography.body1,
|
||||
modifier = Modifier.padding(horizontal = DEFAULT_PADDING)
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.colors.background,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(DEFAULT_PADDING)
|
||||
) {
|
||||
SimpleXCreatedLinkQRCode(
|
||||
connLinkInvitation,
|
||||
padding = PaddingValues(DEFAULT_PADDING),
|
||||
short = showShortLink.value,
|
||||
onShare = { chatModel.markShowingInvitationUsed() }
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
|
||||
CircularProgressIndicator(
|
||||
Modifier.size(36.dp).padding(4.dp),
|
||||
color = MaterialTheme.colors.secondary,
|
||||
strokeWidth = 3.dp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OneTimeLinkContent(connLinkInvitation: CreatedConnLink) {
|
||||
val showShortLink = remember { mutableStateOf(true) }
|
||||
@@ -165,8 +301,8 @@ private fun OneTimeLinkBar(connLinkInvitation: CreatedConnLink, short: Boolean)
|
||||
val link = connLinkInvitation.simplexChatUri(short)
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = MaterialTheme.colors.background,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
|
||||
+156
@@ -1,5 +1,6 @@
|
||||
package chat.simplex.common.views.newchat
|
||||
|
||||
import SectionBottomSpacer
|
||||
import SectionTextFooter
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.*
|
||||
@@ -186,6 +187,161 @@ fun AddGroupLayout(
|
||||
|
||||
fun canCreateProfile(displayName: String): Boolean = displayName.trim().isNotEmpty() && isValidDisplayName(displayName.trim())
|
||||
|
||||
@Composable
|
||||
fun AddGroupViewSheet(
|
||||
chatModel: ChatModel,
|
||||
rh: RemoteHostInfo?,
|
||||
profileImage: MutableState<String?>,
|
||||
onEditImage: () -> Unit,
|
||||
close: () -> Unit,
|
||||
closeAll: () -> Unit
|
||||
) {
|
||||
val rhId = rh?.remoteHostId
|
||||
val view = LocalMultiplatformView()
|
||||
AddGroupSheetLayout(
|
||||
createGroup = { incognito, groupProfile ->
|
||||
hideKeyboard(view)
|
||||
withBGApi {
|
||||
val groupInfo = chatModel.controller.apiNewGroup(rhId, incognito, groupProfile)
|
||||
if (groupInfo != null) {
|
||||
withContext(Dispatchers.Main) {
|
||||
chatModel.chatsContext.updateGroup(rhId = rhId, groupInfo)
|
||||
openGroupChat(rhId, groupInfo.groupId)
|
||||
}
|
||||
setGroupMembers(rhId, groupInfo, chatModel)
|
||||
closeAll.invoke()
|
||||
|
||||
if (!groupInfo.incognito) {
|
||||
ModalManager.end.showModalCloseable(true) { close ->
|
||||
AddGroupMembersView(rhId, groupInfo, creatingGroup = true, chatModel, close)
|
||||
}
|
||||
} else {
|
||||
ModalManager.end.showModalCloseable(true) { close ->
|
||||
GroupLinkView(chatModel, rhId, groupInfo, groupLink = null, onGroupLinkUpdated = null, creatingGroup = true, close)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
},
|
||||
incognitoPref = chatModel.controller.appPrefs.incognito,
|
||||
profileImage = profileImage,
|
||||
onEditImage = onEditImage
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun AddGroupSheetLayout(
|
||||
createGroup: (Boolean, GroupProfile) -> Unit,
|
||||
incognitoPref: SharedPreference<Boolean>,
|
||||
profileImage: MutableState<String?>,
|
||||
onEditImage: () -> Unit
|
||||
) {
|
||||
val displayName = rememberSaveable { mutableStateOf("") }
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val incognito = remember { mutableStateOf(incognitoPref.get()) }
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.background(MaterialTheme.colors.background)
|
||||
.imePadding()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
) {
|
||||
Text(
|
||||
stringResource(MR.strings.create_secret_group_title),
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(top = DEFAULT_BIG_PADDING, bottom = DEFAULT_PADDING),
|
||||
color = Color.Black,
|
||||
textAlign = androidx.compose.ui.text.style.TextAlign.Center,
|
||||
overflow = androidx.compose.ui.text.style.TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.h1
|
||||
)
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING * 3),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.padding(bottom = 24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Box(contentAlignment = Alignment.TopEnd) {
|
||||
Box(contentAlignment = Alignment.Center) {
|
||||
ProfileImage(108.dp, image = profileImage.value)
|
||||
EditImageButton { onEditImage() }
|
||||
}
|
||||
if (profileImage.value != null) {
|
||||
DeleteImageButton { profileImage.value = null }
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_create_group),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
}
|
||||
|
||||
Row(Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING_HALF).fillMaxWidth(), horizontalArrangement = Arrangement.SpaceBetween) {
|
||||
Text(
|
||||
stringResource(MR.strings.group_display_name_field),
|
||||
fontSize = 16.sp
|
||||
)
|
||||
if (!isValidDisplayName(displayName.value.trim())) {
|
||||
Spacer(Modifier.size(DEFAULT_PADDING_HALF))
|
||||
IconButton({ showInvalidNameAlert(mkValidName(displayName.value.trim()), displayName) }, Modifier.size(20.dp)) {
|
||||
Icon(painterResource(MR.images.ic_info), null, tint = MaterialTheme.colors.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
Box(Modifier.padding(horizontal = DEFAULT_PADDING)) {
|
||||
ProfileNameField(displayName, "", { isValidDisplayName(it.trim()) }, focusRequester)
|
||||
}
|
||||
Spacer(Modifier.height(8.dp))
|
||||
|
||||
SettingsActionItem(
|
||||
painterResource(MR.images.ic_check),
|
||||
stringResource(MR.strings.create_group_button),
|
||||
click = {
|
||||
createGroup(
|
||||
incognito.value, GroupProfile(
|
||||
displayName = displayName.value.trim(),
|
||||
fullName = "",
|
||||
shortDescr = null,
|
||||
image = profileImage.value,
|
||||
groupPreferences = GroupPreferences(history = GroupPreference(GroupFeatureEnabled.ON))
|
||||
)
|
||||
)
|
||||
},
|
||||
textColor = MaterialTheme.colors.primary,
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
disabled = !canCreateProfile(displayName.value)
|
||||
)
|
||||
|
||||
IncognitoToggle(incognitoPref, incognito) { ModalManager.start.showModal { IncognitoView() } }
|
||||
|
||||
SectionTextFooter(
|
||||
buildAnnotatedString {
|
||||
append(sharedProfileInfo(chatModel, incognito.value))
|
||||
append("\n")
|
||||
append(annotatedStringResource(MR.strings.group_is_decentralized))
|
||||
}
|
||||
)
|
||||
|
||||
SectionBottomSpacer()
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(1000)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewAddGroupLayout() {
|
||||
|
||||
+100
-30
@@ -38,7 +38,10 @@ import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.cancel
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filter
|
||||
import kotlinx.coroutines.launch
|
||||
import java.net.URI
|
||||
|
||||
@OptIn(ExperimentalMaterialApi::class)
|
||||
@Composable
|
||||
fun ModalData.NewChatSheet(rh: RemoteHostInfo?, close: () -> Unit) {
|
||||
DisposableEffect(Unit) {
|
||||
@@ -46,34 +49,105 @@ fun ModalData.NewChatSheet(rh: RemoteHostInfo?, close: () -> Unit) {
|
||||
connectProgressManager.cancelConnectProgress()
|
||||
}
|
||||
}
|
||||
|
||||
val oneHandUI = remember { appPrefs.oneHandUI.state }
|
||||
var showNewChatViewSheet by remember { mutableStateOf(false) }
|
||||
var showAddGroupSheet by remember { mutableStateOf(false) }
|
||||
var newChatSheetSelection by remember { mutableStateOf(NewChatOption.INVITE) }
|
||||
var newChatSheetShowQR by remember { mutableStateOf(false) }
|
||||
val newChatSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden, skipHalfExpanded = true)
|
||||
val sheetScope = rememberCoroutineScope()
|
||||
val closeAll = { ModalManager.start.closeModals() }
|
||||
// Image picker state for AddGroup sheet
|
||||
val addGroupChosenImage = rememberSaveable { mutableStateOf<URI?>(null) }
|
||||
val addGroupProfileImage = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val imagePickerSheetState = rememberModalBottomSheetState(initialValue = ModalBottomSheetValue.Hidden)
|
||||
|
||||
Box {
|
||||
val closeAll = { ModalManager.start.closeModals() }
|
||||
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
NewChatSheetLayout(
|
||||
addContact = {
|
||||
ModalManager.start.showModalCloseable(endButtons = { 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) }
|
||||
},
|
||||
createGroup = {
|
||||
ModalManager.start.showCustomModal { close -> AddGroupView(chatModel, chatModel.currentRemoteHost.value, close, closeAll) }
|
||||
},
|
||||
rh = rh,
|
||||
close = close
|
||||
)
|
||||
LaunchedEffect(newChatSheetState.currentValue) {
|
||||
if (newChatSheetState.currentValue == ModalBottomSheetValue.Hidden) {
|
||||
if (showNewChatViewSheet) showNewChatViewSheet = false
|
||||
if (showAddGroupSheet) {
|
||||
showAddGroupSheet = false
|
||||
addGroupChosenImage.value = null
|
||||
addGroupProfileImage.value = null
|
||||
}
|
||||
}
|
||||
if (oneHandUI.value) {
|
||||
Column(Modifier.align(Alignment.BottomCenter)) {
|
||||
DefaultAppBar(
|
||||
navigationButton = { NavigationButtonBack(onButtonClicked = close) },
|
||||
fixedTitleText = generalGetString(MR.strings.new_message),
|
||||
onTop = false,
|
||||
}
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
sheetState = imagePickerSheetState,
|
||||
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp),
|
||||
sheetContent = {
|
||||
if (showAddGroupSheet) {
|
||||
GetImageBottomSheet(
|
||||
addGroupChosenImage,
|
||||
onImageChange = { bitmap -> addGroupProfileImage.value = resizeImageToStrSize(cropToSquare(bitmap), maxDataSize = 12500) },
|
||||
hideBottomSheet = { sheetScope.launch { imagePickerSheetState.hide() } }
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.height(1.dp))
|
||||
}
|
||||
}
|
||||
) {
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
sheetState = newChatSheetState,
|
||||
sheetShape = RoundedCornerShape(topStart = 24.dp, topEnd = 24.dp),
|
||||
sheetBackgroundColor = MaterialTheme.colors.secondaryVariant,
|
||||
sheetContent = {
|
||||
if (showNewChatViewSheet) {
|
||||
ModalData().NewChatViewSheet(
|
||||
rh = chatModel.currentRemoteHost.value,
|
||||
selection = newChatSheetSelection,
|
||||
showQRCodeScanner = newChatSheetShowQR,
|
||||
close = { sheetScope.launch { newChatSheetState.hide() } }
|
||||
)
|
||||
} else if (showAddGroupSheet) {
|
||||
AddGroupViewSheet(
|
||||
chatModel = chatModel,
|
||||
rh = chatModel.currentRemoteHost.value,
|
||||
profileImage = addGroupProfileImage,
|
||||
onEditImage = { sheetScope.launch { imagePickerSheetState.show() } },
|
||||
close = { sheetScope.launch { newChatSheetState.hide() } },
|
||||
closeAll = closeAll
|
||||
)
|
||||
} else {
|
||||
Spacer(Modifier.height(1.dp))
|
||||
}
|
||||
}
|
||||
) {
|
||||
Box {
|
||||
Column(modifier = Modifier.fillMaxSize()) {
|
||||
NewChatSheetLayout(
|
||||
addContact = {
|
||||
newChatSheetSelection = NewChatOption.INVITE
|
||||
newChatSheetShowQR = false
|
||||
showNewChatViewSheet = true
|
||||
sheetScope.launch { newChatSheetState.show() }
|
||||
},
|
||||
scanPaste = {
|
||||
newChatSheetSelection = NewChatOption.CONNECT
|
||||
newChatSheetShowQR = appPlatform.isAndroid
|
||||
showNewChatViewSheet = true
|
||||
sheetScope.launch { newChatSheetState.show() }
|
||||
},
|
||||
createGroup = {
|
||||
showAddGroupSheet = true
|
||||
sheetScope.launch { newChatSheetState.show() }
|
||||
},
|
||||
rh = rh,
|
||||
close = close
|
||||
)
|
||||
}
|
||||
if (oneHandUI.value) {
|
||||
Column(Modifier.align(Alignment.BottomCenter)) {
|
||||
DefaultAppBar(
|
||||
navigationButton = { NavigationButtonBack(onButtonClicked = close) },
|
||||
fixedTitleText = generalGetString(MR.strings.new_message),
|
||||
onTop = false,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -96,6 +170,7 @@ fun chatContactType(chat: Chat): ContactType {
|
||||
else -> ContactType.UNLISTED
|
||||
}
|
||||
}
|
||||
|
||||
else -> ContactType.UNLISTED
|
||||
}
|
||||
}
|
||||
@@ -122,7 +197,7 @@ private fun ModalData.NewChatSheetLayout(
|
||||
val total = listState.layoutInfo.totalItemsCount
|
||||
if (prevIndex == 0 && prevOffset == 0) return@LaunchedEffect
|
||||
snapshotFlow { listState.firstVisibleItemIndex to listState.firstVisibleItemScrollOffset }
|
||||
.filter { it == 0 to 0 }
|
||||
.filter { it == 0 to 0 }
|
||||
.collect {
|
||||
if (total <= listState.layoutInfo.totalItemsCount) {
|
||||
listState.scrollToItem(prevIndex, prevOffset)
|
||||
@@ -163,7 +238,6 @@ private fun ModalData.NewChatSheetLayout(
|
||||
previousIndex = currentIndex
|
||||
previousScrollOffset = currentScrollOffset
|
||||
}
|
||||
|
||||
val filteredContactChats = filteredContactChats(
|
||||
showUnreadAndFavorites = showUnreadAndFavorites,
|
||||
searchChatFilteredBySimplexLink = searchChatFilteredBySimplexLink,
|
||||
@@ -171,13 +245,11 @@ private fun ModalData.NewChatSheetLayout(
|
||||
searchText = searchText.value.text,
|
||||
contactChats = allChats
|
||||
)
|
||||
|
||||
val sectionModifier = Modifier.fillMaxWidth()
|
||||
val deletedContactTypes = listOf(ContactType.CHAT_DELETED)
|
||||
val deletedChats by remember(chatModel.chats.value, deletedContactTypes) {
|
||||
derivedStateOf { filterContactTypes(chatModel.chats.value, deletedContactTypes) }
|
||||
}
|
||||
|
||||
val actionButtonsOriginal = listOf(
|
||||
Triple(
|
||||
painterResource(MR.images.ic_add_link),
|
||||
@@ -485,7 +557,6 @@ private fun ContactsSearchBar(
|
||||
CIFileViewScope.progressIndicator(sizeMultiplier = 0.75f)
|
||||
}
|
||||
}
|
||||
|
||||
val hasText = remember { derivedStateOf { searchText.value.text.isNotEmpty() } }
|
||||
if (hasText.value) {
|
||||
val hideSearchOnBack: () -> Unit = { searchText.value = TextFieldValue() }
|
||||
@@ -626,7 +697,6 @@ private val chatsByTypeComparator = Comparator<Chat> { chat1, chat2 ->
|
||||
when {
|
||||
chat1Type.ordinal < chat2Type.ordinal -> -1
|
||||
chat1Type.ordinal > chat2Type.ordinal -> 1
|
||||
|
||||
else -> chat2.chatInfo.chatTs.compareTo(chat1.chatInfo.chatTs)
|
||||
}
|
||||
}
|
||||
|
||||
+249
-84
@@ -23,7 +23,6 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
@@ -98,9 +97,10 @@ fun ModalData.NewChatView(rh: RemoteHostInfo?, selection: NewChatOption, showQRC
|
||||
}
|
||||
}
|
||||
val tabTitles = NewChatOption.values().map {
|
||||
when(it) {
|
||||
when (it) {
|
||||
NewChatOption.INVITE ->
|
||||
stringResource(MR.strings.one_time_link_short)
|
||||
|
||||
NewChatOption.CONNECT ->
|
||||
stringResource(MR.strings.connect_via_link)
|
||||
}
|
||||
@@ -155,6 +155,7 @@ fun ModalData.NewChatView(rh: RemoteHostInfo?, selection: NewChatOption, showQRC
|
||||
NewChatOption.INVITE.ordinal -> {
|
||||
PrepareAndInviteView(rh?.remoteHostId, contactConnection, connLinkInvitation, creatingConnReq)
|
||||
}
|
||||
|
||||
NewChatOption.CONNECT.ordinal -> {
|
||||
ConnectView(rh?.remoteHostId, showQRCodeScanner, pastedLink, close)
|
||||
}
|
||||
@@ -166,6 +167,124 @@ fun ModalData.NewChatView(rh: RemoteHostInfo?, selection: NewChatOption, showQRC
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ModalData.NewChatViewSheet(rh: RemoteHostInfo?, selection: NewChatOption, showQRCodeScanner: Boolean = false, close: () -> Unit) {
|
||||
val selection = remember { stateGetOrPut("selection") { selection } }
|
||||
val showQRCodeScanner = remember { stateGetOrPut("showQRCodeScanner") { showQRCodeScanner } }
|
||||
val contactConnection: MutableState<PendingContactConnection?> = rememberSaveable(stateSaver = serializableSaver()) { mutableStateOf(chatModel.showingInvitation.value?.conn) }
|
||||
val connLinkInvitation by remember { derivedStateOf { chatModel.showingInvitation.value?.connLink ?: CreatedConnLink("", null) } }
|
||||
val creatingConnReq = rememberSaveable { mutableStateOf(false) }
|
||||
val pastedLink = rememberSaveable { mutableStateOf("") }
|
||||
LaunchedEffect(selection.value) {
|
||||
if (
|
||||
selection.value == NewChatOption.INVITE
|
||||
&& connLinkInvitation.connFullLink.isEmpty()
|
||||
&& contactConnection.value == null
|
||||
&& !creatingConnReq.value
|
||||
) {
|
||||
createInvitation(rh?.remoteHostId, creatingConnReq, connLinkInvitation, contactConnection)
|
||||
}
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
if (chatModel.showingInvitation.value != null) {
|
||||
val conn = contactConnection.value
|
||||
if (chatModel.showingInvitation.value?.connChatUsed == false && conn != null) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = generalGetString(MR.strings.keep_unused_invitation_question),
|
||||
text = generalGetString(MR.strings.you_can_view_invitation_link_again),
|
||||
confirmText = generalGetString(MR.strings.delete_verb),
|
||||
dismissText = generalGetString(MR.strings.keep_invitation_link),
|
||||
destructive = true,
|
||||
onConfirm = {
|
||||
withBGApi {
|
||||
val chatInfo = ChatInfo.ContactConnection(conn)
|
||||
controller.deleteChat(Chat(remoteHostId = rh?.remoteHostId, chatInfo = chatInfo, chatItems = listOf()))
|
||||
if (chatModel.chatId.value == chatInfo.id) {
|
||||
chatModel.chatId.value = null
|
||||
ModalManager.start.closeModals()
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
chatModel.showingInvitation.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
val tabTitles = NewChatOption.values().map {
|
||||
when (it) {
|
||||
NewChatOption.INVITE ->
|
||||
stringResource(MR.strings.one_time_link_short)
|
||||
|
||||
NewChatOption.CONNECT ->
|
||||
stringResource(MR.strings.connect_via_link)
|
||||
}
|
||||
}
|
||||
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.wrapContentHeight()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
val scope = rememberCoroutineScope()
|
||||
val pagerState = rememberPagerState(
|
||||
initialPage = selection.value.ordinal,
|
||||
initialPageOffsetFraction = 0f
|
||||
) { NewChatOption.values().size }
|
||||
KeyChangeEffect(pagerState.currentPage) {
|
||||
selection.value = NewChatOption.values()[pagerState.currentPage]
|
||||
}
|
||||
TabRow(
|
||||
selectedTabIndex = pagerState.currentPage,
|
||||
backgroundColor = Color.Transparent,
|
||||
contentColor = MaterialTheme.colors.primary,
|
||||
) {
|
||||
tabTitles.forEachIndexed { index, it ->
|
||||
LeadingIconTab(
|
||||
selected = pagerState.currentPage == index,
|
||||
onClick = {
|
||||
scope.launch {
|
||||
pagerState.animateScrollToPage(index)
|
||||
}
|
||||
},
|
||||
text = { Text(it, fontSize = 13.sp) },
|
||||
icon = {
|
||||
Icon(
|
||||
if (NewChatOption.INVITE.ordinal == index) painterResource(MR.images.ic_repeat_one) else painterResource(MR.images.ic_qr_code),
|
||||
it
|
||||
)
|
||||
},
|
||||
selectedContentColor = MaterialTheme.colors.primary,
|
||||
unselectedContentColor = MaterialTheme.colors.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
HorizontalPager(state = pagerState, Modifier, verticalAlignment = Alignment.Top, userScrollEnabled = appPlatform.isAndroid) { index ->
|
||||
Column(
|
||||
Modifier.fillMaxWidth(),
|
||||
verticalArrangement = Arrangement.Top
|
||||
) {
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
when (index) {
|
||||
NewChatOption.INVITE.ordinal -> {
|
||||
PrepareAndInviteView(rh?.remoteHostId, contactConnection, connLinkInvitation, creatingConnReq)
|
||||
}
|
||||
|
||||
NewChatOption.CONNECT.ordinal -> {
|
||||
ConnectView(rh?.remoteHostId, showQRCodeScanner, pastedLink, close)
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun PrepareAndInviteView(rhId: Long?, contactConnection: MutableState<PendingContactConnection?>, connLinkInvitation: CreatedConnLink, creatingConnReq: MutableState<Boolean>) {
|
||||
if (connLinkInvitation.connFullLink.isNotEmpty()) {
|
||||
@@ -233,7 +352,8 @@ private fun ProfilePickerOption(
|
||||
Text(title, modifier = Modifier.align(Alignment.CenterVertically))
|
||||
if (onInfo != null) {
|
||||
Spacer(Modifier.padding(6.dp))
|
||||
Column(Modifier
|
||||
Column(
|
||||
Modifier
|
||||
.size(48.dp)
|
||||
.clip(CircleShape)
|
||||
.clickable(
|
||||
@@ -288,7 +408,6 @@ fun ActiveProfilePicker(
|
||||
val filteredProfiles = remember(searchTextOrPassword.value) {
|
||||
filteredProfiles(chatModel.users.map { it.user }.sortedBy { !it.activeUser }, searchTextOrPassword.value)
|
||||
}
|
||||
|
||||
var progressByTimeout by rememberSaveable { mutableStateOf(false) }
|
||||
|
||||
LaunchedEffect(switchingProfile.value) {
|
||||
@@ -333,8 +452,10 @@ fun ActiveProfilePicker(
|
||||
)
|
||||
|
||||
if (chatModel.currentUser.value?.userId != user.userId) {
|
||||
AlertManager.shared.showAlertMsg(generalGetString(
|
||||
MR.strings.switching_profile_error_title),
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(
|
||||
MR.strings.switching_profile_error_title
|
||||
),
|
||||
String.format(generalGetString(MR.strings.switching_profile_error_message), user.chatViewName)
|
||||
)
|
||||
}
|
||||
@@ -352,7 +473,7 @@ fun ActiveProfilePicker(
|
||||
}
|
||||
}
|
||||
},
|
||||
image = { ProfileImage(size = 42.dp, image = user.image) }
|
||||
image = { ProfileImage(size = 42.dp, image = user.image) }
|
||||
)
|
||||
}
|
||||
|
||||
@@ -389,7 +510,7 @@ fun ActiveProfilePicker(
|
||||
)
|
||||
}
|
||||
|
||||
BoxWithConstraints {
|
||||
Box {
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
@@ -414,10 +535,12 @@ fun ActiveProfilePicker(
|
||||
when {
|
||||
!showIncognito ->
|
||||
ProfilePickerUserOption(activeProfile)
|
||||
|
||||
incognito -> {
|
||||
IncognitoUserOption()
|
||||
ProfilePickerUserOption(activeProfile)
|
||||
}
|
||||
|
||||
else -> {
|
||||
ProfilePickerUserOption(activeProfile)
|
||||
IncognitoUserOption()
|
||||
@@ -453,34 +576,56 @@ fun ActiveProfilePicker(
|
||||
private fun InviteView(rhId: Long?, connLinkInvitation: CreatedConnLink, contactConnection: MutableState<PendingContactConnection?>) {
|
||||
val showShortLink = remember { mutableStateOf(true) }
|
||||
|
||||
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_one_time_link),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = Modifier
|
||||
.size(100.dp)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.size(160.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
SectionView(stringResource(MR.strings.share_this_1_time_link).uppercase(), headerBottomPadding = 5.dp) {
|
||||
LinkTextView(connLinkInvitation.simplexChatUri(short = showShortLink.value), true)
|
||||
SectionView(stringResource(MR.strings.share_this_1_time_link).uppercase(), headerBottomPadding = 8.dp) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = MaterialTheme.colors.background,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
LinkTextView(connLinkInvitation.simplexChatUri(short = showShortLink.value), true)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
SectionViewWithButton(
|
||||
stringResource(MR.strings.or_show_this_qr_code).uppercase(),
|
||||
titleButton = if (connLinkInvitation.connShortLink != null) {{ ToggleShortLinkButton(showShortLink) }} else null
|
||||
headerBottomPadding = 8.dp,
|
||||
titleButton = if (connLinkInvitation.connShortLink != null) {
|
||||
{ ToggleShortLinkButton(showShortLink) }
|
||||
} else null
|
||||
) {
|
||||
SimpleXCreatedLinkQRCode(connLinkInvitation, short = showShortLink.value, onShare = { chatModel.markShowingInvitationUsed() })
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.colors.background,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
SimpleXCreatedLinkQRCode(
|
||||
connLink = connLinkInvitation,
|
||||
short = showShortLink.value,
|
||||
padding = PaddingValues(vertical = DEFAULT_PADDING),
|
||||
onShare = { chatModel.markShowingInvitationUsed() }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
val incognito by remember(chatModel.showingInvitation.value?.conn?.incognito, controller.appPrefs.incognito.get()) {
|
||||
derivedStateOf {
|
||||
@@ -490,56 +635,64 @@ private fun InviteView(rhId: Long?, connLinkInvitation: CreatedConnLink, contact
|
||||
val currentUser = remember { chatModel.currentUser }.value
|
||||
|
||||
if (currentUser != null) {
|
||||
SectionView(stringResource(MR.strings.new_chat_share_profile).uppercase(), headerBottomPadding = 5.dp) {
|
||||
SectionItemView(
|
||||
padding = PaddingValues(
|
||||
top = 0.dp,
|
||||
bottom = 0.dp,
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
click = {
|
||||
ModalManager.start.showCustomModal(keyboardCoversBar = false) { close ->
|
||||
val search = rememberSaveable { mutableStateOf("") }
|
||||
ModalView(
|
||||
{ close() },
|
||||
showSearch = true,
|
||||
searchAlwaysVisible = true,
|
||||
onSearchValueChanged = { search.value = it },
|
||||
content = {
|
||||
ActiveProfilePicker(
|
||||
search = search,
|
||||
close = close,
|
||||
rhId = rhId,
|
||||
contactConnection = contactConnection.value
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
SectionView(stringResource(MR.strings.new_chat_share_profile).uppercase(), headerBottomPadding = 8.dp) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = MaterialTheme.colors.background,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
if (incognito) {
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Icon(
|
||||
painterResource(MR.images.ic_theater_comedy_filled),
|
||||
contentDescription = stringResource(MR.strings.incognito),
|
||||
tint = Indigo,
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
Spacer(Modifier.width(2.dp))
|
||||
} else {
|
||||
ProfileImage(size = 42.dp, image = currentUser.image)
|
||||
}
|
||||
TextIconSpaced(false)
|
||||
Text(
|
||||
text = if (incognito) stringResource(MR.strings.incognito) else currentUser.chatViewName,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.End) {
|
||||
Icon(
|
||||
painter = painterResource(MR.images.ic_arrow_forward_ios),
|
||||
contentDescription = stringResource(MR.strings.new_chat_share_profile),
|
||||
tint = MaterialTheme.colors.secondary,
|
||||
SectionItemView(
|
||||
padding = PaddingValues(
|
||||
top = 0.dp,
|
||||
bottom = 0.dp,
|
||||
start = 16.dp,
|
||||
end = 16.dp
|
||||
),
|
||||
click = {
|
||||
ModalManager.start.showCustomModal(keyboardCoversBar = false) { close ->
|
||||
val search = rememberSaveable { mutableStateOf("") }
|
||||
ModalView(
|
||||
{ close() },
|
||||
showSearch = true,
|
||||
searchAlwaysVisible = true,
|
||||
onSearchValueChanged = { search.value = it },
|
||||
content = {
|
||||
ActiveProfilePicker(
|
||||
search = search,
|
||||
close = close,
|
||||
rhId = rhId,
|
||||
contactConnection = contactConnection.value
|
||||
)
|
||||
})
|
||||
}
|
||||
}
|
||||
) {
|
||||
if (incognito) {
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Icon(
|
||||
painterResource(MR.images.ic_theater_comedy_filled),
|
||||
contentDescription = stringResource(MR.strings.incognito),
|
||||
tint = Indigo,
|
||||
modifier = Modifier.size(32.dp)
|
||||
)
|
||||
Spacer(Modifier.width(2.dp))
|
||||
} else {
|
||||
ProfileImage(size = 42.dp, image = currentUser.image)
|
||||
}
|
||||
TextIconSpaced(false)
|
||||
Text(
|
||||
text = if (incognito) stringResource(MR.strings.incognito) else currentUser.chatViewName,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Column(modifier = Modifier.fillMaxWidth(), horizontalAlignment = Alignment.End) {
|
||||
Icon(
|
||||
painter = painterResource(MR.images.ic_arrow_forward_ios),
|
||||
contentDescription = stringResource(MR.strings.new_chat_share_profile),
|
||||
tint = MaterialTheme.colors.secondary,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -599,30 +752,39 @@ private fun ConnectView(rhId: Long?, showQRCodeScanner: MutableState<Boolean>, p
|
||||
connectProgressManager.cancelConnectProgress()
|
||||
}
|
||||
}
|
||||
Column(modifier = Modifier.fillMaxWidth()) {
|
||||
|
||||
Box(
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentAlignment = Alignment.TopCenter
|
||||
) {
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_connect_link),
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = Modifier
|
||||
.size(100.dp)
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.align(Alignment.CenterHorizontally)
|
||||
.size(160.dp)
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
SectionView(stringResource(MR.strings.paste_the_link_you_received).uppercase(), headerBottomPadding = 5.dp) {
|
||||
PasteLinkView(rhId, pastedLink, showQRCodeScanner, close)
|
||||
SectionView(stringResource(MR.strings.paste_the_link_you_received).uppercase(), headerBottomPadding = 8.dp) {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(12.dp),
|
||||
color = MaterialTheme.colors.background,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
PasteLinkView(rhId, pastedLink, showQRCodeScanner, close)
|
||||
}
|
||||
}
|
||||
|
||||
if (appPlatform.isAndroid) {
|
||||
Spacer(Modifier.height(10.dp))
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
SectionView(stringResource(MR.strings.or_scan_qr_code).uppercase(), headerBottomPadding = 5.dp) {
|
||||
QRCodeScanner(showQRCodeScanner) { text ->
|
||||
SectionView(stringResource(MR.strings.or_scan_qr_code).uppercase(), headerBottomPadding = 8.dp) {
|
||||
QRCodeScanner(
|
||||
showQRCodeScanner = showQRCodeScanner,
|
||||
clipShape = RoundedCornerShape(12.dp),
|
||||
padding = PaddingValues(horizontal = DEFAULT_PADDING)
|
||||
) { text ->
|
||||
val linkVerified = verifyOnly(text)
|
||||
if (!linkVerified) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
@@ -655,7 +817,10 @@ private fun PasteLinkView(rhId: Long?, pastedLink: MutableState<String>, showQRC
|
||||
}
|
||||
}) {
|
||||
Box(Modifier.weight(1f)) {
|
||||
Text(stringResource(MR.strings.tap_to_paste_link))
|
||||
Text(
|
||||
text = stringResource(MR.strings.tap_to_paste_link),
|
||||
color = Color.LightGray,
|
||||
)
|
||||
}
|
||||
if (connectProgressManager.showConnectProgress != null) {
|
||||
CIFileViewScope.progressIndicator(sizeMultiplier = 0.6f)
|
||||
@@ -686,7 +851,7 @@ fun LinkTextView(link: String, share: Boolean) {
|
||||
}) {
|
||||
BasicTextField(
|
||||
value = link,
|
||||
onValueChange = { },
|
||||
onValueChange = { },
|
||||
enabled = false,
|
||||
textStyle = TextStyle(fontSize = 16.sp, color = MaterialTheme.colors.onBackground),
|
||||
singleLine = true,
|
||||
|
||||
Reference in New Issue
Block a user