diff --git a/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.android.kt b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.android.kt index 95d2520f56..95fc7b6d64 100644 --- a/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.android.kt +++ b/apps/multiplatform/android/src/main/java/chat/simplex/app/model/NtfManager.android.kt @@ -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) { diff --git a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.android.kt b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.android.kt index ee825730ca..d0cad31210 100644 --- a/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.android.kt +++ b/apps/multiplatform/common/src/androidMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.android.kt @@ -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 ) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt index 7a036fffdb..94bf9d1929 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/ChatModel.kt @@ -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) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt index 57a9077bbc..14838e2de0 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/model/SimpleXAPI.kt @@ -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? { 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() } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt index 0e4a67f221..cc23915834 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/platform/NtfManager.kt @@ -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) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt index 85ab5c2ea3..3a44b9e420 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatInfoView.kt @@ -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() } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt index ce7765bcd1..63cf729ed7 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/ChatView.kt @@ -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, 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, 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 = { _, _ -> }, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt index e9a9854456..1b8310e18d 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/AddGroupMembersView.kt @@ -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 { @Composable fun AddGroupMembersLayout( - chatModelIncognito: Boolean, groupInfo: GroupInfo, creatingGroup: Boolean, contactsToAdd: List, @@ -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), diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt index f730c6ef21..a3e5d5af18 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chat/group/GroupMemberInfoView.kt @@ -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) } } - }, + } ) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt index 0be9ef12d8..f924737490 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListNavLinkView.kt @@ -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) { 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, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt index 43b06a2159..e2c316046b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatListView.kt @@ -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) + } } } ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt index 04e0724c27..bd030438e9 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ChatPreviewView.kt @@ -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) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ContactConnectionView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ContactConnectionView.kt index 7931034de3..99d6c5db15 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ContactConnectionView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ContactConnectionView.kt @@ -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) + } } } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ContactRequestView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ContactRequestView.kt index 6ec03ad7f5..8debcce98c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ContactRequestView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ContactRequestView.kt @@ -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) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListView.kt index 2d0ed7eb53..8b65b2b5bc 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/chatlist/ShareListView.kt @@ -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, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/TextEditor.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/TextEditor.kt index 7ebbb7e5cb..45accccc59 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/TextEditor.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/helpers/TextEditor.kt @@ -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, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddContactView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddContactView.kt index 2ee2145259..8542ea52a4 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddContactView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/AddContactView.kt @@ -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 +) { 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, + connReq: String, + contactConnection: MutableState, + 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, + incognito: MutableState, + 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 = {}, ) diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ContactConnectionInfoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ContactConnectionInfoView.kt index 2b971282b3..fe62a7d9da 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ContactConnectionInfoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ContactConnectionInfoView.kt @@ -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 = {}, diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/CreateLinkView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/CreateLinkView.kt index 53f7bdcc73..08afbd4c6c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/CreateLinkView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/CreateLinkView.kt @@ -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 = 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, connReqInvitation: MutableState) { +private fun createInvitation( + m: ChatModel, + creatingConnReq: MutableState, + connReqInvitation: MutableState, + contactConnection: MutableState +) { 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 } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/PasteToConnect.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/PasteToConnect.kt index ba8b8df548..a0d0da69ac 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/PasteToConnect.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/PasteToConnect.kt @@ -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, connectionLink: MutableState, 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 = {} ) } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/QRCode.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/QRCode.kt index 5e43a34197..a848d3777b 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/QRCode.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/QRCode.kt @@ -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 { diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt index b6bcf96749..e3fa922755 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.kt @@ -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, + 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 = {}, ) } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/IncognitoView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/IncognitoView.kt index 4728c0f2b7..e264172f9c 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/IncognitoView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/IncognitoView.kt @@ -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() } } diff --git a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt index 0120e35700..e81746f99f 100644 --- a/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt +++ b/apps/multiplatform/common/src/commonMain/kotlin/chat/simplex/common/views/usersettings/SettingsView.kt @@ -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, - incognitoPref: SharedPreference, 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, - incognito: MutableState, - 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, - prefState: MutableState? = 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 = { {} }, diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml index 753e89a1d4..c060470b15 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/base/strings.xml @@ -7,9 +7,12 @@ Connect via contact link? Connect via invitation link? Connect via group link? + Use current profile + Use new incognito profile Your profile will be sent to the contact that you received this link from. You will join a group this link refers to and connect to its group members. Connect + Connect incognito Opening database… @@ -443,7 +446,7 @@ - You invited your contact + You invited a contact You accepted connection Delete pending connection? The contact you shared this link with will NOT be able to connect! @@ -489,11 +492,13 @@ Your chat profile will be sent\nto your contact scan QR code in the video call, or your contact can share an invitation link.]]> Share 1-time link - Paste the link you received into the box below to connect with your contact. - Your chat profile will be sent to your contact + Paste the link you received to connect with your contact… Learn more About SimpleX address + A new random profile will be shared. + Your profile %1$s will be shared. + To connect, your contact can scan QR code or use the link in the app. If you can\'t meet in person, show QR code in a video call, or share the link. @@ -1247,7 +1252,6 @@ The group is fully decentralized – it is visible only to the members. Group display name: Group full name: - Incognito mode is not supported here - your main profile will be sent to group members Your chat profile will be sent to group members @@ -1301,13 +1305,10 @@ Incognito Your random profile - A random profile will be sent to your contact - A random profile will be sent to the contact that you received this link from - Incognito mode protects the privacy of your main profile name and image — for each new contact a new random profile is created. + Incognito mode protects your privacy by using a new random profile for each contact. It allows having many anonymous connections without any shared data between them in a single chat profile. When you share an incognito profile with somebody, this profile will be used for the groups they invite you to. - To find the profile used for an incognito connection, tap the contact or group name on top of the chat. System diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml index 9576c1ff92..9f415446eb 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/cs/strings.xml @@ -201,7 +201,7 @@ Označit jako nepřečteno Ztlumit Zrušit ztlumení - Pozvali jste kontakt + Pozvali jste kontakt Kontakt, se kterým jste tento odkaz sdíleli, se NEBUDE moci připojit! Připojení, které jste přijali, bude zrušeno! help diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml index 159dd1e5dc..9a9395e306 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/de/strings.xml @@ -292,7 +292,7 @@ Stummschalten Stummschaltung aufheben - Sie haben Ihren Kontakt eingeladen + Sie haben Ihren Kontakt eingeladen Sie haben die Verbindung akzeptiert Ausstehende Verbindung löschen? Der Kontakt, mit dem Sie diesen Link geteilt haben, kann sich NICHT verbinden! diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml index 6e5b82e74e..40fae87821 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/es/strings.xml @@ -914,7 +914,7 @@ Tu rol es observador Comprobar código de seguridad Has aceptado la conexión - Has invitado a tu contacto + Has invitado a tu contacto Te conectarás al grupo cuando el dispositivo anfitrión esté en línea, por favor espera o compruébalo más tarde. Tu configuración Servidores SMP diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/fi/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/fi/strings.xml index d4e5df7055..5256965f64 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/fi/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/fi/strings.xml @@ -1232,7 +1232,7 @@ sinut on kutsuttu ryhmään Poista mykistys Hyväksyit yhteyden - Kutsuit kontaktisi + Kutsuit kontaktisi releellä Varoitus: saatat menettää joitain tietoja! SimpleX Osoite diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml index 1462e04f61..2d225895c1 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/fr/strings.xml @@ -221,7 +221,7 @@ Marquer comme lu Marquer non lu Définir le nom du contact - Vous avez invité votre contact + Vous avez invité votre contact Vous avez accepté la connexion Supprimer la connexion en attente \? La connexion que vous avez acceptée sera annulée ! diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml index 4dc63182f0..62e7f12e33 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/it/strings.xml @@ -568,7 +568,7 @@ Indirizzo di SimpleX Squadra di SimpleX Hai accettato la connessione - Hai invitato il contatto + Hai invitato il contatto Il tuo profilo di chat verrà inviato \nal tuo contatto Il tuo contatto deve essere in linea per completare la connessione. diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/iw/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/iw/strings.xml index e165588a6b..fd429a672e 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/iw/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/iw/strings.xml @@ -1182,7 +1182,7 @@ ממתין לסרטון ממתין לקובץ הודעות קוליות אסורות! - הזמנת את איש הקשר שלך + הזמנת את איש הקשר שלך באפשרותכם לשתף את הכתובת שלכם כקישור או כקוד QR – כל אחד יכול להתחבר אליכם. כאשר אנשים מבקשים להתחבר, באפשרותך לקבל או לדחות זאת. פתח באפליקציה.]]> diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ja/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ja/strings.xml index c3f5dbfec2..0601bc5666 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ja/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ja/strings.xml @@ -715,7 +715,7 @@ SimpleX Chatをご利用いただきありがとうございます! カメラ 繋がりを承認しました - 連絡先に招待を送りました + 連絡先に招待を送りました 承認ずみの接続がキャンセルされます! あなたからリンクを受けた連絡先が接続できなくなります! SimpleXアドレス diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml index 4167a47847..d557dc6221 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/nl/strings.xml @@ -682,7 +682,7 @@ De door u geaccepteerde verbinding wordt geannuleerd! Het contact met wie je deze link hebt gedeeld, kan GEEN verbinding maken! Je hebt de verbinding geaccepteerd - Je hebt je contactpersoon uitgenodigd + Je hebt je contactpersoon uitgenodigd 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). QR code diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml index 3364d15f14..e7423abdfc 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/pl/strings.xml @@ -257,7 +257,7 @@ Wyłącz wyciszenie chce się z Tobą połączyć! Zaakceptowałeś połączenie - Zaprosiłeś swój kontakt + Zaprosiłeś swój kontakt 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). Połącz diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml index 0f3b415469..06ee46ac52 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/pt-rBR/strings.xml @@ -473,7 +473,7 @@ Recebendo mensagens… Aruivo grande! Marcado como lido - Você convidou seu contato + Você convidou seu contato Código QR inválido Mais Você será conectado ao grupo quando o dispositivo do host do grupo estiver online, por favor aguarde ou verifique mais tarde! diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml index da755b65ef..b0dad03aa2 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/ru/strings.xml @@ -294,7 +294,7 @@ Без звука Уведомлять - Вы пригласили Ваш контакт + Вы пригласили Ваш контакт Вы приняли приглашение соединиться Удалить ожидаемое соединение? Контакт, которому Вы отправили эту ссылку, не сможет соединиться! diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/th/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/th/strings.xml index f2f7fd70c7..c669e03bcf 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/th/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/th/strings.xml @@ -1126,7 +1126,7 @@ เพื่อเริ่มแชทใหม่ วิดีโอ เปิดเสียง - คุณได้เชิญผู้ติดต่อของคุณ + คุณได้เชิญผู้ติดต่อของคุณ คุณยอมรับการเชื่อมต่อ ต้องการเชื่อมต่อกับคุณ! ผู้ติดต่อของคุณจะต้องออนไลน์เพื่อให้การเชื่อมต่อเสร็จสมบูรณ์ diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml index 161d641b3b..51e676a12c 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/uk/strings.xml @@ -226,7 +226,7 @@ Відкрийте в мобільному додатку, потім торкніться Підключіть в додатку.]]> Вимкнути звук Увімкнути звук - Ви запросили свого контакта + Ви запросили свого контакта Контакт, якому ви надали це посилання, НЕ зможе підключитися! заповнювач зображення профілю QR-код diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml index 21f154caac..133e5041ff 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rCN/strings.xml @@ -902,7 +902,7 @@ 语音消息禁止发送! 您需要允许您的联系人发送语音消息才能发送它们。 扫描二维码 - 您邀请了您的联系人 + 您邀请了您的联系人 想要与您连接! 您的联系人需要在线才能完成连接。 \n您可以取消此连接并删除联系人(稍后尝试使用新链接)。 diff --git a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml index 714e8fb8f5..da61b8bb45 100644 --- a/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml +++ b/apps/multiplatform/common/src/commonMain/resources/MR/zh-rTW/strings.xml @@ -533,7 +533,7 @@ 連接到 SimpleX Chat 開發人員提出任何問題並同意更新。]]> 開啟新的對話 設定聯絡人名稱 - 你已邀請了你的聯絡人 + 你已邀請了你的聯絡人 你接受了連接 刪除等待中的連接? 當聯絡人發現此連結後,嘗試點擊的聯絡人將無法連接! diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/DefaultDialog.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/DefaultDialog.desktop.kt index e485ee479d..20675dc74e 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/DefaultDialog.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/helpers/DefaultDialog.desktop.kt @@ -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() + } } } diff --git a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.desktop.kt b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.desktop.kt index 66f2d5c6d2..f202318f16 100644 --- a/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.desktop.kt +++ b/apps/multiplatform/common/src/desktopMain/kotlin/chat/simplex/common/views/newchat/ScanToConnectView.desktop.kt @@ -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 ) }