mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-08-27 22:34:51 +00:00
ui: open links from chat list with confirmation (#5519)
* ui: open links from chat list with confirmation * appSettings * ios * core: migrate setting * ios icon * android icon --------- Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
This commit is contained in:
co-authored by
Evgeny Poberezkin
parent
daea3b1056
commit
ef72d8e446
+14
@@ -105,6 +105,7 @@ class AppPreferences {
|
||||
val privacyProtectScreen = mkBoolPreference(SHARED_PREFS_PRIVACY_PROTECT_SCREEN, true)
|
||||
val privacyAcceptImages = mkBoolPreference(SHARED_PREFS_PRIVACY_ACCEPT_IMAGES, true)
|
||||
val privacyLinkPreviews = mkBoolPreference(SHARED_PREFS_PRIVACY_LINK_PREVIEWS, true)
|
||||
val privacyChatListOpenLinks = mkEnumPreference(SHARED_PREFS_PRIVACY_CHAT_LIST_OPEN_LINKS, PrivacyChatListOpenLinksMode.ASK) { PrivacyChatListOpenLinksMode.values().firstOrNull { it.name == this } }
|
||||
private val _simplexLinkMode = mkStrPreference(SHARED_PREFS_PRIVACY_SIMPLEX_LINK_MODE, SimplexLinkMode.default.name)
|
||||
val simplexLinkMode: SharedPreference<SimplexLinkMode> = SharedPreference(
|
||||
get = fun(): SimplexLinkMode {
|
||||
@@ -373,6 +374,7 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_PRIVACY_ACCEPT_IMAGES = "PrivacyAcceptImages"
|
||||
private const val SHARED_PREFS_PRIVACY_TRANSFER_IMAGES_INLINE = "PrivacyTransferImagesInline"
|
||||
private const val SHARED_PREFS_PRIVACY_LINK_PREVIEWS = "PrivacyLinkPreviews"
|
||||
private const val SHARED_PREFS_PRIVACY_CHAT_LIST_OPEN_LINKS = "ChatListOpenLinks"
|
||||
private const val SHARED_PREFS_PRIVACY_SIMPLEX_LINK_MODE = "PrivacySimplexLinkMode"
|
||||
private const val SHARED_PREFS_PRIVACY_SHOW_CHAT_PREVIEWS = "PrivacyShowChatPreviews"
|
||||
private const val SHARED_PREFS_PRIVACY_SAVE_LAST_DRAFT = "PrivacySaveLastDraft"
|
||||
@@ -7071,6 +7073,13 @@ enum class NotificationsMode() {
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class PrivacyChatListOpenLinksMode {
|
||||
@SerialName("yes") YES,
|
||||
@SerialName("no") NO,
|
||||
@SerialName("ask") ASK
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class AppSettings(
|
||||
var networkConfig: NetCfg? = null,
|
||||
@@ -7079,6 +7088,7 @@ data class AppSettings(
|
||||
var privacyAskToApproveRelays: Boolean? = null,
|
||||
var privacyAcceptImages: Boolean? = null,
|
||||
var privacyLinkPreviews: Boolean? = null,
|
||||
var privacyChatListOpenLinks: PrivacyChatListOpenLinksMode? = null,
|
||||
var privacyShowChatPreviews: Boolean? = null,
|
||||
var privacySaveLastDraft: Boolean? = null,
|
||||
var privacyProtectScreen: Boolean? = null,
|
||||
@@ -7114,6 +7124,7 @@ data class AppSettings(
|
||||
if (privacyAskToApproveRelays != def.privacyAskToApproveRelays) { empty.privacyAskToApproveRelays = privacyAskToApproveRelays }
|
||||
if (privacyAcceptImages != def.privacyAcceptImages) { empty.privacyAcceptImages = privacyAcceptImages }
|
||||
if (privacyLinkPreviews != def.privacyLinkPreviews) { empty.privacyLinkPreviews = privacyLinkPreviews }
|
||||
if (privacyChatListOpenLinks != def.privacyChatListOpenLinks) { empty.privacyChatListOpenLinks = privacyChatListOpenLinks }
|
||||
if (privacyShowChatPreviews != def.privacyShowChatPreviews) { empty.privacyShowChatPreviews = privacyShowChatPreviews }
|
||||
if (privacySaveLastDraft != def.privacySaveLastDraft) { empty.privacySaveLastDraft = privacySaveLastDraft }
|
||||
if (privacyProtectScreen != def.privacyProtectScreen) { empty.privacyProtectScreen = privacyProtectScreen }
|
||||
@@ -7160,6 +7171,7 @@ data class AppSettings(
|
||||
privacyAskToApproveRelays?.let { def.privacyAskToApproveRelays.set(it) }
|
||||
privacyAcceptImages?.let { def.privacyAcceptImages.set(it) }
|
||||
privacyLinkPreviews?.let { def.privacyLinkPreviews.set(it) }
|
||||
privacyChatListOpenLinks?.let { def.privacyChatListOpenLinks.set(it) }
|
||||
privacyShowChatPreviews?.let { def.privacyShowChatPreviews.set(it) }
|
||||
privacySaveLastDraft?.let { def.privacySaveLastDraft.set(it) }
|
||||
privacyProtectScreen?.let { def.privacyProtectScreen.set(it) }
|
||||
@@ -7196,6 +7208,7 @@ data class AppSettings(
|
||||
privacyAskToApproveRelays = true,
|
||||
privacyAcceptImages = true,
|
||||
privacyLinkPreviews = true,
|
||||
privacyChatListOpenLinks = PrivacyChatListOpenLinksMode.ASK,
|
||||
privacyShowChatPreviews = true,
|
||||
privacySaveLastDraft = true,
|
||||
privacyProtectScreen = false,
|
||||
@@ -7233,6 +7246,7 @@ data class AppSettings(
|
||||
privacyAskToApproveRelays = def.privacyAskToApproveRelays.get(),
|
||||
privacyAcceptImages = def.privacyAcceptImages.get(),
|
||||
privacyLinkPreviews = def.privacyLinkPreviews.get(),
|
||||
privacyChatListOpenLinks = def.privacyChatListOpenLinks.get(),
|
||||
privacyShowChatPreviews = def.privacyShowChatPreviews.get(),
|
||||
privacySaveLastDraft = def.privacySaveLastDraft.get(),
|
||||
privacyProtectScreen = def.privacyProtectScreen.get(),
|
||||
|
||||
+13
-8
@@ -64,13 +64,14 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>) {
|
||||
when (chat.chatInfo) {
|
||||
is ChatInfo.Direct -> {
|
||||
val contactNetworkStatus = chatModel.contactNetworkStatus(chat.chatInfo.contact)
|
||||
val defaultClickAction = { if (chatModel.chatId.value != chat.id) scope.launch { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) } }
|
||||
ChatListNavLinkLayout(
|
||||
chatLinkPreview = {
|
||||
tryOrShowError("${chat.id}ChatListNavLink", error = { ErrorChatListItem() }) {
|
||||
ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, contactNetworkStatus, disabled, linkMode, inProgress = false, progressByTimeout = false)
|
||||
ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, contactNetworkStatus, disabled, linkMode, inProgress = false, progressByTimeout = false, defaultClickAction)
|
||||
}
|
||||
},
|
||||
click = { if (chatModel.chatId.value != chat.id) scope.launch { directChatAction(chat.remoteHostId, chat.chatInfo.contact, chatModel) } },
|
||||
click = defaultClickAction,
|
||||
dropdownMenuItems = {
|
||||
tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) {
|
||||
ContactMenuItems(chat, chat.chatInfo.contact, chatModel, showMenu, showMarkRead)
|
||||
@@ -82,14 +83,15 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>) {
|
||||
nextChatSelected,
|
||||
)
|
||||
}
|
||||
is ChatInfo.Group ->
|
||||
is ChatInfo.Group -> {
|
||||
val defaultClickAction = { if (!inProgress.value && chatModel.chatId.value != chat.id) scope.launch { groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel, inProgress) } }
|
||||
ChatListNavLinkLayout(
|
||||
chatLinkPreview = {
|
||||
tryOrShowError("${chat.id}ChatListNavLink", error = { ErrorChatListItem() }) {
|
||||
ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, disabled, linkMode, inProgress.value, progressByTimeout)
|
||||
ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, disabled, linkMode, inProgress.value, progressByTimeout, defaultClickAction)
|
||||
}
|
||||
},
|
||||
click = { if (!inProgress.value && chatModel.chatId.value != chat.id) scope.launch { groupChatAction(chat.remoteHostId, chat.chatInfo.groupInfo, chatModel, inProgress) } },
|
||||
click = defaultClickAction,
|
||||
dropdownMenuItems = {
|
||||
tryOrShowError("${chat.id}ChatListNavLinkDropdown", error = {}) {
|
||||
GroupMenuItems(chat, chat.chatInfo.groupInfo, chatModel, showMenu, inProgress, showMarkRead)
|
||||
@@ -100,11 +102,12 @@ fun ChatListNavLinkView(chat: Chat, nextChatSelected: State<Boolean>) {
|
||||
selectedChat,
|
||||
nextChatSelected,
|
||||
)
|
||||
}
|
||||
is ChatInfo.Local -> {
|
||||
ChatListNavLinkLayout(
|
||||
chatLinkPreview = {
|
||||
tryOrShowError("${chat.id}ChatListNavLink", error = { ErrorChatListItem() }) {
|
||||
ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, disabled, linkMode, inProgress = false, progressByTimeout = false)
|
||||
ChatPreviewView(chat, showChatPreviews, chatModel.draft.value, chatModel.draftChatId.value, chatModel.currentUser.value?.profile?.displayName, null, disabled, linkMode, inProgress = false, progressByTimeout = false, {})
|
||||
}
|
||||
},
|
||||
click = { if (chatModel.chatId.value != chat.id) scope.launch { noteFolderChatAction(chat.remoteHostId, chat.chatInfo.noteFolder) } },
|
||||
@@ -925,7 +928,8 @@ fun PreviewChatListNavLinkDirect() {
|
||||
disabled = false,
|
||||
linkMode = SimplexLinkMode.DESCRIPTION,
|
||||
inProgress = false,
|
||||
progressByTimeout = false
|
||||
progressByTimeout = false,
|
||||
{}
|
||||
)
|
||||
},
|
||||
click = {},
|
||||
@@ -970,7 +974,8 @@ fun PreviewChatListNavLinkGroup() {
|
||||
disabled = false,
|
||||
linkMode = SimplexLinkMode.DESCRIPTION,
|
||||
inProgress = false,
|
||||
progressByTimeout = false
|
||||
progressByTimeout = false,
|
||||
{}
|
||||
)
|
||||
},
|
||||
click = {},
|
||||
|
||||
+37
-3
@@ -1,5 +1,6 @@
|
||||
package chat.simplex.common.views.chatlist
|
||||
|
||||
import SectionItemView
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
@@ -27,6 +28,7 @@ import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.GroupInfo
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
@@ -45,7 +47,8 @@ fun ChatPreviewView(
|
||||
disabled: Boolean,
|
||||
linkMode: SimplexLinkMode,
|
||||
inProgress: Boolean,
|
||||
progressByTimeout: Boolean
|
||||
progressByTimeout: Boolean,
|
||||
defaultClickAction: () -> Unit
|
||||
) {
|
||||
val cInfo = chat.chatInfo
|
||||
|
||||
@@ -248,7 +251,38 @@ fun ChatPreviewView(
|
||||
val uriHandler = LocalUriHandler.current
|
||||
when (mc) {
|
||||
is MsgContent.MCLink -> SmallContentPreview {
|
||||
IconButton({ uriHandler.openUriCatching(mc.preview.uri) }, Modifier.desktopPointerHoverIconHand()) {
|
||||
val linkClicksEnabled = remember { appPrefs.privacyChatListOpenLinks.state }.value != PrivacyChatListOpenLinksMode.NO
|
||||
IconButton({
|
||||
when (appPrefs.privacyChatListOpenLinks.get()) {
|
||||
PrivacyChatListOpenLinksMode.YES -> uriHandler.openUriCatching(mc.preview.uri)
|
||||
PrivacyChatListOpenLinksMode.NO -> defaultClickAction()
|
||||
PrivacyChatListOpenLinksMode.ASK -> AlertManager.shared.showAlertDialogButtonsColumn(
|
||||
title = generalGetString(MR.strings.privacy_chat_list_open_web_link_question),
|
||||
text = mc.preview.uri,
|
||||
buttons = {
|
||||
Column {
|
||||
if (chatModel.chatId.value != chat.id) {
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
defaultClickAction()
|
||||
}) {
|
||||
Text(stringResource(MR.strings.open_chat), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
uriHandler.openUriCatching(mc.preview.uri)
|
||||
}
|
||||
) {
|
||||
Text(stringResource(MR.strings.privacy_chat_list_open_web_link), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
},
|
||||
if (linkClicksEnabled) Modifier.desktopPointerHoverIconHand() else Modifier,
|
||||
) {
|
||||
Image(base64ToBitmap(mc.preview.image), null, contentScale = ContentScale.Crop)
|
||||
}
|
||||
Box(Modifier.align(Alignment.TopEnd).size(15.sp.toDp()).background(Color.Black.copy(0.25f), CircleShape), contentAlignment = Alignment.Center) {
|
||||
@@ -527,6 +561,6 @@ private data class ActiveVoicePreview(
|
||||
@Composable
|
||||
fun PreviewChatPreviewView() {
|
||||
SimpleXTheme {
|
||||
ChatPreviewView(Chat.sampleData, true, null, null, "", contactNetworkStatus = NetworkStatus.Connected(), disabled = false, linkMode = SimplexLinkMode.DESCRIPTION, inProgress = false, progressByTimeout = false)
|
||||
ChatPreviewView(Chat.sampleData, true, null, null, "", contactNetworkStatus = NetworkStatus.Connected(), disabled = false, linkMode = SimplexLinkMode.DESCRIPTION, inProgress = false, progressByTimeout = false, {})
|
||||
}
|
||||
}
|
||||
|
||||
+23
@@ -63,6 +63,9 @@ fun PrivacySettingsView(
|
||||
|
||||
SectionView(stringResource(MR.strings.settings_section_title_chats)) {
|
||||
SettingsPreferenceItem(painterResource(MR.images.ic_travel_explore), stringResource(MR.strings.send_link_previews), chatModel.controller.appPrefs.privacyLinkPreviews)
|
||||
ChatListLinksOptions(appPrefs.privacyChatListOpenLinks.state, onSelected = {
|
||||
appPrefs.privacyChatListOpenLinks.set(it)
|
||||
})
|
||||
SettingsPreferenceItem(
|
||||
painterResource(MR.images.ic_chat_bubble),
|
||||
stringResource(MR.strings.privacy_show_last_messages),
|
||||
@@ -199,6 +202,26 @@ fun PrivacySettingsView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ChatListLinksOptions(state: State<PrivacyChatListOpenLinksMode>, onSelected: (PrivacyChatListOpenLinksMode) -> Unit) {
|
||||
val values = remember {
|
||||
PrivacyChatListOpenLinksMode.entries.map {
|
||||
when (it) {
|
||||
PrivacyChatListOpenLinksMode.YES -> it to generalGetString(MR.strings.privacy_chat_list_open_links_yes)
|
||||
PrivacyChatListOpenLinksMode.NO -> it to generalGetString(MR.strings.privacy_chat_list_open_links_no)
|
||||
PrivacyChatListOpenLinksMode.ASK -> it to generalGetString(MR.strings.privacy_chat_list_open_links_ask)
|
||||
}
|
||||
}
|
||||
}
|
||||
ExposedDropDownSettingRow(
|
||||
generalGetString(MR.strings.privacy_chat_list_open_links),
|
||||
values,
|
||||
state,
|
||||
icon = painterResource(MR.images.ic_open_in_new),
|
||||
onSelected = onSelected
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun SimpleXLinkOptions(simplexLinkModeState: State<SimplexLinkMode>, onSelected: (SimplexLinkMode) -> Unit) {
|
||||
val modeValues = listOf(SimplexLinkMode.DESCRIPTION, SimplexLinkMode.FULL)
|
||||
|
||||
@@ -1307,6 +1307,12 @@
|
||||
<string name="privacy_media_blur_radius_soft">Soft</string>
|
||||
<string name="privacy_media_blur_radius_medium">Medium</string>
|
||||
<string name="privacy_media_blur_radius_strong">Strong</string>
|
||||
<string name="privacy_chat_list_open_links">Open links from chat list</string>
|
||||
<string name="privacy_chat_list_open_links_yes">Yes</string>
|
||||
<string name="privacy_chat_list_open_links_no">No</string>
|
||||
<string name="privacy_chat_list_open_links_ask">Ask</string>
|
||||
<string name="privacy_chat_list_open_web_link_question">Open web link?</string>
|
||||
<string name="privacy_chat_list_open_web_link">Open link</string>
|
||||
|
||||
<!-- Settings sections -->
|
||||
<string name="settings_section_title_you">YOU</string>
|
||||
|
||||
Reference in New Issue
Block a user