mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-10-23 15:03:16 +00:00
core, ui: markdown for hyperlinks, warn on unsanitized links, option to sanitize sent links (#6160)
* core: markdown for "hidden" links * update, test * api docs * chatParseUri FFI function * ios: hyperlinks, offer to open sanitized links, an option to send sanitized links (enabled by default) * update markdown * android, desktop: ditto * ios: export localizations * core: rename constructor, change Maybe semantics for web links * rename
This commit is contained in:
1 parent
b4293e361b
commit
ef60ceea12
55 files changed
+1004
-288
No files matched your search
@@ -30,7 +30,7 @@ struct CILinkView: View {
|
||||
VStack(alignment: .leading, spacing: 6) {
|
||||
Text(linkPreview.title)
|
||||
.lineLimit(3)
|
||||
Text(linkPreview.uri.absoluteString)
|
||||
Text(linkPreview.uri)
|
||||
.font(.caption)
|
||||
.lineLimit(1)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
@@ -44,25 +44,71 @@ struct CILinkView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func openBrowserAlert(uri: URL) {
|
||||
func openBrowserAlert(uri: String) {
|
||||
let (url, err) = sanitizeUri(uri)
|
||||
if let url {
|
||||
let uriStr = url.uri.absoluteString
|
||||
showAlert(
|
||||
NSLocalizedString("Open link?", comment: "alert title"),
|
||||
message: uriStr.count > 160 ? "\(uriStr.prefix(160))…" : uriStr,
|
||||
actions: {
|
||||
if let sanitizedUri = url.sanitizedUri {
|
||||
[
|
||||
cancelAlertAction,
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Open full link", comment: "alert action"),
|
||||
style: .destructive,
|
||||
handler: { _ in UIApplication.shared.open(url.uri) }
|
||||
),
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Open clean link", comment: "alert action"),
|
||||
style: .default,
|
||||
handler: { _ in UIApplication.shared.open(sanitizedUri) }
|
||||
)
|
||||
]
|
||||
} else {
|
||||
[
|
||||
cancelAlertAction,
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Open", comment: "alert action"),
|
||||
style: .default,
|
||||
handler: { _ in UIApplication.shared.open(url.uri) }
|
||||
)
|
||||
]
|
||||
}
|
||||
}
|
||||
)
|
||||
} else {
|
||||
showInvalidLinkAlert(uri, error: err)
|
||||
}
|
||||
}
|
||||
|
||||
func showInvalidLinkAlert(_ uri: String, error: String? = nil) {
|
||||
let message = if let error, !error.isEmpty {
|
||||
error + "\n" + uri
|
||||
} else {
|
||||
uri
|
||||
}
|
||||
showAlert(
|
||||
NSLocalizedString("Open link?", comment: "alert title"),
|
||||
message: uri.absoluteString,
|
||||
actions: {[
|
||||
cancelAlertAction,
|
||||
UIAlertAction(
|
||||
title: NSLocalizedString("Open", comment: "alert action"),
|
||||
style: .default,
|
||||
handler: { _ in UIApplication.shared.open(uri) }
|
||||
)
|
||||
]}
|
||||
NSLocalizedString("Invalid link", comment: "alert title"),
|
||||
message: message,
|
||||
actions: {[okAlertAction]}
|
||||
)
|
||||
}
|
||||
|
||||
func sanitizeUri(_ s: String) -> (url: (uri: URL, sanitizedUri: URL?)?, error: String?) {
|
||||
let parsed = parseSanitizeUri(s)
|
||||
return if let uri = URL(string: s), let uriInfo = parsed?.uriInfo {
|
||||
(url: (uri: uri, sanitizedUri: uriInfo.sanitized.flatMap { URL(string: $0) }), error: nil)
|
||||
} else {
|
||||
(url: nil, error: parsed?.parseError)
|
||||
}
|
||||
}
|
||||
|
||||
struct LargeLinkPreview_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let preview = LinkPreview(
|
||||
uri: URL(string: "http://DuckDuckGo.com")!,
|
||||
uri: "http://DuckDuckGo.com",
|
||||
title: "Privacy, simplified.",
|
||||
description: "",
|
||||
image: "data:image/jpg;base64,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 truncated
|
||||
|
||||
@@ -187,23 +187,25 @@ private func handleTextTaps(
|
||||
}
|
||||
}
|
||||
}
|
||||
if let index, let (url, browser) = attributedStringLink(s, for: index) {
|
||||
if let index, let (uri, browser) = attributedStringLink(s, for: index) {
|
||||
if browser {
|
||||
openBrowserAlert(uri: url)
|
||||
} else {
|
||||
openBrowserAlert(uri: uri)
|
||||
} else if let url = URL(string: uri) {
|
||||
UIApplication.shared.open(url)
|
||||
} else {
|
||||
showInvalidLinkAlert(uri)
|
||||
}
|
||||
}
|
||||
})
|
||||
}
|
||||
|
||||
func attributedStringLink(_ s: NSAttributedString, for index: CFIndex) -> (URL, Bool)? {
|
||||
var linkURL: URL?
|
||||
func attributedStringLink(_ s: NSAttributedString, for index: CFIndex) -> (String, Bool)? {
|
||||
var linkURL: String?
|
||||
var browser: Bool = false
|
||||
s.enumerateAttributes(in: NSRange(location: 0, length: s.length)) { attrs, range, stop in
|
||||
if index >= range.location && index < range.location + range.length {
|
||||
if let url = attrs[linkAttrKey] as? NSURL {
|
||||
linkURL = url.absoluteURL
|
||||
if let url = attrs[linkAttrKey] as? String {
|
||||
linkURL = url
|
||||
browser = attrs[webLinkAttrKey] != nil
|
||||
} else if let showSecrets, let i = attrs[secretAttrKey] as? Int {
|
||||
if showSecrets.wrappedValue.contains(i) {
|
||||
@@ -356,22 +358,32 @@ func messageText(
|
||||
case .uri:
|
||||
attrs = linkAttrs()
|
||||
if !preview {
|
||||
let s = t.lowercased()
|
||||
let link = s.hasPrefix("http://") || s.hasPrefix("https://")
|
||||
let link = t.hasPrefix("http://") || t.hasPrefix("https://")
|
||||
? t
|
||||
: "https://" + t
|
||||
attrs[linkAttrKey] = NSURL(string: link)
|
||||
attrs[linkAttrKey] = link
|
||||
attrs[webLinkAttrKey] = true
|
||||
handleTaps = true
|
||||
}
|
||||
case let .simplexLink(linkType, simplexUri, smpHosts):
|
||||
case let .hyperLink(text, uri):
|
||||
attrs = linkAttrs()
|
||||
if let text { t = text }
|
||||
if !preview {
|
||||
attrs[linkAttrKey] = NSURL(string: simplexUri)
|
||||
attrs[linkAttrKey] = uri
|
||||
attrs[webLinkAttrKey] = true
|
||||
handleTaps = true
|
||||
}
|
||||
if case .description = privacySimplexLinkModeDefault.get() {
|
||||
t = simplexLinkText(linkType, smpHosts)
|
||||
case let .simplexLink(text, linkType, simplexUri, smpHosts):
|
||||
attrs = linkAttrs()
|
||||
if !preview {
|
||||
attrs[linkAttrKey] = simplexUri
|
||||
handleTaps = true
|
||||
}
|
||||
if let s = text ?? (privacySimplexLinkModeDefault.get() == .description ? linkType.description : nil) {
|
||||
res.append(NSAttributedString(string: s + " ", attributes: attrs))
|
||||
italic = italic ?? UIFont(descriptor: descr.withSymbolicTraits(.traitItalic) ?? descr, size: descr.pointSize)
|
||||
attrs[.font] = italic
|
||||
t = viaHost(smpHosts)
|
||||
}
|
||||
case let .command(cmdStr):
|
||||
snippet = snippet ?? UIFont.monospacedSystemFont(ofSize: descr.pointSize, weight: .regular)
|
||||
@@ -403,13 +415,13 @@ func messageText(
|
||||
case .email:
|
||||
attrs = linkAttrs()
|
||||
if !preview {
|
||||
attrs[linkAttrKey] = NSURL(string: "mailto:" + ft.text)
|
||||
attrs[linkAttrKey] = "mailto:" + ft.text
|
||||
handleTaps = true
|
||||
}
|
||||
case .phone:
|
||||
attrs = linkAttrs()
|
||||
if !preview {
|
||||
attrs[linkAttrKey] = NSURL(string: "tel:" + t.replacingOccurrences(of: " ", with: ""))
|
||||
attrs[linkAttrKey] = "tel:" + t.replacingOccurrences(of: " ", with: "")
|
||||
handleTaps = true
|
||||
}
|
||||
case .unknown: ()
|
||||
@@ -439,7 +451,11 @@ private func mentionText(_ name: String) -> String {
|
||||
}
|
||||
|
||||
func simplexLinkText(_ linkType: SimplexLinkType, _ smpHosts: [String]) -> String {
|
||||
linkType.description + " " + "(via \(smpHosts.first ?? "?"))"
|
||||
linkType.description + " " + viaHost(smpHosts)
|
||||
}
|
||||
|
||||
func viaHost(_ smpHosts: [String]) -> String {
|
||||
"(via \(smpHosts.first ?? "?"))"
|
||||
}
|
||||
|
||||
struct MsgContentView_Previews: PreviewProvider {
|
||||
|
||||
@@ -49,7 +49,7 @@ struct ComposeLinkView: View {
|
||||
VStack(alignment: .center, spacing: 4) {
|
||||
Text(linkPreview.title)
|
||||
.lineLimit(1)
|
||||
Text(linkPreview.uri.absoluteString)
|
||||
Text(linkPreview.uri)
|
||||
.font(.caption)
|
||||
.lineLimit(1)
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
@@ -63,7 +63,7 @@ struct ComposeLinkView: View {
|
||||
struct SmallLinkPreview_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let preview = LinkPreview(
|
||||
uri: URL(string: "http://DuckDuckGo.com")!,
|
||||
uri: "http://DuckDuckGo.com",
|
||||
title: "Privacy, simplified.",
|
||||
description: "",
|
||||
image: "data:image/jpg;base64,/9j/4AAQSkZJRgABAQAASABIAAD/4QBYRXhpZgAATU0AKgAAAAgAAgESAAMAAAABAAEAAIdpAAQAAAABAAAAJgAAAAAAA6ABAAMAAAABAAEAAKACAAQAAAABAAAAuKADAAQAAAABAAAAYAAAAAD/7QA4UGhvdG9zaG9wIDMuMAA4QklNBAQAAAAAAAA4QklNBCUAAAAAABDUHYzZjwCyBOmACZjs+EJ+/8AAEQgAYAC4AwEiAAIRAQMRAf/EAB8AAAEFAQEBAQEBAAAAAAAAAAABAgMEBQYHCAkKC//EALUQAAIBAwMCBAMFBQQEAAABfQECAwAEEQUSITFBBhNRYQcicRQygZGhCCNCscEVUtHwJDNicoIJChYXGBkaJSYnKCkqNDU2Nzg5OkNERUZHSElKU1RVVldYWVpjZGVmZ2hpanN0dXZ3eHl6g4SFhoeIiYqSk5SVlpeYmZqio6Slpqeoqaqys7S1tre4ubrCw8TFxsfIycrS09TV1tfY2drh4uPk5ebn6Onq8fLz9PX29/j5+v/EAB8BAAMBAQEBAQEBAQEAAAAAAAABAgMEBQYHCAkKC//EALURAAIBAgQEAwQHBQQEAAECdwABAgMRBAUhMQYSQVEHYXETIjKBCBRCkaGxwQkjM1LwFWJy0QoWJDThJfEXGBkaJicoKSo1Njc4OTpDREVGR0hJSlNUVVZXWFlaY2RlZmdoaWpzdHV2d3h5eoKDhIWGh4iJipKTlJWWl5iZmqKjpKWmp6ipqrKztLW2t7i5usLDxMXGx8jJytLT1NXW19jZ2uLj5OXm5+jp6vLz9PX29/j5+v/bAEMAAQEBAQEBAgEBAgMCAgIDBAMDAwMEBgQEBAQEBgcGBgYGBgYHBwcHBwcHBwgICAgICAkJCQkJCwsLCwsLCwsLC//bAEMBAgICAwMDBQMDBQsIBggLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLCwsLC//dAAQADP/aAAwDAQACEQMRAD8A/v4ooooAKKKKACiiigAooooAKK+CP2vP+ChXwZ/ZPibw7dMfEHi2VAYdGs3G9N33TO/IiU9hgu3ZSOa/NzXNL/4KJ/td6JJ49+NXiq2+Cvw7kG/ZNKbDMLcjKblmfI/57SRqewrwMdxBRo1HQoRdWqt1HaP+KT0j838j7XKOCMXiqEcbjKkcPh5bSne8/wDr3BXlN+is+5+43jb45/Bf4bs0fj/xZpGjSL1jvL2KF/8AvlmDfpXjH/DfH7GQuPsv/CydD35x/wAfIx+fT9a/AO58D/8ABJj4UzvF4v8AFfif4l6mp/evpkfkWzP3w2Isg+omb61X/wCF0/8ABJr/AI9f+FQeJPL6ed9vbzPrj7ZivnavFuIT+KhHyc5Sf3wjY+7w/hlgZQv7PF1P70aUKa+SqTUvwP6afBXx2+CnxIZYvAHi3R9ZkfpHZ3sUz/8AfKsW/SvVq/lItvBf/BJX4rTLF4V8UeJ/hpqTH91JqUfn2yv2y2JcD3MqfUV9OaFon/BRH9krQ4vH3wI8XW3xq+HkY3+XDKb/ABCvJxHuaZMDr5Ergd1ruwvFNVrmq0VOK3lSkp29Y6SS+R5GY+HGGi1DD4qVKo9oYmm6XN5RqK9Nvsro/obor4A/ZC/4KH/Bv9qxV8MLnw54vjU+bo9443SFPvG3k4EoHdcB17rjmvv+vqcHjaGKpKth5qUX1X9aPyZ+b5rlOMy3ESwmOpOFRdH+aezT6NXTCiiiuo84KKKKACiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/Q/v4ooooAKKKKACiiigAr8tf+ChP7cWs/BEWfwD+A8R1P4k+JQkUCQr5rWUc52o+zndNIf9Up4H324wD9x/tDfGjw/wDs9fBnX/i/4jAeHRrZpI4c4M87YWKIe7yFV9gc9q/n6+B3iOb4GfCLxL/wU1+Oypq3jzxndT2nhK2uBwZptyvcBeoQBSq4xthjwPvivluIs0lSthKM+WUk5Sl/JBbtebekfM/R+BOHaeIcszxVL2kISUKdP/n7WlrGL/uxXvT8u6uizc6b8I/+CbmmRePPi9HD8Q/j7rifbktLmTz7bSGm582ZzktITyX++5+5tX5z5L8LPgv+0X/wVH12+8ZfEbxneW/2SRxB9o02eTSosdY4XRlgjYZGV++e5Jr8xvF3i7xN4+8UX/jXxney6jquqTNcXVzMcvJI5ySfQdgBwBgDgV+sP/BPX9jj9oL9oXw9H4tuvG2s+DfAVlM8VsthcyJLdSBsyCBNwREDZ3SEHLcBTgkfmuX4j+0MXHB06LdBXagna/8AenK6u+7el9Ej9+zvA/2Jls81r4uMcY7J1px5lHf93ShaVo9FFJNq8pMyPil/wRs/aj8D6dLq3gq70vxdHECxgtZGtrogf3UmAQn2EmT2r8rPEPh3xB4R1u58M+KrGfTdRsnMdxa3MbRTROOzKwBBr+674VfCnTfhNoI0DTtX1jWFAGZtYvpL2U4934X/AICAK8V/aW/Yf/Z9/areHUvibpkkerWsRhg1KxkMFyqHkBiMrIAeQJFYDJxjJr6bNPD+nOkqmAfLP+WTuvk7XX4/I/PeHvG6tSxDo5zH2lLpUhHll6uN7NelmvPY/iir2T4KftA/GD9njxMvir4Q65caTPkGWFTutrgD+GaE/I4+oyOxB5r2n9tb9jTxj+x18RYvD+pTtqmgaqrS6VqezZ5qpjfHIBwsseRuA4IIYdcD4yr80q0sRgcQ4SvCpB+jT8mvzP6Bw2JwOcYGNany1aFRdVdNdmn22aauno9T9tLO0+D/APwUr02Txd8NI4Ph38ftGT7b5NtIYLXWGh58yJwQVkBGd/8ArEP3i6fMP0R/4J7ftw6/8YZ7z9nb9oGJtN+JPhoPFIJ18p75IPlclegnj/5aKOGHzrxnH8rPhXxT4j8D+JbHxj4QvZdO1TTJkuLW5hba8UqHIIP8x0I4PFfsZ8bPEdx+0N8FvDv/AAUl+CgXSfiJ4EuYLXxZBbDALw4CXO0clMEZznMLlSf3Zr7PJM+nzyxUF+9ir1IrRVILeVtlOO+lrr5n5RxfwbRdKGXVXfDzfLRm9ZUKr+GDlq3RqP3UnfllZfy2/ptorw/9m/43aF+0X8FNA+L+gARpq1uGnhByYLlCUmiP+44IHqMHvXuFfsNGtCrTjVpu8ZJNPyZ/LWKwtXDVp4evG04Nxa7NOzX3hRRRWhzhRRRQBBdf8e0n+6f5Vx1djdf8e0n+6f5Vx1AH/9H+/iiiigAooooAKKKKAPw9/wCCvXiPWviH4q+F/wCyN4XlKT+K9TS6uQvoXFvAT7AvI3/AQe1fnF/wVO+IOnXfxx034AeDj5Xhv4ZaXb6TawKfkE7Ro0rY6bgvlofdT61+h3xNj/4Tv/gtd4Q0W/8Anh8P6THLGp6Ax21xOD/324Nfg3+0T4kufGH7QHjjxRdtukvte1GXJ9PPcKPwAAr8a4pxUpLEz6zq8n/btOK0+cpX9Uf1d4c5bCDy+lbSlh3W/wC38RNq/qoQcV5M8fjiaeRYEOGchR9TxX9svw9+GHijSvgB4I+Gnwr1ceGbGztYY728gijluhbohLLAJVeJZJJCN0jo+0Zwu4gj+JgO8REsf3l+YfUV/bf8DNVm+Mv7KtkNF1CTTZ9Z0d4Ir2D/AFls9zF8sidPmj3hhz1Fel4YyhGtiHpzWjur6e9f9Dw/H9VXQwFvgvUv62hb8Oa3zPoDwfp6aPoiaONXuNaa1Zo3ubp43nLDqrmJEXI/3QfWukmjMsTRBihYEbl6jPcZ7ivxk/4JMf8ABOv9ob9hBvFdr8ZvGOma9Yak22wttLiYGV2kMkl1dzSIkkkzcKisX8tSwDYNfs/X7Bj6NOlXlCjUU4/zJWv8j+ZsNUnOmpThyvtufj/+1Z8Hf2bPi58PviF8Avh/4wl1j4iaBZjXG0m71qfU7i3u4FMqt5VxLL5LzR70Kx7AVfJXAXH8sysGUMOh5r+vzwl+wD+y78KP2wPEX7bGn6xqFv4g8QmWa70+fUFGlrdTRmGS4EGATIY2dRvdlXe+0DPH83Nh+x58bPFev3kljpSaVYPcymGS+kEX7oudp2DL/dx/DX4Z4xZxkmCxGHxdTGRTlG0ueUU7q3S93a7S69Oh/SngTnNSjgcZhMc1CnCSlC70966dr/4U7Lq79T5Kr9MP+CWfxHsNH+P138EPF2JvDfxL0640a9gc/I0vls0Rx6kb4x/v1x3iz9hmHwV4KuPFHiLxlaWkltGzt5sBSAsBkIHL7iT0GFJJ7V8qfAnxLc+D/jd4N8V2bFJdP1vT5wR/szoT+YyK/NeD+Lcvx+Ijisuq88ackpPlklruveSvdX2ufsmavC5zlWKw9CV7xaTs1aSV4tXS1Ukmrdj9/P8Agkfrus/DD4ifFP8AY/8AEkrPJ4Z1F7y1DeiSG3mI9m2wv/wI1+5Ffhd4Ki/4Qf8A4Lb+INM0/wCSHxDpDySqOhL2cMx/8fizX7o1/RnC7ccLPDP/AJdTnBeid1+DP5M8RkqmZUselZ4ijSqv1lG0vvcWwooor6Q+BCiiigCC6/49pP8AdP8AKuOrsbr/AI9pP90/yrjqAP/S/v4ooooAKKKKACiiigD8LfiNIfBP/BbLwpq9/wDJDr2kJHGTwCZLS4gH/j0eK/Bj9oPw7c+Evj3428M3ilZLHXtRiIPoJ3x+Ywa/fL/grnoWsfDPx98K/wBrzw5EzyeGNSS0uSvokguYQfZtsy/8CFfnB/wVP+HNho/7QFp8bvCeJvDnxK0231mznQfI0vlqsoz6kbJD/v1+M8U4WUViYW1hV5/+3akVr/4FG3qz+r/DnMYTeX1b6VcP7L/t/Dzenq4Tcl5I/M2v6yP+CR3j4eLP2XbLRZZN0uku9sRnp5bMB/45sr+Tev3u/Line truncated
|
||||
|
||||
@@ -329,10 +329,10 @@ struct ComposeView: View {
|
||||
@Binding var selectedRange: NSRange
|
||||
var disabledText: LocalizedStringKey? = nil
|
||||
|
||||
@State var linkUrl: URL? = nil
|
||||
@State var linkUrl: String? = nil
|
||||
@State var hasSimplexLink: Bool = false
|
||||
@State var prevLinkUrl: URL? = nil
|
||||
@State var pendingLinkUrl: URL? = nil
|
||||
@State var prevLinkUrl: String? = nil
|
||||
@State var pendingLinkUrl: String? = nil
|
||||
@State var cancelledLinks: Set<String> = []
|
||||
|
||||
@Environment(\.colorScheme) private var colorScheme
|
||||
@@ -353,6 +353,8 @@ struct ComposeView: View {
|
||||
@UserDefault(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true
|
||||
@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 = true
|
||||
@State private var updatingCompose = false
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
@@ -454,8 +456,26 @@ struct ComposeView: View {
|
||||
.ignoresSafeArea(.all, edges: .bottom)
|
||||
}
|
||||
.onChange(of: composeState.message) { msg in
|
||||
let parsedMsg = parseSimpleXMarkdown(msg)
|
||||
composeState = composeState.copy(parsedMessage: parsedMsg ?? FormattedText.plain(msg))
|
||||
if updatingCompose {
|
||||
updatingCompose = false
|
||||
return
|
||||
}
|
||||
var parsedMsg = parseSimpleXMarkdown(msg)
|
||||
if privacySanitizeLinks, let parsed = parsedMsg {
|
||||
let r = sanitizeMessage(parsed)
|
||||
if let sanitizedPos = r.sanitizedPos {
|
||||
updatingCompose = true
|
||||
composeState = composeState.copy(message: r.message, parsedMessage: r.parsedMsg)
|
||||
if sanitizedPos < selectedRange.location {
|
||||
selectedRange = NSRange(location: sanitizedPos, length: 0)
|
||||
}
|
||||
parsedMsg = r.parsedMsg
|
||||
} else {
|
||||
composeState = composeState.copy(parsedMessage: parsedMsg)
|
||||
}
|
||||
} else {
|
||||
composeState = composeState.copy(parsedMessage: parsedMsg ?? FormattedText.plain(msg))
|
||||
}
|
||||
if composeState.linkPreviewAllowed {
|
||||
if msg.count > 0 {
|
||||
showLinkPreview(parsedMsg)
|
||||
@@ -464,7 +484,7 @@ struct ComposeView: View {
|
||||
hasSimplexLink = false
|
||||
}
|
||||
} else if msg.count > 0 && !chat.groupFeatureEnabled(.simplexLinks) {
|
||||
(_, hasSimplexLink) = getSimplexLink(parsedMsg)
|
||||
(_, hasSimplexLink) = getMessageLinks(parsedMsg)
|
||||
} else {
|
||||
hasSimplexLink = false
|
||||
}
|
||||
@@ -845,7 +865,7 @@ struct ComposeView: View {
|
||||
switch (composeState.preview) {
|
||||
case let .linkPreview(linkPreview: linkPreview):
|
||||
if let parsedMsg = parseSimpleXMarkdown(msgText),
|
||||
let url = getSimplexLink(parsedMsg).url,
|
||||
let url = getMessageLinks(parsedMsg).url,
|
||||
let linkPreview = linkPreview,
|
||||
url == linkPreview.uri {
|
||||
return .link(text: msgText, preview: linkPreview)
|
||||
@@ -1448,7 +1468,7 @@ struct ComposeView: View {
|
||||
|
||||
private func showLinkPreview(_ parsedMsg: [FormattedText]?) {
|
||||
prevLinkUrl = linkUrl
|
||||
(linkUrl, hasSimplexLink) = getSimplexLink(parsedMsg)
|
||||
(linkUrl, hasSimplexLink) = getMessageLinks(parsedMsg)
|
||||
if let url = linkUrl {
|
||||
if url != composeState.linkPreview?.uri && url != pendingLinkUrl {
|
||||
pendingLinkUrl = url
|
||||
@@ -1465,39 +1485,38 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func getSimplexLink(_ parsedMsg: [FormattedText]?) -> (url: URL?, hasSimplexLink: Bool) {
|
||||
private func getMessageLinks(_ parsedMsg: [FormattedText]?) -> (url: String?, hasSimplexLink: Bool) {
|
||||
guard let parsedMsg else { return (nil, false) }
|
||||
let url: URL? = if let uri = parsedMsg.first(where: { ft in
|
||||
ft.format == .uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text)
|
||||
}) {
|
||||
URL(string: uri.text)
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
let simplexLink = parsedMsgHasSimplexLink(parsedMsg)
|
||||
return (url, simplexLink)
|
||||
for ft in parsedMsg {
|
||||
if let link = ft.linkUri, !cancelledLinks.contains(link) && !isSimplexLink(link) {
|
||||
return (link, simplexLink)
|
||||
}
|
||||
}
|
||||
return (nil, simplexLink)
|
||||
}
|
||||
|
||||
private func isSimplexLink(_ link: String) -> Bool {
|
||||
link.starts(with: "https://simplex.chat") || link.starts(with: "http://simplex.chat")
|
||||
link.starts(with: "https://simplex.chat") || link.starts(with: "http://simplex.chat") || link.starts(with: "simplex:/")
|
||||
}
|
||||
|
||||
private func cancelLinkPreview() {
|
||||
if let pendingLink = pendingLinkUrl?.absoluteString {
|
||||
if let pendingLink = pendingLinkUrl {
|
||||
cancelledLinks.insert(pendingLink)
|
||||
}
|
||||
if let uri = composeState.linkPreview?.uri.absoluteString {
|
||||
if let uri = composeState.linkPreview?.uri {
|
||||
cancelledLinks.insert(uri)
|
||||
}
|
||||
pendingLinkUrl = nil
|
||||
composeState = composeState.copy(preview: .noPreview)
|
||||
}
|
||||
|
||||
private func loadLinkPreview(_ url: URL) {
|
||||
if pendingLinkUrl == url {
|
||||
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 == url {
|
||||
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) {
|
||||
@@ -1516,3 +1535,32 @@ struct ComposeView: View {
|
||||
cancelledLinks = []
|
||||
}
|
||||
}
|
||||
|
||||
func sanitizeMessage(_ parsedMsg: [FormattedText]) -> (message: String, parsedMsg: [FormattedText], sanitizedPos: Int?) {
|
||||
var pos: Int = 0
|
||||
var updatedMsg = ""
|
||||
var sanitizedPos: Int? = nil
|
||||
let updatedParsedMsg = parsedMsg.map { ft in
|
||||
var updated = ft
|
||||
switch ft.format {
|
||||
case .uri:
|
||||
if let sanitized = parseSanitizeUri(ft.text)?.uriInfo?.sanitized {
|
||||
updated = FormattedText(text: sanitized, format: .uri)
|
||||
pos += updated.text.count
|
||||
sanitizedPos = pos
|
||||
}
|
||||
case let .hyperLink(text, uri):
|
||||
if let sanitized = parseSanitizeUri(uri)?.uriInfo?.sanitized {
|
||||
let updatedText = if let text { "[\(text)](\(sanitized))" } else { sanitized }
|
||||
updated = FormattedText(text: updatedText, format: .hyperLink(showText: text, linkUri: sanitized))
|
||||
pos += updated.text.count
|
||||
sanitizedPos = pos
|
||||
}
|
||||
default:
|
||||
pos += ft.text.count
|
||||
}
|
||||
updatedMsg += updated.text
|
||||
return updated
|
||||
}
|
||||
return (message: updatedMsg, parsedMsg: updatedParsedMsg, sanitizedPos: sanitizedPos)
|
||||
}
|
||||
@@ -629,7 +629,7 @@ struct ChatListSearchBar: View {
|
||||
} else {
|
||||
if let link = strHasSingleSimplexLink(t.trimmingCharacters(in: .whitespaces)) { // if SimpleX link is pasted, show connection dialogue
|
||||
searchFocussed = false
|
||||
if case let .simplexLink(linkType, _, smpHosts) = link.format {
|
||||
if case let .simplexLink(_, linkType, _, smpHosts) = link.format {
|
||||
ignoreSearchTextChange = true
|
||||
searchText = simplexLinkText(linkType, smpHosts)
|
||||
}
|
||||
|
||||
@@ -381,7 +381,7 @@ struct ContactsListSearchBar: View {
|
||||
} else {
|
||||
if let link = strHasSingleSimplexLink(t.trimmingCharacters(in: .whitespaces)) { // if SimpleX link is pasted, show connection dialogue
|
||||
searchFocussed = false
|
||||
if case let .simplexLink(linkType, _, smpHosts) = link.format {
|
||||
if case let .simplexLink(_, linkType, _, smpHosts) = link.format {
|
||||
ignoreSearchTextChange = true
|
||||
searchText = simplexLinkText(linkType, smpHosts)
|
||||
}
|
||||
|
||||
@@ -14,6 +14,7 @@ struct DeveloperView: View {
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@AppStorage(GROUP_DEFAULT_CONFIRM_DB_UPGRADES, store: groupDefaults) private var confirmDatabaseUpgrades = false
|
||||
@State private var hintsUnchanged = hintDefaultsUnchanged()
|
||||
@State private var simplexLinkMode = privacySimplexLinkModeDefault.get()
|
||||
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
|
||||
@@ -65,6 +66,21 @@ struct DeveloperView: View {
|
||||
Text("Developer options")
|
||||
}
|
||||
}
|
||||
Section("Deprecated options") {
|
||||
settingsRow("link", color: theme.colors.secondary) {
|
||||
Picker("SimpleX links", selection: $simplexLinkMode) {
|
||||
ForEach(
|
||||
SimpleXLinkMode.values + (SimpleXLinkMode.values.contains(simplexLinkMode) ? [] : [simplexLinkMode])
|
||||
) { mode in
|
||||
Text(mode.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 36)
|
||||
.onChange(of: simplexLinkMode) { mode in
|
||||
privacySimplexLinkModeDefault.set(mode)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,11 @@ struct PrivacySettings: View {
|
||||
@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_SANITIZE_LINKS, store: groupDefaults) private var privacySanitizeLinks = true
|
||||
@AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true
|
||||
@AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES, store: groupDefaults) private var encryptLocalFiles = true
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_ASK_TO_APPROVE_RELAYS, store: groupDefaults) private var askToApproveRelays = true
|
||||
@State private var simplexLinkMode = privacySimplexLinkModeDefault.get()
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
@@ -75,8 +75,12 @@ struct PrivacySettings: View {
|
||||
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
|
||||
}
|
||||
}
|
||||
settingsRow("link", color: theme.colors.secondary) {
|
||||
Toggle("Remove link tracking", isOn: $privacySanitizeLinks)
|
||||
}
|
||||
settingsRow("message", color: theme.colors.secondary) {
|
||||
Toggle("Show last messages", isOn: $showChatPreviews)
|
||||
}
|
||||
@@ -89,19 +93,6 @@ struct PrivacySettings: View {
|
||||
m.draftChatId = nil
|
||||
}
|
||||
}
|
||||
settingsRow("link", color: theme.colors.secondary) {
|
||||
Picker("SimpleX links", selection: $simplexLinkMode) {
|
||||
ForEach(
|
||||
SimpleXLinkMode.values + (SimpleXLinkMode.values.contains(simplexLinkMode) ? [] : [simplexLinkMode])
|
||||
) { mode in
|
||||
Text(mode.text)
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(height: 36)
|
||||
.onChange(of: simplexLinkMode) { mode in
|
||||
privacySimplexLinkModeDefault.set(mode)
|
||||
}
|
||||
} header: {
|
||||
Text("Chats")
|
||||
.foregroundColor(theme.colors.secondary)
|
||||
|
||||
@@ -2563,6 +2563,10 @@ swipe action</note>
|
||||
<target>Потвърждениe за доставка!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Описание</target>
|
||||
@@ -4289,7 +4293,7 @@ More improvements are coming soon!</source>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Невалиден линк</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5460,10 +5464,18 @@ Requires compatible VPN.</source>
|
||||
<target>Отвори конзолата</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Отвори група</target>
|
||||
@@ -6193,6 +6205,10 @@ swipe action</note>
|
||||
<source>Remove image</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Острани член</target>
|
||||
|
||||
@@ -2455,6 +2455,10 @@ swipe action</note>
|
||||
<target>Potvrzení o doručení!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Popis</target>
|
||||
@@ -4128,7 +4132,7 @@ More improvements are coming soon!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5267,10 +5271,18 @@ Vyžaduje povolení sítě VPN.</target>
|
||||
<target>Otevřete konzolu chatu</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<note>new chat action</note>
|
||||
@@ -5976,6 +5988,10 @@ swipe action</note>
|
||||
<source>Remove image</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Odstranit člena</target>
|
||||
|
||||
@@ -2680,6 +2680,10 @@ swipe action</note>
|
||||
<target>Empfangsbestätigungen!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Beschreibung</target>
|
||||
@@ -4517,7 +4521,7 @@ Weitere Verbesserungen sind bald verfügbar!</target>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Ungültiger Link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5770,11 +5774,19 @@ Dies erfordert die Aktivierung eines VPNs.</target>
|
||||
<target>Chat-Konsole öffnen</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Nutzungsbedingungen öffnen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Gruppe öffnen</target>
|
||||
@@ -6564,6 +6576,10 @@ swipe action</note>
|
||||
<target>Bild entfernen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Mitglied entfernen</target>
|
||||
|
||||
@@ -2684,6 +2684,11 @@ swipe action</note>
|
||||
<target>Delivery receipts!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<target>Deprecated options</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Description</target>
|
||||
@@ -4522,7 +4527,7 @@ More improvements are coming soon!</target>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Invalid link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5777,11 +5782,21 @@ Requires compatible VPN.</target>
|
||||
<target>Open chat console</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<target>Open clean link</target>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Open conditions</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<target>Open full link</target>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Open group</target>
|
||||
@@ -6572,6 +6587,11 @@ swipe action</note>
|
||||
<target>Remove image</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<target>Remove link tracking</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Remove member</target>
|
||||
|
||||
@@ -2680,6 +2680,10 @@ swipe action</note>
|
||||
<target>¡Confirmación de entrega!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Descripción</target>
|
||||
@@ -4517,7 +4521,7 @@ More improvements are coming soon!</source>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Enlace no válido</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5770,11 +5774,19 @@ Requiere activación de la VPN.</target>
|
||||
<target>Abrir consola de Chat</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Abrir condiciones</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Grupo abierto</target>
|
||||
@@ -6564,6 +6576,10 @@ swipe action</note>
|
||||
<target>Eliminar imagen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Expulsar miembro</target>
|
||||
|
||||
@@ -2426,6 +2426,10 @@ swipe action</note>
|
||||
<target>Toimituskuittaukset!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Kuvaus</target>
|
||||
@@ -4096,7 +4100,7 @@ More improvements are coming soon!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5233,10 +5237,18 @@ Edellyttää VPN:n sallimista.</target>
|
||||
<target>Avaa keskustelukonsoli</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<note>new chat action</note>
|
||||
@@ -5942,6 +5954,10 @@ swipe action</note>
|
||||
<source>Remove image</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Poista jäsen</target>
|
||||
|
||||
@@ -2661,6 +2661,10 @@ swipe action</note>
|
||||
<target>Justificatifs de réception !</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Description</target>
|
||||
@@ -4474,7 +4478,7 @@ D'autres améliorations sont à venir !</target>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Lien invalide</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5694,11 +5698,19 @@ Nécessite l'activation d'un VPN.</target>
|
||||
<target>Ouvrir la console du chat</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Ouvrir les conditions</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Ouvrir le groupe</target>
|
||||
@@ -6467,6 +6479,10 @@ swipe action</note>
|
||||
<target>Enlever l'image</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Retirer le membre</target>
|
||||
|
||||
@@ -2680,6 +2680,10 @@ swipe action</note>
|
||||
<target>Kézbesítési jelentések!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Leírás</target>
|
||||
@@ -4517,7 +4521,7 @@ További fejlesztések hamarosan!</target>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Érvénytelen hivatkozás</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5770,11 +5774,19 @@ VPN engedélyezése szükséges.</target>
|
||||
<target>Csevegési konzol megnyitása</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Feltételek megnyitása</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Csoport megnyitása</target>
|
||||
@@ -6564,6 +6576,10 @@ swipe action</note>
|
||||
<target>Kép eltávolítása</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Eltávolítás</target>
|
||||
|
||||
@@ -2680,6 +2680,10 @@ swipe action</note>
|
||||
<target>Ricevute di consegna!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Descrizione</target>
|
||||
@@ -4517,7 +4521,7 @@ Altri miglioramenti sono in arrivo!</target>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Link non valido</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5770,11 +5774,19 @@ Richiede l'attivazione della VPN.</target>
|
||||
<target>Apri la console della chat</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Apri le condizioni</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Apri gruppo</target>
|
||||
@@ -6564,6 +6576,10 @@ swipe action</note>
|
||||
<target>Rimuovi immagine</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Rimuovi membro</target>
|
||||
|
||||
@@ -2504,6 +2504,10 @@ swipe action</note>
|
||||
<target>配信通知!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>説明</target>
|
||||
@@ -4177,7 +4181,7 @@ More improvements are coming soon!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5318,10 +5322,18 @@ VPN を有効にする必要があります。</target>
|
||||
<target>チャットのコンソールを開く</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<note>new chat action</note>
|
||||
@@ -6027,6 +6039,10 @@ swipe action</note>
|
||||
<source>Remove image</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>メンバーを除名する</target>
|
||||
|
||||
@@ -2670,6 +2670,10 @@ swipe action</note>
|
||||
<target>Ontvangstbewijzen!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Beschrijving</target>
|
||||
@@ -4498,7 +4502,7 @@ Binnenkort meer verbeteringen!</target>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Ongeldige link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5743,11 +5747,19 @@ Vereist het inschakelen van VPN.</target>
|
||||
<target>Chat console openen</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Open voorwaarden</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Open groep</target>
|
||||
@@ -6530,6 +6542,10 @@ swipe action</note>
|
||||
<target>Verwijder afbeelding</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Lid verwijderen</target>
|
||||
|
||||
@@ -2630,6 +2630,10 @@ swipe action</note>
|
||||
<target>Potwierdzenia dostawy!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Opis</target>
|
||||
@@ -4406,7 +4410,7 @@ More improvements are coming soon!</source>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Nieprawidłowy link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5607,10 +5611,18 @@ Wymaga włączenia VPN.</target>
|
||||
<target>Otwórz konsolę czatu</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Grupa otwarta</target>
|
||||
@@ -6373,6 +6385,10 @@ swipe action</note>
|
||||
<target>Usuń obraz</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Usuń członka</target>
|
||||
|
||||
@@ -2680,6 +2680,10 @@ swipe action</note>
|
||||
<target>Отчёты о доставке!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Описание</target>
|
||||
@@ -4516,7 +4520,7 @@ More improvements are coming soon!</source>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Ошибка ссылки</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5769,11 +5773,19 @@ Requires compatible VPN.</source>
|
||||
<target>Открыть консоль</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Открыть условия</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Открыть группу</target>
|
||||
@@ -6563,6 +6575,10 @@ swipe action</note>
|
||||
<target>Удалить изображение</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Удалить члена группы</target>
|
||||
|
||||
@@ -2414,6 +2414,10 @@ swipe action</note>
|
||||
<target>ใบตอบรับการจัดส่ง!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>คำอธิบาย</target>
|
||||
@@ -4080,7 +4084,7 @@ More improvements are coming soon!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5212,10 +5216,18 @@ Requires compatible VPN.</source>
|
||||
<target>เปิดคอนโซลการแชท</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<note>new chat action</note>
|
||||
@@ -5919,6 +5931,10 @@ swipe action</note>
|
||||
<source>Remove image</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>ลบสมาชิกออก</target>
|
||||
|
||||
@@ -2680,6 +2680,10 @@ swipe action</note>
|
||||
<target>Mesaj gönderildi bilgisi!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Açıklama</target>
|
||||
@@ -4516,7 +4520,7 @@ Daha fazla iyileştirme yakında geliyor!</target>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Geçersiz bağlantı</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5767,11 +5771,19 @@ VPN'nin etkinleştirilmesi gerekir.</target>
|
||||
<target>Sohbet konsolunu aç</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Açık koşullar</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Grubu aç</target>
|
||||
@@ -6561,6 +6573,10 @@ swipe action</note>
|
||||
<target>Resmi kaldır</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Kişiyi sil</target>
|
||||
|
||||
@@ -2679,6 +2679,10 @@ swipe action</note>
|
||||
<target>Квитанції про доставку!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>Опис</target>
|
||||
@@ -4515,7 +4519,7 @@ More improvements are coming soon!</source>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Невірне посилання</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5767,11 +5771,19 @@ Requires compatible VPN.</source>
|
||||
<target>Відкрийте консоль чату</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>Відкриті умови</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>Відкрита група</target>
|
||||
@@ -6561,6 +6573,10 @@ swipe action</note>
|
||||
<target>Видалити зображення</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>Видалити учасника</target>
|
||||
|
||||
@@ -2662,6 +2662,10 @@ swipe action</note>
|
||||
<target>送达回执!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Deprecated options" xml:space="preserve">
|
||||
<source>Deprecated options</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Description" xml:space="preserve">
|
||||
<source>Description</source>
|
||||
<target>描述</target>
|
||||
@@ -4487,7 +4491,7 @@ More improvements are coming soon!</source>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>无效链接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
<note>alert title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||
<source>Invalid migration confirmation</source>
|
||||
@@ -5728,11 +5732,19 @@ Requires compatible VPN.</source>
|
||||
<target>打开聊天控制台</target>
|
||||
<note>authentication reason</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open clean link" xml:space="preserve">
|
||||
<source>Open clean link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open conditions" xml:space="preserve">
|
||||
<source>Open conditions</source>
|
||||
<target>打开条款</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open full link" xml:space="preserve">
|
||||
<source>Open full link</source>
|
||||
<note>alert action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open group" xml:space="preserve">
|
||||
<source>Open group</source>
|
||||
<target>打开群</target>
|
||||
@@ -6497,6 +6509,10 @@ swipe action</note>
|
||||
<target>移除图片</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove link tracking" xml:space="preserve">
|
||||
<source>Remove link tracking</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Remove member" xml:space="preserve">
|
||||
<source>Remove member</source>
|
||||
<target>删除成员</target>
|
||||
|
||||
@@ -390,7 +390,7 @@ enum SharedContent {
|
||||
switch self {
|
||||
case let .image(preview, _): .image(text: comment, image: preview)
|
||||
case let .movie(preview, duration, _): .video(text: comment, image: preview, duration: duration)
|
||||
case let .url(preview): .link(text: preview.uri.absoluteString + (comment == "" ? "" : "\n" + comment), preview: preview)
|
||||
case let .url(preview): .link(text: preview.uri + (comment == "" ? "" : "\n" + comment), preview: preview)
|
||||
case .text: .text(comment)
|
||||
case .data: .file(comment)
|
||||
}
|
||||
@@ -464,12 +464,13 @@ fileprivate func getSharedContent(_ ip: NSItemProvider) async -> Result<SharedCo
|
||||
// Prepare Link message
|
||||
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) {
|
||||
.url(preview: linkPreview)
|
||||
} else {
|
||||
.text(string: url.absoluteString)
|
||||
}
|
||||
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
|
||||
content = .url(preview: linkPreview)
|
||||
} else {
|
||||
content = .text(string: url.absoluteString)
|
||||
}
|
||||
return .success(content)
|
||||
} else { return .failure(ErrorAlert("Error preparing message")) }
|
||||
|
||||
|
||||
@@ -172,7 +172,7 @@ struct ShareView: View {
|
||||
VStack(alignment: .center, spacing: 4) {
|
||||
Text(linkPreview.title)
|
||||
.lineLimit(1)
|
||||
Text(linkPreview.uri.absoluteString)
|
||||
Text(linkPreview.uri)
|
||||
.font(.caption)
|
||||
.lineLimit(1)
|
||||
.foregroundColor(.secondary)
|
||||
|
||||
@@ -178,8 +178,8 @@
|
||||
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
|
||||
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; };
|
||||
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; };
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE.a */; };
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC-ghc9.6.3.a */; };
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC.a */; };
|
||||
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; };
|
||||
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
|
||||
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
|
||||
@@ -545,8 +545,8 @@
|
||||
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = "<group>"; };
|
||||
64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE.a"; sourceTree = "<group>"; };
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC.a"; sourceTree = "<group>"; };
|
||||
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
|
||||
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = "<group>"; };
|
||||
@@ -708,8 +708,8 @@
|
||||
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
|
||||
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
|
||||
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE.a in Frameworks */,
|
||||
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC-ghc9.6.3.a in Frameworks */,
|
||||
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC.a in Frameworks */,
|
||||
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -795,8 +795,8 @@
|
||||
64C829992D54AEEE006B9E89 /* libffi.a */,
|
||||
64C829982D54AEED006B9E89 /* libgmp.a */,
|
||||
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-B5zq2t60gbA45EwEeb0rfE.a */,
|
||||
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC-ghc9.6.3.a */,
|
||||
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.2.1-K3DJnRKS60C6Ik3NluxepC.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
|
||||
@@ -186,6 +186,30 @@ struct ParsedServerAddress: Decodable {
|
||||
var parseError: String
|
||||
}
|
||||
|
||||
public func parseSanitizeUri(_ s: String) -> ParsedUri? {
|
||||
var c = s.cString(using: .utf8)!
|
||||
if let cjson = chat_parse_uri(&c) {
|
||||
if let d = dataFromCString(cjson) {
|
||||
do {
|
||||
return try jsonDecoder.decode(ParsedUri.self, from: d)
|
||||
} catch {
|
||||
logger.error("parseSanitizeUri jsonDecoder.decode error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
public struct ParsedUri: Decodable {
|
||||
public var uriInfo: UriInfo?
|
||||
public var parseError: String
|
||||
}
|
||||
|
||||
public struct UriInfo: Decodable {
|
||||
public var scheme: String
|
||||
public var sanitized: String?
|
||||
}
|
||||
|
||||
@inline(__always)
|
||||
public func fromCString(_ c: UnsafeMutablePointer<CChar>) -> String {
|
||||
let s = String.init(cString: c)
|
||||
|
||||
@@ -27,6 +27,8 @@ 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_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
|
||||
let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
|
||||
public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used
|
||||
@@ -95,6 +97,8 @@ public func registerGroupDefaults() {
|
||||
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: true,
|
||||
GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
|
||||
GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false,
|
||||
GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true,
|
||||
@@ -222,6 +226,8 @@ public let allowShareExtensionGroupDefault = BoolDefault(defaults: groupDefaults
|
||||
|
||||
public let privacyLinkPreviewsGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS)
|
||||
|
||||
public let privacyLinkPreviewsShowAlertGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_LINK_PREVIEWS_SHOW_ALERT)
|
||||
|
||||
// This setting is a main one, while having an unused duplicate from the past: DEFAULT_PRIVACY_ACCEPT_IMAGES
|
||||
public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES)
|
||||
|
||||
|
||||
@@ -4611,6 +4611,11 @@ public struct FormattedText: Decodable, Hashable {
|
||||
public var text: String
|
||||
public var format: Format?
|
||||
|
||||
public init(text: String, format: Format? = nil) {
|
||||
self.text = text
|
||||
self.format = format
|
||||
}
|
||||
|
||||
public static func plain(_ text: String) -> [FormattedText] {
|
||||
text.isEmpty
|
||||
? []
|
||||
@@ -4620,6 +4625,14 @@ public struct FormattedText: Decodable, Hashable {
|
||||
public var isSecret: Bool {
|
||||
if case .secret = format { true } else { false }
|
||||
}
|
||||
|
||||
public var linkUri: String? {
|
||||
switch format {
|
||||
case .uri: text
|
||||
case let .hyperLink(_, linkUri): linkUri
|
||||
default: nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum Format: Decodable, Equatable, Hashable {
|
||||
@@ -4630,7 +4643,8 @@ public enum Format: Decodable, Equatable, Hashable {
|
||||
case secret
|
||||
case colored(color: FormatColor)
|
||||
case uri
|
||||
case simplexLink(linkType: SimplexLinkType, simplexUri: String, smpHosts: [String])
|
||||
case hyperLink(showText: String?, linkUri: String)
|
||||
case simplexLink(showText: String?, linkType: SimplexLinkType, simplexUri: String, smpHosts: [String])
|
||||
case command(commandStr: String)
|
||||
case mention(memberName: String)
|
||||
case email
|
||||
@@ -4748,14 +4762,14 @@ extension ReportReason: Decodable {
|
||||
|
||||
// Struct to use with simplex API
|
||||
public struct LinkPreview: Codable, Equatable, Hashable {
|
||||
public init(uri: URL, title: String, description: String = "", image: String) {
|
||||
public init(uri: String, title: String, description: String = "", image: String) {
|
||||
self.uri = uri
|
||||
self.title = title
|
||||
self.description = description
|
||||
self.image = image
|
||||
}
|
||||
|
||||
public var uri: URL
|
||||
public var uri: String
|
||||
public var title: String
|
||||
// TODO remove once optional in haskell
|
||||
public var description: String = ""
|
||||
|
||||
@@ -446,7 +446,7 @@ public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) {
|
||||
let resized = resizeImageToStrSizeSync(image, maxDataSize: 14000),
|
||||
let title = metadata.title,
|
||||
let uri = metadata.originalURL {
|
||||
linkPreview = LinkPreview(uri: uri, title: title, image: resized)
|
||||
linkPreview = LinkPreview(uri: uri.absoluteString, title: title, image: resized)
|
||||
}
|
||||
}
|
||||
cb(linkPreview)
|
||||
|
||||
@@ -24,6 +24,7 @@ extern char *chat_send_cmd_retry(chat_ctrl ctl, char *cmd, int retryNum);
|
||||
extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait);
|
||||
extern char *chat_parse_markdown(char *str);
|
||||
extern char *chat_parse_server(char *str);
|
||||
extern char *chat_parse_uri(char *str);
|
||||
extern char *chat_password_hash(char *pwd, char *salt);
|
||||
extern char *chat_valid_name(char *name);
|
||||
extern int chat_json_length(char *str);
|
||||
|
||||
Reference in new issue
Block a user