mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-03-31 18:25:56 +00:00
Added screens and implementations mostly with ai
This commit is contained in:
@@ -0,0 +1,136 @@
|
||||
package chat.simplex.app.views.invitation_redesign
|
||||
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.Image
|
||||
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.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
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.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 {
|
||||
Column {
|
||||
Row(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_card_invite_someone),
|
||||
contentDescription = stringResource(MR.strings.create_link_or_qr),
|
||||
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),
|
||||
modifier = Modifier
|
||||
.weight(1f)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll)
|
||||
}
|
||||
},
|
||||
contentScale = ContentScale.FillWidth
|
||||
)
|
||||
}
|
||||
Divider(color = MaterialTheme.colors.onSurface.copy(alpha = 0.06f))
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(vertical = DEFAULT_PADDING_HALF),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll)
|
||||
}
|
||||
},
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
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.create_link_or_qr),
|
||||
style = MaterialTheme.typography.body2.copy(fontWeight = FontWeight.Medium),
|
||||
)
|
||||
}
|
||||
Row(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.CONNECT, showQRCodeScanner = appPlatform.isAndroid, close = closeAll)
|
||||
}
|
||||
},
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
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.paste_link_scan),
|
||||
style = MaterialTheme.typography.body2.copy(fontWeight = FontWeight.Medium),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewConnectBannerCard() {
|
||||
SimpleXTheme {
|
||||
ConnectBannerCard()
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,147 @@
|
||||
package chat.simplex.app.views.invitation_redesign
|
||||
|
||||
import SectionBottomSpacer
|
||||
import SectionItemView
|
||||
import SectionView
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
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.clip
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
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.*
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.CIFileViewScope
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.views.newchat.*
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
fun ModalData.ConnectViewLinkOrQrModal(rhId: Long?, close: () -> Unit) {
|
||||
val showQRCodeScanner = remember { stateGetOrPut("showQRCodeScanner") { true } }
|
||||
val pastedLink = rememberSaveable { mutableStateOf("") }
|
||||
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
connectProgressManager.cancelConnectProgress()
|
||||
}
|
||||
}
|
||||
|
||||
ModalView(close) {
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.connect_via_link), withPadding = false)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
Icon(
|
||||
painterResource(MR.images.ic_add_link),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(80.dp),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING * 1.5f))
|
||||
|
||||
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.clip(RoundedCornerShape(DEFAULT_PADDING))) {
|
||||
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)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ConnectPasteLinkView(rhId: Long?, pastedLink: MutableState<String>, showQRCodeScanner: MutableState<Boolean>, close: () -> Unit) {
|
||||
if (pastedLink.value.isEmpty()) {
|
||||
val clipboard = LocalClipboardManager.current
|
||||
SectionItemView({
|
||||
val str = clipboard.getText()?.text ?: return@SectionItemView
|
||||
val link = strHasSingleSimplexLink(str.trim())
|
||||
if (link != null) {
|
||||
pastedLink.value = link.text
|
||||
showQRCodeScanner.value = false
|
||||
withBGApi {
|
||||
connectFromPaste(rhId, link.text, close) { pastedLink.value = "" }
|
||||
}
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = generalGetString(MR.strings.invalid_contact_link),
|
||||
text = generalGetString(MR.strings.the_text_you_pasted_is_not_a_link)
|
||||
)
|
||||
}
|
||||
}) {
|
||||
Box(Modifier.weight(1f)) {
|
||||
Text(stringResource(MR.strings.tap_to_paste_link))
|
||||
}
|
||||
if (connectProgressManager.showConnectProgress != null) {
|
||||
CIFileViewScope.progressIndicator(sizeMultiplier = 0.6f)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
Row(
|
||||
Modifier.padding(end = DEFAULT_PADDING),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(Modifier.weight(1f)) {
|
||||
LinkTextView(pastedLink.value, false)
|
||||
}
|
||||
if (connectProgressManager.showConnectProgress != null) {
|
||||
CIFileViewScope.progressIndicator(sizeMultiplier = 0.6f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private suspend fun connectFromScanner(rhId: Long?, text: String?, close: () -> Unit): Boolean {
|
||||
if (text != null && strIsSimplexLink(text)) {
|
||||
return connectFromPaste(rhId, text, close)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private suspend fun connectFromPaste(rhId: Long?, link: String, close: () -> Unit, cleanup: (() -> Unit)? = null): Boolean =
|
||||
planAndConnect(
|
||||
rhId,
|
||||
link,
|
||||
close = close,
|
||||
cleanup = cleanup
|
||||
).await()
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewConnectViewLinkOrQrModal() {
|
||||
SimpleXTheme {
|
||||
ModalData().ConnectViewLinkOrQrModal(rhId = null, close = {})
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,141 @@
|
||||
package chat.simplex.app.views.invitation_redesign
|
||||
|
||||
import SectionBottomSpacer
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.Image
|
||||
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() {
|
||||
var showInviteSomeone by remember { mutableStateOf(false) }
|
||||
|
||||
if (showInviteSomeone) {
|
||||
BackHandler { showInviteSomeone = false }
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
NavigationButtonBack(onButtonClicked = { showInviteSomeone = false })
|
||||
}
|
||||
Text(
|
||||
stringResource(MR.strings.invite_someone),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Bold),
|
||||
)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteSomeoneContent()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
} else {
|
||||
val closeAll = { ModalManager.start.closeModals() }
|
||||
Column(
|
||||
Modifier
|
||||
.align(Alignment.Center)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
stringResource(MR.strings.now_you_can),
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
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),
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewEmptyChatListView() {
|
||||
SimpleXTheme {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
EmptyChatListView()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,160 @@
|
||||
package chat.simplex.app.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.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
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.invitation_redesign.OneTimeLinkView
|
||||
import chat.simplex.common.views.usersettings.UserAddressView
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
fun InviteSomeoneView(close: () -> Unit) {
|
||||
ModalView(close) {
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxSize().background(Color.White),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.invite_someone), withPadding = false)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteSomeoneContent()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InviteSomeoneContent() {
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
modifier = Modifier
|
||||
.fillMaxSize().background(Color.White)
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable { close ->
|
||||
OneTimeLinkView(rhId = chatModel.currentRemoteHost.value?.remoteHostId, close = close)
|
||||
}
|
||||
}
|
||||
) {
|
||||
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(
|
||||
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))
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable { closeAddress ->
|
||||
UserAddressView(chatModel = chatModel, shareViaProfile = false, autoCreateAddress = true, close = closeAddress)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth().padding(DEFAULT_PADDING),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_card_public_address),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
|
||||
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING),
|
||||
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
|
||||
@Composable
|
||||
fun PreviewInviteSomeoneView() {
|
||||
SimpleXTheme {
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxSize().background(Color.White),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.invite_someone), withPadding = false)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteSomeoneContent()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,219 @@
|
||||
package chat.simplex.app.views.invitation_redesign
|
||||
|
||||
import SectionBottomSpacer
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
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
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun OneTimeLinkView(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 && ModalManager.start.openModalCount() <= 1) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModalView(close) {
|
||||
OneTimeLinkContent(connLinkInvitation)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OneTimeLinkContent(connLinkInvitation: CreatedConnLink) {
|
||||
val showShortLink = remember { mutableStateOf(true) }
|
||||
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxSize(),
|
||||
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,
|
||||
color = MaterialTheme.colors.secondary,
|
||||
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,
|
||||
color = MaterialTheme.colors.secondary,
|
||||
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(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
SimpleXCreatedLinkQRCode(
|
||||
connLinkInvitation,
|
||||
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
|
||||
private fun OneTimeLinkBar(connLinkInvitation: CreatedConnLink, short: Boolean) {
|
||||
val clipboard = LocalClipboardManager.current
|
||||
val link = connLinkInvitation.simplexChatUri(short)
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(start = DEFAULT_PADDING, end = 4.dp)
|
||||
.heightIn(min = 48.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
link,
|
||||
style = MaterialTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
IconButton(onClick = {
|
||||
chatModel.markShowingInvitationUsed()
|
||||
clipboard.setText(AnnotatedString(simplexChatLink(link)))
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_content_copy),
|
||||
contentDescription = stringResource(MR.strings.copy_verb),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
IconButton(onClick = {
|
||||
chatModel.markShowingInvitationUsed()
|
||||
clipboard.shareText(simplexChatLink(link))
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_share),
|
||||
contentDescription = stringResource(MR.strings.share_verb),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewOneTimeLinkView() {
|
||||
SimpleXTheme {
|
||||
OneTimeLinkContent(
|
||||
connLinkInvitation = CreatedConnLink("https://smp16.simplex.im/i#pT0CA_nnqmLA", null)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -24,12 +24,14 @@ import boofcv.alg.color.ColorFormat
|
||||
import boofcv.android.ConvertCameraImage
|
||||
import boofcv.factory.fiducial.FactoryFiducial
|
||||
import boofcv.struct.image.GrayU8
|
||||
import chat.simplex.common.helpers.showAllowPermissionInSettingsAlert
|
||||
import chat.simplex.common.platform.TAG
|
||||
import chat.simplex.common.ui.theme.DEFAULT_PADDING_HALF
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import com.google.accompanist.permissions.PermissionStatus
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
import com.google.accompanist.permissions.shouldShowRationale
|
||||
import com.google.common.util.concurrent.ListenableFuture
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
@@ -143,7 +145,18 @@ actual fun QRCodeScanner(
|
||||
}
|
||||
}
|
||||
cameraPermissionState.status is PermissionStatus.Denied -> {
|
||||
Button({ cameraPermissionState.launchPermissionRequest() }, modifier = modifier, colors = buttonColors) {
|
||||
val context = LocalContext.current
|
||||
Button(
|
||||
onClick = {
|
||||
if (cameraPermissionState.status.shouldShowRationale) {
|
||||
cameraPermissionState.launchPermissionRequest()
|
||||
} else {
|
||||
context.showAllowPermissionInSettingsAlert()
|
||||
}
|
||||
},
|
||||
modifier = modifier,
|
||||
colors = buttonColors
|
||||
) {
|
||||
Icon(painterResource(MR.images.ic_camera_enhance), null)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
|
||||
Text(stringResource(MR.strings.enable_camera_access))
|
||||
|
||||
@@ -292,7 +292,7 @@ private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFiel
|
||||
if (!chatModel.desktopNoUserNoRemote) {
|
||||
ChatList(searchText = searchText, listState)
|
||||
}
|
||||
EmptyChatListView()
|
||||
NowYouCanView()
|
||||
|
||||
if (chatModel.chats.value.isEmpty() && !chatModel.switchingUsersAndHosts.value && !chatModel.desktopNoUserNoRemote) {
|
||||
if (chatModel.chatRunning.value == null) {
|
||||
@@ -302,7 +302,7 @@ private fun BoxScope.ChatListWithLoadingScreen(searchText: MutableState<TextFiel
|
||||
color = MaterialTheme.colors.secondary
|
||||
)
|
||||
} else {
|
||||
EmptyChatListView()
|
||||
NowYouCanView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -4,6 +4,8 @@ import SectionBottomSpacer
|
||||
import SectionItemView
|
||||
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.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
@@ -12,8 +14,11 @@ import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.*
|
||||
@@ -37,21 +42,21 @@ fun ModalData.ConnectViewLinkOrQrModal(rhId: Long?, close: () -> Unit) {
|
||||
|
||||
ModalView(close) {
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxWidth(),
|
||||
Modifier.fillMaxWidth().background(Color(0xfff5f5f6)),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.connect_via_link), withPadding = false)
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
Icon(
|
||||
painterResource(MR.images.ic_add_link),
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_connect_link),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(80.dp),
|
||||
tint = MaterialTheme.colors.primary
|
||||
contentScale = ContentScale.Fit,
|
||||
modifier = Modifier
|
||||
.size(200.dp)
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING * 1.5f))
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
SectionView(stringResource(MR.strings.paste_the_link_you_received).uppercase(), headerBottomPadding = 5.dp) {
|
||||
ConnectPasteLinkView(rhId, pastedLink, showQRCodeScanner, close)
|
||||
@@ -61,7 +66,12 @@ fun ModalData.ConnectViewLinkOrQrModal(rhId: Long?, close: () -> Unit) {
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
SectionView(stringResource(MR.strings.or_scan_qr_code).uppercase(), headerBottomPadding = 5.dp) {
|
||||
Box(Modifier.clip(RoundedCornerShape(DEFAULT_PADDING))) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.clip(RoundedCornerShape(24.dp))
|
||||
) {
|
||||
QRCodeScanner(showQRCodeScanner) { text ->
|
||||
val linkVerified = strIsSimplexLink(text)
|
||||
if (!linkVerified) {
|
||||
@@ -101,11 +111,30 @@ private fun ConnectPasteLinkView(rhId: Long?, pastedLink: MutableState<String>,
|
||||
)
|
||||
}
|
||||
}) {
|
||||
Box(Modifier.weight(1f)) {
|
||||
Text(stringResource(MR.strings.tap_to_paste_link))
|
||||
}
|
||||
if (connectProgressManager.showConnectProgress != null) {
|
||||
CIFileViewScope.progressIndicator(sizeMultiplier = 0.6f)
|
||||
Box(
|
||||
Modifier
|
||||
.weight(1f)
|
||||
.background(Color(0xFFF2F2F7)) // light gray outer background
|
||||
.padding(8.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.clip(RoundedCornerShape(16.dp))
|
||||
.background(Color.White),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
Text(
|
||||
stringResource(MR.strings.tap_to_paste_link),
|
||||
modifier = Modifier.padding(vertical = 16.dp),
|
||||
color = Color.Black.copy(alpha = 0.3f),
|
||||
fontSize = 18.sp
|
||||
)
|
||||
if (connectProgressManager.showConnectProgress != null) {
|
||||
CIFileViewScope.progressIndicator(sizeMultiplier = 0.6f)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -1,16 +1,15 @@
|
||||
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.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
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.*
|
||||
@@ -20,88 +19,62 @@ import chat.simplex.common.views.newchat.*
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
fun BoxScope.EmptyChatListView() {
|
||||
val closeAll = { ModalManager.start.closeModals() }
|
||||
Column(
|
||||
Modifier
|
||||
.align(Alignment.Center)
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Text(
|
||||
stringResource(MR.strings.now_you_can),
|
||||
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 {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll)
|
||||
}
|
||||
}
|
||||
fun BoxScope.NowYouCanView() {
|
||||
var showInviteSomeone by remember { mutableStateOf(false) }
|
||||
|
||||
if (showInviteSomeone) {
|
||||
BackHandler { showInviteSomeone = false }
|
||||
Column(
|
||||
Modifier
|
||||
.fillMaxSize()
|
||||
.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
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),
|
||||
)
|
||||
}
|
||||
Row(Modifier.fillMaxWidth()) {
|
||||
NavigationButtonBack(onButtonClicked = { showInviteSomeone = false })
|
||||
}
|
||||
Text(
|
||||
stringResource(MR.strings.invite_someone),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Bold),
|
||||
)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
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)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
val closeAll = { ModalManager.start.closeModals() }
|
||||
Column(
|
||||
Modifier
|
||||
.align(Alignment.Center)
|
||||
.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
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),
|
||||
)
|
||||
Text(
|
||||
stringResource(MR.strings.now_you_can),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Bold),
|
||||
modifier = Modifier.padding(horizontal = DEFAULT_PADDING)
|
||||
)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteCardComponent(
|
||||
image = painterResource(MR.images.ic_invitation_card_invite_someone),
|
||||
titleIcon = painterResource(MR.images.ic_repeat_one),
|
||||
title = stringResource(MR.strings.invite_someone_to_chat),
|
||||
onClick = {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
InviteSomeoneView {}
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteCardComponent(
|
||||
image = painterResource(MR.images.ic_invitation_card_one_time_link),
|
||||
titleIcon = painterResource(MR.images.ic_qr_code),
|
||||
title = stringResource(MR.strings.connect_via_link_or_qr),
|
||||
onClick = {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
ConnectViewLinkOrQrModal(chatModel.currentRemoteHost.value?.remoteHostId, {})
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -111,7 +84,7 @@ fun BoxScope.EmptyChatListView() {
|
||||
fun PreviewEmptyChatListView() {
|
||||
SimpleXTheme {
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
EmptyChatListView()
|
||||
NowYouCanView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,94 @@
|
||||
package chat.simplex.common.views.invitation_redesign
|
||||
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.Arrangement
|
||||
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.Card
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.ui.theme.DEFAULT_PADDING
|
||||
|
||||
@Composable
|
||||
fun InviteCardComponent(
|
||||
image: Painter,
|
||||
titleIcon: Painter,
|
||||
title: String,
|
||||
description: String? = null,
|
||||
onClick: () -> Unit
|
||||
) {
|
||||
Card(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
backgroundColor = Color(0xfff5f5f6),
|
||||
elevation = 0.dp,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.clickable(onClick = onClick)
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth(),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Image(
|
||||
image,
|
||||
contentDescription = null,
|
||||
contentScale = ContentScale.FillWidth,
|
||||
modifier = Modifier.fillMaxWidth()
|
||||
)
|
||||
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.Center,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(vertical = 16.dp)
|
||||
) {
|
||||
Icon(
|
||||
titleIcon,
|
||||
contentDescription = null,
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.size(24.dp)
|
||||
)
|
||||
Spacer(Modifier.width(8.dp))
|
||||
Text(
|
||||
title,
|
||||
style = MaterialTheme.typography.body1.copy(
|
||||
fontSize = 18.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
),
|
||||
color = Color.Black
|
||||
)
|
||||
}
|
||||
if (description != null) {
|
||||
Text(
|
||||
description,
|
||||
style = MaterialTheme.typography.body1.copy(
|
||||
fontSize = 14.sp,
|
||||
fontWeight = FontWeight.Medium
|
||||
),
|
||||
color = Color.Black
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -2,6 +2,8 @@ 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
|
||||
@@ -9,143 +11,75 @@ import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
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 androidx.compose.ui.unit.sp
|
||||
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.common.views.usersettings.UserAddressView
|
||||
import chat.simplex.res.MR
|
||||
|
||||
@Composable
|
||||
fun ModalData.InviteSomeoneView(close: () -> Unit) {
|
||||
val closeAll = { ModalManager.start.closeModals() }
|
||||
|
||||
fun InviteSomeoneView(close: () -> Unit) {
|
||||
ModalView(close) {
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxWidth(),
|
||||
Modifier.fillMaxSize().background(Color.White),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.invite_someone), withPadding = false)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable(endButtons = { AddContactLearnMoreButton() }) { _ ->
|
||||
NewChatView(chatModel.currentRemoteHost.value, NewChatOption.INVITE, close = closeAll)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth().padding(DEFAULT_PADDING),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_add_link),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(80.dp),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
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))
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(18.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
.clickable {
|
||||
ModalManager.start.showModalCloseable { closeAddress ->
|
||||
UserAddressView(chatModel = chatModel, shareViaProfile = false, autoCreateAddress = true, close = closeAddress)
|
||||
}
|
||||
}
|
||||
) {
|
||||
Column(
|
||||
Modifier.fillMaxWidth().padding(DEFAULT_PADDING),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_qr_code),
|
||||
contentDescription = null,
|
||||
modifier = Modifier.size(80.dp),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING_HALF))
|
||||
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING),
|
||||
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
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
InviteSomeoneContent()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InviteSomeoneContent() {
|
||||
InviteCardComponent(
|
||||
image = painterResource(MR.images.ic_invitation_card_one_time_link),
|
||||
titleIcon = painterResource(MR.images.ic_add_link),
|
||||
title = stringResource(MR.strings.create_private_1_time_link),
|
||||
description = stringResource(MR.strings.contact_can_use_link_or_scan_qr),
|
||||
onClick = {
|
||||
ModalManager.start.showModalCloseable { close ->
|
||||
OneTimeLinkView(rhId = chatModel.currentRemoteHost.value?.remoteHostId, close = close)
|
||||
}
|
||||
}
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
InviteCardComponent(
|
||||
image = painterResource(MR.images.ic_invitation_card_public_address),
|
||||
titleIcon = painterResource(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),
|
||||
onClick = {
|
||||
ModalManager.start.showModalCloseable { close ->
|
||||
OneTimeLinkView(rhId = chatModel.currentRemoteHost.value?.remoteHostId, close = close)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewInviteSomeoneView() {
|
||||
SimpleXTheme {
|
||||
ModalData().InviteSomeoneView(close = {})
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxSize().background(Color.White),
|
||||
horizontalAlignment = Alignment.CenterHorizontally
|
||||
) {
|
||||
AppBarTitle(stringResource(MR.strings.invite_someone), withPadding = false)
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
InviteSomeoneContent()
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,219 @@
|
||||
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.layout.*
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.runtime.saveable.rememberSaveable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
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
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
fun OneTimeLinkView(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 && ModalManager.start.openModalCount() <= 1) {
|
||||
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
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
ModalView(close) {
|
||||
OneTimeLinkContent(connLinkInvitation)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun OneTimeLinkContent(connLinkInvitation: CreatedConnLink) {
|
||||
val showShortLink = remember { mutableStateOf(true) }
|
||||
|
||||
ColumnWithScrollBar(
|
||||
Modifier.fillMaxSize(),
|
||||
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,
|
||||
color = MaterialTheme.colors.secondary,
|
||||
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,
|
||||
color = MaterialTheme.colors.secondary,
|
||||
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(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
SimpleXCreatedLinkQRCode(
|
||||
connLinkInvitation,
|
||||
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
|
||||
private fun OneTimeLinkBar(connLinkInvitation: CreatedConnLink, short: Boolean) {
|
||||
val clipboard = LocalClipboardManager.current
|
||||
val link = connLinkInvitation.simplexChatUri(short)
|
||||
|
||||
Surface(
|
||||
shape = RoundedCornerShape(24.dp),
|
||||
color = MaterialTheme.appColors.sentMessage,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING)
|
||||
) {
|
||||
Row(
|
||||
Modifier
|
||||
.padding(start = DEFAULT_PADDING, end = 4.dp)
|
||||
.heightIn(min = 48.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text(
|
||||
link,
|
||||
style = MaterialTheme.typography.body2,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
modifier = Modifier.weight(1f)
|
||||
)
|
||||
IconButton(onClick = {
|
||||
chatModel.markShowingInvitationUsed()
|
||||
clipboard.setText(AnnotatedString(simplexChatLink(link)))
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_content_copy),
|
||||
contentDescription = stringResource(MR.strings.copy_verb),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
IconButton(onClick = {
|
||||
chatModel.markShowingInvitationUsed()
|
||||
clipboard.shareText(simplexChatLink(link))
|
||||
}) {
|
||||
Icon(
|
||||
painterResource(MR.images.ic_share),
|
||||
contentDescription = stringResource(MR.strings.share_verb),
|
||||
tint = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewOneTimeLinkView() {
|
||||
SimpleXTheme {
|
||||
OneTimeLinkContent(
|
||||
connLinkInvitation = CreatedConnLink("https://smp16.simplex.im/i#pT0CA_nnqmLA", null)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -83,26 +83,31 @@ fun AddGroupLayout(
|
||||
val focusRequester = remember { FocusRequester() }
|
||||
val incognito = remember { mutableStateOf(incognitoPref.get()) }
|
||||
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
modifier = Modifier.imePadding(),
|
||||
sheetContent = {
|
||||
GetImageBottomSheet(
|
||||
chosenImage,
|
||||
onImageChange = { bitmap -> profileImage.value = resizeImageToStrSize(cropToSquare(bitmap), maxDataSize = 12500) },
|
||||
hideBottomSheet = {
|
||||
scope.launch { bottomSheetModalState.hide() }
|
||||
})
|
||||
},
|
||||
sheetState = bottomSheetModalState,
|
||||
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
|
||||
) {
|
||||
ModalView(close = close) {
|
||||
ColumnWithScrollBar {
|
||||
AppBarTitle(stringResource(MR.strings.create_secret_group_title), hostDevice(rhId))
|
||||
ModalBottomSheetLayout(
|
||||
scrimColor = Color.Black.copy(alpha = 0.12F),
|
||||
modifier = Modifier.imePadding(),
|
||||
sheetContent = {
|
||||
GetImageBottomSheet(
|
||||
chosenImage,
|
||||
onImageChange = { bitmap -> profileImage.value = resizeImageToStrSize(cropToSquare(bitmap), maxDataSize = 12500) },
|
||||
hideBottomSheet = {
|
||||
scope.launch { bottomSheetModalState.hide() }
|
||||
})
|
||||
},
|
||||
sheetState = bottomSheetModalState,
|
||||
sheetShape = RoundedCornerShape(topStart = 18.dp, topEnd = 18.dp)
|
||||
) {
|
||||
ModalView(close = close) {
|
||||
ColumnWithScrollBar {
|
||||
AppBarTitle(stringResource(MR.strings.create_secret_group_title), hostDevice(rhId))
|
||||
|
||||
Row(
|
||||
modifier = Modifier.fillMaxWidth().padding(horizontal = DEFAULT_PADDING * 3),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Box(
|
||||
Modifier
|
||||
.fillMaxWidth()
|
||||
.weight(1f)
|
||||
.padding(bottom = 24.dp),
|
||||
contentAlignment = Alignment.Center
|
||||
) {
|
||||
@@ -116,57 +121,67 @@ fun AddGroupLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
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)
|
||||
}
|
||||
|
||||
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))
|
||||
}
|
||||
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(
|
||||
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)
|
||||
)
|
||||
)
|
||||
)
|
||||
},
|
||||
textColor = MaterialTheme.colors.primary,
|
||||
iconColor = MaterialTheme.colors.primary,
|
||||
disabled = !canCreateProfile(displayName.value)
|
||||
)
|
||||
|
||||
IncognitoToggle(incognitoPref, incognito) { ModalManager.start.showModal { IncognitoView() } }
|
||||
IncognitoToggle(incognitoPref, incognito) { ModalManager.start.showModal { IncognitoView() } }
|
||||
|
||||
SectionTextFooter(
|
||||
buildAnnotatedString {
|
||||
append(sharedProfileInfo(chatModel, incognito.value))
|
||||
append("\n")
|
||||
append(annotatedStringResource(MR.strings.group_is_decentralized))
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(1000)
|
||||
focusRequester.requestFocus()
|
||||
SectionTextFooter(
|
||||
buildAnnotatedString {
|
||||
append(sharedProfileInfo(chatModel, incognito.value))
|
||||
append("\n")
|
||||
append(annotatedStringResource(MR.strings.group_is_decentralized))
|
||||
}
|
||||
)
|
||||
|
||||
LaunchedEffect(Unit) {
|
||||
delay(1000)
|
||||
focusRequester.requestFocus()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun canCreateProfile(displayName: String): Boolean = displayName.trim().isNotEmpty() && isValidDisplayName(displayName.trim())
|
||||
|
||||
@@ -24,6 +24,7 @@ import androidx.compose.ui.draw.alpha
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.input.VisualTransformation
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -452,6 +453,16 @@ private fun InviteView(rhId: Long?, connLinkInvitation: CreatedConnLink, contact
|
||||
val showShortLink = remember { mutableStateOf(true) }
|
||||
Spacer(Modifier.height(10.dp))
|
||||
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_one_time_link),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING * 3)
|
||||
)
|
||||
|
||||
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)
|
||||
}
|
||||
@@ -584,6 +595,16 @@ private fun ConnectView(rhId: Long?, showQRCodeScanner: MutableState<Boolean>, p
|
||||
}
|
||||
}
|
||||
|
||||
Image(
|
||||
painterResource(MR.images.ic_invitation_connect_link),
|
||||
contentDescription = null,
|
||||
modifier = Modifier
|
||||
.fillMaxWidth()
|
||||
.padding(horizontal = DEFAULT_PADDING * 3)
|
||||
)
|
||||
|
||||
Spacer(Modifier.height(DEFAULT_PADDING))
|
||||
|
||||
SectionView(stringResource(MR.strings.paste_the_link_you_received).uppercase(), headerBottomPadding = 5.dp) {
|
||||
PasteLinkView(rhId, pastedLink, showQRCodeScanner, close)
|
||||
}
|
||||
@@ -681,6 +702,13 @@ fun LinkTextView(link: String, share: Boolean) {
|
||||
// So using BasicTextField + manual ...
|
||||
Text("…", fontSize = 16.sp)
|
||||
if (share) {
|
||||
Spacer(Modifier.width(DEFAULT_PADDING))
|
||||
IconButton({
|
||||
chatModel.markShowingInvitationUsed()
|
||||
clipboard.setText(AnnotatedString(simplexChatLink(link)))
|
||||
}, Modifier.size(20.dp)) {
|
||||
Icon(painterResource(MR.images.ic_content_copy), stringResource(MR.strings.copy_verb), tint = MaterialTheme.colors.primary)
|
||||
}
|
||||
Spacer(Modifier.width(DEFAULT_PADDING))
|
||||
IconButton({
|
||||
chatModel.markShowingInvitationUsed()
|
||||
|
||||
@@ -467,6 +467,8 @@
|
||||
<string name="invite_someone">Invite someone</string>
|
||||
<string name="create_private_1_time_link">Create private 1-time link</string>
|
||||
<string name="contact_can_use_link_or_scan_qr">Your contact can use link or scan QR code</string>
|
||||
<string name="send_1_time_link_description">Send 1-time link to your contact via any messenger, it is secure. Ask to use it in the app.</string>
|
||||
<string name="or_show_qr_code_in_person_or_video_call">Or show this QR code in person or in a video call.</string>
|
||||
<string name="create_public_simplex_address">Create public SimpleX address</string>
|
||||
<string name="public_link_for_social_media_email_or_website">Public link for social media, email or website</string>
|
||||
<string name="loading_chats">Loading chats…</string>
|
||||
|
||||
Binary file not shown.
|
Before Width: | Height: | Size: 662 KiB After Width: | Height: | Size: 554 KiB |
Reference in New Issue
Block a user