android: rework incognito mode - choose when making connection (#2867)

* android: rework incognito mode - choose when making connection

* remove commented code

* remove commented code

* change text

* text editor border

* smaller qr code

* chat preview height

* fix spacing

* desktop dialogue

* remove import

---------

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
spaced4ndy
2023-08-08 17:28:18 +04:00
committed by GitHub
parent 1a567c88db
commit b095c09283
43 changed files with 591 additions and 488 deletions

View File

@@ -118,6 +118,7 @@ object NtfManager {
val actionPendingIntent: PendingIntent = PendingIntent.getBroadcast(SimplexApp.context, 0, actionIntent, flags)
val actionButton = when (action) {
NotificationAction.ACCEPT_CONTACT_REQUEST -> generalGetString(MR.strings.accept)
NotificationAction.ACCEPT_CONTACT_REQUEST_INCOGNITO -> generalGetString(MR.strings.accept_contact_incognito_button)
}
builder.addAction(0, actionButton, actionPendingIntent)
}
@@ -260,7 +261,8 @@ object NtfManager {
val chatId = intent?.getStringExtra(ChatIdKey) ?: return
val m = SimplexApp.context.chatModel
when (intent.action) {
NotificationAction.ACCEPT_CONTACT_REQUEST.name -> ntfManager.acceptContactRequestAction(userId, chatId)
NotificationAction.ACCEPT_CONTACT_REQUEST.name -> ntfManager.acceptContactRequestAction(userId, incognito = false, chatId)
NotificationAction.ACCEPT_CONTACT_REQUEST_INCOGNITO.name -> ntfManager.acceptContactRequestAction(userId, incognito = true, chatId)
RejectCallAction -> {
val invitation = m.callInvitations[chatId]
if (invitation != null) {

View File

@@ -13,7 +13,8 @@ actual fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) {
cameraPermissionState.launchPermissionRequest()
}
ConnectContactLayout(
chatModelIncognito = chatModel.incognito.value,
close
chatModel = chatModel,
incognitoPref = chatModel.controller.appPrefs.incognito,
close = close
)
}

View File

@@ -80,7 +80,6 @@ object ChatModel {
}
val performLA by lazy { mutableStateOf(ChatController.appPrefs.performLA.get()) }
val showAdvertiseLAUnavailableAlert = mutableStateOf(false)
val incognito by lazy { mutableStateOf(ChatController.appPrefs.incognito.get()) }
// current WebRTC call
val callManager = CallManager(this)

View File

@@ -331,7 +331,6 @@ object ChatController {
if (justStarted) {
chatModel.currentUser.value = user
chatModel.userCreated.value = true
apiSetIncognito(chatModel.incognito.value)
getUserChatData()
appPrefs.chatLastStart.set(Clock.System.now())
chatModel.chatRunning.value = true
@@ -546,12 +545,6 @@ object ChatController {
throw Error("apiSetXFTPConfig bad response: ${r.responseType} ${r.details}")
}
suspend fun apiSetIncognito(incognito: Boolean) {
val r = sendCmd(CC.SetIncognito(incognito))
if (r is CR.CmdOk) return
throw Exception("failed to set incognito: ${r.responseType} ${r.details}")
}
suspend fun apiExportArchive(config: ArchiveConfig) {
val r = sendCmd(CC.ApiExportArchive(config))
if (r is CR.CmdOk) return
@@ -819,14 +812,14 @@ object ChatController {
suspend fun apiAddContact(): String? {
suspend fun apiAddContact(incognito: Boolean): Pair<String, PendingContactConnection>? {
val userId = chatModel.currentUser.value?.userId ?: run {
Log.e(TAG, "apiAddContact: no current user")
return null
}
val r = sendCmd(CC.APIAddContact(userId))
val r = sendCmd(CC.APIAddContact(userId, incognito))
return when (r) {
is CR.Invitation -> r.connReqInvitation
is CR.Invitation -> r.connReqInvitation to r.connection
else -> {
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiAddContact", generalGetString(MR.strings.connection_error), r)
@@ -836,12 +829,19 @@ object ChatController {
}
}
suspend fun apiConnect(connReq: String): Boolean {
suspend fun apiSetConnectionIncognito(connId: Long, incognito: Boolean): PendingContactConnection? {
val r = sendCmd(CC.ApiSetConnectionIncognito(connId, incognito))
if (r is CR.ConnectionIncognitoUpdated) return r.toConnection
Log.e(TAG, "apiSetConnectionIncognito bad response: ${r.responseType} ${r.details}")
return null
}
suspend fun apiConnect(incognito: Boolean, connReq: String): Boolean {
val userId = chatModel.currentUser.value?.userId ?: run {
Log.e(TAG, "apiConnect: no current user")
return false
}
val r = sendCmd(CC.APIConnect(userId, connReq))
val r = sendCmd(CC.APIConnect(userId, incognito, connReq))
when {
r is CR.SentConfirmation || r is CR.SentInvitation -> return true
r is CR.ContactAlreadyExists -> {
@@ -998,8 +998,8 @@ object ChatController {
return null
}
suspend fun apiAcceptContactRequest(contactReqId: Long): Contact? {
val r = sendCmd(CC.ApiAcceptContact(contactReqId))
suspend fun apiAcceptContactRequest(incognito: Boolean, contactReqId: Long): Contact? {
val r = sendCmd(CC.ApiAcceptContact(incognito, contactReqId))
return when {
r is CR.AcceptingContactRequest -> r.contact
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorAgent
@@ -1805,7 +1805,6 @@ sealed class CC {
class SetTempFolder(val tempFolder: String): CC()
class SetFilesFolder(val filesFolder: String): CC()
class ApiSetXFTPConfig(val config: XFTPFileConfig?): CC()
class SetIncognito(val incognito: Boolean): CC()
class ApiExportArchive(val config: ArchiveConfig): CC()
class ApiImportArchive(val config: ArchiveConfig): CC()
class ApiDeleteStorage: CC()
@@ -1850,8 +1849,9 @@ sealed class CC {
class APIGetGroupMemberCode(val groupId: Long, val groupMemberId: Long): CC()
class APIVerifyContact(val contactId: Long, val connectionCode: String?): CC()
class APIVerifyGroupMember(val groupId: Long, val groupMemberId: Long, val connectionCode: String?): CC()
class APIAddContact(val userId: Long): CC()
class APIConnect(val userId: Long, val connReq: String): CC()
class APIAddContact(val userId: Long, val incognito: Boolean): CC()
class ApiSetConnectionIncognito(val connId: Long, val incognito: Boolean): CC()
class APIConnect(val userId: Long, val incognito: Boolean, val connReq: String): CC()
class ApiDeleteChat(val type: ChatType, val id: Long): CC()
class ApiClearChat(val type: ChatType, val id: Long): CC()
class ApiListContacts(val userId: Long): CC()
@@ -1872,7 +1872,7 @@ sealed class CC {
class ApiSendCallExtraInfo(val contact: Contact, val extraInfo: WebRTCExtraInfo): CC()
class ApiEndCall(val contact: Contact): CC()
class ApiCallStatus(val contact: Contact, val callStatus: WebRTCCallStatus): CC()
class ApiAcceptContact(val contactReqId: Long): CC()
class ApiAcceptContact(val incognito: Boolean, val contactReqId: Long): CC()
class ApiRejectContact(val contactReqId: Long): CC()
class ApiChatRead(val type: ChatType, val id: Long, val range: ItemRange): CC()
class ApiChatUnread(val type: ChatType, val id: Long, val unreadChat: Boolean): CC()
@@ -1908,7 +1908,6 @@ sealed class CC {
is SetTempFolder -> "/_temp_folder $tempFolder"
is SetFilesFolder -> "/_files_folder $filesFolder"
is ApiSetXFTPConfig -> if (config != null) "/_xftp on ${json.encodeToString(config)}" else "/_xftp off"
is SetIncognito -> "/incognito ${onOff(incognito)}"
is ApiExportArchive -> "/_db export ${json.encodeToString(config)}"
is ApiImportArchive -> "/_db import ${json.encodeToString(config)}"
is ApiDeleteStorage -> "/_db delete"
@@ -1956,8 +1955,9 @@ sealed class CC {
is APIGetGroupMemberCode -> "/_get code #$groupId $groupMemberId"
is APIVerifyContact -> "/_verify code @$contactId" + if (connectionCode != null) " $connectionCode" else ""
is APIVerifyGroupMember -> "/_verify code #$groupId $groupMemberId" + if (connectionCode != null) " $connectionCode" else ""
is APIAddContact -> "/_connect $userId"
is APIConnect -> "/_connect $userId $connReq"
is APIAddContact -> "/_connect $userId incognito=${onOff(incognito)}"
is ApiSetConnectionIncognito -> "/_set incognito :$connId ${onOff(incognito)}"
is APIConnect -> "/_connect $userId incognito=${onOff(incognito)} $connReq"
is ApiDeleteChat -> "/_delete ${chatRef(type, id)}"
is ApiClearChat -> "/_clear chat ${chatRef(type, id)}"
is ApiListContacts -> "/_contacts $userId"
@@ -1971,7 +1971,7 @@ sealed class CC {
is ApiShowMyAddress -> "/_show_address $userId"
is ApiSetProfileAddress -> "/_profile_address $userId ${onOff(on)}"
is ApiAddressAutoAccept -> "/_auto_accept $userId ${AutoAccept.cmdString(autoAccept)}"
is ApiAcceptContact -> "/_accept $contactReqId"
is ApiAcceptContact -> "/_accept incognito=${onOff(incognito)} $contactReqId"
is ApiRejectContact -> "/_reject $contactReqId"
is ApiSendCallInvitation -> "/_call invite @${contact.apiId} ${json.encodeToString(callType)}"
is ApiRejectCall -> "/_call reject @${contact.apiId}"
@@ -2006,7 +2006,6 @@ sealed class CC {
is SetTempFolder -> "setTempFolder"
is SetFilesFolder -> "setFilesFolder"
is ApiSetXFTPConfig -> "apiSetXFTPConfig"
is SetIncognito -> "setIncognito"
is ApiExportArchive -> "apiExportArchive"
is ApiImportArchive -> "apiImportArchive"
is ApiDeleteStorage -> "apiDeleteStorage"
@@ -2052,6 +2051,7 @@ sealed class CC {
is APIVerifyContact -> "apiVerifyContact"
is APIVerifyGroupMember -> "apiVerifyGroupMember"
is APIAddContact -> "apiAddContact"
is ApiSetConnectionIncognito -> "apiSetConnectionIncognito"
is APIConnect -> "apiConnect"
is ApiDeleteChat -> "apiDeleteChat"
is ApiClearChat -> "apiClearChat"
@@ -3249,7 +3249,8 @@ sealed class CR {
@Serializable @SerialName("contactCode") class ContactCode(val user: User, val contact: Contact, val connectionCode: String): CR()
@Serializable @SerialName("groupMemberCode") class GroupMemberCode(val user: User, val groupInfo: GroupInfo, val member: GroupMember, val connectionCode: String): CR()
@Serializable @SerialName("connectionVerified") class ConnectionVerified(val user: User, val verified: Boolean, val expectedCode: String): CR()
@Serializable @SerialName("invitation") class Invitation(val user: User, val connReqInvitation: String): CR()
@Serializable @SerialName("invitation") class Invitation(val user: User, val connReqInvitation: String, val connection: PendingContactConnection): CR()
@Serializable @SerialName("connectionIncognitoUpdated") class ConnectionIncognitoUpdated(val user: User, val toConnection: PendingContactConnection): CR()
@Serializable @SerialName("sentConfirmation") class SentConfirmation(val user: User): CR()
@Serializable @SerialName("sentInvitation") class SentInvitation(val user: User): CR()
@Serializable @SerialName("contactAlreadyExists") class ContactAlreadyExists(val user: User, val contact: Contact): CR()
@@ -3378,6 +3379,7 @@ sealed class CR {
is GroupMemberCode -> "groupMemberCode"
is ConnectionVerified -> "connectionVerified"
is Invitation -> "invitation"
is ConnectionIncognitoUpdated -> "connectionIncognitoUpdated"
is SentConfirmation -> "sentConfirmation"
is SentInvitation -> "sentInvitation"
is ContactAlreadyExists -> "contactAlreadyExists"
@@ -3503,6 +3505,7 @@ sealed class CR {
is GroupMemberCode -> withUser(user, "groupInfo: ${json.encodeToString(groupInfo)}\nmember: ${json.encodeToString(member)}\nconnectionCode: $connectionCode")
is ConnectionVerified -> withUser(user, "verified: $verified\nconnectionCode: $expectedCode")
is Invitation -> withUser(user, connReqInvitation)
is ConnectionIncognitoUpdated -> withUser(user, json.encodeToString(toConnection))
is SentConfirmation -> withUser(user, noDetails())
is SentInvitation -> withUser(user, noDetails())
is ContactAlreadyExists -> withUser(user, json.encodeToString(contact))
@@ -3822,6 +3825,7 @@ sealed class ChatErrorType {
is ServerProtocol -> "serverProtocol"
is AgentCommandError -> "agentCommandError"
is InvalidFileDescription -> "invalidFileDescription"
is ConnectionIncognitoChangeProhibited -> "connectionIncognitoChangeProhibited"
is InternalError -> "internalError"
is CEException -> "exception $message"
}
@@ -3895,6 +3899,7 @@ sealed class ChatErrorType {
@Serializable @SerialName("serverProtocol") object ServerProtocol: ChatErrorType()
@Serializable @SerialName("agentCommandError") class AgentCommandError(val message: String): ChatErrorType()
@Serializable @SerialName("invalidFileDescription") class InvalidFileDescription(val message: String): ChatErrorType()
@Serializable @SerialName("connectionIncognitoChangeProhibited") object ConnectionIncognitoChangeProhibited: ChatErrorType()
@Serializable @SerialName("internalError") class InternalError(val message: String): ChatErrorType()
@Serializable @SerialName("exception") class CEException(val message: String): ChatErrorType()
}

View File

@@ -10,7 +10,8 @@ import chat.simplex.res.MR
import kotlinx.coroutines.delay
enum class NotificationAction {
ACCEPT_CONTACT_REQUEST
ACCEPT_CONTACT_REQUEST,
ACCEPT_CONTACT_REQUEST_INCOGNITO
}
lateinit var ntfManager: NtfManager
@@ -29,7 +30,10 @@ abstract class NtfManager {
displayName = cInfo.displayName,
msgText = generalGetString(MR.strings.notification_new_contact_request),
image = cInfo.image,
listOf(NotificationAction.ACCEPT_CONTACT_REQUEST to { acceptContactRequestAction(user.userId, cInfo.id) })
listOf(
NotificationAction.ACCEPT_CONTACT_REQUEST to { acceptContactRequestAction(user.userId, incognito = false, cInfo.id) },
NotificationAction.ACCEPT_CONTACT_REQUEST_INCOGNITO to { acceptContactRequestAction(user.userId, incognito = true, cInfo.id) }
)
)
fun notifyMessageReceived(user: User, cInfo: ChatInfo, cItem: ChatItem) {
@@ -37,7 +41,7 @@ abstract class NtfManager {
displayNotification(user = user, chatId = cInfo.id, displayName = cInfo.displayName, msgText = hideSecrets(cItem))
}
fun acceptContactRequestAction(userId: Long?, chatId: ChatId) {
fun acceptContactRequestAction(userId: Long?, incognito: Boolean, chatId: ChatId) {
val isCurrentUser = ChatModel.currentUser.value?.userId == userId
val cInfo: ChatInfo.ContactRequest? = if (isCurrentUser) {
(ChatModel.getChat(chatId)?.chatInfo as? ChatInfo.ContactRequest) ?: return
@@ -45,7 +49,7 @@ abstract class NtfManager {
null
}
val apiId = chatId.replace("<@", "").toLongOrNull() ?: return
acceptContactRequest(apiId, cInfo, isCurrentUser, ChatModel)
acceptContactRequest(incognito, apiId, cInfo, isCurrentUser, ChatModel)
cancelNotificationsForChat(chatId)
}

View File

@@ -5,6 +5,7 @@ import InfoRowEllipsis
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionItemViewSpaceBetween
import SectionSpacer
import SectionTextFooter
import SectionView
@@ -271,7 +272,10 @@ fun ChatInfoLayout(
SectionSpacer()
if (customUserProfile != null) {
SectionView(generalGetString(MR.strings.incognito).uppercase()) {
InfoRow(generalGetString(MR.strings.incognito_random_profile), customUserProfile.chatViewName)
SectionItemViewSpaceBetween {
Text(generalGetString(MR.strings.incognito_random_profile))
Text(customUserProfile.chatViewName, color = Indigo)
}
}
SectionDividerSpaced()
}

View File

@@ -127,7 +127,6 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) {
searchText,
useLinkPreviews = useLinkPreviews,
linkMode = chatModel.simplexLinkMode.value,
chatModelIncognito = chatModel.incognito.value,
back = {
hideKeyboard(view)
AudioPlayer.stop()
@@ -379,7 +378,6 @@ fun ChatLayout(
searchValue: State<String>,
useLinkPreviews: Boolean,
linkMode: SimplexLinkMode,
chatModelIncognito: Boolean,
back: () -> Unit,
info: () -> Unit,
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
@@ -465,7 +463,7 @@ fun ChatLayout(
) {
ChatItemsList(
chat, unreadCount, composeState, chatItems, searchValue,
useLinkPreviews, linkMode, chatModelIncognito, showMemberInfo, loadPrevMessages, deleteMessage,
useLinkPreviews, linkMode, showMemberInfo, loadPrevMessages, deleteMessage,
receiveFile, cancelFile, joinGroup, acceptCall, acceptFeature,
updateContactStats, updateMemberStats, syncContactConnection, syncMemberConnection, findModelChat, findModelMember,
setReaction, showItemDetails, markRead, setFloatingButton, onComposed,
@@ -634,7 +632,6 @@ fun BoxWithConstraintsScope.ChatItemsList(
searchValue: State<String>,
useLinkPreviews: Boolean,
linkMode: SimplexLinkMode,
chatModelIncognito: Boolean,
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
loadPrevMessages: (ChatInfo) -> Unit,
deleteMessage: (Long, CIDeleteMode) -> Unit,
@@ -1184,7 +1181,6 @@ fun PreviewChatLayout() {
searchValue,
useLinkPreviews = true,
linkMode = SimplexLinkMode.DESCRIPTION,
chatModelIncognito = false,
back = {},
info = {},
showMemberInfo = { _, _ -> },
@@ -1252,7 +1248,6 @@ fun PreviewGroupChatLayout() {
searchValue,
useLinkPreviews = true,
linkMode = SimplexLinkMode.DESCRIPTION,
chatModelIncognito = false,
back = {},
info = {},
showMemberInfo = { _, _ -> },

View File

@@ -20,17 +20,16 @@ import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.input.TextFieldValue
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.ChatInfoToolbarTitle
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.newchat.InfoAboutIncognito
import chat.simplex.common.views.usersettings.SettingsActionItem
import chat.simplex.common.model.GroupInfo
import chat.simplex.common.platform.*
import chat.simplex.common.views.chat.group.GroupPreferencesView
import chat.simplex.res.MR
@Composable
@@ -41,7 +40,6 @@ fun AddGroupMembersView(groupInfo: GroupInfo, creatingGroup: Boolean = false, ch
val searchText = rememberSaveable(stateSaver = TextFieldValue.Saver) { mutableStateOf(TextFieldValue("")) }
BackHandler(onBack = close)
AddGroupMembersLayout(
chatModel.incognito.value,
groupInfo = groupInfo,
creatingGroup = creatingGroup,
contactsToAdd = getContactsToAdd(chatModel, searchText.value.text),
@@ -92,7 +90,6 @@ fun getContactsToAdd(chatModel: ChatModel, search: String): List<Contact> {
@Composable
fun AddGroupMembersLayout(
chatModelIncognito: Boolean,
groupInfo: GroupInfo,
creatingGroup: Boolean,
contactsToAdd: List<Contact>,
@@ -107,19 +104,31 @@ fun AddGroupMembersLayout(
removeContact: (Long) -> Unit,
close: () -> Unit,
) {
@Composable fun profileText() {
Row(
Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = Arrangement.Center
) {
Icon(
painterResource(MR.images.ic_info),
null,
tint = MaterialTheme.colors.secondary,
modifier = Modifier.padding(end = 10.dp).size(20.dp)
)
Text(generalGetString(MR.strings.group_main_profile_sent), textAlign = TextAlign.Center, style = MaterialTheme.typography.body2)
}
}
Column(
Modifier
.fillMaxWidth()
.verticalScroll(rememberScrollState()),
) {
AppBarTitle(stringResource(MR.strings.button_add_members))
InfoAboutIncognito(
chatModelIncognito,
false,
generalGetString(MR.strings.group_unsupported_incognito_main_profile_sent),
generalGetString(MR.strings.group_main_profile_sent),
true
)
profileText()
Spacer(Modifier.size(DEFAULT_PADDING))
Row(
Modifier.fillMaxWidth(),
@@ -350,7 +359,6 @@ fun showProhibitedToInviteIncognitoAlertDialog() {
fun PreviewAddGroupMembersLayout() {
SimpleXTheme {
AddGroupMembersLayout(
chatModelIncognito = false,
groupInfo = GroupInfo.sampleData,
creatingGroup = false,
contactsToAdd = listOf(Contact.sampleData, Contact.sampleData, Contact.sampleData),

View File

@@ -3,6 +3,7 @@ package chat.simplex.common.views.chat.group
import InfoRow
import SectionBottomSpacer
import SectionDividerSpaced
import SectionItemView
import SectionSpacer
import SectionTextFooter
import SectionView
@@ -445,19 +446,42 @@ private fun updateMemberRoleDialog(
}
fun connectViaMemberAddressAlert(connReqUri: String) {
AlertManager.shared.showAlertDialog(
AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.connect_via_member_address_alert_title),
text = generalGetString(MR.strings.connect_via_member_address_alert_desc),
confirmText = generalGetString(MR.strings.connect_via_link_verb),
onConfirm = {
val uri = URI(connReqUri)
withUriAction(uri) { linkType ->
withApi {
Log.d(TAG, "connectViaUri: connecting")
connectViaUri(chatModel, linkType, uri)
text = AnnotatedString(generalGetString(MR.strings.connect_via_member_address_alert_desc)),
buttons = {
Column {
SectionItemView({
AlertManager.shared.hideAlert()
val uri = URI(connReqUri)
withUriAction(uri) { linkType ->
withApi {
Log.d(TAG, "connectViaUri: connecting")
connectViaUri(chatModel, linkType, uri, incognito = false)
}
}
}) {
Text(generalGetString(MR.strings.connect_use_current_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.shared.hideAlert()
val uri = URI(connReqUri)
withUriAction(uri) { linkType ->
withApi {
Log.d(TAG, "connectViaUri: connecting incognito")
connectViaUri(chatModel, linkType, uri, incognito = true)
}
}
}) {
Text(generalGetString(MR.strings.connect_use_new_incognito_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.shared.hideAlert()
}) {
Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
}
},
}
)
}

View File

@@ -1,5 +1,6 @@
package chat.simplex.common.views.chatlist
import SectionItemView
import androidx.compose.foundation.combinedClickable
import androidx.compose.foundation.layout.*
import androidx.compose.material.*
@@ -13,9 +14,12 @@ import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.ui.text.AnnotatedString
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.platform.*
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.*
import chat.simplex.common.views.chat.group.deleteGroupDialog
@@ -23,9 +27,7 @@ import chat.simplex.common.views.chat.group.leaveGroupDialog
import chat.simplex.common.views.chat.item.InvalidJSONView
import chat.simplex.common.views.chat.item.ItemAction
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.newchat.ContactConnectionInfoView
import chat.simplex.common.platform.appPlatform
import chat.simplex.common.platform.ntfManager
import chat.simplex.common.views.newchat.*
import chat.simplex.res.MR
import kotlinx.coroutines.delay
import kotlinx.datetime.Clock
@@ -46,7 +48,7 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
is ChatInfo.Direct -> {
val contactNetworkStatus = chatModel.contactNetworkStatus(chat.chatInfo.contact)
ChatListNavLinkLayout(
chatLinkPreview = { ChatPreviewView(chat, chatModel.draft.value, chatModel.draftChatId.value, chatModel.incognito.value, chatModel.currentUser.value?.profile?.displayName, contactNetworkStatus, stopped, linkMode) },
chatLinkPreview = { ChatPreviewView(chat, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, contactNetworkStatus, stopped, linkMode) },
click = { directChatAction(chat.chatInfo, chatModel) },
dropdownMenuItems = { ContactMenuItems(chat, chatModel, showMenu, showMarkRead) },
showMenu,
@@ -55,7 +57,7 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
}
is ChatInfo.Group ->
ChatListNavLinkLayout(
chatLinkPreview = { ChatPreviewView(chat, chatModel.draft.value, chatModel.draftChatId.value, chatModel.incognito.value, chatModel.currentUser.value?.profile?.displayName, null, stopped, linkMode) },
chatLinkPreview = { ChatPreviewView(chat, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, stopped, linkMode) },
click = { groupChatAction(chat.chatInfo.groupInfo, chatModel) },
dropdownMenuItems = { GroupMenuItems(chat, chat.chatInfo.groupInfo, chatModel, showMenu, showMarkRead) },
showMenu,
@@ -63,7 +65,7 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
)
is ChatInfo.ContactRequest ->
ChatListNavLinkLayout(
chatLinkPreview = { ContactRequestView(chatModel.incognito.value, chat.chatInfo) },
chatLinkPreview = { ContactRequestView(chat.chatInfo) },
click = { contactRequestAlertDialog(chat.chatInfo, chatModel) },
dropdownMenuItems = { ContactRequestMenuItems(chat.chatInfo, chatModel, showMenu) },
showMenu,
@@ -320,11 +322,20 @@ fun LeaveGroupAction(groupInfo: GroupInfo, chatModel: ChatModel, showMenu: Mutab
@Composable
fun ContactRequestMenuItems(chatInfo: ChatInfo.ContactRequest, chatModel: ChatModel, showMenu: MutableState<Boolean>) {
ItemAction(
if (chatModel.incognito.value) stringResource(MR.strings.accept_contact_incognito_button) else stringResource(MR.strings.accept_contact_button),
if (chatModel.incognito.value) painterResource(MR.images.ic_theater_comedy_filled) else painterResource(MR.images.ic_check),
color = if (chatModel.incognito.value) Indigo else MaterialTheme.colors.onBackground,
stringResource(MR.strings.accept_contact_button),
painterResource(MR.images.ic_check),
color = MaterialTheme.colors.onBackground,
onClick = {
acceptContactRequest(chatInfo.apiId, chatInfo, true, chatModel)
acceptContactRequest(incognito = false, chatInfo.apiId, chatInfo, true, chatModel)
showMenu.value = false
}
)
ItemAction(
stringResource(MR.strings.accept_contact_incognito_button),
painterResource(MR.images.ic_theater_comedy),
color = MaterialTheme.colors.onBackground,
onClick = {
acceptContactRequest(incognito = true, chatInfo.apiId, chatInfo, true, chatModel)
showMenu.value = false
}
)
@@ -430,19 +441,37 @@ fun markChatUnread(chat: Chat, chatModel: ChatModel) {
}
fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) {
AlertManager.shared.showAlertDialog(
AlertManager.shared.showAlertDialogButtonsColumn(
title = generalGetString(MR.strings.accept_connection_request__question),
text = generalGetString(MR.strings.if_you_choose_to_reject_the_sender_will_not_be_notified),
confirmText = if (chatModel.incognito.value) generalGetString(MR.strings.accept_contact_incognito_button) else generalGetString(MR.strings.accept_contact_button),
onConfirm = { acceptContactRequest(contactRequest.apiId, contactRequest, true, chatModel) },
dismissText = generalGetString(MR.strings.reject_contact_button),
onDismiss = { rejectContactRequest(contactRequest, chatModel) }
text = AnnotatedString(generalGetString(MR.strings.if_you_choose_to_reject_the_sender_will_not_be_notified)),
buttons = {
Column {
SectionItemView({
AlertManager.shared.hideAlert()
acceptContactRequest(incognito = false, contactRequest.apiId, contactRequest, true, chatModel)
}) {
Text(generalGetString(MR.strings.accept_contact_button), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.shared.hideAlert()
acceptContactRequest(incognito = true, contactRequest.apiId, contactRequest, true, chatModel)
}) {
Text(generalGetString(MR.strings.accept_contact_incognito_button), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.shared.hideAlert()
rejectContactRequest(contactRequest, chatModel)
}) {
Text(generalGetString(MR.strings.reject_contact_button), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
}
}
}
)
}
fun acceptContactRequest(apiId: Long, contactRequest: ChatInfo.ContactRequest?, isCurrentUser: Boolean, chatModel: ChatModel) {
fun acceptContactRequest(incognito: Boolean, apiId: Long, contactRequest: ChatInfo.ContactRequest?, isCurrentUser: Boolean, chatModel: ChatModel) {
withApi {
val contact = chatModel.controller.apiAcceptContactRequest(apiId)
val contact = chatModel.controller.apiAcceptContactRequest(incognito, apiId)
if (contact != null && isCurrentUser && contactRequest != null) {
val chat = Chat(ChatInfo.Direct(contact), listOf())
chatModel.replaceChat(contactRequest.id, chat)
@@ -457,38 +486,6 @@ fun rejectContactRequest(contactRequest: ChatInfo.ContactRequest, chatModel: Cha
}
}
fun contactConnectionAlertDialog(connection: PendingContactConnection, chatModel: ChatModel) {
AlertManager.shared.showAlertDialogButtons(
title = generalGetString(
if (connection.initiated) MR.strings.you_invited_your_contact
else MR.strings.you_accepted_connection
),
text = generalGetString(
if (connection.viaContactUri) MR.strings.you_will_be_connected_when_your_connection_request_is_accepted
else MR.strings.you_will_be_connected_when_your_contacts_device_is_online
),
buttons = {
Row(
Modifier
.fillMaxWidth()
.padding(horizontal = 8.dp, vertical = 2.dp),
horizontalArrangement = Arrangement.Center,
) {
TextButton(onClick = {
AlertManager.shared.hideAlert()
deleteContactConnectionAlert(connection, chatModel) {}
}) {
Text(stringResource(MR.strings.delete_verb))
}
Spacer(Modifier.padding(horizontal = 4.dp))
TextButton(onClick = { AlertManager.shared.hideAlert() }) {
Text(stringResource(MR.strings.ok))
}
}
}
)
}
fun deleteContactConnectionAlert(connection: PendingContactConnection, chatModel: ChatModel, onSuccess: () -> Unit) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.delete_pending_connection__question),
@@ -662,7 +659,6 @@ fun PreviewChatListNavLinkDirect() {
),
null,
null,
false,
null,
null,
stopped = false,
@@ -702,7 +698,6 @@ fun PreviewChatListNavLinkGroup() {
),
null,
null,
false,
null,
null,
stopped = false,
@@ -727,7 +722,7 @@ fun PreviewChatListNavLinkContactRequest() {
SimpleXTheme {
ChatListNavLinkLayout(
chatLinkPreview = {
ContactRequestView(false, ChatInfo.ContactRequest.sampleData)
ContactRequestView(ChatInfo.ContactRequest.sampleData)
},
click = {},
dropdownMenuItems = null,

View File

@@ -1,5 +1,6 @@
package chat.simplex.common.views.chatlist
import SectionItemView
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.lazy.*
@@ -13,9 +14,11 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.graphics.*
import androidx.compose.ui.platform.LocalUriHandler
import androidx.compose.ui.text.AnnotatedString
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.*
import chat.simplex.common.SettingsViewState
import chat.simplex.common.model.*
@@ -221,14 +224,6 @@ private fun ChatListToolbar(chatModel: ChatModel, drawerState: DrawerState, user
},
title = {
Row(verticalAlignment = Alignment.CenterVertically) {
if (chatModel.incognito.value) {
Icon(
painterResource(MR.images.ic_theater_comedy_filled),
stringResource(MR.strings.incognito),
tint = Indigo,
modifier = Modifier.padding(10.dp).size(26.dp)
)
}
Text(
stringResource(MR.strings.your_chats),
color = MaterialTheme.colors.onBackground,
@@ -317,17 +312,37 @@ fun connectIfOpenedViaUri(uri: URI, chatModel: ChatModel) {
ConnectionLinkType.INVITATION -> generalGetString(MR.strings.connect_via_invitation_link)
ConnectionLinkType.GROUP -> generalGetString(MR.strings.connect_via_group_link)
}
AlertManager.shared.showAlertDialog(
AlertManager.shared.showAlertDialogButtonsColumn(
title = title,
text = if (linkType == ConnectionLinkType.GROUP)
generalGetString(MR.strings.you_will_join_group)
AnnotatedString(generalGetString(MR.strings.you_will_join_group))
else
generalGetString(MR.strings.profile_will_be_sent_to_contact_sending_link),
confirmText = generalGetString(MR.strings.connect_via_link_verb),
onConfirm = {
withApi {
Log.d(TAG, "connectIfOpenedViaUri: connecting")
connectViaUri(chatModel, linkType, uri)
AnnotatedString(generalGetString(MR.strings.profile_will_be_sent_to_contact_sending_link)),
buttons = {
Column {
SectionItemView({
AlertManager.shared.hideAlert()
withApi {
Log.d(TAG, "connectIfOpenedViaUri: connecting")
connectViaUri(chatModel, linkType, uri, incognito = false)
}
}) {
Text(generalGetString(MR.strings.connect_use_current_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.shared.hideAlert()
withApi {
Log.d(TAG, "connectIfOpenedViaUri: connecting incognito")
connectViaUri(chatModel, linkType, uri, incognito = true)
}
}) {
Text(generalGetString(MR.strings.connect_use_new_incognito_profile), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
SectionItemView({
AlertManager.shared.hideAlert()
}) {
Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
}
}
}
)

View File

@@ -33,7 +33,6 @@ fun ChatPreviewView(
chat: Chat,
chatModelDraft: ComposeState?,
chatModelDraftChatId: ChatId?,
chatModelIncognito: Boolean,
currentUserProfileDisplayName: String?,
contactNetworkStatus: NetworkStatus?,
stopped: Boolean,
@@ -138,7 +137,7 @@ fun ChatPreviewView(
}
@Composable
fun chatPreviewText(chatModelIncognito: Boolean) {
fun chatPreviewText() {
val ci = chat.chatItems.lastOrNull()
if (ci != null) {
val (text: CharSequence, inlineTextContent) = when {
@@ -175,7 +174,7 @@ fun ChatPreviewView(
}
is ChatInfo.Group ->
when (cInfo.groupInfo.membership.memberStatus) {
GroupMemberStatus.MemInvited -> Text(groupInvitationPreviewText(chatModelIncognito, currentUserProfileDisplayName, cInfo.groupInfo))
GroupMemberStatus.MemInvited -> Text(groupInvitationPreviewText(currentUserProfileDisplayName, cInfo.groupInfo))
GroupMemberStatus.MemAccepted -> Text(stringResource(MR.strings.group_connection_pending), color = MaterialTheme.colors.secondary)
else -> {}
}
@@ -184,6 +183,37 @@ fun ChatPreviewView(
}
}
@Composable
fun chatStatusImage() {
if (cInfo is ChatInfo.Direct) {
val descr = contactNetworkStatus?.statusString
when (contactNetworkStatus) {
is NetworkStatus.Connected ->
IncognitoIcon(chat.chatInfo.incognito)
is NetworkStatus.Error ->
Icon(
painterResource(MR.images.ic_error),
contentDescription = descr,
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.size(19.dp)
)
else ->
CircularProgressIndicator(
Modifier
.padding(horizontal = 2.dp)
.size(15.dp),
color = MaterialTheme.colors.secondary,
strokeWidth = 1.5.dp
)
}
} else {
IncognitoIcon(chat.chatInfo.incognito)
}
}
Row {
Box(contentAlignment = Alignment.BottomEnd) {
ChatInfoImage(cInfo, size = 72.dp)
@@ -199,14 +229,14 @@ fun ChatPreviewView(
chatPreviewTitle()
val height = with(LocalDensity.current) { 46.sp.toDp() }
Row(Modifier.heightIn(min = height)) {
chatPreviewText(chatModelIncognito)
chatPreviewText()
}
}
val ts = chat.chatItems.lastOrNull()?.timestampText ?: getTimestampText(chat.chatInfo.updatedAt)
Box(
contentAlignment = Alignment.TopEnd
) {
val ts = chat.chatItems.lastOrNull()?.timestampText ?: getTimestampText(chat.chatInfo.updatedAt)
Text(
ts,
color = MaterialTheme.colors.secondary,
@@ -262,24 +292,33 @@ fun ChatPreviewView(
)
}
}
if (cInfo is ChatInfo.Direct) {
Box(
Modifier.padding(top = 52.dp),
contentAlignment = Alignment.Center
) {
ChatStatusImage(contactNetworkStatus)
}
Box(
Modifier.padding(top = 50.dp),
contentAlignment = Alignment.Center
) {
chatStatusImage()
}
}
}
}
@Composable
private fun groupInvitationPreviewText(chatModelIncognito: Boolean, currentUserProfileDisplayName: String?, groupInfo: GroupInfo): String {
fun IncognitoIcon(incognito: Boolean) {
if (incognito) {
Icon(
painterResource(MR.images.ic_theater_comedy),
contentDescription = null,
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.size(21.dp)
)
}
}
@Composable
private fun groupInvitationPreviewText(currentUserProfileDisplayName: String?, groupInfo: GroupInfo): String {
return if (groupInfo.membership.memberIncognito)
String.format(stringResource(MR.strings.group_preview_join_as), groupInfo.membership.memberProfile.displayName)
else if (chatModelIncognito)
String.format(stringResource(MR.strings.group_preview_join_as), currentUserProfileDisplayName ?: "")
else
stringResource(MR.strings.group_preview_you_are_invited)
}
@@ -289,28 +328,6 @@ fun unreadCountStr(n: Int): String {
return if (n < 1000) "$n" else "${n / 1000}" + stringResource(MR.strings.thousand_abbreviation)
}
@Composable
fun ChatStatusImage(s: NetworkStatus?) {
val descr = s?.statusString
if (s is NetworkStatus.Error) {
Icon(
painterResource(MR.images.ic_error),
contentDescription = descr,
tint = MaterialTheme.colors.secondary,
modifier = Modifier
.size(19.dp)
)
} else if (s !is NetworkStatus.Connected) {
CircularProgressIndicator(
Modifier
.padding(horizontal = 2.dp)
.size(15.dp),
color = MaterialTheme.colors.secondary,
strokeWidth = 1.5.dp
)
}
}
@Preview/*(
uiMode = Configuration.UI_MODE_NIGHT_YES,
showBackground = true,
@@ -319,6 +336,6 @@ fun ChatStatusImage(s: NetworkStatus?) {
@Composable
fun PreviewChatPreviewView() {
SimpleXTheme {
ChatPreviewView(Chat.sampleData, null, null, false, "", contactNetworkStatus = NetworkStatus.Connected(), stopped = false, linkMode = SimplexLinkMode.DESCRIPTION)
ChatPreviewView(Chat.sampleData, null, null, "", contactNetworkStatus = NetworkStatus.Connected(), stopped = false, linkMode = SimplexLinkMode.DESCRIPTION)
}
}

View File

@@ -39,16 +39,22 @@ fun ContactConnectionView(contactConnection: PendingContactConnection) {
val height = with(LocalDensity.current) { 46.sp.toDp() }
Text(contactConnection.description, Modifier.heightIn(min = height), maxLines = 2, color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight)
}
val ts = getTimestampText(contactConnection.updatedAt)
Column(
Modifier.fillMaxHeight(),
Box(
contentAlignment = Alignment.TopEnd
) {
val ts = getTimestampText(contactConnection.updatedAt)
Text(
ts,
color = MaterialTheme.colors.secondary,
style = MaterialTheme.typography.body2,
modifier = Modifier.padding(bottom = 5.dp)
)
Box(
Modifier.padding(top = 50.dp),
contentAlignment = Alignment.Center
) {
IncognitoIcon(contactConnection.incognito)
}
}
}
}

View File

@@ -18,7 +18,7 @@ import chat.simplex.common.model.getTimestampText
import chat.simplex.res.MR
@Composable
fun ContactRequestView(chatModelIncognito: Boolean, contactRequest: ChatInfo.ContactRequest) {
fun ContactRequestView(contactRequest: ChatInfo.ContactRequest) {
Row {
ChatInfoImage(contactRequest, size = 72.dp)
Column(
@@ -32,7 +32,7 @@ fun ContactRequestView(chatModelIncognito: Boolean, contactRequest: ChatInfo.Con
overflow = TextOverflow.Ellipsis,
style = MaterialTheme.typography.h3,
fontWeight = FontWeight.Bold,
color = if (chatModelIncognito) Indigo else MaterialTheme.colors.primary
color = MaterialTheme.colors.primary
)
val height = with(LocalDensity.current) { 46.sp.toDp() }
Text(stringResource(MR.strings.contact_wants_to_connect_with_you), Modifier.heightIn(min = height), maxLines = 2, color = if (isInDarkTheme()) MessagePreviewDark else MessagePreviewLight)

View File

@@ -121,14 +121,6 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
color = MaterialTheme.colors.onBackground,
fontWeight = FontWeight.SemiBold,
)
if (chatModel.incognito.value) {
Icon(
painterResource(MR.images.ic_theater_comedy_filled),
stringResource(MR.strings.incognito),
tint = Indigo,
modifier = Modifier.padding(10.dp).size(26.dp)
)
}
}
},
onTitleClick = null,

View File

@@ -1,7 +1,10 @@
package chat.simplex.common.views.helpers
import androidx.compose.foundation.BorderStroke
import androidx.compose.foundation.border
import androidx.compose.foundation.interaction.MutableInteractionSource
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.foundation.text.BasicTextField
import androidx.compose.foundation.text.KeyboardOptions
import androidx.compose.material.*
@@ -48,19 +51,20 @@ fun TextEditor(
Modifier
.fillMaxWidth()
.padding(contentPadding)
.heightIn(min = 52.dp),
// .border(border = BorderStroke(1.dp, strokeColor), shape = RoundedCornerShape(26.dp)),
.heightIn(min = 52.dp)
.border(border = BorderStroke(1.dp, strokeColor), shape = RoundedCornerShape(14.dp)),
contentAlignment = Alignment.Center,
) {
val modifier = modifier
val textFieldModifier = modifier
.fillMaxWidth()
.navigationBarsWithImePadding()
.onFocusChanged { focused = it.isFocused }
.padding(10.dp)
BasicTextField(
value = value.value,
onValueChange = { value.value = it },
modifier = if (focusRequester == null) modifier else modifier.focusRequester(focusRequester),
modifier = if (focusRequester == null) textFieldModifier else textFieldModifier.focusRequester(focusRequester),
textStyle = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground, lineHeight = 22.sp),
keyboardOptions = KeyboardOptions(
capitalization = KeyboardCapitalization.None,

View File

@@ -1,7 +1,7 @@
package chat.simplex.common.views.newchat
import SectionBottomSpacer
import SectionSpacer
import SectionTextFooter
import SectionView
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.layout.*
@@ -14,20 +14,26 @@ import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.platform.LocalClipboardManager
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.text.style.TextAlign
import androidx.compose.ui.unit.dp
import chat.simplex.common.model.*
import chat.simplex.common.platform.shareText
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.SettingsActionItem
import chat.simplex.common.views.usersettings.*
import chat.simplex.res.MR
@Composable
fun AddContactView(connReqInvitation: String, connIncognito: Boolean) {
fun AddContactView(
chatModel: ChatModel,
connReqInvitation: String,
contactConnection: MutableState<PendingContactConnection?>
) {
val clipboard = LocalClipboardManager.current
AddContactLayout(
chatModel = chatModel,
incognitoPref = chatModel.controller.appPrefs.incognito,
connReq = connReqInvitation,
connIncognito = connIncognito,
contactConnection = contactConnection,
share = { clipboard.shareText(connReqInvitation) },
learnMore = {
ModalManager.center.showModal {
@@ -45,57 +51,63 @@ fun AddContactView(connReqInvitation: String, connIncognito: Boolean) {
}
@Composable
fun AddContactLayout(connReq: String, connIncognito: Boolean, share: () -> Unit, learnMore: () -> Unit) {
fun AddContactLayout(
chatModel: ChatModel,
incognitoPref: SharedPreference<Boolean>,
connReq: String,
contactConnection: MutableState<PendingContactConnection?>,
share: () -> Unit,
learnMore: () -> Unit
) {
val incognito = remember { mutableStateOf(incognitoPref.get()) }
LaunchedEffect(incognito.value) {
withApi {
val contactConnVal = contactConnection.value
if (contactConnVal != null) {
chatModel.controller.apiSetConnectionIncognito(contactConnVal.pccConnId, incognito.value)?.let {
contactConnection.value = it
chatModel.updateContactConnection(it)
}
}
}
}
Column(
Modifier
.verticalScroll(rememberScrollState()),
verticalArrangement = Arrangement.SpaceBetween,
) {
AppBarTitle(stringResource(MR.strings.add_contact))
OneTimeLinkProfileText(connIncognito)
SectionSpacer()
SectionView(stringResource(MR.strings.one_time_link_short).uppercase()) {
OneTimeLinkSection(connReq, share, learnMore)
if (connReq.isNotEmpty()) {
QRCode(
connReq, Modifier
.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF)
.aspectRatio(1f)
)
} else {
CircularProgressIndicator(
Modifier
.size(36.dp)
.padding(4.dp)
.align(Alignment.CenterHorizontally),
color = MaterialTheme.colors.secondary,
strokeWidth = 3.dp
)
}
IncognitoToggle(incognitoPref, incognito) { ModalManager.start.showModal { IncognitoView() } }
ShareLinkButton(share)
OneTimeLinkLearnMoreButton(learnMore)
}
SectionTextFooter(sharedProfileInfo(chatModel, incognito.value))
SectionBottomSpacer()
}
}
@Composable
fun OneTimeLinkProfileText(connIncognito: Boolean) {
Row(Modifier.padding(horizontal = DEFAULT_PADDING)) {
InfoAboutIncognito(
connIncognito,
true,
generalGetString(MR.strings.incognito_random_profile_description),
generalGetString(MR.strings.your_profile_will_be_sent)
)
}
}
@Composable
fun ColumnScope.OneTimeLinkSection(connReq: String, share: () -> Unit, learnMore: () -> Unit) {
if (connReq.isNotEmpty()) {
QRCode(
connReq, Modifier
.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF)
.aspectRatio(1f)
)
} else {
CircularProgressIndicator(
Modifier
.size(36.dp)
.padding(4.dp)
.align(Alignment.CenterHorizontally),
color = MaterialTheme.colors.secondary,
strokeWidth = 3.dp
)
}
ShareLinkButton(share)
OneTimeLinkLearnMoreButton(learnMore)
}
@Composable
fun ShareLinkButton(onClick: () -> Unit) {
SettingsActionItem(
@@ -117,39 +129,38 @@ fun OneTimeLinkLearnMoreButton(onClick: () -> Unit) {
}
@Composable
fun InfoAboutIncognito(chatModelIncognito: Boolean, supportedIncognito: Boolean = true, onText: String, offText: String, centered: Boolean = false) {
if (chatModelIncognito) {
Row(
Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = if (centered) Arrangement.Center else Arrangement.Start
) {
Icon(
if (supportedIncognito) painterResource(MR.images.ic_theater_comedy_filled) else painterResource(MR.images.ic_info),
stringResource(MR.strings.incognito),
tint = if (supportedIncognito) Indigo else WarningOrange,
modifier = Modifier.padding(end = 10.dp).size(20.dp)
)
Text(onText, textAlign = if (centered) TextAlign.Center else TextAlign.Left, style = MaterialTheme.typography.body2)
}
fun IncognitoToggle(
incognitoPref: SharedPreference<Boolean>,
incognito: MutableState<Boolean>,
onClickInfo: () -> Unit
) {
SettingsActionItemWithContent(
icon = if (incognito.value) painterResource(MR.images.ic_theater_comedy_filled) else painterResource(MR.images.ic_theater_comedy),
text = null,
click = onClickInfo,
iconColor = if (incognito.value) Indigo else MaterialTheme.colors.secondary,
extraPadding = false
) {
SharedPreferenceToggleWithIcon(
stringResource(MR.strings.incognito),
painterResource(MR.images.ic_info),
stopped = false,
onClickInfo = onClickInfo,
preference = incognitoPref,
preferenceState = incognito
)
}
}
fun sharedProfileInfo(
chatModel: ChatModel,
incognito: Boolean
): String {
val name = chatModel.currentUser.value?.displayName ?: ""
return if (incognito) {
generalGetString(MR.strings.connect__a_new_random_profile_will_be_shared)
} else {
Row(
Modifier
.fillMaxWidth()
.padding(vertical = 4.dp),
verticalAlignment = Alignment.CenterVertically,
horizontalArrangement = if (centered) Arrangement.Center else Arrangement.Start
) {
Icon(
painterResource(MR.images.ic_info),
stringResource(MR.strings.incognito),
tint = MaterialTheme.colors.secondary,
modifier = Modifier.padding(end = 10.dp).size(20.dp)
)
Text(offText, textAlign = if (centered) TextAlign.Center else TextAlign.Left, style = MaterialTheme.typography.body2)
}
String.format(generalGetString(MR.strings.connect__your_profile_will_be_shared), name)
}
}
@@ -162,8 +173,10 @@ fun InfoAboutIncognito(chatModelIncognito: Boolean, supportedIncognito: Boolean
fun PreviewAddContactView() {
SimpleXTheme {
AddContactLayout(
chatModel = ChatModel,
incognitoPref = SharedPreference({ false }, {}),
connReq = "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D",
connIncognito = false,
contactConnection = mutableStateOf(PendingContactConnection.getSampleData()),
share = {},
learnMore = {},
)

View File

@@ -2,16 +2,18 @@ package chat.simplex.common.views.newchat
import SectionBottomSpacer
import SectionDividerSpaced
import SectionTextFooter
import SectionView
import androidx.compose.desktop.ui.tooling.preview.Preview
import androidx.compose.foundation.*
import androidx.compose.foundation.layout.*
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.graphics.Color
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.*
@@ -19,10 +21,10 @@ import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.chat.LocalAliasEditor
import chat.simplex.common.views.chatlist.deleteContactConnectionAlert
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.SettingsActionItem
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.PendingContactConnection
import chat.simplex.common.platform.shareText
import chat.simplex.common.views.usersettings.*
import chat.simplex.res.MR
@Composable
@@ -49,10 +51,10 @@ fun ContactConnectionInfoView(
}
val clipboard = LocalClipboardManager.current
ContactConnectionInfoLayout(
chatModel = chatModel,
connReq = connReqInvitation,
contactConnection,
connIncognito = contactConnection.incognito,
focusAlias,
contactConnection = contactConnection,
focusAlias = focusAlias,
deleteConnection = { deleteContactConnectionAlert(contactConnection, chatModel, close) },
onLocalAliasChanged = { setContactAlias(contactConnection, it, chatModel) },
share = { if (connReqInvitation != null) clipboard.shareText(connReqInvitation) },
@@ -73,22 +75,43 @@ fun ContactConnectionInfoView(
@Composable
private fun ContactConnectionInfoLayout(
chatModel: ChatModel,
connReq: String?,
contactConnection: PendingContactConnection,
connIncognito: Boolean,
focusAlias: Boolean,
deleteConnection: () -> Unit,
onLocalAliasChanged: (String) -> Unit,
share: () -> Unit,
learnMore: () -> Unit,
) {
@Composable fun incognitoEnabled() {
if (contactConnection.incognito) {
SettingsActionItemWithContent(
icon = painterResource(MR.images.ic_theater_comedy_filled),
text = null,
click = { ModalManager.start.showModal { IncognitoView() } },
iconColor = Indigo,
extraPadding = false
) {
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Text(stringResource(MR.strings.incognito), Modifier.padding(end = 4.dp))
Icon(
painterResource(MR.images.ic_info),
null,
tint = MaterialTheme.colors.primary
)
}
}
}
}
Column(
Modifier
.verticalScroll(rememberScrollState()),
) {
AppBarTitle(
stringResource(
if (contactConnection.initiated) MR.strings.you_invited_your_contact
if (contactConnection.initiated) MR.strings.you_invited_a_contact
else MR.strings.you_accepted_connection
)
)
@@ -101,7 +124,6 @@ private fun ContactConnectionInfoLayout(
),
Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING)
)
OneTimeLinkProfileText(connIncognito)
if (contactConnection.groupLinkId == null) {
LocalAliasEditor(contactConnection.localAlias, center = false, leadingIcon = true, focus = focusAlias, updateValue = onLocalAliasChanged)
@@ -109,11 +131,20 @@ private fun ContactConnectionInfoLayout(
SectionView {
if (!connReq.isNullOrEmpty() && contactConnection.initiated) {
OneTimeLinkSection(connReq, share, learnMore)
QRCode(
connReq, Modifier
.padding(horizontal = DEFAULT_PADDING, vertical = DEFAULT_PADDING_HALF)
.aspectRatio(1f)
)
incognitoEnabled()
ShareLinkButton(share)
OneTimeLinkLearnMoreButton(learnMore)
} else {
incognitoEnabled()
OneTimeLinkLearnMoreButton(learnMore)
}
}
SectionTextFooter(sharedProfileInfo(chatModel, contactConnection.incognito))
SectionDividerSpaced(maxBottomPadding = false)
@@ -149,9 +180,9 @@ private fun setContactAlias(contactConnection: PendingContactConnection, localAl
private fun PreviewContactConnectionInfoView() {
SimpleXTheme {
ContactConnectionInfoLayout(
chatModel = ChatModel,
connReq = "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D",
PendingContactConnection.getSampleData(),
connIncognito = false,
contactConnection = PendingContactConnection.getSampleData(),
focusAlias = false,
deleteConnection = {},
onLocalAliasChanged = {},

View File

@@ -10,6 +10,7 @@ import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.PendingContactConnection
import chat.simplex.common.views.helpers.ModalManager
import chat.simplex.common.views.helpers.withApi
import chat.simplex.common.views.usersettings.UserAddressView
@@ -23,10 +24,16 @@ enum class CreateLinkTab {
fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) {
val selection = remember { mutableStateOf(initialSelection) }
val connReqInvitation = rememberSaveable { m.connReqInv }
val contactConnection: MutableState<PendingContactConnection?> = rememberSaveable { mutableStateOf(null) }
val creatingConnReq = rememberSaveable { mutableStateOf(false) }
LaunchedEffect(selection.value) {
if (selection.value == CreateLinkTab.ONE_TIME && connReqInvitation.value.isNullOrEmpty() && !creatingConnReq.value) {
createInvitation(m, creatingConnReq, connReqInvitation)
if (
selection.value == CreateLinkTab.ONE_TIME
&& connReqInvitation.value.isNullOrEmpty()
&& contactConnection.value == null
&& !creatingConnReq.value
) {
createInvitation(m, creatingConnReq, connReqInvitation, contactConnection)
}
}
/** When [AddContactView] is open, we don't need to drop [chatModel.connReqInv].
@@ -42,9 +49,12 @@ fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) {
}
val tabTitles = CreateLinkTab.values().map {
when {
it == CreateLinkTab.ONE_TIME && connReqInvitation.value.isNullOrEmpty() -> stringResource(MR.strings.create_one_time_link)
it == CreateLinkTab.ONE_TIME -> stringResource(MR.strings.one_time_link)
it == CreateLinkTab.LONG_TERM -> stringResource(MR.strings.your_simplex_contact_address)
it == CreateLinkTab.ONE_TIME && connReqInvitation.value.isNullOrEmpty() && contactConnection.value == null ->
stringResource(MR.strings.create_one_time_link)
it == CreateLinkTab.ONE_TIME ->
stringResource(MR.strings.one_time_link)
it == CreateLinkTab.LONG_TERM ->
stringResource(MR.strings.your_simplex_contact_address)
else -> ""
}
}
@@ -56,7 +66,7 @@ fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) {
Column(Modifier.weight(1f)) {
when (selection.value) {
CreateLinkTab.ONE_TIME -> {
AddContactView(connReqInvitation.value ?: "", m.incognito.value)
AddContactView(m, connReqInvitation.value ?: "", contactConnection)
}
CreateLinkTab.LONG_TERM -> {
UserAddressView(m, viaCreateLinkView = true, close = {})
@@ -89,12 +99,18 @@ fun CreateLinkView(m: ChatModel, initialSelection: CreateLinkTab) {
}
}
private fun createInvitation(m: ChatModel, creatingConnReq: MutableState<Boolean>, connReqInvitation: MutableState<String?>) {
private fun createInvitation(
m: ChatModel,
creatingConnReq: MutableState<Boolean>,
connReqInvitation: MutableState<String?>,
contactConnection: MutableState<PendingContactConnection?>
) {
creatingConnReq.value = true
withApi {
val connReq = m.controller.apiAddContact()
if (connReq != null) {
connReqInvitation.value = connReq
val r = m.controller.apiAddContact(incognito = m.controller.appPrefs.incognito.get())
if (r != null) {
connReqInvitation.value = r.first
contactConnection.value = r.second
} else {
creatingConnReq.value = false
}

View File

@@ -1,22 +1,26 @@
package chat.simplex.common.views.newchat
import SectionBottomSpacer
import SectionTextFooter
import androidx.compose.desktop.ui.tooling.preview.Preview
import chat.simplex.common.platform.Log
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.Text
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import dev.icerock.moko.resources.compose.painterResource
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.platform.LocalClipboardManager
import androidx.compose.ui.text.buildAnnotatedString
import androidx.compose.ui.unit.dp
import chat.simplex.common.platform.TAG
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.SharedPreference
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.IncognitoView
import chat.simplex.common.views.usersettings.SettingsActionItem
import chat.simplex.res.MR
import java.net.URI
@@ -25,85 +29,98 @@ fun PasteToConnectView(chatModel: ChatModel, close: () -> Unit) {
val connectionLink = remember { mutableStateOf("") }
val clipboard = LocalClipboardManager.current
PasteToConnectLayout(
chatModel.incognito.value,
chatModel = chatModel,
incognitoPref = chatModel.controller.appPrefs.incognito,
connectionLink = connectionLink,
pasteFromClipboard = {
connectionLink.value = clipboard.getText()?.text ?: return@PasteToConnectLayout
},
connectViaLink = { connReqUri ->
try {
val uri = URI(connReqUri)
withUriAction(uri) { linkType ->
val action = suspend {
Log.d(TAG, "connectViaUri: connecting")
if (connectViaUri(chatModel, linkType, uri)) {
close()
}
}
if (linkType == ConnectionLinkType.GROUP) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.connect_via_group_link),
text = generalGetString(MR.strings.you_will_join_group),
confirmText = generalGetString(MR.strings.connect_via_link_verb),
onConfirm = { withApi { action() } }
)
} else action()
}
} catch (e: RuntimeException) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.invalid_connection_link),
text = generalGetString(MR.strings.this_string_is_not_a_connection_link)
)
}
},
close = close
)
}
@Composable
fun PasteToConnectLayout(
chatModelIncognito: Boolean,
chatModel: ChatModel,
incognitoPref: SharedPreference<Boolean>,
connectionLink: MutableState<String>,
pasteFromClipboard: () -> Unit,
connectViaLink: (String) -> Unit,
close: () -> Unit
) {
val incognito = remember { mutableStateOf(incognitoPref.get()) }
fun connectViaLink(connReqUri: String) {
try {
val uri = URI(connReqUri)
withUriAction(uri) { linkType ->
val action = suspend {
Log.d(TAG, "connectViaUri: connecting")
if (connectViaUri(chatModel, linkType, uri, incognito = incognito.value)) {
close()
}
}
if (linkType == ConnectionLinkType.GROUP) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.connect_via_group_link),
text = generalGetString(MR.strings.you_will_join_group),
confirmText = if (incognito.value) generalGetString(MR.strings.connect_via_link_incognito) else generalGetString(MR.strings.connect_via_link_verb),
onConfirm = { withApi { action() } }
)
} else action()
}
} catch (e: RuntimeException) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.invalid_connection_link),
text = generalGetString(MR.strings.this_string_is_not_a_connection_link)
)
}
}
Column(
Modifier.verticalScroll(rememberScrollState()).padding(horizontal = DEFAULT_PADDING),
verticalArrangement = Arrangement.SpaceBetween,
) {
AppBarTitle(stringResource(MR.strings.connect_via_link), false)
Text(stringResource(MR.strings.paste_connection_link_below_to_connect))
InfoAboutIncognito(
chatModelIncognito,
true,
generalGetString(MR.strings.incognito_random_profile_from_contact_description),
generalGetString(MR.strings.profile_will_be_sent_to_contact_sending_link)
)
Box(Modifier.padding(top = DEFAULT_PADDING, bottom = 6.dp)) {
TextEditor(connectionLink, Modifier.height(180.dp), contentPadding = PaddingValues())
TextEditor(
connectionLink,
Modifier.height(180.dp),
contentPadding = PaddingValues(),
placeholder = stringResource(MR.strings.paste_the_link_you_received_to_connect_with_your_contact)
)
}
Row(
Modifier.fillMaxWidth().padding(bottom = 6.dp),
horizontalArrangement = Arrangement.Start,
) {
if (connectionLink.value == "") {
SimpleButton(text = stringResource(MR.strings.paste_button), icon = painterResource(MR.images.ic_content_paste)) {
pasteFromClipboard()
}
} else {
SimpleButton(text = stringResource(MR.strings.clear_verb), icon = painterResource(MR.images.ic_close)) {
connectionLink.value = ""
}
}
Spacer(Modifier.weight(1f).fillMaxWidth())
SimpleButton(text = stringResource(MR.strings.connect_button), icon = painterResource(MR.images.ic_link)) {
connectViaLink(connectionLink.value)
}
if (connectionLink.value == "") {
SettingsActionItem(
painterResource(MR.images.ic_content_paste),
stringResource(MR.strings.paste_button),
click = pasteFromClipboard,
)
} else {
SettingsActionItem(
painterResource(MR.images.ic_close),
stringResource(MR.strings.clear_verb),
click = { connectionLink.value = "" },
)
}
Text(annotatedStringResource(MR.strings.you_can_also_connect_by_clicking_the_link))
SettingsActionItem(
painterResource(MR.images.ic_link),
stringResource(MR.strings.connect_button),
click = { connectViaLink(connectionLink.value) },
)
IncognitoToggle(incognitoPref, incognito) { ModalManager.start.showModal { IncognitoView() } }
SectionTextFooter(
buildAnnotatedString {
append(sharedProfileInfo(chatModel, incognito.value))
append("\n\n")
append(annotatedStringResource(MR.strings.you_can_also_connect_by_clicking_the_link))
}
)
SectionBottomSpacer()
}
}
@@ -117,17 +134,11 @@ fun PasteToConnectLayout(
fun PreviewPasteToConnectTextbox() {
SimpleXTheme {
PasteToConnectLayout(
chatModelIncognito = false,
chatModel = ChatModel,
incognitoPref = SharedPreference({ false }, {}),
connectionLink = remember { mutableStateOf("") },
pasteFromClipboard = {},
connectViaLink = { link ->
try {
println(link)
// withApi { chatModel.controller.apiConnect(link) }
} catch (e: Exception) {
e.printStackTrace()
}
},
close = {}
)
}
}

View File

@@ -37,7 +37,7 @@ fun QRCode(
bitmap = qr,
contentDescription = stringResource(MR.strings.image_descr_qr_code),
Modifier
.widthIn(max = 500.dp)
.widthIn(max = 360.dp)
.then(modifier)
.clickable {
scope.launch {

View File

@@ -1,23 +1,23 @@
package chat.simplex.common.views.newchat
import SectionBottomSpacer
import SectionTextFooter
import androidx.compose.desktop.ui.tooling.preview.Preview
import chat.simplex.common.platform.Log
import androidx.compose.foundation.layout.*
import androidx.compose.foundation.rememberScrollState
import androidx.compose.foundation.verticalScroll
import androidx.compose.material.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.text.buildAnnotatedString
import dev.icerock.moko.resources.compose.stringResource
import androidx.compose.ui.unit.dp
import androidx.compose.ui.unit.sp
import chat.simplex.common.model.*
import chat.simplex.common.platform.TAG
import chat.simplex.common.model.ChatModel
import chat.simplex.common.model.json
import chat.simplex.common.ui.theme.DEFAULT_PADDING
import chat.simplex.common.ui.theme.SimpleXTheme
import chat.simplex.common.ui.theme.*
import chat.simplex.common.views.helpers.*
import chat.simplex.common.views.usersettings.*
import chat.simplex.res.MR
import kotlinx.serialization.SerialName
import kotlinx.serialization.Serializable
@@ -26,36 +26,6 @@ import java.net.URI
@Composable
expect fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit)
@Composable
fun QRCodeScanner(close: () -> Unit) {
QRCodeScanner { connReqUri ->
try {
val uri = URI(connReqUri)
withUriAction(uri) { linkType ->
val action = suspend {
Log.d(TAG, "connectViaUri: connecting")
if (connectViaUri(ChatModel, linkType, uri)) {
close()
}
}
if (linkType == ConnectionLinkType.GROUP) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.connect_via_group_link),
text = generalGetString(MR.strings.you_will_join_group),
confirmText = generalGetString(MR.strings.connect_via_link_verb),
onConfirm = { withApi { action() } }
)
} else action()
}
} catch (e: RuntimeException) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.invalid_QR_code),
text = generalGetString(MR.strings.this_QR_code_is_not_a_link)
)
}
}
}
enum class ConnectionLinkType {
CONTACT, INVITATION, GROUP
}
@@ -93,8 +63,8 @@ fun withUriAction(uri: URI, run: suspend (ConnectionLinkType) -> Unit) {
}
}
suspend fun connectViaUri(chatModel: ChatModel, action: ConnectionLinkType, uri: URI): Boolean {
val r = chatModel.controller.apiConnect(uri.toString())
suspend fun connectViaUri(chatModel: ChatModel, action: ConnectionLinkType, uri: URI, incognito: Boolean): Boolean {
val r = chatModel.controller.apiConnect(incognito, uri.toString())
if (r) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.connection_request_sent),
@@ -110,28 +80,65 @@ suspend fun connectViaUri(chatModel: ChatModel, action: ConnectionLinkType, uri:
}
@Composable
fun ConnectContactLayout(chatModelIncognito: Boolean, close: () -> Unit) {
fun ConnectContactLayout(
chatModel: ChatModel,
incognitoPref: SharedPreference<Boolean>,
close: () -> Unit
) {
val incognito = remember { mutableStateOf(incognitoPref.get()) }
@Composable
fun QRCodeScanner(close: () -> Unit) {
QRCodeScanner { connReqUri ->
try {
val uri = URI(connReqUri)
withUriAction(uri) { linkType ->
val action = suspend {
Log.d(TAG, "connectViaUri: connecting")
if (connectViaUri(ChatModel, linkType, uri, incognito = incognito.value)) {
close()
}
}
if (linkType == ConnectionLinkType.GROUP) {
AlertManager.shared.showAlertDialog(
title = generalGetString(MR.strings.connect_via_group_link),
text = generalGetString(MR.strings.you_will_join_group),
confirmText = if (incognito.value) generalGetString(MR.strings.connect_via_link_incognito) else generalGetString(MR.strings.connect_via_link_verb),
onConfirm = { withApi { action() } }
)
} else action()
}
} catch (e: RuntimeException) {
AlertManager.shared.showAlertMsg(
title = generalGetString(MR.strings.invalid_QR_code),
text = generalGetString(MR.strings.this_QR_code_is_not_a_link)
)
}
}
}
Column(
Modifier.verticalScroll(rememberScrollState()).padding(horizontal = DEFAULT_PADDING),
verticalArrangement = Arrangement.spacedBy(12.dp)
verticalArrangement = Arrangement.SpaceBetween
) {
AppBarTitle(stringResource(MR.strings.scan_QR_code), false)
InfoAboutIncognito(
chatModelIncognito,
true,
generalGetString(MR.strings.incognito_random_profile_description),
generalGetString(MR.strings.your_profile_will_be_sent)
)
Box(
Modifier
.fillMaxWidth()
.aspectRatio(ratio = 1F)
.padding(bottom = 12.dp)
) { QRCodeScanner(close) }
Text(
annotatedStringResource(MR.strings.if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link),
lineHeight = 22.sp
IncognitoToggle(incognitoPref, incognito) { ModalManager.start.showModal { IncognitoView() } }
SectionTextFooter(
buildAnnotatedString {
append(sharedProfileInfo(chatModel, incognito.value))
append("\n\n")
append(annotatedStringResource(MR.strings.if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link))
}
)
SectionBottomSpacer()
}
}
@@ -150,7 +157,8 @@ fun URI.getQueryParameter(param: String): String? {
fun PreviewConnectContactLayout() {
SimpleXTheme {
ConnectContactLayout(
chatModelIncognito = false,
chatModel = ChatModel,
incognitoPref = SharedPreference({ false }, {}),
close = {},
)
}

View File

@@ -31,7 +31,6 @@ fun IncognitoLayout() {
Text(generalGetString(MR.strings.incognito_info_protects))
Text(generalGetString(MR.strings.incognito_info_allows))
Text(generalGetString(MR.strings.incognito_info_share))
Text(generalGetString(MR.strings.incognito_info_find))
SectionBottomSpacer()
}
}

View File

@@ -37,16 +37,12 @@ fun SettingsView(chatModel: ChatModel, setPerformLA: (Boolean) -> Unit, drawerSt
val user = chatModel.currentUser.value
val stopped = chatModel.chatRunning.value == false
MaintainIncognitoState(chatModel)
if (user != null) {
val requireAuth = remember { chatModel.controller.appPrefs.performLA.state }
SettingsLayout(
profile = user.profile,
stopped,
chatModel.chatDbEncrypted.value == true,
chatModel.incognito,
chatModel.controller.appPrefs.incognito,
user.displayName,
setPerformLA = setPerformLA,
showModal = { modalView -> { ModalManager.start.showModal { modalView(chatModel) } } },
@@ -118,8 +114,6 @@ fun SettingsLayout(
profile: LocalProfile,
stopped: Boolean,
encrypted: Boolean,
incognito: MutableState<Boolean>,
incognitoPref: SharedPreference<Boolean>,
userDisplayName: String,
setPerformLA: (Boolean) -> Unit,
showModal: (@Composable (ChatModel) -> Unit) -> (() -> Unit),
@@ -155,7 +149,6 @@ fun SettingsLayout(
}
val profileHidden = rememberSaveable { mutableStateOf(false) }
SettingsActionItem(painterResource(MR.images.ic_manage_accounts), stringResource(MR.strings.your_chat_profiles), { withAuth(generalGetString(MR.strings.auth_open_chat_profiles), generalGetString(MR.strings.auth_log_in_using_credential)) { showSettingsModalWithSearch { it, search -> UserProfilesView(it, search, profileHidden) } } }, disabled = stopped, extraPadding = true)
SettingsIncognitoActionItem(incognitoPref, incognito, stopped) { showModal { IncognitoView() }() }
SettingsActionItem(painterResource(MR.images.ic_qr_code), stringResource(MR.strings.your_simplex_contact_address), showCustomModal { it, close -> UserAddressView(it, shareViaProfile = it.currentUser.value!!.addressShared, close = close) }, disabled = stopped, extraPadding = true)
ChatPreferencesItem(showCustomModal, stopped = stopped)
}
@@ -212,43 +205,6 @@ expect fun SettingsSectionApp(
withAuth: (title: String, desc: String, block: () -> Unit) -> Unit
)
@Composable
fun SettingsIncognitoActionItem(
incognitoPref: SharedPreference<Boolean>,
incognito: MutableState<Boolean>,
stopped: Boolean,
onClickInfo: () -> Unit,
) {
SettingsPreferenceItemWithInfo(
if (incognito.value) painterResource(MR.images.ic_theater_comedy_filled) else painterResource(MR.images.ic_theater_comedy),
if (incognito.value) Indigo else MaterialTheme.colors.secondary,
stringResource(MR.strings.incognito),
stopped,
onClickInfo,
incognitoPref,
incognito
)
}
@Composable
fun MaintainIncognitoState(chatModel: ChatModel) {
// Cache previous value and once it changes in background, update it via API
var cachedIncognito by remember { mutableStateOf(chatModel.incognito.value) }
LaunchedEffect(chatModel.incognito.value) {
// Don't do anything if nothing changed
if (cachedIncognito == chatModel.incognito.value) return@LaunchedEffect
try {
chatModel.controller.apiSetIncognito(chatModel.incognito.value)
} catch (e: Exception) {
// Rollback the state
chatModel.controller.appPrefs.incognito.set(cachedIncognito)
// Crash the app
throw e
}
cachedIncognito = chatModel.incognito.value
}
}
@Composable private fun DatabaseItem(encrypted: Boolean, openDatabaseView: () -> Unit, stopped: Boolean) {
SectionItemViewWithIcon(openDatabaseView) {
Row(
@@ -453,21 +409,6 @@ fun SettingsPreferenceItem(
}
}
@Composable
fun SettingsPreferenceItemWithInfo(
icon: Painter,
iconTint: Color,
text: String,
stopped: Boolean,
onClickInfo: () -> Unit,
pref: SharedPreference<Boolean>,
prefState: MutableState<Boolean>? = null
) {
SettingsActionItemWithContent(icon, null, click = if (stopped) null else onClickInfo, iconColor = iconTint, extraPadding = true,) {
SharedPreferenceToggleWithIcon(text, painterResource(MR.images.ic_info), stopped, onClickInfo, pref, prefState)
}
}
@Composable
fun PreferenceToggle(
text: String,
@@ -523,8 +464,6 @@ fun PreviewSettingsLayout() {
profile = LocalProfile.sampleData,
stopped = false,
encrypted = false,
incognito = remember { mutableStateOf(false) },
incognitoPref = SharedPreference({ false }, {}),
userDisplayName = "Alice",
setPerformLA = { _ -> },
showModal = { {} },

View File

@@ -7,9 +7,12 @@
<string name="connect_via_contact_link">Connect via contact link?</string>
<string name="connect_via_invitation_link">Connect via invitation link?</string>
<string name="connect_via_group_link">Connect via group link?</string>
<string name="connect_use_current_profile">Use current profile</string>
<string name="connect_use_new_incognito_profile">Use new incognito profile</string>
<string name="profile_will_be_sent_to_contact_sending_link">Your profile will be sent to the contact that you received this link from.</string>
<string name="you_will_join_group">You will join a group this link refers to and connect to its group members.</string>
<string name="connect_via_link_verb">Connect</string>
<string name="connect_via_link_incognito">Connect incognito</string>
<!-- MainActivity.kt -->
<string name="opening_database">Opening database…</string>
@@ -443,7 +446,7 @@
<!-- Pending contact connection alert dialogues -->
<string name="you_invited_your_contact">You invited your contact</string>
<string name="you_invited_a_contact">You invited a contact</string>
<string name="you_accepted_connection">You accepted connection</string>
<string name="delete_pending_connection__question">Delete pending connection?</string>
<string name="contact_you_shared_link_with_wont_be_able_to_connect">The contact you shared this link with will NOT be able to connect!</string>
@@ -489,11 +492,13 @@
<string name="your_chat_profile_will_be_sent_to_your_contact">Your chat profile will be sent\nto your contact</string>
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link"><![CDATA[If you cannot meet in person, you can <b>scan QR code in the video call</b>, or your contact can share an invitation link.]]></string>
<string name="share_invitation_link">Share 1-time link</string>
<string name="paste_connection_link_below_to_connect">Paste the link you received into the box below to connect with your contact.</string>
<string name="your_profile_will_be_sent">Your chat profile will be sent to your contact</string>
<string name="paste_the_link_you_received_to_connect_with_your_contact">Paste the link you received to connect with your contact</string>
<string name="learn_more">Learn more</string>
<string name="learn_more_about_address">About SimpleX address</string>
<string name="connect__a_new_random_profile_will_be_shared">A new random profile will be shared.</string>
<string name="connect__your_profile_will_be_shared">Your profile %1$s will be shared.</string>
<!-- Add Contact Learn More - AddContactLearnMore.kt -->
<string name="scan_qr_to_connect_to_contact">To connect, your contact can scan QR code or use the link in the app.</string>
<string name="if_you_cant_meet_in_person">If you can\'t meet in person, show QR code in a video call, or share the link.</string>
@@ -1247,7 +1252,6 @@
<string name="group_is_decentralized">The group is fully decentralized it is visible only to the members.</string>
<string name="group_display_name_field">Group display name:</string>
<string name="group_full_name_field">Group full name:</string>
<string name="group_unsupported_incognito_main_profile_sent">Incognito mode is not supported here - your main profile will be sent to group members</string>
<string name="group_main_profile_sent">Your chat profile will be sent to group members</string>
@@ -1301,13 +1305,10 @@
<!-- Incognito mode -->
<string name="incognito">Incognito</string>
<string name="incognito_random_profile">Your random profile</string>
<string name="incognito_random_profile_description">A random profile will be sent to your contact</string>
<string name="incognito_random_profile_from_contact_description">A random profile will be sent to the contact that you received this link from</string>
<string name="incognito_info_protects">Incognito mode protects the privacy of your main profile name and image — for each new contact a new random profile is created.</string>
<string name="incognito_info_protects">Incognito mode protects your privacy by using a new random profile for each contact.</string>
<string name="incognito_info_allows">It allows having many anonymous connections without any shared data between them in a single chat profile.</string>
<string name="incognito_info_share">When you share an incognito profile with somebody, this profile will be used for the groups they invite you to.</string>
<string name="incognito_info_find">To find the profile used for an incognito connection, tap the contact or group name on top of the chat.</string>
<!-- Default themes -->
<string name="theme_system">System</string>

View File

@@ -201,7 +201,7 @@
<string name="mark_unread">Označit jako nepřečteno</string>
<string name="mute_chat">Ztlumit</string>
<string name="unmute_chat">Zrušit ztlumení</string>
<string name="you_invited_your_contact">Pozvali jste kontakt</string>
<string name="you_invited_a_contact">Pozvali jste kontakt</string>
<string name="contact_you_shared_link_with_wont_be_able_to_connect">Kontakt, se kterým jste tento odkaz sdíleli, se NEBUDE moci připojit!</string>
<string name="connection_you_accepted_will_be_cancelled">Připojení, které jste přijali, bude zrušeno!</string>
<string name="icon_descr_help">help</string>

View File

@@ -292,7 +292,7 @@
<string name="mute_chat">Stummschalten</string>
<string name="unmute_chat">Stummschaltung aufheben</string>
<!-- Pending contact connection alert dialogues -->
<string name="you_invited_your_contact">Sie haben Ihren Kontakt eingeladen</string>
<string name="you_invited_a_contact">Sie haben Ihren Kontakt eingeladen</string>
<string name="you_accepted_connection">Sie haben die Verbindung akzeptiert</string>
<string name="delete_pending_connection__question">Ausstehende Verbindung löschen?</string>
<string name="contact_you_shared_link_with_wont_be_able_to_connect">Der Kontakt, mit dem Sie diesen Link geteilt haben, kann sich NICHT verbinden!</string>

View File

@@ -914,7 +914,7 @@
<string name="you_are_observer">Tu rol es observador</string>
<string name="verify_security_code">Comprobar código de seguridad</string>
<string name="you_accepted_connection">Has aceptado la conexión</string>
<string name="you_invited_your_contact">Has invitado a tu contacto</string>
<string name="you_invited_a_contact">Has invitado a tu contacto</string>
<string name="you_will_be_connected_when_group_host_device_is_online">Te conectarás al grupo cuando el dispositivo anfitrión esté en línea, por favor espera o compruébalo más tarde.</string>
<string name="your_settings">Tu configuración</string>
<string name="your_SMP_servers">Servidores SMP</string>

View File

@@ -1232,7 +1232,7 @@
<string name="group_preview_you_are_invited">sinut on kutsuttu ryhmään</string>
<string name="unmute_chat">Poista mykistys</string>
<string name="you_accepted_connection">Hyväksyit yhteyden</string>
<string name="you_invited_your_contact">Kutsuit kontaktisi</string>
<string name="you_invited_a_contact">Kutsuit kontaktisi</string>
<string name="call_connection_via_relay">releellä</string>
<string name="database_downgrade_warning">Varoitus: saatat menettää joitain tietoja!</string>
<string name="icon_descr_address">SimpleX Osoite</string>

View File

@@ -221,7 +221,7 @@
<string name="mark_read">Marquer comme lu</string>
<string name="mark_unread">Marquer non lu</string>
<string name="set_contact_name">Définir le nom du contact</string>
<string name="you_invited_your_contact">Vous avez invité votre contact</string>
<string name="you_invited_a_contact">Vous avez invité votre contact</string>
<string name="you_accepted_connection">Vous avez accepté la connexion</string>
<string name="delete_pending_connection__question">Supprimer la connexion en attente \?</string>
<string name="connection_you_accepted_will_be_cancelled">La connexion que vous avez acceptée sera annulée !</string>

View File

@@ -568,7 +568,7 @@
<string name="icon_descr_address">Indirizzo di SimpleX</string>
<string name="icon_descr_simplex_team">Squadra di SimpleX</string>
<string name="you_accepted_connection">Hai accettato la connessione</string>
<string name="you_invited_your_contact">Hai invitato il contatto</string>
<string name="you_invited_a_contact">Hai invitato il contatto</string>
<string name="your_chat_profile_will_be_sent_to_your_contact">Il tuo profilo di chat verrà inviato
\nal tuo contatto</string>
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">Il tuo contatto deve essere in linea per completare la connessione.

View File

@@ -1182,7 +1182,7 @@
<string name="waiting_for_video">ממתין לסרטון</string>
<string name="waiting_for_file">ממתין לקובץ</string>
<string name="voice_messages_prohibited">הודעות קוליות אסורות!</string>
<string name="you_invited_your_contact">הזמנת את איש הקשר שלך</string>
<string name="you_invited_a_contact">הזמנת את איש הקשר שלך</string>
<string name="you_can_share_your_address">באפשרותכם לשתף את הכתובת שלכם כקישור או כקוד QR כל אחד יכול להתחבר אליכם.</string>
<string name="you_can_accept_or_reject_connection">כאשר אנשים מבקשים להתחבר, באפשרותך לקבל או לדחות זאת.</string>
<string name="you_can_also_connect_by_clicking_the_link"><![CDATA[באפשרותכם גם להתחבר על־ידי לחיצה על הקישור. אם הוא נפתח בדפדפן, ליחצו על הכפתור <b>פתח באפליקציה</b>.]]></string>

View File

@@ -715,7 +715,7 @@
<string name="thank_you_for_installing_simplex">SimpleX Chatをご利用いただきありがとうございます</string>
<string name="use_camera_button">カメラ</string>
<string name="you_accepted_connection">繋がりを承認しました</string>
<string name="you_invited_your_contact">連絡先に招待を送りました</string>
<string name="you_invited_a_contact">連絡先に招待を送りました</string>
<string name="connection_you_accepted_will_be_cancelled">承認ずみの接続がキャンセルされます!</string>
<string name="contact_you_shared_link_with_wont_be_able_to_connect">あなたからリンクを受けた連絡先が接続できなくなります!</string>
<string name="icon_descr_address">SimpleXアドレス</string>

View File

@@ -682,7 +682,7 @@
<string name="connection_you_accepted_will_be_cancelled">De door u geaccepteerde verbinding wordt geannuleerd!</string>
<string name="contact_you_shared_link_with_wont_be_able_to_connect">Het contact met wie je deze link hebt gedeeld, kan GEEN verbinding maken!</string>
<string name="you_accepted_connection">Je hebt de verbinding geaccepteerd</string>
<string name="you_invited_your_contact">Je hebt je contactpersoon uitgenodigd</string>
<string name="you_invited_a_contact">Je hebt je contactpersoon uitgenodigd</string>
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">Uw contactpersoon moet online zijn om de verbinding te voltooien.
\nU kunt deze verbinding verbreken en het contact verwijderen (en later proberen met een nieuwe link).</string>
<string name="image_descr_qr_code">QR code</string>

View File

@@ -257,7 +257,7 @@
<string name="unmute_chat">Wyłącz wyciszenie</string>
<string name="contact_wants_to_connect_with_you">chce się z Tobą połączyć!</string>
<string name="you_accepted_connection">Zaakceptowałeś połączenie</string>
<string name="you_invited_your_contact">Zaprosiłeś swój kontakt</string>
<string name="you_invited_a_contact">Zaprosiłeś swój kontakt</string>
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">Twój kontakt musi być online, aby połączenie zostało zakończone.
\nMożesz anulować to połączenie i usunąć kontakt (i spróbować później z nowym linkiem).</string>
<string name="connect_button">Połącz</string>

View File

@@ -473,7 +473,7 @@
<string name="simplex_service_notification_text">Recebendo mensagens…</string>
<string name="large_file">Aruivo grande!</string>
<string name="mark_read">Marcado como lido</string>
<string name="you_invited_your_contact">Você convidou seu contato</string>
<string name="you_invited_a_contact">Você convidou seu contato</string>
<string name="invalid_QR_code">Código QR inválido</string>
<string name="icon_descr_more_button">Mais</string>
<string name="you_will_be_connected_when_group_host_device_is_online">Você será conectado ao grupo quando o dispositivo do host do grupo estiver online, por favor aguarde ou verifique mais tarde!</string>

View File

@@ -294,7 +294,7 @@
<string name="mute_chat">Без звука</string>
<string name="unmute_chat">Уведомлять</string>
<!-- Pending contact connection alert dialogues -->
<string name="you_invited_your_contact">Вы пригласили Ваш контакт</string>
<string name="you_invited_a_contact">Вы пригласили Ваш контакт</string>
<string name="you_accepted_connection">Вы приняли приглашение соединиться</string>
<string name="delete_pending_connection__question">Удалить ожидаемое соединение?</string>
<string name="contact_you_shared_link_with_wont_be_able_to_connect">Контакт, которому Вы отправили эту ссылку, не сможет соединиться!</string>

View File

@@ -1126,7 +1126,7 @@
<string name="to_start_a_new_chat_help_header">เพื่อเริ่มแชทใหม่</string>
<string name="gallery_video_button">วิดีโอ</string>
<string name="unmute_chat">เปิดเสียง</string>
<string name="you_invited_your_contact">คุณได้เชิญผู้ติดต่อของคุณ</string>
<string name="you_invited_a_contact">คุณได้เชิญผู้ติดต่อของคุณ</string>
<string name="you_accepted_connection">คุณยอมรับการเชื่อมต่อ</string>
<string name="contact_wants_to_connect_with_you">ต้องการเชื่อมต่อกับคุณ!</string>
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">ผู้ติดต่อของคุณจะต้องออนไลน์เพื่อให้การเชื่อมต่อเสร็จสมบูรณ์

View File

@@ -226,7 +226,7 @@
<string name="mobile_tap_open_in_mobile_app_then_tap_connect_in_app"><![CDATA[📱 мобільний: торкніться <b>Відкрийте в мобільному додатку</b>, потім торкніться <b>Підключіть</b> в додатку.]]></string>
<string name="mute_chat">Вимкнути звук</string>
<string name="unmute_chat">Увімкнути звук</string>
<string name="you_invited_your_contact">Ви запросили свого контакта</string>
<string name="you_invited_a_contact">Ви запросили свого контакта</string>
<string name="contact_you_shared_link_with_wont_be_able_to_connect">Контакт, якому ви надали це посилання, НЕ зможе підключитися!</string>
<string name="icon_descr_profile_image_placeholder">заповнювач зображення профілю</string>
<string name="image_descr_qr_code">QR-код</string>

View File

@@ -902,7 +902,7 @@
<string name="voice_messages_prohibited">语音消息禁止发送!</string>
<string name="you_need_to_allow_to_send_voice">您需要允许您的联系人发送语音消息才能发送它们。</string>
<string name="scan_QR_code">扫描二维码</string>
<string name="you_invited_your_contact">您邀请了您的联系人</string>
<string name="you_invited_a_contact">您邀请了您的联系人</string>
<string name="contact_wants_to_connect_with_you">想要与您连接!</string>
<string name="alert_text_connection_pending_they_need_to_be_online_can_delete_and_retry">您的联系人需要在线才能完成连接。
\n您可以取消此连接并删除联系人稍后尝试使用新链接</string>

View File

@@ -533,7 +533,7 @@
<string name="you_can_connect_to_simplex_chat_founder"><![CDATA[你可以透過 <font color="#0088ff">連接到 SimpleX Chat 開發人員提出任何問題並同意更新</font>。]]></string>
<string name="to_start_a_new_chat_help_header">開啟新的對話</string>
<string name="set_contact_name">設定聯絡人名稱</string>
<string name="you_invited_your_contact">你已邀請了你的聯絡人</string>
<string name="you_invited_a_contact">你已邀請了你的聯絡人</string>
<string name="you_accepted_connection">你接受了連接</string>
<string name="delete_pending_connection__question">刪除等待中的連接?</string>
<string name="contact_you_shared_link_with_wont_be_able_to_connect">當聯絡人發現此連結後,嘗試點擊的聯絡人將無法連接!</string>

View File

@@ -1,7 +1,13 @@
package chat.simplex.common.views.helpers
import androidx.compose.foundation.*
import androidx.compose.foundation.shape.RoundedCornerShape
import androidx.compose.material.MaterialTheme
import androidx.compose.material.Surface
import androidx.compose.runtime.*
import androidx.compose.ui.Modifier
import androidx.compose.ui.input.key.*
import androidx.compose.ui.unit.dp
import androidx.compose.ui.window.*
import chat.simplex.common.DialogParams
import chat.simplex.res.MR
@@ -21,6 +27,8 @@ actual fun DefaultDialog(
) {
Dialog(
undecorated = true,
transparent = true,
resizable = false,
title = "",
onCloseRequest = onDismissRequest,
onPreviewKeyEvent = { event ->
@@ -29,7 +37,12 @@ actual fun DefaultDialog(
} else false
}
) {
content()
Surface(
Modifier
.border(border = BorderStroke(1.dp, MaterialTheme.colors.secondary.copy(alpha = 0.3F)), shape = RoundedCornerShape(8))
) {
content()
}
}
}

View File

@@ -6,7 +6,8 @@ import chat.simplex.common.model.ChatModel
@Composable
actual fun ScanToConnectView(chatModel: ChatModel, close: () -> Unit) {
ConnectContactLayout(
chatModelIncognito = chatModel.incognito.value,
chatModel = chatModel,
incognitoPref = chatModel.controller.appPrefs.incognito,
close = close
)
}