mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-04-27 12:56:03 +00:00
ui: opt-in alert for link previews (#6799)
* ios: opt-in alert for link previews * rename back * kotlin: opt-in alert for link previews * reset hints, refactor * refactor hints * move functions * better UX * ios buttons * ios: two buttons * kotlin refactor * kotlin: two buttons * show spinner only after preview decision --------- Co-authored-by: Evgeny @ SimpleX Chat <259188159+evgeny-simplex@users.noreply.github.com>
This commit is contained in:
@@ -370,6 +370,8 @@ struct ComposeView: View {
|
||||
@UserDefault(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
|
||||
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_SANITIZE_LINKS, store: groupDefaults) private var privacySanitizeLinks = false
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS, store: groupDefaults) private var useLinkPreviews = true
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS_SHOW_ALERT, store: groupDefaults) private var linkPreviewsShowAlert = true
|
||||
@State private var updatingCompose = false
|
||||
@State private var relayListExpanded = false
|
||||
@StateObject private var channelRelaysModel = ChannelRelaysModel.shared
|
||||
@@ -561,7 +563,7 @@ struct ComposeView: View {
|
||||
} else {
|
||||
composeState = composeState.copy(parsedMessage: parsedMsg ?? FormattedText.plain(msg))
|
||||
}
|
||||
if composeState.linkPreviewAllowed && UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS) {
|
||||
if composeState.linkPreviewAllowed && useLinkPreviews {
|
||||
if !msg.isEmpty {
|
||||
showLinkPreview(parsedMsg)
|
||||
} else {
|
||||
@@ -1878,21 +1880,55 @@ struct ComposeView: View {
|
||||
// Spec: spec/client/compose.md#loadLinkPreview
|
||||
private func loadLinkPreview(_ urlStr: String) {
|
||||
if pendingLinkUrl == urlStr, let url = URL(string: urlStr) {
|
||||
composeState = composeState.copy(preview: .linkPreview(linkPreview: nil))
|
||||
getLinkPreview(url: url) { linkPreview in
|
||||
if let linkPreview, pendingLinkUrl == urlStr {
|
||||
privacyLinkPreviewsShowAlertGroupDefault.set(false) // to avoid showing alert to current users, show alert in v6.5
|
||||
composeState = composeState.copy(preview: .linkPreview(linkPreview: linkPreview))
|
||||
} else {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
composeState = composeState.copy(preview: .noPreview)
|
||||
if linkPreviewsShowAlert {
|
||||
showLinkPreviewsConfirmAlert { enable in
|
||||
if let enable {
|
||||
linkPreviewsShowAlert = false
|
||||
useLinkPreviews = enable
|
||||
UserDefaults.standard.set(enable, forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
|
||||
if enable {
|
||||
fetchLinkPreview(url, urlStr: urlStr)
|
||||
} else {
|
||||
pendingLinkUrl = nil
|
||||
composeState = composeState.copy(preview: .noPreview)
|
||||
}
|
||||
} else {
|
||||
cancelLinkPreview()
|
||||
}
|
||||
}
|
||||
pendingLinkUrl = nil
|
||||
return
|
||||
}
|
||||
fetchLinkPreview(url, urlStr: urlStr)
|
||||
}
|
||||
}
|
||||
|
||||
private func fetchLinkPreview(_ url: URL, urlStr: String) {
|
||||
composeState = composeState.copy(preview: .linkPreview(linkPreview: nil))
|
||||
getLinkPreview(url: url) { linkPreview in
|
||||
if let linkPreview, pendingLinkUrl == urlStr {
|
||||
composeState = composeState.copy(preview: .linkPreview(linkPreview: linkPreview))
|
||||
} else {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||
composeState = composeState.copy(preview: .noPreview)
|
||||
}
|
||||
}
|
||||
pendingLinkUrl = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func showLinkPreviewsConfirmAlert(onChoice: @escaping (Bool?) -> Void) {
|
||||
showAlert(
|
||||
NSLocalizedString("Enable link previews?", comment: "alert title"),
|
||||
message: NSLocalizedString("Sending a link preview may reveal your IP address to the website. You can change this in Privacy settings later.", comment: "alert message"),
|
||||
actions: {
|
||||
[
|
||||
UIAlertAction(title: NSLocalizedString("Disable", comment: "alert button"), style: .destructive) { _ in onChoice(false) },
|
||||
UIAlertAction(title: NSLocalizedString("Enable", comment: "alert button"), style: .default) { _ in onChoice(true) }
|
||||
]
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
private func resetLinkPreview() {
|
||||
linkUrl = nil
|
||||
prevLinkUrl = nil
|
||||
|
||||
@@ -76,7 +76,7 @@ extension AppSettings {
|
||||
c.privacyEncryptLocalFiles = privacyEncryptLocalFilesGroupDefault.get()
|
||||
c.privacyAskToApproveRelays = privacyAskToApproveRelaysGroupDefault.get()
|
||||
c.privacyAcceptImages = privacyAcceptImagesGroupDefault.get()
|
||||
c.privacyLinkPreviews = def.bool(forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
|
||||
c.privacyLinkPreviews = privacyLinkPreviewsGroupDefault.get()
|
||||
c.privacyShowChatPreviews = def.bool(forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS)
|
||||
c.privacySaveLastDraft = def.bool(forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT)
|
||||
c.privacyProtectScreen = def.bool(forKey: DEFAULT_PRIVACY_PROTECT_SCREEN)
|
||||
|
||||
@@ -91,6 +91,11 @@ struct DeveloperView: View {
|
||||
UserDefaults.standard.set(val, forKey: def)
|
||||
}
|
||||
}
|
||||
for def in hintGroupDefaults {
|
||||
if let val = groupAppDefaults[def] as? Bool {
|
||||
groupDefaults.set(val, forKey: def)
|
||||
}
|
||||
}
|
||||
hintsUnchanged = true
|
||||
}
|
||||
}
|
||||
@@ -98,6 +103,8 @@ struct DeveloperView: View {
|
||||
private func hintDefaultsUnchanged() -> Bool {
|
||||
hintDefaults.allSatisfy { def in
|
||||
appDefaults[def] as? Bool == UserDefaults.standard.bool(forKey: def)
|
||||
} && hintGroupDefaults.allSatisfy { def in
|
||||
groupAppDefaults[def] as? Bool == groupDefaults.bool(forKey: def)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -13,7 +13,7 @@ struct PrivacySettings: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@EnvironmentObject var theme: AppTheme
|
||||
@AppStorage(DEFAULT_PRIVACY_ACCEPT_IMAGES) private var autoAcceptImages = true
|
||||
@AppStorage(DEFAULT_PRIVACY_LINK_PREVIEWS) private var useLinkPreviews = true
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS, store: groupDefaults) private var useLinkPreviews = true
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_SANITIZE_LINKS, store: groupDefaults) private var privacySanitizeLinks = false
|
||||
@AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true
|
||||
@AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true
|
||||
@@ -74,8 +74,8 @@ struct PrivacySettings: View {
|
||||
settingsRow("network", color: theme.colors.secondary) {
|
||||
Toggle("Send link previews", isOn: $useLinkPreviews)
|
||||
.onChange(of: useLinkPreviews) { linkPreviews in
|
||||
privacyLinkPreviewsGroupDefault.set(linkPreviews)
|
||||
privacyLinkPreviewsShowAlertGroupDefault.set(false) // to avoid showing alert to current users, show alert in v6.5
|
||||
UserDefaults.standard.set(linkPreviews, forKey: DEFAULT_PRIVACY_LINK_PREVIEWS)
|
||||
privacyLinkPreviewsShowAlertGroupDefault.set(false)
|
||||
}
|
||||
}
|
||||
settingsRow("link", color: theme.colors.secondary) {
|
||||
|
||||
@@ -150,6 +150,10 @@ let hintDefaults = [
|
||||
DEFAULT_SHOW_DELETE_CONTACT_NOTICE
|
||||
]
|
||||
|
||||
let hintGroupDefaults = [
|
||||
GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS_SHOW_ALERT
|
||||
]
|
||||
|
||||
// not used anymore
|
||||
enum ConnectViaLinkTab: String {
|
||||
case scan
|
||||
|
||||
@@ -465,8 +465,7 @@ fileprivate func getSharedContent(_ ip: NSItemProvider) async -> Result<SharedCo
|
||||
case .url:
|
||||
if let url = try? await ip.loadItem(forTypeIdentifier: type.identifier) as? URL {
|
||||
let content: SharedContent
|
||||
if privacyLinkPreviewsGroupDefault.get(), let linkPreview = await getLinkPreview(for: url) {
|
||||
privacyLinkPreviewsShowAlertGroupDefault.set(false) // to avoid showing alert to current users, show alert in v6.5
|
||||
if privacyLinkPreviewsGroupDefault.get() && !privacyLinkPreviewsShowAlertGroupDefault.get(), let linkPreview = await getLinkPreview(for: url) {
|
||||
content = .url(preview: linkPreview)
|
||||
} else {
|
||||
content = .text(string: url.absoluteString)
|
||||
|
||||
@@ -26,7 +26,7 @@ public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic" // no longer
|
||||
let GROUP_DEFAULT_APP_LOCAL_AUTH_ENABLED = "appLocalAuthEnabled"
|
||||
public let GROUP_DEFAULT_ALLOW_SHARE_EXTENSION = "allowShareExtension"
|
||||
// replaces DEFAULT_PRIVACY_LINK_PREVIEWS
|
||||
let GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews"
|
||||
public let GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS = "privacyLinkPreviews"
|
||||
public let GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS_SHOW_ALERT = "privacyLinkPreviewsShowAlert"
|
||||
public let GROUP_DEFAULT_PRIVACY_SANITIZE_LINKS = "privacySanitizeLinks"
|
||||
// This setting is a main one, while having an unused duplicate from the past: DEFAULT_PRIVACY_ACCEPT_IMAGES
|
||||
@@ -70,46 +70,48 @@ public let APP_GROUP_NAME = "group.chat.simplex.app"
|
||||
|
||||
public let groupDefaults = UserDefaults(suiteName: APP_GROUP_NAME)!
|
||||
|
||||
public let groupAppDefaults: [String: Any] = [
|
||||
GROUP_DEFAULT_NTF_ENABLE_LOCAL: false,
|
||||
GROUP_DEFAULT_NTF_ENABLE_PERIODIC: false,
|
||||
GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS: OnionHosts.no.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.session.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE: SMPProxyMode.unknown.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK: SMPProxyFallback.allowProtected.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_SMP_WEB_PORT_SERVERS: SMPWebPortServers.preset.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT_BACKGROUND: NetCfg.defaults.tcpConnectTimeout.backgroundTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT_INTERACTIVE: NetCfg.defaults.tcpConnectTimeout.interactiveTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_BACKGROUND: NetCfg.defaults.tcpTimeout.backgroundTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_INTERACTIVE: NetCfg.defaults.tcpTimeout.interactiveTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB: NetCfg.defaults.tcpTimeoutPerKb,
|
||||
GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY: NetCfg.defaults.rcvConcurrency,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL: NetCfg.defaults.smpPingInterval,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PING_COUNT: NetCfg.defaults.smpPingCount,
|
||||
GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive,
|
||||
GROUP_DEFAULT_NETWORK_TCP_KEEP_IDLE: KeepAliveOpts.defaults.keepIdle,
|
||||
GROUP_DEFAULT_NETWORK_TCP_KEEP_INTVL: KeepAliveOpts.defaults.keepIntvl,
|
||||
GROUP_DEFAULT_NETWORK_TCP_KEEP_CNT: KeepAliveOpts.defaults.keepCnt,
|
||||
GROUP_DEFAULT_INCOGNITO: false,
|
||||
GROUP_DEFAULT_STORE_DB_PASSPHRASE: true,
|
||||
GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false,
|
||||
GROUP_DEFAULT_APP_LOCAL_AUTH_ENABLED: true,
|
||||
GROUP_DEFAULT_ALLOW_SHARE_EXTENSION: false,
|
||||
GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS: true,
|
||||
GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS_SHOW_ALERT: true,
|
||||
GROUP_DEFAULT_PRIVACY_SANITIZE_LINKS: false,
|
||||
GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
|
||||
GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false,
|
||||
GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true,
|
||||
GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS: true,
|
||||
GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner,
|
||||
GROUP_DEFAULT_CONFIRM_DB_UPGRADES: false,
|
||||
GROUP_DEFAULT_CALL_KIT_ENABLED: true,
|
||||
GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED: false,
|
||||
GROUP_DEFAULT_ONE_HAND_UI: true,
|
||||
GROUP_DEFAULT_CHAT_BOTTOM_BAR: true
|
||||
]
|
||||
|
||||
public func registerGroupDefaults() {
|
||||
groupDefaults.register(defaults: [
|
||||
GROUP_DEFAULT_NTF_ENABLE_LOCAL: false,
|
||||
GROUP_DEFAULT_NTF_ENABLE_PERIODIC: false,
|
||||
GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS: OnionHosts.no.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_SESSION_MODE: TransportSessionMode.session.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PROXY_MODE: SMPProxyMode.unknown.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PROXY_FALLBACK: SMPProxyFallback.allowProtected.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_SMP_WEB_PORT_SERVERS: SMPWebPortServers.preset.rawValue,
|
||||
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT_BACKGROUND: NetCfg.defaults.tcpConnectTimeout.backgroundTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT_INTERACTIVE: NetCfg.defaults.tcpConnectTimeout.interactiveTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_BACKGROUND: NetCfg.defaults.tcpTimeout.backgroundTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_INTERACTIVE: NetCfg.defaults.tcpTimeout.interactiveTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB: NetCfg.defaults.tcpTimeoutPerKb,
|
||||
GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY: NetCfg.defaults.rcvConcurrency,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL: NetCfg.defaults.smpPingInterval,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PING_COUNT: NetCfg.defaults.smpPingCount,
|
||||
GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive,
|
||||
GROUP_DEFAULT_NETWORK_TCP_KEEP_IDLE: KeepAliveOpts.defaults.keepIdle,
|
||||
GROUP_DEFAULT_NETWORK_TCP_KEEP_INTVL: KeepAliveOpts.defaults.keepIntvl,
|
||||
GROUP_DEFAULT_NETWORK_TCP_KEEP_CNT: KeepAliveOpts.defaults.keepCnt,
|
||||
GROUP_DEFAULT_INCOGNITO: false,
|
||||
GROUP_DEFAULT_STORE_DB_PASSPHRASE: true,
|
||||
GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false,
|
||||
GROUP_DEFAULT_APP_LOCAL_AUTH_ENABLED: true,
|
||||
GROUP_DEFAULT_ALLOW_SHARE_EXTENSION: false,
|
||||
GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS: true,
|
||||
GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS_SHOW_ALERT: true,
|
||||
GROUP_DEFAULT_PRIVACY_SANITIZE_LINKS: false,
|
||||
GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
|
||||
GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false,
|
||||
GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true,
|
||||
GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS: true,
|
||||
GROUP_DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner,
|
||||
GROUP_DEFAULT_CONFIRM_DB_UPGRADES: false,
|
||||
GROUP_DEFAULT_CALL_KIT_ENABLED: true,
|
||||
GROUP_DEFAULT_PQ_EXPERIMENTAL_ENABLED: false,
|
||||
GROUP_DEFAULT_ONE_HAND_UI: true,
|
||||
GROUP_DEFAULT_CHAT_BOTTOM_BAR: true
|
||||
])
|
||||
groupDefaults.register(defaults: groupAppDefaults)
|
||||
}
|
||||
|
||||
public enum AppState: String, Codable {
|
||||
|
||||
+1
@@ -266,6 +266,7 @@ class AppPreferences {
|
||||
showReportsInSupportChatAlert to true,
|
||||
showDeleteConversationNotice to true,
|
||||
showDeleteContactNotice to true,
|
||||
privacyLinkPreviewsShowAlert to true,
|
||||
)
|
||||
|
||||
private fun mkIntPreference(prefName: String, default: Int) =
|
||||
|
||||
+68
-9
@@ -1,6 +1,7 @@
|
||||
@file:UseSerializers(UriSerializer::class, ComposeMessageSerializer::class)
|
||||
package chat.simplex.common.views.chat
|
||||
|
||||
import SectionItemView
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.clickable
|
||||
import androidx.compose.foundation.layout.*
|
||||
@@ -19,6 +20,8 @@ import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.text.TextRange
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
@@ -399,20 +402,46 @@ fun ComposeView(
|
||||
val recState: MutableState<RecordingState> = remember { mutableStateOf(RecordingState.NotStarted) }
|
||||
AttachmentSelection(composeState, attachmentOption, composeState::processPickedFile) { uris, text -> CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(uris, text) } }
|
||||
|
||||
suspend fun fetchAndUpdateLinkPreview(url: String) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
|
||||
val lp = getLinkPreview(url)
|
||||
if (lp != null && pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(lp))
|
||||
pendingLinkUrl.value = null
|
||||
} else if (pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.NoPreview)
|
||||
pendingLinkUrl.value = null
|
||||
}
|
||||
}
|
||||
|
||||
fun loadLinkPreview(url: String, wait: Long? = null) {
|
||||
if (pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
|
||||
withLongRunningApi(slow = 60_000) {
|
||||
if (wait != null) delay(wait)
|
||||
val lp = getLinkPreview(url)
|
||||
if (lp != null && pendingLinkUrl.value == url) {
|
||||
chatModel.controller.appPrefs.privacyLinkPreviewsShowAlert.set(false) // to avoid showing alert to current users, show alert in v6.5
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(lp))
|
||||
pendingLinkUrl.value = null
|
||||
} else if (pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.NoPreview)
|
||||
pendingLinkUrl.value = null
|
||||
if (pendingLinkUrl.value != url) return@withLongRunningApi
|
||||
if (chatModel.controller.appPrefs.privacyLinkPreviewsShowAlert.get()
|
||||
&& !chatModel.controller.appPrefs.networkUseSocksProxy.get()) {
|
||||
showLinkPreviewsConfirmAlert { enable ->
|
||||
if (enable != null) {
|
||||
chatModel.controller.appPrefs.privacyLinkPreviewsShowAlert.set(false)
|
||||
chatModel.controller.appPrefs.privacyLinkPreviews.set(enable)
|
||||
if (enable) {
|
||||
withLongRunningApi(slow = 60_000) { fetchAndUpdateLinkPreview(url) }
|
||||
} else if (pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.NoPreview)
|
||||
pendingLinkUrl.value = null
|
||||
}
|
||||
} else {
|
||||
cancelledLinks.add(url)
|
||||
if (pendingLinkUrl.value == url) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.NoPreview)
|
||||
pendingLinkUrl.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
return@withLongRunningApi
|
||||
}
|
||||
fetchAndUpdateLinkPreview(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1672,6 +1701,36 @@ fun ComposeView(
|
||||
}
|
||||
}
|
||||
|
||||
private fun showLinkPreviewsConfirmAlert(onChoice: (Boolean?) -> Unit) {
|
||||
AlertManager.shared.showAlertDialogButtonsColumn(
|
||||
title = generalGetString(MR.strings.link_previews_alert_title),
|
||||
text = AnnotatedString(generalGetString(MR.strings.link_previews_alert_desc)),
|
||||
onDismissRequest = { onChoice(null) },
|
||||
buttons = {
|
||||
Column {
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
onChoice(false)
|
||||
}) {
|
||||
Text(stringResource(MR.strings.link_previews_alert_disable), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = Color.Red)
|
||||
}
|
||||
SectionItemView({
|
||||
AlertManager.shared.hideAlert()
|
||||
onChoice(true)
|
||||
}) {
|
||||
Text(stringResource(MR.strings.link_previews_alert_enable), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.primary)
|
||||
}
|
||||
// SectionItemView({
|
||||
// AlertManager.shared.hideAlert()
|
||||
// onChoice(null)
|
||||
// }) {
|
||||
// Text(stringResource(MR.strings.cancel_verb), Modifier.fillMaxWidth(), textAlign = TextAlign.Center, color = MaterialTheme.colors.onBackground)
|
||||
// }
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun OwnerChannelRelayBar(
|
||||
chatModel: ChatModel,
|
||||
|
||||
@@ -2970,4 +2970,11 @@
|
||||
|
||||
<!-- ChatListNavLinkView.kt channel-related -->
|
||||
<string name="unblock_subscriber_for_all_question">Unblock subscriber for all?</string>
|
||||
|
||||
<!-- ComposeView.kt link previews opt-in alert -->
|
||||
<string name="link_previews_alert_title">Enable link previews?</string>
|
||||
<string name="link_previews_alert_desc">Sending a link preview may reveal your IP address to the website. You can change this in Privacy settings later.</string>
|
||||
<string name="link_previews_alert_enable">Enable</string>
|
||||
<string name="link_previews_alert_disable">Disable</string>
|
||||
<!-- <string name="link_previews_alert_dont_ask_again">Don't ask again</string> -->
|
||||
</resources>
|
||||
Reference in New Issue
Block a user