android: simplex link mode setting, ios: untrusted simplex links (#1412)

* android: simplex link mode setting, ios: untrusted simplex links

* correction

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>

Co-authored-by: JRoberts <8711996+jr-simplex@users.noreply.github.com>
This commit is contained in:
Evgeny Poberezkin
2022-11-25 09:55:51 +00:00
committed by GitHub
parent de7548a9a8
commit 9225f437e9
16 changed files with 119 additions and 45 deletions
@@ -79,6 +79,7 @@ class ChatModel(val controller: ChatController) {
val sharedContent = mutableStateOf(null as SharedContent?)
val filesToDelete = mutableSetOf<File>()
val simplexLinkMode = mutableStateOf(controller.appPrefs.simplexLinkMode.get())
fun updateUserProfile(profile: LocalProfile) {
val user = currentUser.value
@@ -1496,17 +1497,17 @@ object MsgContentSerializer : KSerializer<MsgContent> {
@Serializable
class FormattedText(val text: String, val format: Format? = null) {
// TODO make it dependent on simplexLinkMode preference
val link: String? = when (format) {
fun link(mode: SimplexLinkMode): String? = when (format) {
is Format.Uri -> text
is Format.SimplexLink -> format.simplexUri
is Format.SimplexLink -> if (mode == SimplexLinkMode.BROWSER) text else format.simplexUri
is Format.Email -> "mailto:$text"
is Format.Phone -> "tel:$text"
else -> null
}
// TODO make it dependent on simplexLinkMode preference
val viewText: String =
if (format is Format.SimplexLink) simplexLinkText(format.linkType, format.smpHosts) else text
fun viewText(mode: SimplexLinkMode): String =
if (format is Format.SimplexLink && mode == SimplexLinkMode.DESCRIPTION) simplexLinkText(format.linkType, format.smpHosts) else text
fun simplexLinkText(linkType: SimplexLinkType, smpHosts: List<String>): String =
"${linkType.description} (${String.format(generalGetString(R.string.simplex_link_connection), smpHosts.firstOrNull() ?: "?")})"
@@ -1521,8 +1522,7 @@ sealed class Format {
@Serializable @SerialName("secret") class Secret: Format()
@Serializable @SerialName("colored") class Colored(val color: FormatColor): Format()
@Serializable @SerialName("uri") class Uri: Format()
// TODO trustedUri: Boolean
@Serializable @SerialName("simplexLink") class SimplexLink(val linkType: SimplexLinkType, val simplexUri: String, val smpHosts: List<String>): Format()
@Serializable @SerialName("simplexLink") class SimplexLink(val linkType: SimplexLinkType, val simplexUri: String, val trustedUri: Boolean, val smpHosts: List<String>): Format()
@Serializable @SerialName("email") class Email: Format()
@Serializable @SerialName("phone") class Phone: Format()
@@ -119,6 +119,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: () -> Unit) {
chatModel.chatItems,
searchText,
useLinkPreviews = useLinkPreviews,
linkMode = chatModel.simplexLinkMode.value,
chatModelIncognito = chatModel.incognito.value,
back = {
hideKeyboard(view)
@@ -242,6 +243,7 @@ fun ChatLayout(
chatItems: List<ChatItem>,
searchValue: State<String>,
useLinkPreviews: Boolean,
linkMode: SimplexLinkMode,
chatModelIncognito: Boolean,
back: () -> Unit,
info: () -> Unit,
@@ -291,7 +293,7 @@ fun ChatLayout(
BoxWithConstraints(Modifier.fillMaxHeight().padding(contentPadding)) {
ChatItemsList(
chat, unreadCount, composeState, chatItems, searchValue,
useLinkPreviews, chatModelIncognito, showMemberInfo, loadPrevMessages, deleteMessage,
useLinkPreviews, linkMode, chatModelIncognito, showMemberInfo, loadPrevMessages, deleteMessage,
receiveFile, joinGroup, acceptCall, markRead, setFloatingButton, onComposed,
)
}
@@ -449,6 +451,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
chatItems: List<ChatItem>,
searchValue: State<String>,
useLinkPreviews: Boolean,
linkMode: SimplexLinkMode,
chatModelIncognito: Boolean,
showMemberInfo: (GroupInfo, GroupMember) -> Unit,
loadPrevMessages: (ChatInfo) -> Unit,
@@ -561,11 +564,11 @@ fun BoxWithConstraintsScope.ChatItemsList(
} else {
Spacer(Modifier.size(42.dp))
}
ChatItemView(chat.chatInfo, cItem, composeState, provider, showMember = showMember, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall, scrollToItem = scrollToItem)
ChatItemView(chat.chatInfo, cItem, composeState, provider, showMember = showMember, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall, scrollToItem = scrollToItem)
}
} else {
Box(Modifier.padding(start = 104.dp, end = 12.dp).then(swipeableModifier)) {
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall, scrollToItem = scrollToItem)
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = {}, acceptCall = acceptCall, scrollToItem = scrollToItem)
}
}
} else { // direct message
@@ -576,7 +579,7 @@ fun BoxWithConstraintsScope.ChatItemsList(
end = if (sent) 12.dp else 76.dp,
).then(swipeableModifier)
) {
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = joinGroup, acceptCall = acceptCall, scrollToItem = scrollToItem)
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, deleteMessage = deleteMessage, receiveFile = receiveFile, joinGroup = joinGroup, acceptCall = acceptCall, scrollToItem = scrollToItem)
}
}
@@ -950,6 +953,7 @@ fun PreviewChatLayout() {
chatItems = chatItems,
searchValue,
useLinkPreviews = true,
linkMode = SimplexLinkMode.DESCRIPTION,
chatModelIncognito = false,
back = {},
info = {},
@@ -1007,6 +1011,7 @@ fun PreviewGroupChatLayout() {
chatItems = chatItems,
searchValue,
useLinkPreviews = true,
linkMode = SimplexLinkMode.DESCRIPTION,
chatModelIncognito = false,
back = {},
info = {},
@@ -14,8 +14,7 @@ import androidx.compose.ui.res.stringResource
import androidx.compose.ui.tooling.preview.Preview
import androidx.compose.ui.unit.dp
import chat.simplex.app.R
import chat.simplex.app.model.CIDirection
import chat.simplex.app.model.ChatItem
import chat.simplex.app.model.*
import chat.simplex.app.ui.theme.HighOrLowlight
import chat.simplex.app.ui.theme.SimpleXTheme
import chat.simplex.app.views.chat.item.*
@@ -53,6 +52,7 @@ fun ContextItemView(
MarkdownText(
contextItem.text, contextItem.formattedText,
sender = contextItem.memberDisplayName, senderBold = true, maxLines = 3,
linkMode = SimplexLinkMode.DESCRIPTION,
modifier = Modifier.fillMaxWidth(),
)
}
@@ -205,6 +205,6 @@ class ChatItemProvider: PreviewParameterProvider<ChatItem> {
fun PreviewCIFileFramedItemView(@PreviewParameter(ChatItemProvider::class) chatItem: ChatItem) {
val showMenu = remember { mutableStateOf(false) }
SimpleXTheme {
FramedItemView(ChatInfo.Direct.sampleData, chatItem, showMenu = showMenu, receiveFile = {})
FramedItemView(ChatInfo.Direct.sampleData, chatItem, linkMode = SimplexLinkMode.DESCRIPTION, showMenu = showMenu, receiveFile = {})
}
}
@@ -34,6 +34,7 @@ fun ChatItemView(
imageProvider: (() -> ImageGalleryProvider)? = null,
showMember: Boolean = false,
useLinkPreviews: Boolean,
linkMode: SimplexLinkMode,
deleteMessage: (Long, CIDeleteMode) -> Unit,
receiveFile: (Long) -> Unit,
joinGroup: (Long) -> Unit,
@@ -73,7 +74,7 @@ fun ChatItemView(
EmojiItemView(cItem)
} else {
val onLinkLongClick = { _: String -> showMenu.value = true }
FramedItemView(cInfo, cItem, uriHandler, imageProvider, showMember = showMember, showMenu, receiveFile, onLinkLongClick, scrollToItem)
FramedItemView(cInfo, cItem, uriHandler, imageProvider, showMember = showMember, linkMode = linkMode, showMenu, receiveFile, onLinkLongClick, scrollToItem)
}
DropdownMenu(
expanded = showMenu.value,
@@ -235,6 +236,7 @@ fun PreviewChatItemView() {
1, CIDirection.DirectSnd(), Clock.System.now(), "hello"
),
useLinkPreviews = true,
linkMode = SimplexLinkMode.DESCRIPTION,
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
deleteMessage = { _, _ -> },
receiveFile = {},
@@ -253,6 +255,7 @@ fun PreviewChatItemViewDeletedContent() {
ChatInfo.Direct.sampleData,
ChatItem.getDeletedContentSampleData(),
useLinkPreviews = true,
linkMode = SimplexLinkMode.DESCRIPTION,
composeState = remember { mutableStateOf(ComposeState(useLinkPreviews = true)) },
deleteMessage = { _, _ -> },
receiveFile = {},
@@ -40,6 +40,7 @@ fun FramedItemView(
uriHandler: UriHandler? = null,
imageProvider: (() -> ImageGalleryProvider)? = null,
showMember: Boolean = false,
linkMode: SimplexLinkMode,
showMenu: MutableState<Boolean>,
receiveFile: (Long) -> Unit,
onLinkLongClick: (link: String) -> Unit = {},
@@ -63,7 +64,8 @@ fun FramedItemView(
qi.text
MarkdownText(
text, qi.formattedText, sender = qi.sender(membership()), senderBold = true, maxLines = 3,
style = TextStyle(fontSize = 15.sp, color = MaterialTheme.colors.onSurface)
style = TextStyle(fontSize = 15.sp, color = MaterialTheme.colors.onSurface),
linkMode = linkMode
)
}
}
@@ -114,7 +116,7 @@ fun FramedItemView(
fun ciFileView(ci: ChatItem, text: String) {
CIFileView(ci.file, ci.meta.itemEdited, receiveFile)
if (text != "") {
CIMarkdownText(ci, showMember, uriHandler)
CIMarkdownText(ci, showMember, linkMode = linkMode, uriHandler)
}
}
@@ -153,27 +155,27 @@ fun FramedItemView(
if (mc.text == "") {
metaColor = Color.White
} else {
CIMarkdownText(ci, showMember, uriHandler)
CIMarkdownText(ci, showMember, linkMode, uriHandler)
}
}
is MsgContent.MCVoice -> {
CIVoiceView(mc.duration, ci.file, ci.meta.itemEdited, ci.chatDir.sent, mc.text != "" || ci.quotedItem != null, ci, metaColor)
if (mc.text != "") {
CIMarkdownText(ci, showMember, uriHandler)
CIMarkdownText(ci, showMember, linkMode, uriHandler)
}
}
is MsgContent.MCFile -> ciFileView(ci, mc.text)
is MsgContent.MCUnknown ->
if (ci.file == null) {
CIMarkdownText(ci, showMember, uriHandler, onLinkLongClick)
CIMarkdownText(ci, showMember, linkMode, uriHandler, onLinkLongClick)
} else {
ciFileView(ci, mc.text)
}
is MsgContent.MCLink -> {
ChatItemLinkView(mc.preview)
CIMarkdownText(ci, showMember, uriHandler, onLinkLongClick)
CIMarkdownText(ci, showMember, linkMode, uriHandler, onLinkLongClick)
}
else -> CIMarkdownText(ci, showMember, uriHandler, onLinkLongClick)
else -> CIMarkdownText(ci, showMember, linkMode, uriHandler, onLinkLongClick)
}
}
}
@@ -191,13 +193,14 @@ fun FramedItemView(
fun CIMarkdownText(
ci: ChatItem,
showMember: Boolean,
linkMode: SimplexLinkMode,
uriHandler: UriHandler?,
onLinkLongClick: (link: String) -> Unit = {}
) {
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
MarkdownText(
ci.content.text, ci.formattedText, if (showMember) ci.memberDisplayName else null,
metaText = ci.timestampText, edited = ci.meta.itemEdited,
metaText = ci.timestampText, edited = ci.meta.itemEdited, linkMode = linkMode,
uriHandler = uriHandler, senderBold = true, onLinkLongClick = onLinkLongClick
)
}
@@ -248,6 +251,7 @@ fun PreviewTextItemViewSnd(@PreviewParameter(EditedProvider::class) edited: Bool
ChatItem.getSampleData(
1, CIDirection.DirectSnd(), Clock.System.now(), "hello", itemEdited = edited,
),
linkMode = SimplexLinkMode.DESCRIPTION,
showMenu = showMenu,
receiveFile = {}
)
@@ -264,6 +268,7 @@ fun PreviewTextItemViewRcv(@PreviewParameter(EditedProvider::class) edited: Bool
ChatItem.getSampleData(
1, CIDirection.DirectRcv(), Clock.System.now(), "hello", itemEdited = edited
),
linkMode = SimplexLinkMode.DESCRIPTION,
showMenu = showMenu,
receiveFile = {}
)
@@ -284,6 +289,7 @@ fun PreviewTextItemViewLong(@PreviewParameter(EditedProvider::class) edited: Boo
"Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.",
itemEdited = edited
),
linkMode = SimplexLinkMode.DESCRIPTION,
showMenu = showMenu,
receiveFile = {}
)
@@ -305,6 +311,7 @@ fun PreviewTextItemViewQuote(@PreviewParameter(EditedProvider::class) edited: Bo
quotedItem = CIQuote.getSample(1, Clock.System.now(), "hi", chatDir = CIDirection.DirectRcv()),
itemEdited = edited
),
linkMode = SimplexLinkMode.DESCRIPTION,
showMenu = showMenu,
receiveFile = {}
)
@@ -326,6 +333,7 @@ fun PreviewTextItemViewEmoji(@PreviewParameter(EditedProvider::class) edited: Bo
quotedItem = CIQuote.getSample(1, Clock.System.now(), "Lorem ipsum dolor sit amet", chatDir = CIDirection.DirectRcv()),
itemEdited = edited
),
linkMode = SimplexLinkMode.DESCRIPTION,
showMenu = showMenu,
receiveFile = {}
)
@@ -354,6 +362,7 @@ fun PreviewQuoteWithTextAndImage(@PreviewParameter(EditedProvider::class) edited
quotedItem = ciQuote,
itemEdited = edited
),
linkMode = SimplexLinkMode.DESCRIPTION,
showMenu = showMenu,
receiveFile = {}
)
@@ -382,6 +391,7 @@ fun PreviewQuoteWithLongTextAndImage(@PreviewParameter(EditedProvider::class) ed
quotedItem = ciQuote,
itemEdited = edited
),
linkMode = SimplexLinkMode.DESCRIPTION,
showMenu = showMenu,
receiveFile = {}
)
@@ -409,6 +419,7 @@ fun PreviewQuoteWithLongTextAndFile(@PreviewParameter(EditedProvider::class) edi
quotedItem = ciQuote,
itemEdited = edited
),
linkMode = SimplexLinkMode.DESCRIPTION,
showMenu = showMenu,
receiveFile = {}
)
@@ -10,6 +10,7 @@ import androidx.compose.ui.input.pointer.pointerInput
import androidx.compose.ui.platform.*
import androidx.compose.ui.text.*
import androidx.compose.ui.text.font.FontWeight
import androidx.compose.ui.text.style.TextDecoration
import androidx.compose.ui.text.style.TextOverflow
import androidx.compose.ui.unit.*
import androidx.core.text.BidiFormatter
@@ -49,6 +50,7 @@ fun MarkdownText (
uriHandler: UriHandler? = null,
senderBold: Boolean = false,
modifier: Modifier = Modifier,
linkMode: SimplexLinkMode,
onLinkLongClick: (link: String) -> Unit = {}
) {
val textLayoutDirection = remember (text) {
@@ -79,12 +81,16 @@ fun MarkdownText (
for (ft in formattedText) {
if (ft.format == null) append(ft.text)
else {
val link = ft.link
val link = ft.link(linkMode)
if (link != null) {
hasLinks = true
val ftStyle = ft.format.style
val ftStyle = if (ft.format is Format.SimplexLink && !ft.format.trustedUri && linkMode == SimplexLinkMode.BROWSER) {
SpanStyle(color = Color.Red, textDecoration = TextDecoration.Underline)
} else {
ft.format.style
}
withAnnotation(tag = "URL", annotation = link) {
withStyle(ftStyle) { append(ft.viewText) }
withStyle(ftStyle) { append(ft.viewText(linkMode)) }
}
} else {
withStyle(ft.format.style) { append(ft.text) }
@@ -33,6 +33,7 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
chat.chatStats.unreadCount > 0 || chat.chatStats.unreadChat
}
val stopped = chatModel.chatRunning.value == false
val linkMode = chatModel.controller.appPrefs.simplexLinkMode.get()
LaunchedEffect(chat.id) {
showMenu.value = false
delay(500L)
@@ -40,7 +41,7 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
when (chat.chatInfo) {
is ChatInfo.Direct ->
ChatListNavLinkLayout(
chatLinkPreview = { ChatPreviewView(chat, chatModel.incognito.value, chatModel.currentUser.value?.profile?.displayName, stopped) },
chatLinkPreview = { ChatPreviewView(chat, chatModel.incognito.value, chatModel.currentUser.value?.profile?.displayName, stopped, linkMode) },
click = { directChatAction(chat.chatInfo, chatModel) },
dropdownMenuItems = { ContactMenuItems(chat, chatModel, showMenu, showMarkRead) },
showMenu,
@@ -48,7 +49,7 @@ fun ChatListNavLinkView(chat: Chat, chatModel: ChatModel) {
)
is ChatInfo.Group ->
ChatListNavLinkLayout(
chatLinkPreview = { ChatPreviewView(chat, chatModel.incognito.value, chatModel.currentUser.value?.profile?.displayName, stopped) },
chatLinkPreview = { ChatPreviewView(chat, chatModel.incognito.value, chatModel.currentUser.value?.profile?.displayName, stopped, linkMode) },
click = { groupChatAction(chat.chatInfo.groupInfo, chatModel) },
dropdownMenuItems = { GroupMenuItems(chat, chat.chatInfo.groupInfo, chatModel, showMenu, showMarkRead) },
showMenu,
@@ -586,7 +587,8 @@ fun PreviewChatListNavLinkDirect() {
),
false,
null,
stopped = false
stopped = false,
linkMode = SimplexLinkMode.DESCRIPTION
)
},
click = {},
@@ -623,7 +625,8 @@ fun PreviewChatListNavLinkGroup() {
),
false,
null,
stopped = false
stopped = false,
linkMode = SimplexLinkMode.DESCRIPTION
)
},
click = {},
@@ -26,7 +26,7 @@ import chat.simplex.app.views.chat.item.MarkdownText
import chat.simplex.app.views.helpers.*
@Composable
fun ChatPreviewView(chat: Chat, chatModelIncognito: Boolean, currentUserProfileDisplayName: String?, stopped: Boolean) {
fun ChatPreviewView(chat: Chat, chatModelIncognito: Boolean, currentUserProfileDisplayName: String?, stopped: Boolean, linkMode: SimplexLinkMode) {
val cInfo = chat.chatInfo
@Composable
@@ -86,6 +86,7 @@ fun ChatPreviewView(chat: Chat, chatModelIncognito: Boolean, currentUserProfileD
ci.text,
ci.formattedText,
sender = if (cInfo is ChatInfo.Group && !ci.chatDir.sent) ci.memberDisplayName else null,
linkMode = linkMode,
senderBold = true,
metaText = null,
maxLines = 2,
@@ -232,6 +233,6 @@ fun ChatStatusImage(chat: Chat) {
@Composable
fun PreviewChatPreviewView() {
SimpleXTheme {
ChatPreviewView(Chat.sampleData, false, "", stopped = false)
ChatPreviewView(Chat.sampleData, false, "", stopped = false, linkMode = SimplexLinkMode.DESCRIPTION)
}
}
@@ -1,18 +1,20 @@
package chat.simplex.app.views.usersettings
import SectionDivider
import SectionItemView
import SectionSpacer
import SectionTextFooter
import SectionView
import androidx.compose.foundation.layout.*
import androidx.compose.material.icons.Icons
import androidx.compose.material.icons.outlined.*
import androidx.compose.runtime.Composable
import androidx.compose.runtime.*
import androidx.compose.ui.Alignment
import androidx.compose.ui.Modifier
import androidx.compose.ui.res.stringResource
import chat.simplex.app.R
import chat.simplex.app.model.ChatModel
import chat.simplex.app.views.helpers.AppBarTitle
import chat.simplex.app.model.*
import chat.simplex.app.views.helpers.*
@Composable
fun PrivacySettingsView(
@@ -23,6 +25,7 @@ fun PrivacySettingsView(
Modifier.fillMaxWidth(),
horizontalAlignment = Alignment.Start
) {
val simplexLinkMode = chatModel.controller.appPrefs.simplexLinkMode
AppBarTitle(stringResource(R.string.your_privacy))
SectionView(stringResource(R.string.settings_section_title_device)) {
ChatLockItem(chatModel.performLA, setPerformLA)
@@ -38,6 +41,34 @@ fun PrivacySettingsView(
}
SettingsPreferenceItem(Icons.Outlined.TravelExplore, stringResource(R.string.send_link_previews), chatModel.controller.appPrefs.privacyLinkPreviews)
SectionDivider()
SectionItemView { SimpleXLinkOptions(chatModel.simplexLinkMode, onSelected = {
simplexLinkMode.set(it)
chatModel.simplexLinkMode.value = it
}) }
}
if (chatModel.simplexLinkMode.value == SimplexLinkMode.BROWSER) {
SectionTextFooter(stringResource(R.string.simplex_link_mode_browser_warning))
}
}
}
@Composable
private fun SimpleXLinkOptions(simplexLinkModeState: State<SimplexLinkMode>, onSelected: (SimplexLinkMode) -> Unit) {
val values = remember {
SimplexLinkMode.values().map {
when (it) {
SimplexLinkMode.DESCRIPTION -> it to generalGetString(R.string.simplex_link_mode_description)
SimplexLinkMode.FULL -> it to generalGetString(R.string.simplex_link_mode_full)
SimplexLinkMode.BROWSER -> it to generalGetString(R.string.simplex_link_mode_browser)
}
}
}
ExposedDropDownSettingRow(
generalGetString(R.string.simplex_link_mode),
values,
simplexLinkModeState,
icon = null,
enabled = remember { mutableStateOf(true) },
onSelected = onSelected
)
}
@@ -46,6 +46,11 @@
<string name="simplex_link_invitation">SimpleX Einmal-Link</string>
<string name="simplex_link_group">SimpleX Gruppen-Link</string>
<string name="simplex_link_connection">über <xliff:g id="serverHost" example="smp.simplex.im">%1$s</xliff:g></string>
<string name="simplex_link_mode">***SimpleX links</string>
<string name="simplex_link_mode_description">***Description</string>
<string name="simplex_link_mode_full">***Full link</string>
<string name="simplex_link_mode_browser">***Via browser</string>
<string name="simplex_link_mode_browser_warning">***Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red.</string>
<!-- SimpleXAPI.kt -->
<string name="error_saving_smp_servers">Fehler beim Speichern der SMP-Server</string>
@@ -46,6 +46,11 @@
<string name="simplex_link_invitation">SimpleX одноразовая ссылка</string>
<string name="simplex_link_group">SimpleX ссылка группы</string>
<string name="simplex_link_connection">через <xliff:g id="serverHost" example="smp.simplex.im">%1$s</xliff:g></string>
<string name="simplex_link_mode">SimpleX ссылки</string>
<string name="simplex_link_mode_description">Описание</string>
<string name="simplex_link_mode_full">Полная ссылка</string>
<string name="simplex_link_mode_browser">В браузере</string>
<string name="simplex_link_mode_browser_warning">Использование ссылки в браузере может уменьшить конфиденциальность и безопасность соединения. Ссылки на неизвестные сайты будут красными.</string>
<!-- SimpleXAPI.kt -->
<string name="error_saving_smp_servers">Ошибка при сохранении SMP серверов</string>
@@ -43,9 +43,14 @@
<!-- FormattedText, SimpleX links - ChatModel.kt -->
<string name="simplex_link_contact">SimpleX contact address</string>
<string name="simplex_link_invitation">SimpleX 1-time invitation</string>
<string name="simplex_link_invitation">SimpleX one-time invitation</string>
<string name="simplex_link_group">SimpleX group link</string>
<string name="simplex_link_connection">via <xliff:g id="serverHost" example="smp.simplex.im">%1$s</xliff:g></string>
<string name="simplex_link_mode">SimpleX links</string>
<string name="simplex_link_mode_description">Description</string>
<string name="simplex_link_mode_full">Full link</string>
<string name="simplex_link_mode_browser">Via browser</string>
<string name="simplex_link_mode_browser_warning">Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red.</string>
<!-- SimpleXAPI.kt -->
<string name="error_saving_smp_servers">Error saving SMP servers</string>
@@ -10,7 +10,6 @@ import SwiftUI
import SimpleXChat
private let uiLinkColor = UIColor(red: 0, green: 0.533, blue: 1, alpha: 1)
private let linkColor = Color(uiColor: uiLinkColor)
struct MsgContentView: View {
var text: String
@@ -70,11 +69,13 @@ private func formatText(_ ft: FormattedText, _ preview: Bool) -> Text {
case .secret: return Text(t).foregroundColor(.clear).underline(color: .primary)
case let .colored(color): return Text(t).foregroundColor(color.uiColor)
case .uri: return linkText(t, t, preview, prefix: "")
case let .simplexLink(linkType, simplexUri, smpHosts):
case let .simplexLink(linkType, simplexUri, trustedUri, smpHosts):
switch privacySimplexLinkModeDefault.get() {
case .description: return linkText(simplexLinkText(linkType, smpHosts), simplexUri, preview, prefix: "")
case .full: return linkText(t, simplexUri, preview, prefix: "")
case .browser: return linkText(t, t, preview, prefix: "")
case .browser: return trustedUri
? linkText(t, t, preview, prefix: "")
: linkText(t, t, preview, prefix: "", color: .red, uiColor: .red)
}
case .email: return linkText(t, t, preview, prefix: "mailto:")
case .phone: return linkText(t, t.replacingOccurrences(of: " ", with: ""), preview, prefix: "tel:")
@@ -84,13 +85,12 @@ private func formatText(_ ft: FormattedText, _ preview: Bool) -> Text {
}
}
private func linkText(_ s: String, _ link: String,
_ preview: Bool, prefix: String) -> Text {
private func linkText(_ s: String, _ link: String, _ preview: Bool, prefix: String, color: Color = Color(uiColor: uiLinkColor), uiColor: UIColor = uiLinkColor) -> Text {
preview
? Text(s).foregroundColor(linkColor).underline(color: linkColor)
? Text(s).foregroundColor(color).underline(color: color)
: Text(AttributedString(s, attributes: AttributeContainer([
.link: NSURL(string: prefix + link) as Any,
.foregroundColor: uiLinkColor as Any
.foregroundColor: uiColor as Any
]))).underline()
}
@@ -53,7 +53,7 @@ struct PrivacySettings: View {
Text("Chats")
} footer: {
if case .browser = simplexLinkMode {
Text("Opening the link in the browser may reduce connection privacy and security.")
Text("Opening the link in the browser may reduce connection privacy and security. Untrusted SimpleX links will be red.")
}
}
}
+1 -2
View File
@@ -1733,8 +1733,7 @@ public enum Format: Decodable, Equatable {
case secret
case colored(color: FormatColor)
case uri
// TODO trustedUri: Bool
case simplexLink(linkType: SimplexLinkType, simplexUri: String, smpHosts: [String])
case simplexLink(linkType: SimplexLinkType, simplexUri: String, trustedUri: Bool, smpHosts: [String])
case email
case phone
}