Compare commits

..

44 Commits

Author SHA1 Message Date
Evgeny Poberezkin ceecaf6592 simplexmq 2024-07-29 19:36:51 +01:00
Evgeny Poberezkin fb831439e9 ios: load chats and refresh call invitations before chat is started 2024-07-29 19:35:48 +01:00
Evgeny Poberezkin 018d9a1064 core: allow getting call invitations and notificationn token when chat is stopped 2024-07-29 19:17:44 +01:00
Evgeny Poberezkin 4b382841a1 check chat is running 2024-07-29 18:51:01 +01:00
Evgeny Poberezkin 6834138270 fix race in the tests 2024-07-29 18:19:01 +01:00
Evgeny Poberezkin bf83ba40a3 simplexmq 2024-07-29 17:39:49 +01:00
Evgeny Poberezkin ca166d7f96 use priority database access 2024-07-29 15:12:18 +01:00
Evgeny Poberezkin 42669454b0 multiplatform: load chat data before starting chat 2024-07-29 11:37:32 +01:00
Evgeny Poberezkin 243f3a7516 revert some 2024-07-29 10:57:40 +01:00
spaced4ndy aadba41e66 core: move db actions out of synchronous execution on chat start 2024-07-29 13:33:31 +04:00
Evgeny Poberezkin ce1b66cef2 ios: optimize marking messages read (#4530)
* ios: optimize marking messages read

* remove view ifs
2024-07-29 09:49:43 +01:00
Evgeny Poberezkin 3e18fdc34d ios: link framework into share extension without embedding 2024-07-28 23:28:53 +01:00
Evgeny Poberezkin c09075d71e ios: update core library 2024-07-28 23:05:41 +01:00
Evgeny Poberezkin 41b4d7851a ios: improve chat list layout (#4528) 2024-07-28 21:53:21 +01:00
Evgeny Poberezkin 92bec5eabb core: 6.0.0.2 (simplexmq 6.0.0.4) 2024-07-28 18:11:38 +01:00
Stanislav Dmitrenko 24587ecc92 scripts (windows): add follow redirects to curl (#4526) 2024-07-28 17:55:58 +01:00
Evgeny Poberezkin 6865515f43 ios: share extension (#4466)
* ios: share extension (#4414)

* ios: add share extension target

* ios: Add UI

* ios: send file from share-sheet

* image utils

* ShareError

* error handling; ui-cleanup

* progress bar; completion for direct chat

* cleanup

* cleanup

* ios: unify filter and sort between forward and share sheets

* ios: match share sheet styling with the main app

* ios: fix text input stroke width

* ios: align compose views

* more of the same...

* ShareAPI

* remove combine

* minor

* Better error descriptions

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>

* ios: enable file sending workers in share extension (#4474)

* ios: align compose background, row height and fallback images for share-sheet (#4467)

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>

* ios: coordinate database access between share extension, the app and notifications extension (#4472)

* ios: database management proposal

* Add SEState

* Global event loop

* minor

* reset state

* use apiCreateItem for local chats

* simplify waiting for suspension

* loading bar

* Dismiss share sheet with error

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>

* send image message (#4481)

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>

* ios: improve share extension completion handling (#4486)

* improve completion handling

* minor

* show only spinner for group send

* rework event loop, errorAlert

* group chat timeout loading bar

* state machine WIP

* event loop actor

* alert

* errors text

* default

* file error

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>

* ios: add remaining share types; process attachment in background on launch (#4510)

* add remaining share types; process attachment in background on launch

* cleanup diff

* revert `makeVideoQualityLower`

* reduce diff

* reduce diff

* iOS15 support

* process events when sharing link and text

* cleanup

* remove video file on failure

* cleanup CompletionHandler

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>

* ios: share extension - additional alerts and media previews (#4521)

* add remaining share types; process attachment in background on launch

* cleanup diff

* revert `makeVideoQualityLower`

* reduce diff

* reduce diff

* iOS15 support

* process events when sharing link and text

* cleanup

* remove video file on failure

* cleanup CompletionHandler

* media previews

* network timeout alert

* revert framework compiler optimisation flag

* suspend chat after sheet dismiss

* activate chat

* update

* fix search

* sendMessageColor, file preview, chat deselect, simplify error action

* cleanup

* interupt database closing when sheet is reopened quickly

* cleanup redundant alert check

* restore package

* refactor previews, remove link preview

* show link preview when becomes available

* comment

* dont fail on invalid image

* suspend

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>

* ios: descriptive database errors (#4527)

* ios: set share extension as inactive when suspending chat

---------

Co-authored-by: Arturs Krumins <auth@levitatingpineapple.com>
2024-07-28 17:54:58 +01:00
Evgeny Poberezkin 5ee6f40e75 cli: allow passing ChatOpts parameter (#4525) 2024-07-28 12:06:07 +01:00
Evgeny Poberezkin 637189cc2d core: multiline output (#4520) 2024-07-26 15:21:06 +01:00
Evgeny Poberezkin 13bfc9e92b core: update app settings for migration (#4518) 2024-07-26 10:30:49 +01:00
Stanislav Dmitrenko 032c5d3a5b android, desktop: blur for media (#4508)
* android, desktop: blur for media

* change

* new option and applied blur to other elements

* new line

* added to migration

* long click handling

* hover on desktop

* changes

* change

* showDownloadButton function

* file rename

* don't blur when menu is visible

* rename
2024-07-26 09:29:13 +01:00
Evgeny Poberezkin a53333be20 ios: increase wallpaper scale (#4517) 2024-07-26 09:11:42 +01:00
Stanislav Dmitrenko a966f6b19d ios: blur for media (#4512)
* ios: blur for media

* line

* one more place

* changes for video

* using notification center

* change

* unused code

* string

* simplify

* refactor ifs

* fix

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-07-26 08:38:22 +01:00
Arturs Krumins 70d577260b ios: fix crash when changing screen orientation with tiled wallpaper (#4511)
* ios: fix crash, when displaying wallpaper

* simpler

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-07-25 20:19:44 +01:00
Stanislav Dmitrenko ea12982788 ios: fix two-line preview on IOS 17+ (#4514) 2024-07-25 14:53:10 +01:00
Stanislav Dmitrenko 6fca6c22c5 ios: interactive media and link previews in the list of chats (#4487)
* ios: interactive media and link previews in the list of chats

* commented out voice preview

* voice message support and various fixes

* changes to video

* changes

* playing voice in chat list with scrolling

* revert

This reverts commit 60f57403d1.

* prevent feedback loop

* version of dependency

* voice

* fix param

* working voice

* reacting on messages and chat deletion

* fix two videos in a row

* video item layout

* fix

---------

Co-authored-by: Levitating Pineapple <noreply@levitatingpineapple.com>
Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-07-23 18:11:42 +01:00
Evgeny Poberezkin e343dd017b core: fix nix build for iOS build with tagged JSON (e.g., Flutter apps) (#4484) 2024-07-22 19:35:36 +01:00
spaced4ndy 70a94d772a multiplatform: optimize subscription indicator (#4503) 2024-07-22 20:40:22 +04:00
spaced4ndy 2689d1e27b ios: optimize subscription indicator (#4501) 2024-07-22 19:32:54 +04:00
spaced4ndy 4a9b54fbaf core: get subs total api (#4500) 2024-07-22 19:06:53 +04:00
Evgeny Poberezkin f10a0ce58e core: receive only one notification message on push notification (#4504)
* core: receive only one notification message on push notification

* ios: receive only one notification message on push

* update stats to include notification server stats

* Codable

* update simplexmq
2024-07-22 15:48:57 +01:00
spaced4ndy 6d488ba489 android: improve proxy errors (#4489) 2024-07-19 20:10:31 +04:00
spaced4ndy 95776e0951 ios: improve proxy errors (#4485) 2024-07-19 18:39:12 +04:00
spaced4ndy bf9cdf3053 android: allow sending messages immediately on joinConnection, acceptContact (#4483) 2024-07-19 12:50:27 +04:00
spaced4ndy fa73e63a79 ios: allow sending messages immediately on joinConnection, acceptContact (#4478) 2024-07-19 11:31:43 +04:00
spaced4ndy bfab76ed90 core: allow sending messages immediately on joinConnection, acceptContact (#4465) 2024-07-18 20:33:51 +04:00
Stanislav Dmitrenko 905295ee5f android, desktop: interactive media and link previews in the list of chats (#4460)
* android, desktop: chat item content preview in chat list

* better

* moved code

* layout

* multiplier for files

* small

* changes

* changes

* changes

* no padding

* changes

* color

* multiplier

* changes

* fix state inconsistency in gallery

* voice messages improvements

* showing draft

* re-layout preview

* rename and padding

* fix

* padding

* link icon

* without offset

* image

* hand on hover

* color

---------

Co-authored-by: Evgeny Poberezkin <evgeny@poberezkin.com>
2024-07-18 16:26:06 +01:00
Evgeny Poberezkin a1e707ac1b ui: translations (#4469)
* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1934 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Hungarian)

Currently translated at 99.6% (1928 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1934 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1681 of 1681 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/

* Translated using Weblate (Dutch)

Currently translated at 99.6% (1928 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1934 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1934 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1681 of 1681 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Indonesian)

Currently translated at 2.8% (55 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/id/

* Translated using Weblate (German)

Currently translated at 97.7% (1908 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1934 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Hungarian)

Currently translated at 99.6% (1928 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1934 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1681 of 1681 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/it/

* Translated using Weblate (Dutch)

Currently translated at 99.6% (1928 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/nl/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1934 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1934 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1681 of 1681 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/hu/

* Translated using Weblate (Indonesian)

Currently translated at 2.8% (55 of 1934 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/id/

* Translated using Weblate (German)

Currently translated at 97.7% (1908 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 98.7% (1927 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (Chinese (Simplified))

Currently translated at 100.0% (1952 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/zh_Hans/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1952 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* Translated using Weblate (German)

Currently translated at 100.0% (1952 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/de/

* Translated using Weblate (German)

Currently translated at 100.0% (1681 of 1681 strings)

Translation: SimpleX Chat/SimpleX Chat iOS
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/ios/de/

* Translated using Weblate (Italian)

Currently translated at 100.0% (1952 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/it/

* Translated using Weblate (Japanese)

Currently translated at 92.8% (1813 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ja/

* Translated using Weblate (Arabic)

Currently translated at 100.0% (1952 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/ar/

* Translated using Weblate (Hungarian)

Currently translated at 100.0% (1952 of 1952 strings)

Translation: SimpleX Chat/SimpleX Chat Android
Translate-URL: https://hosted.weblate.org/projects/simplex-chat/android/hu/

* process localizations

---------

Co-authored-by: 大王叫我来巡山 <hamburger2048@users.noreply.hosted.weblate.org>
Co-authored-by: summoner001 <summoner@vivaldi.net>
Co-authored-by: Random <random-r@users.noreply.hosted.weblate.org>
Co-authored-by: M1K4 <oomikaoo@gmail.com>
Co-authored-by: Ghost of Sparta <makesocialfoss32@keemail.me>
Co-authored-by: MM <mecymyse@gmail.com>
Co-authored-by: mlanp <github@lang.xyz>
Co-authored-by: Miyu Sakatsuki <miyu-sakatsuki@outlook.jp>
Co-authored-by: jonnysemon <jonnysemon@users.noreply.hosted.weblate.org>
2024-07-17 19:00:47 +01:00
Evgeny Poberezkin fea51b75e9 ios: fix start chat api 2024-07-17 14:20:58 +01:00
Evgeny Poberezkin ff8bbf11e7 core: allow start in extension without subscriptions but with enabled files (#4464)
* core: allow start in extension without subscriptions but with enabled files

* only start sending files

* update

* update

* update simplexmq
2024-07-17 14:14:19 +01:00
Evgeny Poberezkin 391e9d57f2 docs: update transparency 2024-07-16 21:31:15 +01:00
Evgeny Poberezkin dae9f8575d ui: change labels in server stats, move percentage toggle (#4468)
* ui: change labels in server stats, move percentage toggle

* localizations
2024-07-16 20:41:00 +01:00
spaced4ndy 065d9be614 multiplatform: better errors (#4463) 2024-07-16 17:19:58 +04:00
spaced4ndy 4251762553 ios: better errors (#4462) 2024-07-16 17:19:37 +04:00
160 changed files with 6769 additions and 1722 deletions
+5 -1
View File
@@ -354,6 +354,9 @@ final class ChatModel: ObservableObject {
addChat(Chat(chatInfo: cInfo, chatItems: [cItem]))
res = true
}
if cItem.isDeletedContent || cItem.meta.itemDeleted != nil {
VoiceItemState.stopVoiceInChatView(cInfo, cItem)
}
// update current chat
return chatId == cInfo.id ? _upsertChatItem(cInfo, cItem) : res
}
@@ -420,6 +423,7 @@ final class ChatModel: ObservableObject {
}
}
}
VoiceItemState.stopVoiceInChatView(cInfo, cItem)
}
func nextChatItemData<T>(_ chatItemId: Int64, previous: Bool, map: @escaping (ChatItem) -> T?) -> T? {
@@ -774,7 +778,7 @@ struct UnreadChatItemCounts: Equatable {
var unreadBelow: Int
}
final class Chat: ObservableObject, Identifiable {
final class Chat: ObservableObject, Identifiable, ChatLike {
@Published var chatInfo: ChatInfo
@Published var chatItems: [ChatItem]
@Published var chatStats: ChatStats
+48 -55
View File
@@ -217,7 +217,7 @@ func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) asyn
}
func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool {
let r = chatSendCmdSync(.startChat(mainApp: true), ctrl)
let r = chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true), ctrl)
switch r {
case .chatStarted: return true
case .chatRunning: return false
@@ -225,6 +225,15 @@ func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool {
}
}
func apiCheckChatRunning() throws -> Bool {
let r = chatSendCmdSync(.checkChatRunning)
switch r {
case .chatRunning: return true
case .chatStopped: return false
default: throw r
}
}
func apiStopChat() async throws {
let r = await chatSendCmd(.apiStopChat)
switch r {
@@ -397,7 +406,7 @@ private func sendMessageErrorAlert(_ r: ChatResponse) {
logger.error("send message error: \(String(describing: r))")
AlertManager.shared.showAlertMsg(
title: "Error sending message",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
@@ -405,7 +414,7 @@ private func createChatItemErrorAlert(_ r: ChatResponse) {
logger.error("apiCreateChatItem error: \(String(describing: r))")
AlertManager.shared.showAlertMsg(
title: "Error creating message",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
@@ -699,7 +708,7 @@ func apiConnect_(incognito: Bool, connReq: String) async -> ((ConnReqType, Pendi
message: "Please check that you used the correct link or ask your contact to send you another one."
)
return (nil, alert)
case .chatCmdError(_, .errorAgent(.SMP(.AUTH))):
case .chatCmdError(_, .errorAgent(.SMP(_, .AUTH))):
let alert = mkAlert(
title: "Connection error (AUTH)",
message: "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection."
@@ -732,7 +741,7 @@ private func connectionErrorAlert(_ r: ChatResponse) -> Alert {
} else {
return mkAlert(
title: "Connection error",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
}
@@ -898,7 +907,7 @@ func apiAcceptContactRequest(incognito: Bool, contactReqId: Int64) async -> Cont
let am = AlertManager.shared
if case let .acceptingContactRequest(_, contact) = r { return contact }
if case .chatCmdError(_, .errorAgent(.SMP(.AUTH))) = r {
if case .chatCmdError(_, .errorAgent(.SMP(_, .AUTH))) = r {
am.showAlertMsg(
title: "Connection error (AUTH)",
message: "Sender may have deleted the connection request."
@@ -909,7 +918,7 @@ func apiAcceptContactRequest(incognito: Bool, contactReqId: Int64) async -> Cont
logger.error("apiAcceptContactRequest error: \(String(describing: r))")
am.showAlertMsg(
title: "Error accepting contact request",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
return nil
@@ -935,7 +944,7 @@ func uploadStandaloneFile(user: any UserLike, file: CryptoFile, ctrl: chat_ctrl?
return (fileTransferMeta, nil)
} else {
logger.error("uploadStandaloneFile error: \(String(describing: r))")
return (nil, String(describing: r))
return (nil, responseError(r))
}
}
@@ -945,7 +954,7 @@ func downloadStandaloneFile(user: any UserLike, url: String, file: CryptoFile, c
return (rcvFileTransfer, nil)
} else {
logger.error("downloadStandaloneFile error: \(String(describing: r))")
return (nil, String(describing: r))
return (nil, responseError(r))
}
}
@@ -1025,7 +1034,7 @@ func apiReceiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool, in
if !auto {
am.showAlertMsg(
title: "Error receiving file",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
}
@@ -1091,47 +1100,6 @@ func deleteRemoteCtrl(_ rcId: Int64) async throws {
try await sendCommandOkResp(.deleteRemoteCtrl(remoteCtrlId: rcId))
}
struct ErrorAlert {
var title: LocalizedStringKey
var message: LocalizedStringKey
}
func getNetworkErrorAlert(_ r: ChatResponse) -> ErrorAlert? {
switch r {
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))):
return ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))):
return ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .HOST))):
return ErrorAlert(title: "Connection error", message: "Server address is incompatible with network settings: \(serverHostname(addr)).")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TRANSPORT(.version)))):
return ErrorAlert(title: "Connection error", message: "Server version is incompatible with your app: \(serverHostname(addr)).")
case let .chatCmdError(_, .errorAgent(.SMP(.PROXY(proxyErr)))):
return proxyErrorAlert(proxyErr)
case let .chatCmdError(_, .errorAgent(.PROXY(_, _, .protocolError(.PROXY(proxyErr))))):
return proxyErrorAlert(proxyErr)
default:
return nil
}
}
private func proxyErrorAlert(_ proxyErr: ProxyError) -> ErrorAlert? {
switch proxyErr {
case .BROKER(brokerErr: .TIMEOUT):
return ErrorAlert(title: "Private routing error", message: "Please try later.")
case .BROKER(brokerErr: .NETWORK):
return ErrorAlert(title: "Private routing error", message: "Please try later.")
case .NO_SESSION:
return ErrorAlert(title: "Private routing error", message: "Please try later.")
case .BROKER(brokerErr: .HOST):
return ErrorAlert(title: "Private routing error", message: "Server address is incompatible with network settings.")
case .BROKER(brokerErr: .TRANSPORT(.version)):
return ErrorAlert(title: "Private routing error", message: "Server version is incompatible with network settings.")
default:
return nil
}
}
func networkErrorAlert(_ r: ChatResponse) -> Alert? {
if let alert = getNetworkErrorAlert(r) {
return mkAlert(title: alert.title, message: alert.message)
@@ -1143,7 +1111,10 @@ func networkErrorAlert(_ r: ChatResponse) -> Alert? {
func acceptContactRequest(incognito: Bool, contactRequest: UserContactRequest) async {
if let contact = await apiAcceptContactRequest(incognito: incognito, contactReqId: contactRequest.apiId) {
let chat = Chat(chatInfo: ChatInfo.direct(contact: contact), chatItems: [])
DispatchQueue.main.async { ChatModel.shared.replaceChat(contactRequest.id, chat) }
DispatchQueue.main.async {
ChatModel.shared.replaceChat(contactRequest.id, chat)
ChatModel.shared.setContactNetworkStatus(contact, .connected)
}
}
}
@@ -1280,7 +1251,7 @@ func apiJoinGroup(_ groupId: Int64) async throws -> JoinGroupResult {
let r = await chatSendCmd(.apiJoinGroup(groupId: groupId))
switch r {
case let .userAcceptedGroupSent(_, groupInfo, _): return .joined(groupInfo: groupInfo)
case .chatCmdError(_, .errorAgent(.SMP(.AUTH))): return .invitationRemoved
case .chatCmdError(_, .errorAgent(.SMP(_, .AUTH))): return .invitationRemoved
case .chatCmdError(_, .errorStore(.groupNotFound)): return .groupNotFound
default: throw r
}
@@ -1386,6 +1357,14 @@ func apiGetVersion() throws -> CoreVersionInfo {
throw r
}
func getAgentSubsTotal() throws -> (SMPServerSubs, Bool) {
let userId = try currentUserId("getAgentSubsTotal")
let r = chatSendCmdSync(.getAgentSubsTotal(userId: userId), log: false)
if case let .agentSubsTotal(_, subsTotal, hasSession) = r { return (subsTotal, hasSession) }
logger.error("getAgentSubsTotal error: \(String(describing: r))")
throw r
}
func getAgentServersSummary() throws -> PresentedServersSummary {
let userId = try currentUserId("getAgentServersSummary")
let r = chatSendCmdSync(.getAgentServersSummary(userId: userId), log: false)
@@ -1469,15 +1448,16 @@ func startChat(refreshInvitations: Bool = true) throws {
logger.debug("startChat")
let m = ChatModel.shared
try setNetworkConfig(getNetCfg())
let justStarted = try apiStartChat()
let chatRunning = try apiCheckChatRunning()
m.users = try listUsers()
if justStarted {
if !chatRunning {
try getUserChatData()
NtfManager.shared.setNtfBadgeCount(m.totalUnreadCountForAllUsers())
if (refreshInvitations) {
try refreshCallInvitations()
}
(m.savedToken, m.tokenStatus, m.notificationMode, m.notificationServer) = apiGetNtfToken()
_ = try apiStartChat()
// deviceToken is set when AppDelegate.application(didRegisterForRemoteNotificationsWithDeviceToken:) is called,
// when it is called before startChat
if let token = m.deviceToken {
@@ -1649,6 +1629,19 @@ func processReceivedMsg(_ res: ChatResponse) async {
}
}
}
case let .contactSndReady(user, contact):
if active(user) && contact.directOrUsed {
await MainActor.run {
m.updateContact(contact)
if let conn = contact.activeConn {
m.dismissConnReqView(conn.id)
m.removeChat(conn.id)
}
}
}
await MainActor.run {
m.setContactNetworkStatus(contact, .connected)
}
case let .receivedContactRequest(user, contactRequest):
if active(user) {
let cInfo = ChatInfo.contactRequest(contactRequest: contactRequest)
+12
View File
@@ -36,6 +36,18 @@ private func _suspendChat(timeout: Int) {
}
}
let seSubscriber = seMessageSubscriber {
switch $0 {
case let .state(state):
switch state {
case .inactive:
if AppChatState.shared.value.inactive { activateChat() }
case .sendingMessage:
if AppChatState.shared.value.canSuspend { suspendChat() }
}
}
}
func suspendChat() {
suspendLockQueue.sync {
_suspendChat(timeout: appSuspendTimeout)
+14 -12
View File
@@ -112,7 +112,7 @@ struct ChatInfoView: View {
case abortSwitchAddressAlert
case syncConnectionForceAlert
case queueInfo(info: String)
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -155,23 +155,25 @@ struct ChatInfoView: View {
}
Section {
if let code = connectionCode { verifyCodeButton(code) }
contactPreferencesButton()
sendReceiptsOption()
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
synchronizeConnectionButton()
Group {
if let code = connectionCode { verifyCodeButton(code) }
contactPreferencesButton()
sendReceiptsOption()
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
synchronizeConnectionButton()
}
// } else if developerTools {
// synchronizeConnectionButtonForce()
// }
}
.disabled(!contact.ready || !contact.active)
NavigationLink {
ChatWallpaperEditorSheet(chat: chat)
} label: {
Label("Chat theme", systemImage: "photo")
}
// } else if developerTools {
// synchronizeConnectionButtonForce()
// }
}
.disabled(!contact.ready || !contact.active)
if let conn = contact.activeConn {
Section {
@@ -271,7 +273,7 @@ struct ChatInfoView: View {
}
}
.actionSheet(isPresented: $showDeleteContactActionSheet) {
if contact.ready && contact.active {
if contact.sndReady && contact.active {
return ActionSheet(
title: Text("Delete contact?\nThis cannot be undone!"),
buttons: [
@@ -9,6 +9,7 @@ import SwiftUI
class AnimatedImageView: UIView {
var image: UIImage? = nil
var imageView: UIImageView? = nil
var cMode: UIView.ContentMode = .scaleAspectFit
override init(frame: CGRect) {
super.init(frame: frame)
@@ -18,11 +19,12 @@ class AnimatedImageView: UIView {
fatalError("Not implemented")
}
convenience init(image: UIImage) {
convenience init(image: UIImage, contentMode: UIView.ContentMode) {
self.init()
self.image = image
self.cMode = contentMode
imageView = UIImageView(gifImage: image)
imageView!.contentMode = .scaleAspectFit
imageView!.contentMode = contentMode
self.addSubview(imageView!)
}
@@ -35,7 +37,7 @@ class AnimatedImageView: UIView {
if let subview = self.subviews.first as? UIImageView {
if image.imageData != subview.gifImage?.imageData {
imageView = UIImageView(gifImage: image)
imageView!.contentMode = .scaleAspectFit
imageView!.contentMode = contentMode
self.addSubview(imageView!)
subview.removeFromSuperview()
}
@@ -47,13 +49,15 @@ class AnimatedImageView: UIView {
struct SwiftyGif: UIViewRepresentable {
private let image: UIImage
private let contentMode: UIView.ContentMode
init(image: UIImage) {
init(image: UIImage, contentMode: UIView.ContentMode = .scaleAspectFit) {
self.image = image
self.contentMode = contentMode
}
func makeUIView(context: Context) -> AnimatedImageView {
AnimatedImageView(image: image)
AnimatedImageView(image: image, contentMode: contentMode)
}
func updateUIView(_ imageView: AnimatedImageView, context: Context) {
@@ -14,39 +14,45 @@ struct CIFileView: View {
@EnvironmentObject var theme: AppTheme
let file: CIFile?
let edited: Bool
var smallViewSize: CGFloat?
var body: some View {
let metaReserve = edited
? " "
: " "
Button(action: fileAction) {
HStack(alignment: .bottom, spacing: 6) {
fileIndicator()
.padding(.top, 5)
.padding(.bottom, 3)
if let file = file {
let prettyFileSize = ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary)
VStack(alignment: .leading, spacing: 2) {
Text(file.fileName)
.lineLimit(1)
.multilineTextAlignment(.leading)
.foregroundColor(theme.colors.onBackground)
Text(prettyFileSize + metaReserve)
.font(.caption)
.lineLimit(1)
.multilineTextAlignment(.leading)
.foregroundColor(theme.colors.secondary)
if smallViewSize != nil {
fileIndicator()
.onTapGesture(perform: fileAction)
} else {
let metaReserve = edited
? " "
: " "
Button(action: fileAction) {
HStack(alignment: .bottom, spacing: 6) {
fileIndicator()
.padding(.top, 5)
.padding(.bottom, 3)
if let file = file {
let prettyFileSize = ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary)
VStack(alignment: .leading, spacing: 2) {
Text(file.fileName)
.lineLimit(1)
.multilineTextAlignment(.leading)
.foregroundColor(theme.colors.onBackground)
Text(prettyFileSize + metaReserve)
.font(.caption)
.lineLimit(1)
.multilineTextAlignment(.leading)
.foregroundColor(theme.colors.secondary)
}
} else {
Text(metaReserve)
}
} else {
Text(metaReserve)
}
.padding(.top, 4)
.padding(.bottom, 6)
.padding(.leading, 10)
.padding(.trailing, 12)
}
.padding(.top, 4)
.padding(.bottom, 6)
.padding(.leading, 10)
.padding(.trailing, 12)
.disabled(!itemInteractive)
}
.disabled(!itemInteractive)
}
private var itemInteractive: Bool {
@@ -195,21 +201,22 @@ struct CIFileView: View {
}
private func fileIcon(_ icon: String, color: Color = Color(uiColor: .tertiaryLabel), innerIcon: String? = nil, innerIconSize: CGFloat? = nil) -> some View {
ZStack(alignment: .center) {
let size = smallViewSize ?? 30
return ZStack(alignment: .center) {
Image(systemName: icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.frame(width: size, height: size)
.foregroundColor(color)
if let innerIcon = innerIcon,
let innerIconSize = innerIconSize {
let innerIconSize = innerIconSize, (smallViewSize == nil || file?.showStatusIconInSmallView == true) {
Image(systemName: innerIcon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxHeight: 16)
.frame(width: innerIconSize, height: innerIconSize)
.foregroundColor(.white)
.padding(.top, 12)
.padding(.top, size / 2.5)
}
}
}
@@ -15,22 +15,33 @@ struct CIImageView: View {
var preview: UIImage?
let maxWidth: CGFloat
var imgWidth: CGFloat?
@State private var showFullScreenImage = false
var smallView: Bool = false
@Binding var showFullScreenImage: Bool
@State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0
var body: some View {
let file = chatItem.file
VStack(alignment: .center, spacing: 6) {
if let uiImage = getLoadedImage(file) {
imageView(uiImage)
Group { if smallView { smallViewImageView(uiImage) } else { imageView(uiImage) } }
.fullScreenCover(isPresented: $showFullScreenImage) {
FullScreenMediaView(chatItem: chatItem, image: uiImage, showView: $showFullScreenImage)
}
.if(!smallView) { view in
view.modifier(PrivacyBlur(blurred: $blurred))
}
.onTapGesture { showFullScreenImage = true }
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenImage = false
}
} else if let preview {
imageView(preview)
Group {
if smallView {
smallViewImageView(preview)
} else {
imageView(preview).modifier(PrivacyBlur(blurred: $blurred))
}
}
.onTapGesture {
if let file = file {
switch file.fileStatus {
@@ -83,6 +94,9 @@ struct CIImageView: View {
}
}
}
.onDisappear {
showFullScreenImage = false
}
}
private func imageView(_ img: UIImage) -> some View {
@@ -98,7 +112,26 @@ struct CIImageView: View {
.frame(width: w, height: w * img.size.height / img.size.width)
.scaledToFit()
}
loadingIndicator()
if !blurred || !showDownloadButton(chatItem.file?.fileStatus) {
loadingIndicator()
}
}
}
private func smallViewImageView(_ img: UIImage) -> some View {
ZStack(alignment: .topTrailing) {
if img.imageData == nil {
Image(uiImage: img)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: maxWidth, height: maxWidth)
} else {
SwiftyGif(image: img, contentMode: .scaleAspectFill)
.frame(width: maxWidth, height: maxWidth)
}
if chatItem.file?.showStatusIconInSmallView == true {
loadingIndicator()
}
}
}
@@ -145,4 +178,12 @@ struct CIImageView: View {
.tint(.white)
.padding(8)
}
private func showDownloadButton(_ fileStatus: CIFileStatus?) -> Bool {
switch fileStatus {
case .rcvInvitation: true
case .rcvAborted: true
default: false
}
}
}
@@ -12,6 +12,7 @@ import SimpleXChat
struct CILinkView: View {
@EnvironmentObject var theme: AppTheme
let linkPreview: LinkPreview
@State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0
var body: some View {
VStack(alignment: .center, spacing: 6) {
@@ -19,6 +20,7 @@ struct CILinkView: View {
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
.modifier(PrivacyBlur(blurred: $blurred))
}
VStack(alignment: .leading, spacing: 6) {
Text(linkPreview.title)
@@ -27,7 +27,7 @@ struct CIRcvDecryptionError: View {
case syncNotSupportedContactAlert
case syncNotSupportedMemberAlert
case decryptionErrorAlert
case error(title: LocalizedStringKey, error: LocalizedStringKey)
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -62,7 +62,7 @@ struct CIRcvDecryptionError: View {
case .syncNotSupportedContactAlert: return Alert(title: Text("Fix not supported by contact"), message: message())
case .syncNotSupportedMemberAlert: return Alert(title: Text("Fix not supported by group member"), message: message())
case .decryptionErrorAlert: return Alert(title: Text("Decryption error"), message: message())
case let .error(title, error): return Alert(title: Text(title), message: Text(error))
case let .error(title, error): return mkAlert(title: title, message: error)
}
}
}
@@ -20,45 +20,44 @@ struct CIVideoView: View {
@State private var videoPlaying: Bool = false
private let maxWidth: CGFloat
private var videoWidth: CGFloat?
private let smallView: Bool
@State private var player: AVPlayer?
@State private var fullPlayer: AVPlayer?
@State private var url: URL?
@State private var urlDecrypted: URL?
@State private var decryptionInProgress: Bool = false
@State private var showFullScreenPlayer = false
@Binding private var showFullScreenPlayer: Bool
@State private var timeObserver: Any? = nil
@State private var fullScreenTimeObserver: Any? = nil
@State private var publisher: AnyCancellable? = nil
private var sizeMultiplier: CGFloat { smallView ? 0.38 : 1 }
@State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0
init(chatItem: ChatItem, preview: UIImage?, duration: Int, maxWidth: CGFloat, videoWidth: CGFloat?) {
init(chatItem: ChatItem, preview: UIImage?, duration: Int, maxWidth: CGFloat, videoWidth: CGFloat?, smallView: Bool = false, showFullscreenPlayer: Binding<Bool>) {
self.chatItem = chatItem
self.preview = preview
self._duration = State(initialValue: duration)
self.maxWidth = maxWidth
self.videoWidth = videoWidth
if let url = getLoadedVideo(chatItem.file) {
let decrypted = chatItem.file?.fileSource?.cryptoArgs == nil ? url : chatItem.file?.fileSource?.decryptedGet()
self._urlDecrypted = State(initialValue: decrypted)
if let decrypted = decrypted {
self._player = State(initialValue: VideoPlayerView.getOrCreatePlayer(decrypted, false))
self._fullPlayer = State(initialValue: AVPlayer(url: decrypted))
}
self._url = State(initialValue: url)
}
self.smallView = smallView
self._showFullScreenPlayer = showFullscreenPlayer
}
var body: some View {
let file = chatItem.file
ZStack {
ZStack(alignment: smallView ? .topLeading : .center) {
ZStack(alignment: .topLeading) {
if let file = file, let preview = preview, let player = player, let decrypted = urlDecrypted {
if let file = file, let preview = preview, let decrypted = urlDecrypted, smallView {
smallVideoView(decrypted, file, preview)
} else if let file = file, let preview = preview, let player = player, let decrypted = urlDecrypted {
videoView(player, decrypted, file, preview, duration)
} else if let file = file, let defaultPreview = preview, file.loaded && urlDecrypted == nil, smallView {
smallVideoViewEncrypted(file, defaultPreview)
} else if let file = file, let defaultPreview = preview, file.loaded && urlDecrypted == nil {
videoViewEncrypted(file, defaultPreview, duration)
} else if let preview {
imageView(preview)
.onTapGesture {
if let file = file {
} else if let preview, let file {
Group { if smallView { smallViewImageView(preview, file) } else { imageView(preview) } }
.onTapGesture {
switch file.fileStatus {
case .rcvInvitation, .rcvAborted:
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
@@ -82,21 +81,64 @@ struct CIVideoView: View {
default: ()
}
}
}
}
durationProgress()
if !smallView {
durationProgress()
}
}
if let file = file, showDownloadButton(file.fileStatus) {
Button {
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
} label: {
if !blurred, let file, showDownloadButton(file.fileStatus) {
if !smallView {
Button {
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
} label: {
playPauseIcon("play.fill")
}
} else if !file.showStatusIconInSmallView {
playPauseIcon("play.fill")
.onTapGesture {
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
}
}
}
}
.fullScreenCover(isPresented: $showFullScreenPlayer) {
if let decrypted = urlDecrypted {
fullScreenPlayer(decrypted)
}
}
.onAppear {
setupPlayer(chatItem.file)
}
.onChange(of: chatItem.file) { file in
// ChatItem can be changed in small view on chat list screen
setupPlayer(file)
}
.onDisappear {
showFullScreenPlayer = false
}
}
private func showDownloadButton(_ fileStatus: CIFileStatus) -> Bool {
private func setupPlayer(_ file: CIFile?) {
let newUrl = getLoadedVideo(file)
if newUrl == url {
return
}
url = nil
urlDecrypted = nil
player = nil
fullPlayer = nil
if let newUrl {
let decrypted = file?.fileSource?.cryptoArgs == nil ? newUrl : file?.fileSource?.decryptedGet()
urlDecrypted = decrypted
if let decrypted = decrypted {
player = VideoPlayerView.getOrCreatePlayer(decrypted, false)
fullPlayer = AVPlayer(url: decrypted)
}
url = newUrl
}
}
private func showDownloadButton(_ fileStatus: CIFileStatus?) -> Bool {
switch fileStatus {
case .rcvInvitation: true
case .rcvAborted: true
@@ -109,11 +151,6 @@ struct CIVideoView: View {
ZStack(alignment: .center) {
let canBePlayed = !chatItem.chatDir.sent || file.fileStatus == CIFileStatus.sndComplete || (file.fileStatus == .sndStored && file.fileProtocol == .local)
imageView(defaultPreview)
.fullScreenCover(isPresented: $showFullScreenPlayer) {
if let decrypted = urlDecrypted {
fullScreenPlayer(decrypted)
}
}
.onTapGesture {
decrypt(file: file) {
showFullScreenPlayer = urlDecrypted != nil
@@ -122,20 +159,22 @@ struct CIVideoView: View {
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenPlayer = false
}
if !decryptionInProgress {
Button {
decrypt(file: file) {
if urlDecrypted != nil {
videoPlaying = true
player?.play()
if !blurred {
if !decryptionInProgress {
Button {
decrypt(file: file) {
if urlDecrypted != nil {
videoPlaying = true
player?.play()
}
}
} label: {
playPauseIcon(canBePlayed ? "play.fill" : "play.slash")
}
} label: {
playPauseIcon(canBePlayed ? "play.fill" : "play.slash")
.disabled(!canBePlayed)
} else {
videoDecryptionProgress()
}
.disabled(!canBePlayed)
} else {
videoDecryptionProgress()
}
}
}
@@ -154,9 +193,7 @@ struct CIVideoView: View {
videoPlaying = false
}
}
.fullScreenCover(isPresented: $showFullScreenPlayer) {
fullScreenPlayer(url)
}
.modifier(PrivacyBlur(enabled: !videoPlaying, blurred: $blurred))
.onTapGesture {
switch player.timeControlStatus {
case .playing:
@@ -172,7 +209,7 @@ struct CIVideoView: View {
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenPlayer = false
}
if !videoPlaying {
if !videoPlaying && !blurred {
Button {
m.stopPreviousRecPlay = url
player.play()
@@ -194,14 +231,53 @@ struct CIVideoView: View {
}
}
private func smallVideoViewEncrypted(_ file: CIFile, _ preview: UIImage) -> some View {
return ZStack(alignment: .topLeading) {
let canBePlayed = !chatItem.chatDir.sent || file.fileStatus == CIFileStatus.sndComplete || (file.fileStatus == .sndStored && file.fileProtocol == .local)
smallViewImageView(preview, file)
.onTapGesture {
decrypt(file: file) {
showFullScreenPlayer = urlDecrypted != nil
}
}
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenPlayer = false
}
if file.showStatusIconInSmallView {
// Show nothing
} else if !decryptionInProgress {
playPauseIcon(canBePlayed ? "play.fill" : "play.slash")
} else {
videoDecryptionProgress()
}
}
}
private func smallVideoView(_ url: URL, _ file: CIFile, _ preview: UIImage) -> some View {
return ZStack(alignment: .topLeading) {
smallViewImageView(preview, file)
.onTapGesture {
showFullScreenPlayer = true
}
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenPlayer = false
}
if !file.showStatusIconInSmallView {
playPauseIcon("play.fill")
}
}
}
private func playPauseIcon(_ image: String, _ color: Color = .white) -> some View {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 12, height: 12)
.frame(width: smallView ? 12 * sizeMultiplier * 1.6 : 12, height: smallView ? 12 * sizeMultiplier * 1.6 : 12)
.foregroundColor(color)
.padding(.leading, 4)
.frame(width: 40, height: 40)
.padding(.leading, smallView ? 0 : 4)
.frame(width: 40 * sizeMultiplier, height: 40 * sizeMultiplier)
.background(Color.black.opacity(0.35))
.clipShape(Circle())
}
@@ -209,9 +285,9 @@ struct CIVideoView: View {
private func videoDecryptionProgress(_ color: Color = .white) -> some View {
ProgressView()
.progressViewStyle(.circular)
.frame(width: 12, height: 12)
.frame(width: smallView ? 12 * sizeMultiplier : 12, height: smallView ? 12 * sizeMultiplier : 12)
.tint(color)
.frame(width: 40, height: 40)
.frame(width: smallView ? 40 * sizeMultiplier * 0.9 : 40, height: smallView ? 40 * sizeMultiplier * 0.9 : 40)
.background(Color.black.opacity(0.35))
.clipShape(Circle())
}
@@ -247,7 +323,23 @@ struct CIVideoView: View {
.resizable()
.scaledToFit()
.frame(width: w)
fileStatusIcon()
.modifier(PrivacyBlur(blurred: $blurred))
if !blurred || !showDownloadButton(chatItem.file?.fileStatus) {
fileStatusIcon()
}
}
}
private func smallViewImageView(_ img: UIImage, _ file: CIFile) -> some View {
ZStack(alignment: .center) {
Image(uiImage: img)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: maxWidth, height: maxWidth)
if file.showStatusIconInSmallView {
fileStatusIcon()
.allowsHitTesting(false)
}
}
}
@@ -322,7 +414,7 @@ struct CIVideoView: View {
.aspectRatio(contentMode: .fit)
.frame(width: size, height: size)
.foregroundColor(.white)
.padding(padding)
.padding(smallView ? 0 : padding)
}
private func progressView() -> some View {
@@ -330,7 +422,7 @@ struct CIVideoView: View {
.progressViewStyle(.circular)
.frame(width: 16, height: 16)
.tint(.white)
.padding(11)
.padding(smallView ? 0 : 11)
}
private func progressCircle(_ progress: Int64, _ total: Int64) -> some View {
@@ -342,7 +434,7 @@ struct CIVideoView: View {
)
.rotationEffect(.degrees(-90))
.frame(width: 16, height: 16)
.padding([.trailing, .top], 11)
.padding([.trailing, .top], smallView ? 0 : 11)
}
// TODO encrypt: where file size is checked?
@@ -382,7 +474,8 @@ struct CIVideoView: View {
)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now()) {
m.stopPreviousRecPlay = url
// Prevent feedback loop - setting `ChatModel`s property causes `onAppear` to be called on iOS17+
if m.stopPreviousRecPlay != url { m.stopPreviousRecPlay = url }
if let player = fullPlayer {
player.play()
var played = false
@@ -419,10 +512,12 @@ struct CIVideoView: View {
urlDecrypted = await file.fileSource?.decryptedGetOrCreate(&ChatModel.shared.filesToDelete)
await MainActor.run {
if let decrypted = urlDecrypted {
player = VideoPlayerView.getOrCreatePlayer(decrypted, false)
if !smallView {
player = VideoPlayerView.getOrCreatePlayer(decrypted, false)
}
fullPlayer = AVPlayer(url: decrypted)
}
decryptionInProgress = true
decryptionInProgress = false
completed?()
}
}
@@ -15,15 +15,26 @@ struct CIVoiceView: View {
var chatItem: ChatItem
let recordingFile: CIFile?
let duration: Int
@Binding var audioPlayer: AudioPlayer?
@Binding var playbackState: VoiceMessagePlaybackState
@Binding var playbackTime: TimeInterval?
@State var audioPlayer: AudioPlayer? = nil
@State var playbackState: VoiceMessagePlaybackState = .noPlayback
@State var playbackTime: TimeInterval? = nil
@Binding var allowMenu: Bool
var smallViewSize: CGFloat?
@State private var seek: (TimeInterval) -> Void = { _ in }
var body: some View {
Group {
if chatItem.chatDir.sent {
if smallViewSize != nil {
HStack(spacing: 10) {
player()
playerTime()
.allowsHitTesting(false)
if .playing == playbackState || (playbackTime ?? 0) > 0 || !allowMenu {
playbackSlider()
}
}
} else if chatItem.chatDir.sent {
VStack (alignment: .trailing, spacing: 6) {
HStack {
if .playing == playbackState || (playbackTime ?? 0) > 0 || !allowMenu {
@@ -54,7 +65,13 @@ struct CIVoiceView: View {
}
private func player() -> some View {
VoiceMessagePlayer(
let sizeMultiplier: CGFloat = if let sz = smallViewSize {
voiceMessageSizeBasedOnSquareSize(sz) / 56
} else {
1
}
return VoiceMessagePlayer(
chat: chat,
chatItem: chatItem,
recordingFile: recordingFile,
recordingTime: TimeInterval(duration),
@@ -63,7 +80,8 @@ struct CIVoiceView: View {
audioPlayer: $audioPlayer,
playbackState: $playbackState,
playbackTime: $playbackTime,
allowMenu: $allowMenu
allowMenu: $allowMenu,
sizeMultiplier: sizeMultiplier
)
}
@@ -119,6 +137,7 @@ struct VoiceMessagePlayerTime: View {
}
struct VoiceMessagePlayer: View {
@ObservedObject var chat: Chat
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
@@ -130,7 +149,9 @@ struct VoiceMessagePlayer: View {
@Binding var audioPlayer: AudioPlayer?
@Binding var playbackState: VoiceMessagePlaybackState
@Binding var playbackTime: TimeInterval?
@Binding var allowMenu: Bool
var sizeMultiplier: CGFloat
var body: some View {
ZStack {
@@ -190,49 +211,113 @@ struct VoiceMessagePlayer: View {
}
}
.onAppear {
if audioPlayer == nil {
let small = sizeMultiplier != 1
audioPlayer = small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.audioPlayer : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.audioPlayer
playbackState = (small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.playbackState : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.playbackState) ?? .noPlayback
playbackTime = small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.playbackTime : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.playbackTime
}
seek = { to in audioPlayer?.seek(to) }
audioPlayer?.onTimer = { playbackTime = $0 }
let audioPath: URL? = if let recordingSource = getLoadedFileSource(recordingFile) {
getAppFilePath(recordingSource.filePath)
} else {
nil
}
let chatId = chatModel.chatId
let userId = chatModel.currentUser?.userId
audioPlayer?.onTimer = {
playbackTime = $0
notifyStateChange()
// Manual check here is needed because when this view is not visible, SwiftUI don't react on stopPreviousRecPlay, chatId and current user changes and audio keeps playing when it should stop
if (audioPath != nil && chatModel.stopPreviousRecPlay != audioPath) || chatModel.chatId != chatId || chatModel.currentUser?.userId != userId {
stopPlayback()
}
}
audioPlayer?.onFinishPlayback = {
playbackState = .noPlayback
playbackTime = TimeInterval(0)
notifyStateChange()
}
// One voice message was paused, then scrolled far from it, started to play another one, drop to stopped state
if let audioPath, chatModel.stopPreviousRecPlay != audioPath {
stopPlayback()
}
}
.onChange(of: chatModel.stopPreviousRecPlay) { it in
if let recordingFileName = getLoadedFileSource(recordingFile)?.filePath,
chatModel.stopPreviousRecPlay != getAppFilePath(recordingFileName) {
audioPlayer?.stop()
playbackState = .noPlayback
playbackTime = TimeInterval(0)
stopPlayback()
}
}
.onChange(of: playbackState) { state in
allowMenu = state == .paused || state == .noPlayback
// Notify activeContentPreview in ChatPreviewView that playback is finished
if state == .noPlayback, let recordingFileName = getLoadedFileSource(recordingFile)?.filePath,
chatModel.stopPreviousRecPlay == getAppFilePath(recordingFileName) {
chatModel.stopPreviousRecPlay = nil
}
}
.onChange(of: chatModel.chatId) { _ in
stopPlayback()
}
.onDisappear {
if sizeMultiplier == 1 && chatModel.chatId == nil {
stopPlayback()
}
}
}
@ViewBuilder private func playbackButton() -> some View {
switch playbackState {
case .noPlayback:
Button {
if let recordingSource = getLoadedFileSource(recordingFile) {
startPlayback(recordingSource)
}
} label: {
if sizeMultiplier != 1 {
switch playbackState {
case .noPlayback:
playPauseIcon("play.fill", theme.colors.primary)
}
case .playing:
Button {
audioPlayer?.pause()
playbackState = .paused
} label: {
.onTapGesture {
if let recordingSource = getLoadedFileSource(recordingFile) {
startPlayback(recordingSource)
}
}
case .playing:
playPauseIcon("pause.fill", theme.colors.primary)
}
case .paused:
Button {
audioPlayer?.play()
playbackState = .playing
} label: {
.onTapGesture {
audioPlayer?.pause()
playbackState = .paused
notifyStateChange()
}
case .paused:
playPauseIcon("play.fill", theme.colors.primary)
.onTapGesture {
audioPlayer?.play()
playbackState = .playing
notifyStateChange()
}
}
} else {
switch playbackState {
case .noPlayback:
Button {
if let recordingSource = getLoadedFileSource(recordingFile) {
startPlayback(recordingSource)
}
} label: {
playPauseIcon("play.fill", theme.colors.primary)
}
case .playing:
Button {
audioPlayer?.pause()
playbackState = .paused
notifyStateChange()
} label: {
playPauseIcon("pause.fill", theme.colors.primary)
}
case .paused:
Button {
audioPlayer?.play()
playbackState = .playing
notifyStateChange()
} label: {
playPauseIcon("play.fill", theme.colors.primary)
}
}
}
}
@@ -242,28 +327,49 @@ struct VoiceMessagePlayer: View {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.frame(width: 20 * sizeMultiplier, height: 20 * sizeMultiplier)
.foregroundColor(color)
.padding(.leading, image == "play.fill" ? 4 : 0)
.frame(width: 56, height: 56)
.frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
if recordingTime > 0 {
ProgressCircle(length: recordingTime, progress: $playbackTime)
.frame(width: 53, height: 53) // this + ProgressCircle lineWidth = background circle diameter
.frame(width: 53 * sizeMultiplier, height: 53 * sizeMultiplier) // this + ProgressCircle lineWidth = background circle diameter
}
}
}
private func downloadButton(_ recordingFile: CIFile, _ icon: String) -> some View {
Button {
Task {
if let user = chatModel.currentUser {
await receiveFile(user: user, fileId: recordingFile.fileId)
Group {
if sizeMultiplier != 1 {
playPauseIcon(icon, theme.colors.primary)
.onTapGesture {
Task {
if let user = chatModel.currentUser {
await receiveFile(user: user, fileId: recordingFile.fileId)
}
}
}
} else {
Button {
Task {
if let user = chatModel.currentUser {
await receiveFile(user: user, fileId: recordingFile.fileId)
}
}
} label: {
playPauseIcon(icon, theme.colors.primary)
}
}
} label: {
playPauseIcon(icon, theme.colors.primary)
}
}
func notifyStateChange() {
if sizeMultiplier != 1 {
VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)] = VoiceItemState(audioPlayer: audioPlayer, playbackState: playbackState, playbackTime: playbackTime)
} else {
VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)] = VoiceItemState(audioPlayer: audioPlayer, playbackState: playbackState, playbackTime: playbackTime)
}
}
@@ -288,33 +394,96 @@ struct VoiceMessagePlayer: View {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: size, height: size)
.frame(width: size * sizeMultiplier, height: size * sizeMultiplier)
.foregroundColor(Color(uiColor: .tertiaryLabel))
.frame(width: 56, height: 56)
.frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
}
private func loadingIcon() -> some View {
ProgressView()
.frame(width: 30, height: 30)
.frame(width: 56, height: 56)
.frame(width: 30 * sizeMultiplier, height: 30 * sizeMultiplier)
.frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
}
private func startPlayback(_ recordingSource: CryptoFile) {
chatModel.stopPreviousRecPlay = getAppFilePath(recordingSource.filePath)
let audioPath = getAppFilePath(recordingSource.filePath)
let chatId = chatModel.chatId
let userId = chatModel.currentUser?.userId
chatModel.stopPreviousRecPlay = audioPath
audioPlayer = AudioPlayer(
onTimer: { playbackTime = $0 },
onTimer: {
playbackTime = $0
notifyStateChange()
// Manual check here is needed because when this view is not visible, SwiftUI don't react on stopPreviousRecPlay, chatId and current user changes and audio keeps playing when it should stop
if chatModel.stopPreviousRecPlay != audioPath || chatModel.chatId != chatId || chatModel.currentUser?.userId != userId {
stopPlayback()
}
},
onFinishPlayback: {
playbackState = .noPlayback
playbackTime = TimeInterval(0)
notifyStateChange()
}
)
audioPlayer?.start(fileSource: recordingSource, at: playbackTime)
playbackState = .playing
notifyStateChange()
}
private func stopPlayback() {
audioPlayer?.stop()
playbackState = .noPlayback
playbackTime = TimeInterval(0)
notifyStateChange()
}
}
func voiceMessageSizeBasedOnSquareSize(_ squareSize: CGFloat) -> CGFloat {
let squareToCircleRatio = 0.935
return squareSize + squareSize * (1 - squareToCircleRatio)
}
class VoiceItemState {
var audioPlayer: AudioPlayer?
var playbackState: VoiceMessagePlaybackState
var playbackTime: TimeInterval?
init(audioPlayer: AudioPlayer? = nil, playbackState: VoiceMessagePlaybackState, playbackTime: TimeInterval? = nil) {
self.audioPlayer = audioPlayer
self.playbackState = playbackState
self.playbackTime = playbackTime
}
static func id(_ chat: Chat, _ chatItem: ChatItem) -> String {
"\(chat.id) \(chatItem.id)"
}
static func id(_ chatInfo: ChatInfo, _ chatItem: ChatItem) -> String {
"\(chatInfo.id) \(chatItem.id)"
}
static func stopVoiceInSmallView(_ chatInfo: ChatInfo, _ chatItem: ChatItem) {
let id = id(chatInfo, chatItem)
if let item = smallView[id] {
item.audioPlayer?.stop()
ChatModel.shared.stopPreviousRecPlay = nil
}
}
static func stopVoiceInChatView(_ chatInfo: ChatInfo, _ chatItem: ChatItem) {
let id = id(chatInfo, chatItem)
if let item = chatView[id] {
item.audioPlayer?.stop()
ChatModel.shared.stopPreviousRecPlay = nil
}
}
static var smallView: [String: VoiceItemState] = [:]
static var chatView: [String: VoiceItemState] = [:]
}
struct CIVoiceView_Previews: PreviewProvider {
@@ -339,15 +508,12 @@ struct CIVoiceView_Previews: PreviewProvider {
chatItem: ChatItem.getVoiceMsgContentSample(),
recordingFile: CIFile.getSample(fileName: "voice.m4a", fileSize: 65536, fileStatus: .rcvComplete),
duration: 30,
audioPlayer: .constant(nil),
playbackState: .constant(.playing),
playbackTime: .constant(TimeInterval(20)),
allowMenu: Binding.constant(true)
)
ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(), revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWtFile, revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, revealed: Binding.constant(false), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(), revealed: Binding.constant(false), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), revealed: Binding.constant(false), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWtFile, revealed: Binding.constant(false), allowMenu: .constant(true))
}
.previewLayout(.fixed(width: 360, height: 360))
}
@@ -13,21 +13,23 @@ import SimpleXChat
struct FramedCIVoiceView: View {
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
let recordingFile: CIFile?
let duration: Int
@State var audioPlayer: AudioPlayer? = nil
@State var playbackState: VoiceMessagePlaybackState = .noPlayback
@State var playbackTime: TimeInterval? = nil
@Binding var allowMenu: Bool
@Binding var audioPlayer: AudioPlayer?
@Binding var playbackState: VoiceMessagePlaybackState
@Binding var playbackTime: TimeInterval?
@State private var seek: (TimeInterval) -> Void = { _ in }
var body: some View {
HStack {
VoiceMessagePlayer(
chat: chat,
chatItem: chatItem,
recordingFile: recordingFile,
recordingTime: TimeInterval(duration),
@@ -36,7 +38,8 @@ struct FramedCIVoiceView: View {
audioPlayer: $audioPlayer,
playbackState: $playbackState,
playbackTime: $playbackTime,
allowMenu: $allowMenu
allowMenu: $allowMenu,
sizeMultiplier: 1
)
VoiceMessagePlayerTime(
recordingTime: TimeInterval(duration),
File diff suppressed because one or more lines are too long
@@ -22,7 +22,7 @@ struct ChatItemForwardingView: View {
@FocusState private var searchFocused
@State private var alert: SomeAlert?
@State private var hasSimplexLink_: Bool?
private let chatsToForwardTo = filterChatsToForwardTo()
private let chatsToForwardTo = filterChatsToForwardTo(chats: ChatModel.shared.chats)
var body: some View {
NavigationView {
@@ -67,22 +67,6 @@ struct ChatItemForwardingView: View {
}
}
private func foundChat(_ chat: Chat, _ searchStr: String) -> Bool {
let cInfo = chat.chatInfo
return switch cInfo {
case let .direct(contact):
viewNameContains(cInfo, searchStr) ||
contact.profile.displayName.localizedLowercase.contains(searchStr) ||
contact.fullName.localizedLowercase.contains(searchStr)
default:
viewNameContains(cInfo, searchStr)
}
func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool {
cInfo.chatViewName.localizedLowercase.contains(s)
}
}
private func prohibitedByPref(_ chat: Chat) -> Bool {
// preference checks should match checks in compose view
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
@@ -162,27 +146,6 @@ struct ChatItemForwardingView: View {
}
}
private func filterChatsToForwardTo() -> [Chat] {
var filteredChats = ChatModel.shared.chats.filter { c in
c.chatInfo.chatType != .local && canForwardToChat(c)
}
if let privateNotes = ChatModel.shared.chats.first(where: { $0.chatInfo.chatType == .local }) {
filteredChats.insert(privateNotes, at: 0)
}
return filteredChats
}
private func canForwardToChat(_ chat: Chat) -> Bool {
switch chat.chatInfo {
case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv
case let .group(groupInfo): groupInfo.sendMsgEnabled
case let .local(noteFolder): noteFolder.sendMsgEnabled
case .contactRequest: false
case .contactConnection: false
case .invalidJSON: false
}
}
#Preview {
ChatItemForwardingView(
ci: ChatItem.getSample(1, .directSnd, .now, "hello"),
@@ -190,3 +153,4 @@ private func canForwardToChat(_ chat: Chat) -> Bool {
composeState: Binding.constant(ComposeState(message: "hello"))
).environmentObject(CurrentColors.toAppTheme())
}
+4 -15
View File
@@ -16,28 +16,20 @@ struct ChatItemView: View {
var maxWidth: CGFloat = .infinity
@Binding var revealed: Bool
@Binding var allowMenu: Bool
@Binding var audioPlayer: AudioPlayer?
@Binding var playbackState: VoiceMessagePlaybackState
@Binding var playbackTime: TimeInterval?
init(
chat: Chat,
chatItem: ChatItem,
showMember: Bool = false,
maxWidth: CGFloat = .infinity,
revealed: Binding<Bool>,
allowMenu: Binding<Bool> = .constant(false),
audioPlayer: Binding<AudioPlayer?> = .constant(nil),
playbackState: Binding<VoiceMessagePlaybackState> = .constant(.noPlayback),
playbackTime: Binding<TimeInterval?> = .constant(nil)
allowMenu: Binding<Bool> = .constant(false)
) {
self.chat = chat
self.chatItem = chatItem
self.maxWidth = maxWidth
_revealed = revealed
_allowMenu = allowMenu
_audioPlayer = audioPlayer
_playbackState = playbackState
_playbackTime = playbackTime
}
var body: some View {
@@ -48,7 +40,7 @@ struct ChatItemView: View {
if let mc = ci.content.msgContent, mc.isText && isShortEmoji(ci.content.text) {
EmojiItemView(chat: chat, chatItem: ci)
} else if ci.content.text.isEmpty, case let .voice(_, duration) = ci.content.msgContent {
CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, audioPlayer: $audioPlayer, playbackState: $playbackState, playbackTime: $playbackTime, allowMenu: $allowMenu)
CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: $allowMenu)
} else if ci.content.msgContent == nil {
ChatItemContentView(chat: chat, chatItem: chatItem, revealed: $revealed, msgContentView: { Text(ci.text) }) // msgContent is unreachable branch in this case
} else {
@@ -84,10 +76,7 @@ struct ChatItemView: View {
maxWidth: maxWidth,
imgWidth: adjustedMaxWidth,
videoWidth: adjustedMaxWidth,
allowMenu: $allowMenu,
audioPlayer: $audioPlayer,
playbackState: $playbackState,
playbackTime: $playbackTime
allowMenu: $allowMenu
)
}
}
+39 -32
View File
@@ -96,6 +96,7 @@ struct ChatView: View {
}
.onChange(of: chatModel.chatId) { cId in
showChatInfoSheet = false
stopAudioPlayer()
if let cId {
if let c = chatModel.getChat(cId) {
chat = c
@@ -117,6 +118,7 @@ struct ChatView: View {
.environmentObject(scrollModel)
.onDisappear {
VideoPlayerView.players.removeAll()
stopAudioPlayer()
if chatModel.chatId == cInfo.id && !presentationMode.wrappedValue.isPresented {
chatModel.chatId = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
@@ -378,7 +380,7 @@ struct ChatView: View {
@ViewBuilder private func connectingText() -> some View {
if case let .direct(contact) = chat.chatInfo,
!contact.ready,
!contact.sndReady,
contact.active,
!contact.nextSendGrpInv {
Text("connecting…")
@@ -589,6 +591,11 @@ struct ChatView: View {
}
}
func stopAudioPlayer() {
VoiceItemState.chatView.values.forEach { $0.audioPlayer?.stop() }
VoiceItemState.chatView = [:]
}
@ViewBuilder private func chatItemView(_ ci: ChatItem, _ maxWidth: CGFloat) -> some View {
ChatItemWithMenu(
chat: chat,
@@ -620,10 +627,6 @@ struct ChatView: View {
@State private var allowMenu: Bool = true
@State private var audioPlayer: AudioPlayer?
@State private var playbackState: VoiceMessagePlaybackState = .noPlayback
@State private var playbackTime: TimeInterval?
var revealed: Bool { chatItem == revealedChatItem }
var body: some View {
@@ -643,23 +646,38 @@ struct ChatView: View {
}
}
.onAppear {
markRead(
chatItems: range.flatMap { m.reversedChatItems[$0] }
?? [chatItem]
)
}
}
private func markRead(chatItems: Array<ChatItem>.SubSequence) {
let unreadItems = chatItems.filter { $0.isRcvNew }
if unreadItems.isEmpty { return }
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
if m.chatId == chat.chatInfo.id {
Task {
for unreadItem in unreadItems {
await apiMarkChatItemRead(chat.chatInfo, unreadItem)
if let range {
if let items = unreadItems(range) {
waitToMarkRead {
for ci in items {
await apiMarkChatItemRead(chat.chatInfo, ci)
}
}
}
} else if chatItem.isRcvNew {
waitToMarkRead {
await apiMarkChatItemRead(chat.chatInfo, chatItem)
}
}
}
}
private func unreadItems(_ range: ClosedRange<Int>) -> [ChatItem]? {
let items = range.compactMap { i in
if i >= 0 && i < m.reversedChatItems.count {
let ci = m.reversedChatItems[i]
return if ci.isRcvNew { ci } else { nil }
} else {
return nil
}
}
return if items.isEmpty { nil } else { items }
}
private func waitToMarkRead(_ op: @Sendable @escaping () async -> Void) {
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
if m.chatId == chat.chatInfo.id {
Task(operation: op)
}
}
}
@@ -742,10 +760,7 @@ struct ChatView: View {
chatItem: ci,
maxWidth: maxWidth,
revealed: .constant(revealed),
allowMenu: $allowMenu,
audioPlayer: $audioPlayer,
playbackState: $playbackState,
playbackTime: $playbackTime
allowMenu: $allowMenu
)
.modifier(ChatItemClipped(ci))
.contextMenu { menu(ci, range, live: composeState.liveMessage != nil) }
@@ -772,14 +787,6 @@ struct ChatView: View {
}
.frame(maxWidth: maxWidth, maxHeight: .infinity, alignment: alignment)
.frame(minWidth: 0, maxWidth: .infinity, alignment: alignment)
.onDisappear {
if ci.content.msgContent?.isVoice == true {
allowMenu = true
audioPlayer?.stop()
playbackState = .noPlayback
playbackTime = TimeInterval(0)
}
}
.sheet(isPresented: $showChatItemInfoSheet, onDismiss: {
chatItemInfo = nil
}) {
@@ -10,35 +10,6 @@ import SwiftUI
import LinkPresentation
import SimpleXChat
func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) {
logger.debug("getLinkMetadata: fetching URL preview")
LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in
if let e = error {
logger.error("Error retrieving link metadata: \(e.localizedDescription)")
}
if let metadata = metadata,
let imageProvider = metadata.imageProvider,
imageProvider.canLoadObject(ofClass: UIImage.self) {
imageProvider.loadObject(ofClass: UIImage.self){ object, error in
var linkPreview: LinkPreview? = nil
if let error = error {
logger.error("Couldn't load image preview from link metadata with error: \(error.localizedDescription)")
} else {
if let image = object as? UIImage,
let resized = resizeImageToStrSize(image, maxDataSize: 14000),
let title = metadata.title,
let uri = metadata.originalURL {
linkPreview = LinkPreview(uri: uri, title: title, image: resized)
}
}
cb(linkPreview)
}
} else {
cb(nil)
}
}
}
struct ComposeLinkView: View {
@EnvironmentObject var theme: AppTheme
let linkPreview: LinkPreview?
@@ -67,7 +67,6 @@ struct SendMessageView: View {
.fixedSize(horizontal: false, vertical: true)
}
}
if progressByTimeout {
ProgressView()
.scaleEffect(1.4)
@@ -87,7 +86,7 @@ struct SendMessageView: View {
.padding(.vertical, 1)
.background(theme.colors.background)
.clipShape(composeShape)
.overlay(composeShape.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true))
.overlay(composeShape.strokeBorder(.secondary, lineWidth: 0.5).opacity(0.7))
}
.onChange(of: composeState.message, perform: { text in updateFont(text) })
.onChange(of: composeState.inProgress) { inProgress in
@@ -35,7 +35,7 @@ struct AddGroupMembersViewCommon: View {
private enum AddGroupMembersAlert: Identifiable {
case prohibitedToInviteIncognito
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -122,7 +122,7 @@ struct AddGroupMembersViewCommon: View {
message: Text("You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile")
)
case let .error(title, error):
return Alert(title: Text(title), message: Text(error))
return mkAlert(title: title, message: error)
}
}
.onChange(of: selectedContacts) { _ in
@@ -40,7 +40,7 @@ struct GroupChatInfoView: View {
case blockForAllAlert(mem: GroupMember)
case unblockForAllAlert(mem: GroupMember)
case removeMemberAlert(mem: GroupMember)
case error(title: LocalizedStringKey, error: LocalizedStringKey)
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -158,7 +158,7 @@ struct GroupChatInfoView: View {
case let .blockForAllAlert(mem): return blockForAllAlert(groupInfo, mem)
case let .unblockForAllAlert(mem): return unblockForAllAlert(groupInfo, mem)
case let .removeMemberAlert(mem): return removeMemberAlert(mem)
case let .error(title, error): return Alert(title: Text(title), message: Text(error))
case let .error(title, error): return mkAlert(title: title, message: error)
}
}
.onAppear {
@@ -22,7 +22,7 @@ struct GroupLinkView: View {
private enum GroupLinkAlert: Identifiable {
case deleteLink
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -113,7 +113,7 @@ struct GroupLinkView: View {
}, secondaryButton: .cancel()
)
case let .error(title, error):
return Alert(title: Text(title), message: Text(error))
return mkAlert(title: title, message: error)
}
}
.onChange(of: groupLinkMemberRole) { _ in
@@ -37,7 +37,7 @@ struct GroupMemberInfoView: View {
case syncConnectionForceAlert
case planAndConnectAlert(alert: PlanAndConnectAlert)
case queueInfo(info: String)
case error(title: LocalizedStringKey, error: LocalizedStringKey)
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -237,7 +237,7 @@ struct GroupMemberInfoView: View {
case .syncConnectionForceAlert: return syncConnectionForceAlert({ syncMemberConnection(force: true) })
case let .planAndConnectAlert(alert): return planAndConnectAlert(alert, dismiss: true)
case let .queueInfo(info): return queueInfoAlert(info)
case let .error(title, error): return Alert(title: Text(title), message: Text(error))
case let .error(title, error): return mkAlert(title: title, message: error)
}
}
.actionSheet(item: $sheet) { s in planAndConnectActionSheet(s, dismiss: true) }
+2 -1
View File
@@ -129,8 +129,9 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
from: nil,
for: nil
)
NotificationCenter.default.post(name: .chatViewWillBeginScrolling, object: nil)
}
/// Scrolls up
func scrollToNextPage() {
tableView.setContentOffset(
@@ -9,25 +9,34 @@
import SwiftUI
import SimpleXChat
private let rowHeights: [DynamicTypeSize: CGFloat] = [
.xSmall: 68,
.small: 72,
.medium: 76,
.large: 80,
.xLarge: 88,
.xxLarge: 94,
.xxxLarge: 104,
.accessibility1: 90,
.accessibility2: 100,
.accessibility3: 120,
.accessibility4: 130,
.accessibility5: 140
let dynamicSizes: [
DynamicTypeSize: (
rowHeight: CGFloat,
profileImageSize: CGFloat,
mediaSize: CGFloat,
chatInfoSize: CGFloat,
unreadCorner: CGFloat,
unreadPadding: CGFloat
)
] = [
.xSmall: (68, 55, 33, 18, 9, 3),
.small: (72, 57, 34, 18, 9, 3),
.medium: (76, 60, 36, 18, 10, 4),
.large: (80, 63, 38, 20, 10, 4),
.xLarge: (88, 67, 41, 20, 10, 4),
.xxLarge: (94, 71, 44, 22, 11, 4),
.xxxLarge: (104, 75, 48, 24, 12, 5),
.accessibility1: (104, 75, 48, 24, 12, 5),
.accessibility2: (114, 75, 48, 24, 12, 5),
.accessibility3: (124, 75, 48, 24, 12, 5),
.accessibility4: (134, 75, 48, 24, 12, 5),
.accessibility5: (144, 75, 48, 24, 12, 5)
]
struct ChatListNavLink: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var dynamicTypeSize
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
@ObservedObject var chat: Chat
@State private var showContactRequestDialog = false
@State private var showJoinGroupDialog = false
@@ -38,6 +47,8 @@ struct ChatListNavLink: View {
@State private var inProgress = false
@State private var progressByTimeout = false
var dynamicRowHeight: CGFloat { dynamicSizes[userFont]?.rowHeight ?? 80 }
var body: some View {
Group {
switch chat.chatInfo {
@@ -70,7 +81,7 @@ struct ChatListNavLink: View {
Group {
if contact.activeConn == nil && contact.profile.contactLink != nil {
ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false))
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button {
showDeleteContactActionSheet = true
@@ -100,7 +111,7 @@ struct ChatListNavLink: View {
clearChatButton()
}
Button {
if contact.ready || !contact.active {
if contact.sndReady || !contact.active {
showDeleteContactActionSheet = true
} else {
AlertManager.shared.showAlert(deletePendingContactAlert(chat, contact))
@@ -110,11 +121,11 @@ struct ChatListNavLink: View {
}
.tint(.red)
}
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
}
}
.actionSheet(isPresented: $showDeleteContactActionSheet) {
if contact.ready && contact.active {
if contact.sndReady && contact.active {
return ActionSheet(
title: Text("Delete contact?\nThis cannot be undone!"),
buttons: [
@@ -139,7 +150,7 @@ struct ChatListNavLink: View {
switch (groupInfo.membership.memberStatus) {
case .memInvited:
ChatPreviewView(chat: chat, progressByTimeout: $progressByTimeout)
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
joinGroupButton()
if groupInfo.canDelete {
@@ -159,7 +170,7 @@ struct ChatListNavLink: View {
.disabled(inProgress)
case .memAccepted:
ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false))
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
.onTapGesture {
AlertManager.shared.showAlert(groupInvitationAcceptedAlert())
}
@@ -178,7 +189,7 @@ struct ChatListNavLink: View {
label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) },
disabled: !groupInfo.ready
)
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
.swipeActions(edge: .leading, allowsFullSwipe: true) {
markReadButton()
toggleFavoriteButton()
@@ -205,7 +216,7 @@ struct ChatListNavLink: View {
label: { ChatPreviewView(chat: chat, progressByTimeout: Binding.constant(false)) },
disabled: !noteFolder.ready
)
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
.swipeActions(edge: .leading, allowsFullSwipe: true) {
markReadButton()
}
@@ -321,7 +332,7 @@ struct ChatListNavLink: View {
}
.tint(.red)
}
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
.onTapGesture { showContactRequestDialog = true }
.confirmationDialog("Accept connection request?", isPresented: $showContactRequestDialog, titleVisibility: .visible) {
Button("Accept") { Task { await acceptContactRequest(incognito: false, contactRequest: contactRequest) } }
@@ -349,7 +360,7 @@ struct ChatListNavLink: View {
}
.tint(theme.colors.primary)
}
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
.appSheet(isPresented: $showContactConnectionInfo) {
Group {
if case let .contactConnection(contactConnection) = chat.chatInfo {
@@ -469,7 +480,7 @@ struct ChatListNavLink: View {
Text("invalid chat data")
.foregroundColor(.red)
.padding(4)
.frame(height: rowHeights[dynamicTypeSize])
.frame(height: dynamicRowHeight)
.onTapGesture { showInvalidJSON = true }
.appSheet(isPresented: $showInvalidJSON) {
invalidJSONView(json)
@@ -21,6 +21,7 @@ struct ChatListView: View {
@State private var newChatMenuOption: NewChatMenuOption? = nil
@State private var userPickerVisible = false
@State private var showConnectDesktop = false
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
var body: some View {
@@ -162,6 +163,10 @@ struct ChatListView: View {
chatModel.chatToTop = nil
chatModel.popChat(chatId)
}
stopAudioPlayer()
}
.onChange(of: chatModel.currentUser?.userId) { _ in
stopAudioPlayer()
}
if cs.isEmpty && !chatModel.chats.isEmpty {
Text("No filtered chats").foregroundColor(theme.colors.secondary)
@@ -217,6 +222,11 @@ struct ChatListView: View {
}
}
func stopAudioPlayer() {
VoiceItemState.smallView.values.forEach { $0.audioPlayer?.stop() }
VoiceItemState.smallView = [:]
}
private func filteredChats() -> [Chat] {
if let linkChatId = searchChatFilteredBySimplexLink {
return chatModel.chats.filter { $0.id == linkChatId }
@@ -266,9 +276,9 @@ struct ChatListView: View {
}
struct SubsStatusIndicator: View {
@State private var serversSummary: PresentedServersSummary?
@State private var subs: SMPServerSubs = SMPServerSubs.newSMPServerSubs
@State private var hasSess: Bool = false
@State private var timer: Timer? = nil
@State private var timerCounter = 0
@State private var showServersSummary = false
@AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false
@@ -277,12 +287,10 @@ struct SubsStatusIndicator: View {
Button {
showServersSummary = true
} label: {
let subs = serversSummary?.allUsersSMP.smpTotals.subs ?? SMPServerSubs.newSMPServerSubs
let sess = serversSummary?.allUsersSMP.smpTotals.sessions ?? ServerSessions.newServerSessions
HStack(spacing: 4) {
SubscriptionStatusIndicatorView(subs: subs, sess: sess)
SubscriptionStatusIndicatorView(subs: subs, hasSess: hasSess)
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: subs, sess: sess)
SubscriptionStatusPercentageView(subs: subs, hasSess: hasSess)
}
}
}
@@ -293,14 +301,14 @@ struct SubsStatusIndicator: View {
stopTimer()
}
.sheet(isPresented: $showServersSummary) {
ServersSummaryView(serversSummary: $serversSummary)
ServersSummaryView()
}
}
private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
if AppChatState.shared.value == .active {
getServersSummary()
getSubsTotal()
}
}
}
@@ -310,11 +318,11 @@ struct SubsStatusIndicator: View {
timer = nil
}
private func getServersSummary() {
private func getSubsTotal() {
do {
serversSummary = try getAgentServersSummary()
(subs, hasSess) = try getAgentSubsTotal()
} catch let error {
logger.error("getAgentServersSummary error: \(responseError(error))")
logger.error("getSubsTotal error: \(responseError(error))")
}
}
}
@@ -12,18 +12,24 @@ import SimpleXChat
struct ChatPreviewView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
@ObservedObject var chat: Chat
@Binding var progressByTimeout: Bool
@State var deleting: Bool = false
var darkGreen = Color(red: 0, green: 0.5, blue: 0)
@State private var activeContentPreview: ActiveContentPreview? = nil
@State private var showFullscreenGallery: Bool = false
@AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true
var dynamicMediaSize: CGFloat { dynamicSizes[userFont]?.mediaSize ?? 36 }
var dynamicChatInfoSize: CGFloat { dynamicSizes[userFont]?.chatInfoSize ?? 18 }
var body: some View {
let cItem = chat.chatItems.last
return HStack(spacing: 8) {
ZStack(alignment: .bottomTrailing) {
ChatInfoImage(chat: chat, size: 63)
ChatInfoImage(chat: chat, size: dynamicSizes[userFont]?.profileImageSize ?? 63)
chatPreviewImageOverlayIcon()
.padding([.bottom, .trailing], 1)
}
@@ -43,11 +49,38 @@ struct ChatPreviewView: View {
.padding(.horizontal, 8)
ZStack(alignment: .topTrailing) {
chatMessagePreview(cItem)
let chat = activeContentPreview?.chat ?? chat
let ci = activeContentPreview?.ci ?? chat.chatItems.last
let mc = ci?.content.msgContent
HStack(alignment: .top) {
let deleted = ci?.isDeletedContent == true || ci?.meta.itemDeleted != nil
let showContentPreview = (showChatPreviews && chatModel.draftChatId != chat.id && !deleted) || activeContentPreview != nil
if let ci, showContentPreview {
chatItemContentPreview(chat, ci)
}
let mcIsVoice = switch mc { case .voice: true; default: false }
if !mcIsVoice || !showContentPreview || mc?.text != "" || chatModel.draftChatId == chat.id {
let hasFilePreview = if case .file = mc { true } else { false }
chatMessagePreview(cItem, hasFilePreview)
} else {
Spacer()
chatInfoIcon(chat).frame(minWidth: 37, alignment: .trailing)
}
}
.onChange(of: chatModel.stopPreviousRecPlay?.path) { _ in
checkActiveContentPreview(chat, ci, mc)
}
.onChange(of: activeContentPreview) { _ in
checkActiveContentPreview(chat, ci, mc)
}
.onChange(of: showFullscreenGallery) { _ in
checkActiveContentPreview(chat, ci, mc)
}
chatStatusImage()
.padding(.top, 26)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.trailing, 8)
Spacer()
@@ -57,6 +90,33 @@ struct ChatPreviewView: View {
.padding(.bottom, -8)
.onChange(of: chatModel.deletedChats.contains(chat.chatInfo.id)) { contains in
deleting = contains
// Stop voice when deleting the chat
if contains, let ci = activeContentPreview?.ci {
VoiceItemState.stopVoiceInSmallView(chat.chatInfo, ci)
}
}
func checkActiveContentPreview(_ chat: Chat, _ ci: ChatItem?, _ mc: MsgContent?) {
let playing = chatModel.stopPreviousRecPlay
if case .voice = activeContentPreview?.mc, playing == nil {
activeContentPreview = nil
} else if activeContentPreview == nil {
if case .image = mc, let ci, let mc, showFullscreenGallery {
activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc)
}
if case .video = mc, let ci, let mc, showFullscreenGallery {
activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc)
}
if case .voice = mc, let ci, let mc, let fileSource = ci.file?.fileSource, playing?.path.hasSuffix(fileSource.filePath) == true {
activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc)
}
} else if case .voice = activeContentPreview?.mc {
if let playing, let fileSource = ci?.file?.fileSource, !playing.path.hasSuffix(fileSource.filePath) {
activeContentPreview = nil
}
} else if !showFullscreenGallery {
activeContentPreview = nil
}
}
}
@@ -113,39 +173,50 @@ struct ChatPreviewView: View {
.kerning(-2)
}
private func chatPreviewLayout(_ text: Text, draft: Bool = false) -> some View {
private func chatPreviewLayout(_ text: Text?, draft: Bool = false, _ hasFilePreview: Bool = false) -> some View {
ZStack(alignment: .topTrailing) {
let t = text
.lineLimit(2)
.lineLimit(userFont <= .xxxLarge ? 2 : 1)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .topLeading)
.padding(.leading, 8)
.padding(.trailing, 36)
.padding(.leading, hasFilePreview ? 0 : 8)
.padding(.trailing, hasFilePreview ? 38 : 36)
.offset(x: hasFilePreview ? -2 : 0)
.fixedSize(horizontal: false, vertical: true)
if !showChatPreviews && !draft {
t.privacySensitive(true).redacted(reason: .privacy)
} else {
t
}
let s = chat.chatStats
if s.unreadCount > 0 || s.unreadChat {
unreadCountText(s.unreadCount)
.font(.caption)
.foregroundColor(.white)
.padding(.horizontal, 4)
.frame(minWidth: 18, minHeight: 18)
.background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? theme.colors.primary : theme.colors.secondary)
.cornerRadius(10)
} else if !chat.chatInfo.ntfsEnabled && chat.chatInfo.chatType != .local {
Image(systemName: "speaker.slash.fill")
.foregroundColor(theme.colors.secondary)
} else if chat.chatInfo.chatSettings?.favorite ?? false {
Image(systemName: "star.fill")
.resizable()
.scaledToFill()
.frame(width: 18, height: 18)
.padding(.trailing, 1)
.foregroundColor(.secondary.opacity(0.65))
}
chatInfoIcon(chat).frame(minWidth: 37, alignment: .trailing)
}
}
@ViewBuilder private func chatInfoIcon(_ chat: Chat) -> some View {
let s = chat.chatStats
if s.unreadCount > 0 || s.unreadChat {
unreadCountText(s.unreadCount)
.font(userFont <= .xxxLarge ? .caption : .caption2)
.foregroundColor(.white)
.padding(.horizontal, dynamicSizes[userFont]?.unreadPadding ?? 4)
.frame(minWidth: dynamicChatInfoSize, minHeight: dynamicChatInfoSize)
.background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? theme.colors.primary : theme.colors.secondary)
.cornerRadius(dynamicSizes[userFont]?.unreadCorner ?? 10)
} else if !chat.chatInfo.ntfsEnabled && chat.chatInfo.chatType != .local {
Image(systemName: "speaker.slash.fill")
.resizable()
.scaledToFill()
.frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize)
.foregroundColor(theme.colors.secondary)
} else if chat.chatInfo.chatSettings?.favorite ?? false {
Image(systemName: "star.fill")
.resizable()
.scaledToFill()
.frame(width: dynamicChatInfoSize, height: dynamicChatInfoSize)
.padding(.trailing, 1)
.foregroundColor(theme.colors.secondary.opacity(0.65))
} else {
Color.clear.frame(width: 0)
}
}
@@ -172,7 +243,7 @@ struct ChatPreviewView: View {
func chatItemPreview(_ cItem: ChatItem) -> Text {
let itemText = cItem.meta.itemDeleted == nil ? cItem.text : markedDeletedText()
let itemFormattedText = cItem.meta.itemDeleted == nil ? cItem.formattedText : nil
return messageText(itemText, itemFormattedText, cItem.memberDisplayName, icon: attachment(), preview: true, showSecrets: false, secondaryColor: theme.colors.secondary)
return messageText(itemText, itemFormattedText, cItem.memberDisplayName, icon: nil, preview: true, showSecrets: false, secondaryColor: theme.colors.secondary)
// same texts are in markedDeletedText in MarkedDeletedItemView, but it returns LocalizedStringKey;
// can be refactored into a single function if functions calling these are changed to return same type
@@ -196,18 +267,18 @@ struct ChatPreviewView: View {
}
}
@ViewBuilder private func chatMessagePreview(_ cItem: ChatItem?) -> some View {
@ViewBuilder private func chatMessagePreview(_ cItem: ChatItem?, _ hasFilePreview: Bool = false) -> some View {
if chatModel.draftChatId == chat.id, let draft = chatModel.draft {
chatPreviewLayout(messageDraft(draft), draft: true)
chatPreviewLayout(messageDraft(draft), draft: true, hasFilePreview)
} else if let cItem = cItem {
chatPreviewLayout(itemStatusMark(cItem) + chatItemPreview(cItem))
chatPreviewLayout(itemStatusMark(cItem) + chatItemPreview(cItem), hasFilePreview)
} else {
switch (chat.chatInfo) {
case let .direct(contact):
if contact.activeConn == nil && contact.profile.contactLink != nil {
chatPreviewInfoText("Tap to Connect")
.foregroundColor(theme.colors.primary)
} else if !contact.ready && contact.activeConn != nil {
} else if !contact.sndReady && contact.activeConn != nil {
if contact.nextSendGrpInv {
chatPreviewInfoText("send direct message")
} else if contact.active {
@@ -225,6 +296,54 @@ struct ChatPreviewView: View {
}
}
@ViewBuilder func chatItemContentPreview(_ chat: Chat, _ ci: ChatItem) -> some View {
let mc = ci.content.msgContent
switch mc {
case let .link(_, preview):
smallContentPreview(size: dynamicMediaSize) {
ZStack(alignment: .topTrailing) {
Image(uiImage: UIImage(base64Encoded: preview.image) ?? UIImage(systemName: "arrow.up.right")!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: dynamicMediaSize, height: dynamicMediaSize)
ZStack {
Image(systemName: "arrow.up.right")
.resizable()
.foregroundColor(Color.white)
.font(.system(size: 15, weight: .black))
.frame(width: 8, height: 8)
}
.frame(width: 16, height: 16)
.background(Color.black.opacity(0.25))
.cornerRadius(8)
}
.onTapGesture {
UIApplication.shared.open(preview.uri)
}
}
case let .image(_, image):
smallContentPreview(size: dynamicMediaSize) {
CIImageView(chatItem: ci, preview: UIImage(base64Encoded: image), maxWidth: dynamicMediaSize, smallView: true, showFullScreenImage: $showFullscreenGallery)
.environmentObject(ReverseListScrollModel<ChatItem>())
}
case let .video(_,image, duration):
smallContentPreview(size: dynamicMediaSize) {
CIVideoView(chatItem: ci, preview: UIImage(base64Encoded: image), duration: duration, maxWidth: dynamicMediaSize, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery)
.environmentObject(ReverseListScrollModel<ChatItem>())
}
case let .voice(_, duration):
smallContentPreviewVoice(size: dynamicMediaSize) {
CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: Binding.constant(true), smallViewSize: dynamicMediaSize)
}
case .file:
smallContentPreviewFile(size: dynamicMediaSize) {
CIFileView(file: ci.file, edited: ci.meta.itemEdited, smallViewSize: dynamicMediaSize)
}
default: EmptyView()
}
}
@ViewBuilder private func groupInvitationPreviewText(_ groupInfo: GroupInfo) -> some View {
groupInfo.membership.memberIncognito
? chatPreviewInfoText("join as \(groupInfo.membership.memberProfile.displayName)")
@@ -294,10 +413,45 @@ struct ChatPreviewView: View {
}
}
func smallContentPreview(size: CGFloat, _ view: @escaping () -> some View) -> some View {
view()
.frame(width: size, height: size)
.cornerRadius(8)
.overlay(RoundedRectangle(cornerSize: CGSize(width: 8, height: 8))
.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true))
.padding(.vertical, size / 6)
.padding(.leading, 3)
.offset(x: 6)
}
func smallContentPreviewVoice(size: CGFloat, _ view: @escaping () -> some View) -> some View {
view()
.frame(height: voiceMessageSizeBasedOnSquareSize(size))
.padding(.vertical, size / 6)
.padding(.leading, 8)
}
func smallContentPreviewFile(size: CGFloat, _ view: @escaping () -> some View) -> some View {
view()
.frame(width: size, height: size)
.padding(.vertical, size / 7)
.padding(.leading, 5)
}
func unreadCountText(_ n: Int) -> Text {
Text(n > 999 ? "\(n / 1000)k" : n > 0 ? "\(n)" : "")
}
private struct ActiveContentPreview: Equatable {
var chat: Chat
var ci: ChatItem
var mc: MsgContent
static func == (lhs: ActiveContentPreview, rhs: ActiveContentPreview) -> Bool {
lhs.chat.id == rhs.chat.id && lhs.ci.id == rhs.ci.id && lhs.mc == rhs.mc
}
}
struct ChatPreviewView_Previews: PreviewProvider {
static var previews: some View {
Group {
@@ -21,7 +21,7 @@ struct ContactConnectionInfo: View {
enum CCInfoAlert: Identifiable {
case deleteInvitationAlert
case error(title: LocalizedStringKey, error: LocalizedStringKey)
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -102,7 +102,7 @@ struct ContactConnectionInfo: View {
} success: {
dismiss()
}
case let .error(title, error): return Alert(title: Text(title), message: Text(error))
case let .error(title, error): return mkAlert(title: title, message: error)
}
}
.onAppear {
@@ -12,12 +12,13 @@ import SimpleXChat
struct ContactRequestView: View {
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
var contactRequest: UserContactRequest
@ObservedObject var chat: Chat
var body: some View {
HStack(spacing: 8) {
ChatInfoImage(chat: chat, size: 63)
ChatInfoImage(chat: chat, size: dynamicSizes[userFont]?.profileImageSize ?? 63)
.padding(.leading, 4)
VStack(alignment: .leading, spacing: 0) {
HStack(alignment: .top) {
@@ -12,11 +12,12 @@ import SimpleXChat
struct ServersSummaryView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@Binding var serversSummary: PresentedServersSummary?
@State private var serversSummary: PresentedServersSummary? = nil
@State private var selectedUserCategory: PresentedUserCategory = .allUsers
@State private var selectedServerType: PresentedServerType = .smp
@State private var selectedSMPServer: String? = nil
@State private var selectedXFTPServer: String? = nil
@State private var timer: Timer? = nil
@State private var alert: SomeAlert?
@AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false
@@ -47,10 +48,36 @@ struct ServersSummaryView: View {
if m.users.filter({ u in u.user.activeUser || !u.user.hidden }).count == 1 {
selectedUserCategory = .currentUser
}
getServersSummary()
startTimer()
}
.onDisappear {
stopTimer()
}
.alert(item: $alert) { $0.alert }
}
private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
if AppChatState.shared.value == .active {
getServersSummary()
}
}
}
private func getServersSummary() {
do {
serversSummary = try getAgentServersSummary()
} catch let error {
logger.error("getAgentServersSummary error: \(responseError(error))")
}
}
private func stopTimer() {
timer?.invalidate()
timer = nil
}
private func shareButton() -> some View {
Button {
if let serversSummary = serversSummary {
@@ -75,8 +102,8 @@ struct ServersSummaryView: View {
Group {
if m.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 {
Picker("User selection", selection: $selectedUserCategory) {
Text("All users").tag(PresentedUserCategory.allUsers)
Text("Current user").tag(PresentedUserCategory.currentUser)
Text("All profiles").tag(PresentedUserCategory.allUsers)
Text("Current profile").tag(PresentedUserCategory.currentUser)
}
.pickerStyle(.segmented)
}
@@ -176,12 +203,13 @@ struct ServersSummaryView: View {
Section {
infoRow("Active connections", numOrDash(totals.subs.ssActive))
infoRow("Total", numOrDash(totals.subs.total))
Toggle("Show percentage", isOn: $showSubscriptionPercentage)
} header: {
HStack {
Text("Message reception")
SubscriptionStatusIndicatorView(subs: totals.subs, sess: totals.sessions)
SubscriptionStatusIndicatorView(subs: totals.subs, hasSess: totals.sessions.hasSess)
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: totals.subs, sess: totals.sessions)
SubscriptionStatusPercentageView(subs: totals.subs, hasSess: totals.sessions.hasSess)
}
}
}
@@ -259,9 +287,9 @@ struct ServersSummaryView: View {
if let subs = srvSumm.subs {
Spacer()
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: subs, sess: srvSumm.sessionsOrNew)
SubscriptionStatusPercentageView(subs: subs, hasSess: srvSumm.sessionsOrNew.hasSess)
}
SubscriptionStatusIndicatorView(subs: subs, sess: srvSumm.sessionsOrNew)
SubscriptionStatusIndicatorView(subs: subs, hasSess: srvSumm.sessionsOrNew.hasSess)
} else if let sess = srvSumm.sessions {
Spacer()
Image(systemName: "arrow.up.circle")
@@ -355,6 +383,7 @@ struct ServersSummaryView: View {
Task {
do {
try await resetAgentServersStats()
getServersSummary()
} catch let error {
alert = SomeAlert(
alert: mkAlert(
@@ -379,11 +408,11 @@ struct ServersSummaryView: View {
struct SubscriptionStatusIndicatorView: View {
@EnvironmentObject var m: ChatModel
var subs: SMPServerSubs
var sess: ServerSessions
var hasSess: Bool
var body: some View {
let onionHosts = networkUseOnionHostsGroupDefault.get()
let (color, variableValue, opacity, _) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, sess)
let (color, variableValue, opacity, _) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, hasSess)
if #available(iOS 16.0, *) {
Image(systemName: "dot.radiowaves.up.forward", variableValue: variableValue)
.foregroundColor(color)
@@ -397,18 +426,18 @@ struct SubscriptionStatusIndicatorView: View {
struct SubscriptionStatusPercentageView: View {
@EnvironmentObject var m: ChatModel
var subs: SMPServerSubs
var sess: ServerSessions
var hasSess: Bool
var body: some View {
let onionHosts = networkUseOnionHostsGroupDefault.get()
let (_, _, _, statusPercent) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, sess)
let (_, _, _, statusPercent) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, hasSess)
Text(verbatim: "\(Int(floor(statusPercent * 100)))%")
.foregroundColor(.secondary)
.font(.caption)
}
}
func subscriptionStatusColorAndPercentage(_ online: Bool, _ onionHosts: OnionHosts, _ subs: SMPServerSubs, _ sess: ServerSessions) -> (Color, Double, Double, Double) {
func subscriptionStatusColorAndPercentage(_ online: Bool, _ onionHosts: OnionHosts, _ subs: SMPServerSubs, _ hasSess: Bool) -> (Color, Double, Double, Double) {
func roundedToQuarter(_ n: Double) -> Double {
n >= 1 ? 1
: n <= 0 ? 0
@@ -423,12 +452,12 @@ func subscriptionStatusColorAndPercentage(_ online: Bool, _ onionHosts: OnionHos
? (
subs.ssActive == 0
? (
sess.ssConnected == 0 ? noConnColorAndPercent : (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
hasSess ? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) : noConnColorAndPercent
)
: ( // ssActive > 0
sess.ssConnected == 0
? (.orange, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) // This would mean implementation error
: (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
hasSess
? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
: (.orange, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) // This would mean implementation error
)
)
: noConnColorAndPercent
@@ -481,9 +510,9 @@ struct SMPServerSummaryView: View {
} header: {
HStack {
Text("Message reception")
SubscriptionStatusIndicatorView(subs: subs, sess: summary.sessionsOrNew)
SubscriptionStatusIndicatorView(subs: subs, hasSess: summary.sessionsOrNew.hasSess)
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: subs, sess: summary.sessionsOrNew)
SubscriptionStatusPercentageView(subs: subs, hasSess: summary.sessionsOrNew.hasSess)
}
}
}
@@ -588,7 +617,7 @@ struct DetailedSMPStatsView: View {
infoRow(Text(verbatim: "NO_MSG errors"), numOrDash(stats._ackNoMsgErrs)).padding(.leading, 24)
infoRow("other errors", numOrDash(stats._ackOtherErrs)).padding(.leading, 24)
}
Section {
Section("Connections") {
infoRow("Created", numOrDash(stats._connCreated))
infoRow("Secured", numOrDash(stats._connCreated))
infoRow("Completed", numOrDash(stats._connCompleted))
@@ -597,8 +626,12 @@ struct DetailedSMPStatsView: View {
infoRowTwoValues("Subscribed", "attempts", stats._connSubscribed, stats._connSubAttempts)
infoRow("Subscriptions ignored", numOrDash(stats._connSubIgnored))
infoRow("Subscription errors", numOrDash(stats._connSubErrs))
}
Section {
infoRowTwoValues("Enabled", "attempts", stats._ntfKey, stats._ntfKeyAttempts)
infoRowTwoValues("Disabled", "attempts", stats._ntfKeyDeleted, stats._ntfKeyDeleteAttempts)
} header: {
Text("Connections")
Text("Connection notifications")
} footer: {
Text("Starting from \(localTimestamp(statsStartedAt)).")
}
@@ -711,7 +744,5 @@ struct DetailedXFTPStatsView: View {
}
#Preview {
ServersSummaryView(
serversSummary: Binding.constant(nil)
)
ServersSummaryView()
}
@@ -64,7 +64,7 @@ struct DatabaseErrorView: View {
case let .migrationError(mtrError):
titleText("Incompatible database version")
fileNameText(dbFile)
Text("Error: ") + Text(DatabaseErrorView.mtrErrorDescription(mtrError))
Text("Error: ") + Text(mtrErrorDescription(mtrError))
}
case let .errorSQL(dbFile, migrationSQLError):
titleText("Database error")
@@ -105,15 +105,6 @@ struct DatabaseErrorView: View {
Text("Migrations: \(ms.joined(separator: ", "))")
}
static func mtrErrorDescription(_ err: MTRError) -> LocalizedStringKey {
switch err {
case let .noDown(dbMigrations):
return "database version is newer than the app, but no down migration for: \(dbMigrations.joined(separator: ", "))"
case let .different(appMigration, dbMigration):
return "different migration in the app/database: \(appMigration) / \(dbMigration)"
}
}
private func databaseKeyField(onSubmit: @escaping () -> Void) -> some View {
PassphraseField(key: $dbKey, placeholder: "Enter passphrase…", valid: validKey(dbKey), onSubmit: onSubmit)
}
@@ -16,18 +16,10 @@ struct ChatInfoImage: View {
var color = Color(uiColor: .tertiarySystemGroupedBackground)
var body: some View {
var iconName: String
switch chat.chatInfo {
case .direct: iconName = "person.crop.circle.fill"
case .group: iconName = "person.2.circle.fill"
case .local: iconName = "folder.circle.fill"
case .contactRequest: iconName = "person.crop.circle.fill"
default: iconName = "circle.fill"
}
let iconColor = if case .local = chat.chatInfo { theme.appColors.primaryVariant2 } else { color }
return ProfileImage(
imageStr: chat.chatInfo.image,
iconName: iconName,
iconName: chatIconName(chat.chatInfo),
size: size,
color: iconColor
)
@@ -23,8 +23,10 @@ struct ChatViewBackground: ViewModifier {
var image = context.resolve(image)
let rect = CGRectMake(0, 0, size.width, size.height)
func repeatDraw(_ imageScale: CGFloat) {
// Prevent range bounds crash and dividing by zero
if size.height == 0 || size.width == 0 || image.size.height == 0 || image.size.width == 0 { return }
image.shading = .color(tint)
let scale = imageScale * 1.57 // for some reason a wallpaper on iOS looks smaller than on Android
let scale = imageScale * 2.5 // scale wallpaper for iOS
for h in 0 ... Int(size.height / image.size.height / scale) {
for w in 0 ... Int(size.width / image.size.width / scale) {
let rect = CGRectMake(CGFloat(w) * image.size.width * scale, CGFloat(h) * image.size.height * scale, image.size.width * scale, image.size.height * scale)
@@ -1,26 +0,0 @@
//
// VideoUtils.swift
// SimpleX (iOS)
//
// Created by Avently on 25.12.2023.
// Copyright © 2023 SimpleX Chat. All rights reserved.
//
import AVFoundation
import Foundation
import SimpleXChat
func makeVideoQualityLower(_ input: URL, outputUrl: URL) async -> Bool {
let asset: AVURLAsset = AVURLAsset(url: input, options: nil)
if let s = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480) {
s.outputURL = outputUrl
s.outputFileType = .mp4
s.metadataItemFilter = AVMetadataItemFilter.forSharing()
await s.export()
if let err = s.error {
logger.error("Failed to export video with error: \(err)")
}
return s.status == .completed
}
return false
}
@@ -17,3 +17,37 @@ extension View {
}
}
}
extension Notification.Name {
static let chatViewWillBeginScrolling = Notification.Name("chatWillBeginScrolling")
}
struct PrivacyBlur: ViewModifier {
var enabled: Bool = true
@Binding var blurred: Bool
@AppStorage(DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) private var blurRadius: Int = 0
func body(content: Content) -> some View {
if blurRadius > 0 {
// parallel ifs are necessary here because otherwise some views flicker,
// e.g. when playing video
content
.blur(radius: blurred && enabled ? CGFloat(blurRadius) * 0.5 : 0)
.overlay {
if (blurred && enabled) {
Color.clear.contentShape(Rectangle())
.onTapGesture {
blurred = false
}
}
}
.onReceive(NotificationCenter.default.publisher(for: .chatViewWillBeginScrolling)) { _ in
if !blurred {
blurred = true
}
}
} else {
content
}
}
}
@@ -662,7 +662,7 @@ private struct PassphraseConfirmationView: View {
if case .chatCmdError(_, .errorDatabase(.errorOpen(.errorNotADatabase))) = error as? ChatResponse {
showErrorOnMigrationIfNeeded(.errorNotADatabase(dbFile: ""), $alert)
} else {
alert = .error(title: "Error", error: NSLocalizedString("Error verifying passphrase:", comment: "") + " " + String(String(describing: error)))
alert = .error(title: "Error", error: NSLocalizedString("Error verifying passphrase:", comment: "") + " " + String(responseError(error)))
}
}
}
@@ -331,7 +331,7 @@ struct MigrateToDevice: View {
case let .migrationError(mtrError):
("Incompatible database version",
nil,
"\(NSLocalizedString("Error: ", comment: "")) \(DatabaseErrorView.mtrErrorDescription(mtrError))",
"\(NSLocalizedString("Error: ", comment: "")) \(mtrErrorDescription(mtrError))",
nil)
}
default: ("Error", nil, "Unknown error", nil)
@@ -37,7 +37,7 @@ struct ConnectDesktopView: View {
case badInvitationError
case badVersionError(version: String?)
case desktopDisconnectedError
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -160,7 +160,7 @@ struct ConnectDesktopView: View {
case .desktopDisconnectedError:
Alert(title: Text("Connection terminated"))
case let .error(title, error):
Alert(title: Text(title), message: Text(error))
mkAlert(title: title, message: error)
}
}
.interactiveDismissDisabled(m.activeRemoteCtrl)
@@ -32,6 +32,7 @@ extension AppSettings {
if let val = privacyShowChatPreviews { def.setValue(val, forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) }
if let val = privacySaveLastDraft { def.setValue(val, forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT) }
if let val = privacyProtectScreen { def.setValue(val, forKey: DEFAULT_PRIVACY_PROTECT_SCREEN) }
if let val = privacyMediaBlurRadius { def.setValue(val, forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) }
if let val = notificationMode { ChatModel.shared.notificationMode = val.toNotificationsMode() }
if let val = notificationPreviewMode { ntfPreviewModeGroupDefault.set(val) }
if let val = webrtcPolicyRelay { def.setValue(val, forKey: DEFAULT_WEBRTC_POLICY_RELAY) }
@@ -62,6 +63,7 @@ extension AppSettings {
c.privacyShowChatPreviews = def.bool(forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS)
c.privacySaveLastDraft = def.bool(forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT)
c.privacyProtectScreen = def.bool(forKey: DEFAULT_PRIVACY_PROTECT_SCREEN)
c.privacyMediaBlurRadius = def.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS)
c.notificationMode = AppSettingsNotificationMode.from(ChatModel.shared.notificationMode)
c.notificationPreviewMode = ntfPreviewModeGroupDefault.get()
c.webrtcPolicyRelay = def.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
@@ -32,7 +32,6 @@ struct NetworkAndServers: View {
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
@AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false
@State private var cfgLoaded = false
@State private var currentNetCfg = NetCfg.defaults
@State private var netCfg = NetCfg.defaults
@@ -62,8 +61,6 @@ struct NetworkAndServers: View {
Text("XFTP servers")
}
Toggle("Subscription percentage", isOn: $showSubscriptionPercentage)
Picker("Use .onion hosts", selection: $onionHosts) {
ForEach(OnionHosts.values, id: \.self) { Text($0.text) }
}
@@ -22,6 +22,7 @@ struct PrivacySettings: View {
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
@State private var currentLAMode = privacyLocalAuthModeDefault.get()
@AppStorage(DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) private var privacyMediaBlurRadius: Int = 0
@State private var contactReceipts = false
@State private var contactReceiptsReset = false
@State private var contactReceiptsOverrides = 0
@@ -113,6 +114,22 @@ struct PrivacySettings: View {
privacyAcceptImagesGroupDefault.set($0)
}
}
settingsRow("circle.rectangle.filled.pattern.diagonalline", color: theme.colors.secondary) {
Picker("Blur media", selection: $privacyMediaBlurRadius) {
let values = [0, 12, 24, 48] + ([0, 12, 24, 48].contains(privacyMediaBlurRadius) ? [] : [privacyMediaBlurRadius])
ForEach(values, id: \.self) { radius in
let text: String = switch radius {
case 0: NSLocalizedString("Off", comment: "blur media")
case 12: NSLocalizedString("Soft", comment: "blur media")
case 24: NSLocalizedString("Medium", comment: "blur media")
case 48: NSLocalizedString("Strong", comment: "blur media")
default: "\(radius)"
}
Text(text)
}
}
}
.frame(height: 36)
settingsRow("network.badge.shield.half.filled", color: theme.colors.secondary) {
Toggle("Protect IP address", isOn: $askToApproveRelays)
}
@@ -175,10 +175,6 @@ func testServerConnection(server: Binding<ServerCfg>) async -> ProtocolTestFailu
}
}
func serverHostname(_ srv: String) -> String {
parseServerAddress(srv)?.hostnames.first ?? srv
}
struct ProtocolServerView_Previews: PreviewProvider {
static var previews: some View {
ProtocolServerView(
@@ -34,6 +34,7 @@ let DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS = "privacyShowChatPreviews"
let DEFAULT_PRIVACY_SAVE_LAST_DRAFT = "privacySaveLastDraft"
let DEFAULT_PRIVACY_PROTECT_SCREEN = "privacyProtectScreen"
let DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET = "privacyDeliveryReceiptsSet"
let DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS = "privacyMediaBlurRadius"
let DEFAULT_EXPERIMENTAL_CALLS = "experimentalCalls"
let DEFAULT_CHAT_ARCHIVE_NAME = "chatArchiveName"
let DEFAULT_CHAT_ARCHIVE_TIME = "chatArchiveTime"
@@ -87,6 +88,7 @@ let appDefaults: [String: Any] = [
DEFAULT_PRIVACY_SAVE_LAST_DRAFT: true,
DEFAULT_PRIVACY_PROTECT_SCREEN: false,
DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET: false,
DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS: 0,
DEFAULT_EXPERIMENTAL_CALLS: false,
DEFAULT_CHAT_V3_DB_MIGRATION: V3DBMigrationState.offer.rawValue,
DEFAULT_DEVELOPER_TOOLS: false,
@@ -30,7 +30,7 @@ struct UserAddressView: View {
case deleteAddress
case profileAddress(on: Bool)
case shareOnCreate
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -185,7 +185,7 @@ struct UserAddressView: View {
}, secondaryButton: .cancel()
)
case let .error(title, error):
return Alert(title: Text(title), message: Text(error))
return mkAlert(title: title, message: error)
}
}
}
@@ -30,7 +30,7 @@ struct UserProfilesView: View {
case hiddenProfilesNotice
case muteProfileAlert
case activateUserError(error: String)
case error(title: LocalizedStringKey, error: LocalizedStringKey = "")
case error(title: LocalizedStringKey, error: LocalizedStringKey?)
var id: String {
switch self {
@@ -172,7 +172,7 @@ struct UserProfilesView: View {
message: Text(err)
)
case let .error(title, error):
return Alert(title: Text(title), message: Text(error))
return mkAlert(title: title, message: error)
}
}
}
@@ -709,8 +709,8 @@
<target>Всички нови съобщения от %@ ще бъдат скрити!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1705,8 +1705,8 @@ This is your own one-time link!</source>
<target>Текуща парола…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5639,6 +5639,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5779,6 +5783,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Показване на визуализация</target>
@@ -6002,10 +6010,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7060,8 +7064,8 @@ Repeat join request?</source>
<target>Можете да го направите видим за вашите контакти в SimpleX чрез Настройки.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Вече можете да изпращате съобщения до %@</target>
<note>notification body</note>
</trans-unit>
@@ -7468,7 +7472,7 @@ SimpleX сървърите не могат да видят вашия профи
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>блокиран от админ</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -687,8 +687,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1635,8 +1635,8 @@ This is your own one-time link!</source>
<target>Aktuální přístupová fráze…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5437,6 +5437,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5572,6 +5576,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Zobrazení náhledu</target>
@@ -5789,10 +5797,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6793,8 +6797,8 @@ Repeat join request?</source>
<source>You can make it visible to your SimpleX contacts via Settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Nyní můžete posílat zprávy %@</target>
<note>notification body</note>
</trans-unit>
@@ -7187,7 +7191,7 @@ Servery SimpleX nevidí váš profil.</target>
</trans-unit>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -554,6 +554,7 @@
</trans-unit>
<trans-unit id="Accent" xml:space="preserve">
<source>Accent</source>
<target>Akzent</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Accept" xml:space="preserve">
@@ -579,10 +580,12 @@
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
<target>Bestätigt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Acknowledgement errors" xml:space="preserve">
<source>Acknowledgement errors</source>
<target>Fehler bei der Bestätigung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
@@ -631,14 +634,17 @@
</trans-unit>
<trans-unit id="Additional accent" xml:space="preserve">
<source>Additional accent</source>
<target>Erste Akzentfarbe</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Additional accent 2" xml:space="preserve">
<source>Additional accent 2</source>
<target>Zusätzlicher Akzent 2</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Additional secondary" xml:space="preserve">
<source>Additional secondary</source>
<target>Zweite Akzentfarbe</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Address" xml:space="preserve">
@@ -668,6 +674,7 @@
</trans-unit>
<trans-unit id="Advanced settings" xml:space="preserve">
<source>Advanced settings</source>
<target>Erweiterte Einstellungen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All app data is deleted." xml:space="preserve">
@@ -677,7 +684,7 @@
</trans-unit>
<trans-unit id="All chats and messages will be deleted - this cannot be undone!" xml:space="preserve">
<source>All chats and messages will be deleted - this cannot be undone!</source>
<target>Alle Chats und Nachrichten werden gelöscht! Dies kann nicht rückgängig gemacht werden!</target>
<target>Alle Chats und Nachrichten werden gelöscht. Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All data is erased when it is entered." xml:space="preserve">
@@ -687,6 +694,7 @@
</trans-unit>
<trans-unit id="All data is private to your device." xml:space="preserve">
<source>All data is private to your device.</source>
<target>Alle Daten sind auf Ihrem Gerät geschützt.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All group members will remain connected." xml:space="preserve">
@@ -696,12 +704,12 @@
</trans-unit>
<trans-unit id="All messages will be deleted - this cannot be undone!" xml:space="preserve">
<source>All messages will be deleted - this cannot be undone!</source>
<target>Es werden alle Nachrichten gelöscht. Dieser Vorgang kann nicht rückgängig gemacht werden!</target>
<target>Es werden alle Nachrichten gelöscht. Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." xml:space="preserve">
<source>All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you.</source>
<target>Alle Nachrichten werden gelöscht - dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht.</target>
<target>Alle Nachrichten werden gelöscht . Dies kann nicht rückgängig gemacht werden! Die Nachrichten werden NUR bei Ihnen gelöscht.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All new messages from %@ will be hidden!" xml:space="preserve">
@@ -709,8 +717,9 @@
<target>Von %@ werden alle neuen Nachrichten ausgeblendet!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Alle Profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -915,6 +924,7 @@
</trans-unit>
<trans-unit id="Apply to" xml:space="preserve">
<source>Apply to</source>
<target>Anwenden auf</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive and upload" xml:space="preserve">
@@ -994,6 +1004,7 @@
</trans-unit>
<trans-unit id="Background" xml:space="preserve">
<source>Background</source>
<target>Hintergrund-Farbe</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve">
@@ -1023,6 +1034,7 @@
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<target>Schwarz</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block" xml:space="preserve">
@@ -1137,6 +1149,7 @@
</trans-unit>
<trans-unit id="Cannot forward message" xml:space="preserve">
<source>Cannot forward message</source>
<target>Die Nachricht kann nicht weitergeleitet werden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cannot receive file" xml:space="preserve">
@@ -1212,6 +1225,7 @@
</trans-unit>
<trans-unit id="Chat colors" xml:space="preserve">
<source>Chat colors</source>
<target>Chat-Farben</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat console" xml:space="preserve">
@@ -1261,6 +1275,7 @@
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Chat-Design</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
@@ -1295,14 +1310,17 @@
</trans-unit>
<trans-unit id="Chunks deleted" xml:space="preserve">
<source>Chunks deleted</source>
<target>Daten-Pakete gelöscht</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chunks downloaded" xml:space="preserve">
<source>Chunks downloaded</source>
<target>Daten-Pakete heruntergeladen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chunks uploaded" xml:space="preserve">
<source>Chunks uploaded</source>
<target>Daten-Pakete hochgeladen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Clear" xml:space="preserve">
@@ -1332,6 +1350,7 @@
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<target>Farbvariante</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Compare file" xml:space="preserve">
@@ -1346,6 +1365,7 @@
</trans-unit>
<trans-unit id="Completed" xml:space="preserve">
<source>Completed</source>
<target>Abgeschlossen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure ICE servers" xml:space="preserve">
@@ -1463,6 +1483,7 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Connected" xml:space="preserve">
<source>Connected</source>
<target>Verbunden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve">
@@ -1472,6 +1493,7 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Connected servers" xml:space="preserve">
<source>Connected servers</source>
<target>Verbundene Server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve">
@@ -1481,6 +1503,7 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Connecting" xml:space="preserve">
<source>Connecting</source>
<target>Verbinden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve">
@@ -1530,10 +1553,12 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Connection with desktop stopped" xml:space="preserve">
<source>Connection with desktop stopped</source>
<target>Die Verbindung mit dem Desktop wurde gestoppt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections" xml:space="preserve">
<source>Connections</source>
<target>Verbindungen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
@@ -1593,6 +1618,7 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Copy error" xml:space="preserve">
<source>Copy error</source>
<target>Fehlermeldung kopieren</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Core version: v%@" xml:space="preserve">
@@ -1672,6 +1698,7 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Created" xml:space="preserve">
<source>Created</source>
<target>Erstellt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Created at" xml:space="preserve">
@@ -1709,8 +1736,9 @@ Das ist Ihr eigener Einmal-Link!</target>
<target>Aktuelles Passwort…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<target>Aktueller Profil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1725,6 +1753,7 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Customize theme" xml:space="preserve">
<source>Customize theme</source>
<target>Design anpassen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dark" xml:space="preserve">
@@ -1734,6 +1763,7 @@ Das ist Ihr eigener Einmal-Link!</target>
</trans-unit>
<trans-unit id="Dark mode colors" xml:space="preserve">
<source>Dark mode colors</source>
<target>Farben für die dunkle Variante</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Database ID" xml:space="preserve">
@@ -1923,7 +1953,7 @@ Das ist Ihr eigener Einmal-Link!</target>
<source>Delete contact?
This cannot be undone!</source>
<target>Kontakt löschen?
Das kann nicht rückgängig gemacht werden!</target>
Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete database" xml:space="preserve">
@@ -2023,7 +2053,7 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Delete pending connection?" xml:space="preserve">
<source>Delete pending connection?</source>
<target>Die ausstehende Verbindung löschen?</target>
<target>Ausstehende Verbindung löschen?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delete profile" xml:space="preserve">
@@ -2043,6 +2073,7 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<target>Gelöscht</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted at" xml:space="preserve">
@@ -2057,6 +2088,7 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Deletion errors" xml:space="preserve">
<source>Deletion errors</source>
<target>Fehler beim Löschen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delivery" xml:space="preserve">
@@ -2101,10 +2133,12 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Detailed statistics" xml:space="preserve">
<source>Detailed statistics</source>
<target>Detaillierte Statistiken</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Details" xml:space="preserve">
<source>Details</source>
<target>Details</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Develop" xml:space="preserve">
@@ -2264,6 +2298,7 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Download errors" xml:space="preserve">
<source>Download errors</source>
<target>Fehler beim Herunterladen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve">
@@ -2278,10 +2313,12 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Downloaded" xml:space="preserve">
<source>Downloaded</source>
<target>Heruntergeladen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Downloaded files" xml:space="preserve">
<source>Downloaded files</source>
<target>Heruntergeladene Dateien</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Downloading archive" xml:space="preserve">
@@ -2656,6 +2693,7 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Error exporting theme: %@" xml:space="preserve">
<source>Error exporting theme: %@</source>
<target>Fehler beim Exportieren des Designs: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error importing chat database" xml:space="preserve">
@@ -2685,10 +2723,12 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Error reconnecting server" xml:space="preserve">
<source>Error reconnecting server</source>
<target>Fehler beim Wiederherstellen der Verbindung zum Server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reconnecting servers" xml:space="preserve">
<source>Error reconnecting servers</source>
<target>Fehler beim Wiederherstellen der Verbindungen zu den Servern</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error removing member" xml:space="preserve">
@@ -2698,6 +2738,7 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Error resetting statistics" xml:space="preserve">
<source>Error resetting statistics</source>
<target>Fehler beim Zurücksetzen der Statistiken</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error saving %@ servers" xml:space="preserve">
@@ -2833,6 +2874,7 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Errors" xml:space="preserve">
<source>Errors</source>
<target>Fehler</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Even when disabled in the conversation." xml:space="preserve">
@@ -2862,6 +2904,7 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="Export theme" xml:space="preserve">
<source>Export theme</source>
<target>Design exportieren</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Exported database archive." xml:space="preserve">
@@ -2901,22 +2944,27 @@ Das kann nicht rückgängig gemacht werden!</target>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
<target>Datei-Fehler</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="File not found - most likely file was deleted or cancelled." xml:space="preserve">
<source>File not found - most likely file was deleted or cancelled.</source>
<target>Datei nicht gefunden - höchstwahrscheinlich wurde die Datei gelöscht oder der Transfer abgebrochen.</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="File server error: %@" xml:space="preserve">
<source>File server error: %@</source>
<target>Datei-Server Fehler: %@</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="File status" xml:space="preserve">
<source>File status</source>
<target>Datei-Status</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="File status: %@" xml:space="preserve">
<source>File status: %@</source>
<target>Datei-Status: %@</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="File will be deleted from servers." xml:space="preserve">
@@ -3110,10 +3158,12 @@ Fehler: %2$@</target>
</trans-unit>
<trans-unit id="Good afternoon!" xml:space="preserve">
<source>Good afternoon!</source>
<target>Guten Nachmittag!</target>
<note>message preview</note>
</trans-unit>
<trans-unit id="Good morning!" xml:space="preserve">
<source>Good morning!</source>
<target>Guten Morgen!</target>
<note>message preview</note>
</trans-unit>
<trans-unit id="Group" xml:space="preserve">
@@ -3238,12 +3288,12 @@ Fehler: %2$@</target>
</trans-unit>
<trans-unit id="Group will be deleted for all members - this cannot be undone!" xml:space="preserve">
<source>Group will be deleted for all members - this cannot be undone!</source>
<target>Die Gruppe wird für alle Mitglieder gelöscht - dies kann nicht rückgängig gemacht werden!</target>
<target>Die Gruppe wird für alle Mitglieder gelöscht. Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group will be deleted for you - this cannot be undone!" xml:space="preserve">
<source>Group will be deleted for you - this cannot be undone!</source>
<target>Die Gruppe wird für Sie gelöscht - dies kann nicht rückgängig gemacht werden!</target>
<target>Die Gruppe wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Help" xml:space="preserve">
@@ -3398,6 +3448,7 @@ Fehler: %2$@</target>
</trans-unit>
<trans-unit id="Import theme" xml:space="preserve">
<source>Import theme</source>
<target>Design importieren</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Importing archive" xml:space="preserve">
@@ -3524,6 +3575,7 @@ Fehler: %2$@</target>
</trans-unit>
<trans-unit id="Interface colors" xml:space="preserve">
<source>Interface colors</source>
<target>Interface-Farben</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invalid QR code" xml:space="preserve">
@@ -3871,6 +3923,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Mitglied inaktiv</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
@@ -3885,11 +3938,12 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Member will be removed from group - this cannot be undone!" xml:space="preserve">
<source>Member will be removed from group - this cannot be undone!</source>
<target>Das Mitglied wird aus der Gruppe entfernt - dies kann nicht rückgängig gemacht werden!</target>
<target>Das Mitglied wird aus der Gruppe entfernt. Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Menus" xml:space="preserve">
<source>Menus</source>
<target>Menüs</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message delivery error" xml:space="preserve">
@@ -3914,10 +3968,12 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<target>Nachricht weitergeleitet</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<target>Die Nachricht kann später zugestellt werden, wenn das Mitglied aktiv wird.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
@@ -3961,10 +4017,12 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Message status" xml:space="preserve">
<source>Message status</source>
<target>Nachrichten-Status</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message status: %@" xml:space="preserve">
<source>Message status: %@</source>
<target>Nachrichten-Status: %@</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
@@ -3994,10 +4052,12 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Messages received" xml:space="preserve">
<source>Messages received</source>
<target>Empfangene Nachrichten</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Messages sent" xml:space="preserve">
<source>Messages sent</source>
<target>Gesendete Nachrichten</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
@@ -4237,6 +4297,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<target>Bisher keine direkte Verbindung. Nachricht wird von einem Admin weitergeleitet.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
@@ -4256,6 +4317,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="No info, try to reload" xml:space="preserve">
<source>No info, try to reload</source>
<target>Keine Information - es wird versucht neu zu laden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
@@ -4444,6 +4506,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Open server settings" xml:space="preserve">
<source>Open server settings</source>
<target>Server-Einstellungen öffnen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Open user profiles" xml:space="preserve">
@@ -4557,6 +4620,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Pending" xml:space="preserve">
<source>Pending</source>
<target>Ausstehend</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
@@ -4587,6 +4651,8 @@ Das ist Ihr Link für die Gruppe %@!</target>
<trans-unit id="Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.&#10;Please share any other issues with the developers." xml:space="preserve">
<source>Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.
Please share any other issues with the developers.</source>
<target>Bitte überprüfen Sie, ob sich das Mobiltelefon und die Desktop-App im gleichen lokalen Netzwerk befinden, und die Desktop-Firewall die Verbindung erlaubt.
Bitte teilen Sie weitere mögliche Probleme den Entwicklern mit.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please check that you used the correct link or ask your contact to send you another one." xml:space="preserve">
@@ -4692,6 +4758,7 @@ Fehler: %@</target>
</trans-unit>
<trans-unit id="Previously connected servers" xml:space="preserve">
<source>Previously connected servers</source>
<target>Bisher verbundene Server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Privacy &amp; security" xml:space="preserve">
@@ -4765,6 +4832,7 @@ Fehler: %@</target>
</trans-unit>
<trans-unit id="Profile theme" xml:space="preserve">
<source>Profile theme</source>
<target>Profil-Design</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile update will be sent to your contacts." xml:space="preserve">
@@ -4851,10 +4919,12 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Proxied" xml:space="preserve">
<source>Proxied</source>
<target>Proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Proxied servers" xml:space="preserve">
<source>Proxied servers</source>
<target>Proxy-Server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Push notifications" xml:space="preserve">
@@ -4924,6 +4994,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Receive errors" xml:space="preserve">
<source>Receive errors</source>
<target>Fehler beim Empfang</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received at" xml:space="preserve">
@@ -4948,14 +5019,17 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Received messages" xml:space="preserve">
<source>Received messages</source>
<target>Empfangene Nachrichten</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received reply" xml:space="preserve">
<source>Received reply</source>
<target>Empfangene Antwort</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received total" xml:space="preserve">
<source>Received total</source>
<target>Summe aller empfangenen Nachrichten</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving address will be changed to a different server. Address change will complete after sender comes online." xml:space="preserve">
@@ -4990,6 +5064,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Reconnect" xml:space="preserve">
<source>Reconnect</source>
<target>Neu verbinden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect all connected servers to force message delivery. It uses additional traffic." xml:space="preserve">
@@ -4999,18 +5074,22 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Reconnect all servers" xml:space="preserve">
<source>Reconnect all servers</source>
<target>Alle Server neu verbinden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect all servers?" xml:space="preserve">
<source>Reconnect all servers?</source>
<target>Alle Server neu verbinden?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect server to force message delivery. It uses additional traffic." xml:space="preserve">
<source>Reconnect server to force message delivery. It uses additional traffic.</source>
<target>Um die Auslieferung von Nachrichten zu erzwingen, wird der Server neu verbunden. Dafür wird weiterer Datenverkehr benötigt.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect server?" xml:space="preserve">
<source>Reconnect server?</source>
<target>Server neu verbinden?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect servers?" xml:space="preserve">
@@ -5065,6 +5144,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Remove image" xml:space="preserve">
<source>Remove image</source>
<target>Bild entfernen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove member" xml:space="preserve">
@@ -5139,10 +5219,12 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<target>Alle Statistiken zurücksetzen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics?" xml:space="preserve">
<source>Reset all statistics?</source>
<target>Alle Statistiken zurücksetzen?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset colors" xml:space="preserve">
@@ -5152,6 +5234,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Reset to app theme" xml:space="preserve">
<source>Reset to app theme</source>
<target>Auf das App-Design zurücksetzen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset to defaults" xml:space="preserve">
@@ -5161,6 +5244,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Reset to user theme" xml:space="preserve">
<source>Reset to user theme</source>
<target>Auf das Benutzer-spezifische Design zurücksetzen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Restart the app to create a new chat profile" xml:space="preserve">
@@ -5235,6 +5319,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="SMP server" xml:space="preserve">
<source>SMP server</source>
<target>SMP-Server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
@@ -5354,10 +5439,12 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Scale" xml:space="preserve">
<source>Scale</source>
<target>Skalieren</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scan / Paste link" xml:space="preserve">
<source>Scan / Paste link</source>
<target>Link scannen / einfügen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scan QR code" xml:space="preserve">
@@ -5402,6 +5489,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Secondary" xml:space="preserve">
<source>Secondary</source>
<target>Zweite Farbe</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Secure queue" xml:space="preserve">
@@ -5411,6 +5499,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Secured" xml:space="preserve">
<source>Secured</source>
<target>Abgesichert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Security assessment" xml:space="preserve">
@@ -5430,6 +5519,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Selected chat preferences prohibit this message." xml:space="preserve">
<source>Selected chat preferences prohibit this message.</source>
<target>Diese Nachricht ist wegen der gewählten Chat-Einstellungen nicht erlaubt.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Self-destruct" xml:space="preserve">
@@ -5484,6 +5574,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Send errors" xml:space="preserve">
<source>Send errors</source>
<target>Fehler beim Senden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send link previews" xml:space="preserve">
@@ -5598,6 +5689,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Sent directly" xml:space="preserve">
<source>Sent directly</source>
<target>Direkt gesendet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent file event" xml:space="preserve">
@@ -5612,6 +5704,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Sent messages" xml:space="preserve">
<source>Sent messages</source>
<target>Gesendete Nachrichten</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent messages will be deleted after set time." xml:space="preserve">
@@ -5621,18 +5714,22 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Sent reply" xml:space="preserve">
<source>Sent reply</source>
<target>Gesendete Antwort</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent total" xml:space="preserve">
<source>Sent total</source>
<target>Summe aller gesendeten Nachrichten</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent via proxy" xml:space="preserve">
<source>Sent via proxy</source>
<target>Über einen Proxy gesendet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Server-Adresse</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings." xml:space="preserve">
@@ -5661,6 +5758,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Server type" xml:space="preserve">
<source>Server type</source>
<target>Server-Typ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings." xml:space="preserve">
@@ -5668,6 +5766,10 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
<target>Die Server-Version ist nicht mit den Netzwerk-Einstellungen kompatibel.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5679,10 +5781,12 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Servers info" xml:space="preserve">
<source>Servers info</source>
<target>Server-Informationen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers statistics will be reset - this cannot be undone!" xml:space="preserve">
<source>Servers statistics will be reset - this cannot be undone!</source>
<target>Die Serverstatistiken werden zurückgesetzt. Dies kann nicht rückgängig gemacht werden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Session code" xml:space="preserve">
@@ -5702,6 +5806,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Set default theme" xml:space="preserve">
<source>Set default theme</source>
<target>Default-Design einstellen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set group preferences" xml:space="preserve">
@@ -5809,6 +5914,10 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
<target>Nachrichtenstatus anzeigen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Vorschau anzeigen</target>
@@ -5826,6 +5935,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="SimpleX" xml:space="preserve">
<source>SimpleX</source>
<target>SimpleX</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX Address" xml:space="preserve">
@@ -5905,6 +6015,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Size" xml:space="preserve">
<source>Size</source>
<target>Größe</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Skip" xml:space="preserve">
@@ -5954,10 +6065,12 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Starting from %@." xml:space="preserve">
<source>Starting from %@.</source>
<target>Beginnend mit %@.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Statistics" xml:space="preserve">
<source>Statistics</source>
<target>Statistiken</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stop" xml:space="preserve">
@@ -6027,18 +6140,17 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Subscribed" xml:space="preserve">
<source>Subscribed</source>
<target>Abonniert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription errors" xml:space="preserve">
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<target>Fehler beim Abonnieren</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<target>Nicht beachtete Abonnements</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Support SimpleX Chat" xml:space="preserve">
@@ -6123,6 +6235,7 @@ Aktivieren Sie es in den *Netzwerk &amp; Server* Einstellungen.</target>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<target>Temporärer Datei-Fehler</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Test failed at step %@." xml:space="preserve">
@@ -6264,6 +6377,7 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro
</trans-unit>
<trans-unit id="Themes" xml:space="preserve">
<source>Themes</source>
<target>Design</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="These settings are for your current profile **%@**." xml:space="preserve">
@@ -6333,6 +6447,7 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro
</trans-unit>
<trans-unit id="This link was used with another mobile device, please create a new link on the desktop." xml:space="preserve">
<source>This link was used with another mobile device, please create a new link on the desktop.</source>
<target>Dieser Link wurde schon mit einem anderen Mobiltelefon genutzt. Bitte erstellen sie einen neuen Link in der Desktop-App.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This setting applies to messages in your current chat profile **%@**." xml:space="preserve">
@@ -6342,6 +6457,7 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro
</trans-unit>
<trans-unit id="Title" xml:space="preserve">
<source>Title</source>
<target>Bezeichnung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To ask any questions and to receive updates:" xml:space="preserve">
@@ -6413,6 +6529,7 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<target>Summe aller Abonnements</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Transport isolation" xml:space="preserve">
@@ -6422,6 +6539,7 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt
</trans-unit>
<trans-unit id="Transport sessions" xml:space="preserve">
<source>Transport sessions</source>
<target>Transport-Sitzungen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Trying to connect to the server used to receive messages from this contact (error: %@)." xml:space="preserve">
@@ -6618,6 +6736,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit>
<trans-unit id="Upload errors" xml:space="preserve">
<source>Upload errors</source>
<target>Fehler beim Hochladen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upload failed" xml:space="preserve">
@@ -6632,10 +6751,12 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit>
<trans-unit id="Uploaded" xml:space="preserve">
<source>Uploaded</source>
<target>Hochgeladen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Uploaded files" xml:space="preserve">
<source>Uploaded files</source>
<target>Hochgeladene Dateien</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Uploading archive" xml:space="preserve">
@@ -6715,6 +6836,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Benutzer-Auswahl</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
@@ -6854,10 +6976,12 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit>
<trans-unit id="Wallpaper accent" xml:space="preserve">
<source>Wallpaper accent</source>
<target>Wallpaper-Akzent</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wallpaper background" xml:space="preserve">
<source>Wallpaper background</source>
<target>Wallpaper-Hintergrund</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Warning: starting chat on multiple devices is not supported and will cause message delivery failures" xml:space="preserve">
@@ -6967,6 +7091,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit>
<trans-unit id="Wrong key or unknown file chunk address - most likely file is deleted." xml:space="preserve">
<source>Wrong key or unknown file chunk address - most likely file is deleted.</source>
<target>Falscher Schlüssel oder unbekannte Daten-Paketadresse der Datei - höchstwahrscheinlich wurde die Datei gelöscht.</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="Wrong passphrase!" xml:space="preserve">
@@ -6976,6 +7101,7 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
</trans-unit>
<trans-unit id="XFTP server" xml:space="preserve">
<source>XFTP server</source>
<target>XFTP-Server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
@@ -7062,6 +7188,7 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="You are not connected to these servers. Private routing is used to deliver messages to them." xml:space="preserve">
<source>You are not connected to these servers. Private routing is used to deliver messages to them.</source>
<target>Sie sind nicht mit diesen Servern verbunden. Zur Auslieferung von Nachrichten an diese Server wird privates Routing genutzt.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can accept calls from lock screen, without device and app authentication." xml:space="preserve">
@@ -7099,8 +7226,8 @@ Verbindungsanfrage wiederholen?</target>
<target>Sie können sie über Einstellungen für Ihre SimpleX-Kontakte sichtbar machen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Sie können nun Nachrichten an %@ versenden</target>
<note>notification body</note>
</trans-unit>
@@ -7472,6 +7599,7 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
</trans-unit>
<trans-unit id="attempts" xml:space="preserve">
<source>attempts</source>
<target>Versuche</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
@@ -7507,7 +7635,7 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>wurde vom Administrator blockiert</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7666,6 +7794,7 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
</trans-unit>
<trans-unit id="decryption errors" xml:space="preserve">
<source>decryption errors</source>
<target>Entschlüsselungs-Fehler</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="default (%@)" xml:space="preserve">
@@ -7720,6 +7849,7 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
</trans-unit>
<trans-unit id="duplicates" xml:space="preserve">
<source>duplicates</source>
<target>Duplikate</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="e2e encrypted" xml:space="preserve">
@@ -7804,6 +7934,7 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
</trans-unit>
<trans-unit id="expired" xml:space="preserve">
<source>expired</source>
<target>abgelaufen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
@@ -7838,6 +7969,7 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<target>Inaktiv</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
@@ -8019,10 +8151,12 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
</trans-unit>
<trans-unit id="other" xml:space="preserve">
<source>other</source>
<target>andere</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="other errors" xml:space="preserve">
<source>other errors</source>
<target>Andere Fehler</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="owner" xml:space="preserve">
@@ -8348,7 +8482,7 @@ Zuletzt empfangene Nachricht: %2$@</target>
</trans-unit>
<trans-unit id="NSCameraUsageDescription" xml:space="preserve">
<source>SimpleX needs camera access to scan QR codes to connect to other users and for video calls.</source>
<target>SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Nutzern zu scannen und Videoanrufe durchzuführen.</target>
<target>SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Benutzern zu scannen und Videoanrufe durchzuführen.</target>
<note>Privacy - Camera Usage Description</note>
</trans-unit>
<trans-unit id="NSFaceIDUsageDescription" xml:space="preserve">
@@ -718,9 +718,9 @@
<target>All new messages from %@ will be hidden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<target>All users</target>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>All profiles</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1738,9 +1738,9 @@ This is your own one-time link!</target>
<target>Current passphrase…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<target>Current user</target>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<target>Current profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5773,6 +5773,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Server version is incompatible with network settings.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<target>Server version is incompatible with network settings: %@.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<target>Server version is incompatible with your app: %@.</target>
@@ -5918,6 +5923,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Show message status</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<target>Show percentage</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Show preview</target>
@@ -6148,11 +6158,6 @@ Enable in *Network &amp; servers* settings.</target>
<target>Subscription errors</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<target>Subscription percentage</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<target>Subscriptions ignored</target>
@@ -7231,9 +7236,9 @@ Repeat join request?</target>
<target>You can make it visible to your SimpleX contacts via Settings.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<target>You can now send messages to %@</target>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>You can now chat with %@</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
@@ -7640,7 +7645,7 @@ SimpleX servers cannot see your profile.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>blocked by admin</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -709,8 +709,8 @@
<target>¡Los mensajes nuevos de %@ estarán ocultos!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1709,8 +1709,8 @@ This is your own one-time link!</source>
<target>Contraseña actual…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5668,6 +5668,10 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
<target>La versión del servidor es incompatible con la configuración de red.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5809,6 +5813,10 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
<target>Estado del mensaje</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Mostrar vista previa</target>
@@ -6033,10 +6041,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7099,8 +7103,8 @@ Repeat join request?</source>
<target>Puedes hacerlo visible para tus contactos de SimpleX en Configuración.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Ya puedes enviar mensajes a %@</target>
<note>notification body</note>
</trans-unit>
@@ -7507,7 +7511,7 @@ Los servidores SimpleX no pueden ver tu perfil.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>bloqueado por administrador</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -682,8 +682,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1628,8 +1628,8 @@ This is your own one-time link!</source>
<target>Nykyinen tunnuslause…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5424,6 +5424,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5559,6 +5563,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Näytä esikatselu</target>
@@ -5775,10 +5783,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6778,8 +6782,8 @@ Repeat join request?</source>
<source>You can make it visible to your SimpleX contacts via Settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Voit nyt lähettää viestejä %@:lle</target>
<note>notification body</note>
</trans-unit>
@@ -7172,7 +7176,7 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
</trans-unit>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -709,8 +709,8 @@
<target>Tous les nouveaux messages de %@ seront cachés !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1709,8 +1709,8 @@ Il s'agit de votre propre lien unique !</target>
<target>Phrase secrète actuelle…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5668,6 +5668,10 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
<target>La version du serveur est incompatible avec les paramètres du réseau.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5809,6 +5813,10 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
<target>Afficher le statut du message</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Afficher l'aperçu</target>
@@ -6033,10 +6041,6 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7099,8 +7103,8 @@ Répéter la demande d'adhésion ?</target>
<target>Vous pouvez le rendre visible à vos contacts SimpleX via Paramètres.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Vous pouvez maintenant envoyer des messages à %@</target>
<note>notification body</note>
</trans-unit>
@@ -7507,7 +7511,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>bloqué par l'administrateur</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -554,6 +554,7 @@
</trans-unit>
<trans-unit id="Accent" xml:space="preserve">
<source>Accent</source>
<target>Kiemelés</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Accept" xml:space="preserve">
@@ -579,10 +580,12 @@
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
<target>Nyugtázva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Acknowledgement errors" xml:space="preserve">
<source>Acknowledgement errors</source>
<target>Nyugtázott hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
@@ -631,14 +634,17 @@
</trans-unit>
<trans-unit id="Additional accent" xml:space="preserve">
<source>Additional accent</source>
<target>További kiemelés</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Additional accent 2" xml:space="preserve">
<source>Additional accent 2</source>
<target>További kiemelés 2</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Additional secondary" xml:space="preserve">
<source>Additional secondary</source>
<target>További másodlagos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Address" xml:space="preserve">
@@ -668,6 +674,7 @@
</trans-unit>
<trans-unit id="Advanced settings" xml:space="preserve">
<source>Advanced settings</source>
<target>Haladó beállítások</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All app data is deleted." xml:space="preserve">
@@ -687,6 +694,7 @@
</trans-unit>
<trans-unit id="All data is private to your device." xml:space="preserve">
<source>All data is private to your device.</source>
<target>Minden adat biztonságban van a készülékén.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All group members will remain connected." xml:space="preserve">
@@ -709,8 +717,9 @@
<target>Minden új üzenet elrejtésre kerül tőle: %@!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Minden profil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -915,6 +924,7 @@
</trans-unit>
<trans-unit id="Apply to" xml:space="preserve">
<source>Apply to</source>
<target>Alkalmazás erre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive and upload" xml:space="preserve">
@@ -994,6 +1004,7 @@
</trans-unit>
<trans-unit id="Background" xml:space="preserve">
<source>Background</source>
<target>Háttér</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve">
@@ -1023,6 +1034,7 @@
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<target>Fekete</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block" xml:space="preserve">
@@ -1137,6 +1149,7 @@
</trans-unit>
<trans-unit id="Cannot forward message" xml:space="preserve">
<source>Cannot forward message</source>
<target>Nem lehet továbbítani az üzenetet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cannot receive file" xml:space="preserve">
@@ -1212,6 +1225,7 @@
</trans-unit>
<trans-unit id="Chat colors" xml:space="preserve">
<source>Chat colors</source>
<target>Csevegés színei</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat console" xml:space="preserve">
@@ -1261,6 +1275,7 @@
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Csevegés témája</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
@@ -1295,14 +1310,17 @@
</trans-unit>
<trans-unit id="Chunks deleted" xml:space="preserve">
<source>Chunks deleted</source>
<target>Törölt fájltöredékek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chunks downloaded" xml:space="preserve">
<source>Chunks downloaded</source>
<target>Letöltött fájltöredékek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chunks uploaded" xml:space="preserve">
<source>Chunks uploaded</source>
<target>Feltöltött fájltöredékek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Clear" xml:space="preserve">
@@ -1332,6 +1350,7 @@
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<target>Színmód</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Compare file" xml:space="preserve">
@@ -1346,6 +1365,7 @@
</trans-unit>
<trans-unit id="Completed" xml:space="preserve">
<source>Completed</source>
<target>Elkészült</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure ICE servers" xml:space="preserve">
@@ -1463,6 +1483,7 @@ Ez az egyszer használatos hivatkozása!</target>
</trans-unit>
<trans-unit id="Connected" xml:space="preserve">
<source>Connected</source>
<target>Kapcsolódva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve">
@@ -1472,6 +1493,7 @@ Ez az egyszer használatos hivatkozása!</target>
</trans-unit>
<trans-unit id="Connected servers" xml:space="preserve">
<source>Connected servers</source>
<target>Kapcsolódott kiszolgálók</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve">
@@ -1481,6 +1503,7 @@ Ez az egyszer használatos hivatkozása!</target>
</trans-unit>
<trans-unit id="Connecting" xml:space="preserve">
<source>Connecting</source>
<target>Kapcsolódás</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve">
@@ -1530,10 +1553,12 @@ Ez az egyszer használatos hivatkozása!</target>
</trans-unit>
<trans-unit id="Connection with desktop stopped" xml:space="preserve">
<source>Connection with desktop stopped</source>
<target>A kapcsolat a számítógéppel megszakadt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections" xml:space="preserve">
<source>Connections</source>
<target>Kapcsolatok</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
@@ -1593,6 +1618,7 @@ Ez az egyszer használatos hivatkozása!</target>
</trans-unit>
<trans-unit id="Copy error" xml:space="preserve">
<source>Copy error</source>
<target>Másolási hiba</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Core version: v%@" xml:space="preserve">
@@ -1672,6 +1698,7 @@ Ez az egyszer használatos hivatkozása!</target>
</trans-unit>
<trans-unit id="Created" xml:space="preserve">
<source>Created</source>
<target>Létrehozva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Created at" xml:space="preserve">
@@ -1709,8 +1736,9 @@ Ez az egyszer használatos hivatkozása!</target>
<target>Jelenlegi jelmondat…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<target>Jelenlegi profil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1725,6 +1753,7 @@ Ez az egyszer használatos hivatkozása!</target>
</trans-unit>
<trans-unit id="Customize theme" xml:space="preserve">
<source>Customize theme</source>
<target>Téma személyre szabása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dark" xml:space="preserve">
@@ -1734,6 +1763,7 @@ Ez az egyszer használatos hivatkozása!</target>
</trans-unit>
<trans-unit id="Dark mode colors" xml:space="preserve">
<source>Dark mode colors</source>
<target>Sötét mód színei</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Database ID" xml:space="preserve">
@@ -2043,6 +2073,7 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<target>Törölve</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted at" xml:space="preserve">
@@ -2057,6 +2088,7 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Deletion errors" xml:space="preserve">
<source>Deletion errors</source>
<target>Törlési hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delivery" xml:space="preserve">
@@ -2101,10 +2133,12 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Detailed statistics" xml:space="preserve">
<source>Detailed statistics</source>
<target>Részletes statisztikák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Details" xml:space="preserve">
<source>Details</source>
<target>Részletek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Develop" xml:space="preserve">
@@ -2264,6 +2298,7 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Download errors" xml:space="preserve">
<source>Download errors</source>
<target>Letöltési hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve">
@@ -2278,10 +2313,12 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Downloaded" xml:space="preserve">
<source>Downloaded</source>
<target>Letöltve</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Downloaded files" xml:space="preserve">
<source>Downloaded files</source>
<target>Letöltött fájlok</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Downloading archive" xml:space="preserve">
@@ -2656,6 +2693,7 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Error exporting theme: %@" xml:space="preserve">
<source>Error exporting theme: %@</source>
<target>Hiba a téma exportálásakor: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error importing chat database" xml:space="preserve">
@@ -2685,10 +2723,12 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Error reconnecting server" xml:space="preserve">
<source>Error reconnecting server</source>
<target>Hiba a kiszolgálóhoz való újrakapcsolódáskor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reconnecting servers" xml:space="preserve">
<source>Error reconnecting servers</source>
<target>Hiba a kiszolgálókhoz való újrakapcsolódáskor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error removing member" xml:space="preserve">
@@ -2698,6 +2738,7 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Error resetting statistics" xml:space="preserve">
<source>Error resetting statistics</source>
<target>Hiba a statisztikák visszaállításakor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error saving %@ servers" xml:space="preserve">
@@ -2833,6 +2874,7 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Errors" xml:space="preserve">
<source>Errors</source>
<target>Hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Even when disabled in the conversation." xml:space="preserve">
@@ -2862,6 +2904,7 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="Export theme" xml:space="preserve">
<source>Export theme</source>
<target>Téma exportálása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Exported database archive." xml:space="preserve">
@@ -2901,22 +2944,27 @@ Ez a művelet nem vonható vissza!</target>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
<target>Fájlhiba</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="File not found - most likely file was deleted or cancelled." xml:space="preserve">
<source>File not found - most likely file was deleted or cancelled.</source>
<target>A fájl nem található - valószínűleg a fájlt törölték vagy visszavonták.</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="File server error: %@" xml:space="preserve">
<source>File server error: %@</source>
<target>Fájlkiszolgáló hiba: %@</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="File status" xml:space="preserve">
<source>File status</source>
<target>Fájlállapot</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="File status: %@" xml:space="preserve">
<source>File status: %@</source>
<target>Fájlállapot: %@</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="File will be deleted from servers." xml:space="preserve">
@@ -3110,10 +3158,12 @@ Hiba: %2$@</target>
</trans-unit>
<trans-unit id="Good afternoon!" xml:space="preserve">
<source>Good afternoon!</source>
<target>Jó napot!</target>
<note>message preview</note>
</trans-unit>
<trans-unit id="Good morning!" xml:space="preserve">
<source>Good morning!</source>
<target>Jó reggelt!</target>
<note>message preview</note>
</trans-unit>
<trans-unit id="Group" xml:space="preserve">
@@ -3398,6 +3448,7 @@ Hiba: %2$@</target>
</trans-unit>
<trans-unit id="Import theme" xml:space="preserve">
<source>Import theme</source>
<target>Téma importálása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Importing archive" xml:space="preserve">
@@ -3524,6 +3575,7 @@ Hiba: %2$@</target>
</trans-unit>
<trans-unit id="Interface colors" xml:space="preserve">
<source>Interface colors</source>
<target>Kezelőfelület színei</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invalid QR code" xml:space="preserve">
@@ -3871,6 +3923,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Inaktív tag</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
@@ -3890,6 +3943,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="Menus" xml:space="preserve">
<source>Menus</source>
<target>Menük</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message delivery error" xml:space="preserve">
@@ -3914,10 +3968,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<target>Továbbított üzenet</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<target>Az üzenet később is kézbesíthető, ha a tag aktívvá válik.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
@@ -3961,10 +4017,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="Message status" xml:space="preserve">
<source>Message status</source>
<target>Üzenetállapot</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message status: %@" xml:space="preserve">
<source>Message status: %@</source>
<target>Üzenetállapot: %@</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
@@ -3994,10 +4052,12 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="Messages received" xml:space="preserve">
<source>Messages received</source>
<target>Fogadott üzenetek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Messages sent" xml:space="preserve">
<source>Messages sent</source>
<target>Elküldött üzenetek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
@@ -4237,6 +4297,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<target>Még nincs közvetlen kapcsolat, az üzenetet az admin továbbítja.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
@@ -4256,6 +4317,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="No info, try to reload" xml:space="preserve">
<source>No info, try to reload</source>
<target>Nincs információ, próbálja meg újratölteni</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
@@ -4444,6 +4506,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="Open server settings" xml:space="preserve">
<source>Open server settings</source>
<target>Kiszolgáló beállításainak megnyitása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Open user profiles" xml:space="preserve">
@@ -4557,6 +4620,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
</trans-unit>
<trans-unit id="Pending" xml:space="preserve">
<source>Pending</source>
<target>Függő</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
@@ -4587,6 +4651,8 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
<trans-unit id="Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.&#10;Please share any other issues with the developers." xml:space="preserve">
<source>Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.
Please share any other issues with the developers.</source>
<target>Ellenőrizze, hogy a mobil és az asztali számítógép ugyanahhoz a helyi hálózathoz csatlakozik-e, valamint az asztali számítógép tűzfalában engedélyezve van-e a kapcsolat.
Minden további problémát osszon meg a fejlesztőkkel.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please check that you used the correct link or ask your contact to send you another one." xml:space="preserve">
@@ -4692,6 +4758,7 @@ Hiba: %@</target>
</trans-unit>
<trans-unit id="Previously connected servers" xml:space="preserve">
<source>Previously connected servers</source>
<target>Korábban kapcsolódott kiszolgálók</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Privacy &amp; security" xml:space="preserve">
@@ -4765,6 +4832,7 @@ Hiba: %@</target>
</trans-unit>
<trans-unit id="Profile theme" xml:space="preserve">
<source>Profile theme</source>
<target>Profiltéma</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile update will be sent to your contacts." xml:space="preserve">
@@ -4851,10 +4919,12 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Proxied" xml:space="preserve">
<source>Proxied</source>
<target>Proxyzott</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Proxied servers" xml:space="preserve">
<source>Proxied servers</source>
<target>Proxyzott kiszolgálók</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Push notifications" xml:space="preserve">
@@ -4924,6 +4994,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Receive errors" xml:space="preserve">
<source>Receive errors</source>
<target>Üzenetfogadási hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received at" xml:space="preserve">
@@ -4948,14 +5019,17 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Received messages" xml:space="preserve">
<source>Received messages</source>
<target>Fogadott üzenetek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received reply" xml:space="preserve">
<source>Received reply</source>
<target>Fogadott válasz</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received total" xml:space="preserve">
<source>Received total</source>
<target>Összes fogadott</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving address will be changed to a different server. Address change will complete after sender comes online." xml:space="preserve">
@@ -4990,6 +5064,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Reconnect" xml:space="preserve">
<source>Reconnect</source>
<target>Újrakapcsolás</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect all connected servers to force message delivery. It uses additional traffic." xml:space="preserve">
@@ -4999,18 +5074,22 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Reconnect all servers" xml:space="preserve">
<source>Reconnect all servers</source>
<target>Újrakapcsolódás minden kiszolgálóhoz</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect all servers?" xml:space="preserve">
<source>Reconnect all servers?</source>
<target>Újrakapcsolódás minden kiszolgálóhoz?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect server to force message delivery. It uses additional traffic." xml:space="preserve">
<source>Reconnect server to force message delivery. It uses additional traffic.</source>
<target>A kiszolgálóhoz való újrakapcsolódás az üzenet kézbesítésének kikényszerítéséhez. Ez további adatforgalmat használ.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect server?" xml:space="preserve">
<source>Reconnect server?</source>
<target>Újrakapcsolódás a kiszolgálóhoz?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect servers?" xml:space="preserve">
@@ -5065,6 +5144,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Remove image" xml:space="preserve">
<source>Remove image</source>
<target>Kép eltávolítása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove member" xml:space="preserve">
@@ -5139,10 +5219,12 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<target>Minden statisztika visszaállítása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics?" xml:space="preserve">
<source>Reset all statistics?</source>
<target>Minden statisztika visszaállítása?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset colors" xml:space="preserve">
@@ -5152,6 +5234,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Reset to app theme" xml:space="preserve">
<source>Reset to app theme</source>
<target>Alkalmazás témájának visszaállítása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset to defaults" xml:space="preserve">
@@ -5161,6 +5244,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Reset to user theme" xml:space="preserve">
<source>Reset to user theme</source>
<target>Felhasználó által létrehozott téma visszaállítása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Restart the app to create a new chat profile" xml:space="preserve">
@@ -5235,6 +5319,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="SMP server" xml:space="preserve">
<source>SMP server</source>
<target>SMP-kiszolgáló</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
@@ -5354,10 +5439,12 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Scale" xml:space="preserve">
<source>Scale</source>
<target>Méretezés</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scan / Paste link" xml:space="preserve">
<source>Scan / Paste link</source>
<target>Hivatkozás beolvasása / beillesztése</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scan QR code" xml:space="preserve">
@@ -5402,6 +5489,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Secondary" xml:space="preserve">
<source>Secondary</source>
<target>Másodlagos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Secure queue" xml:space="preserve">
@@ -5411,6 +5499,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Secured" xml:space="preserve">
<source>Secured</source>
<target>Biztosítva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Security assessment" xml:space="preserve">
@@ -5430,6 +5519,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Selected chat preferences prohibit this message." xml:space="preserve">
<source>Selected chat preferences prohibit this message.</source>
<target>A kiválasztott csevegési beállítások tiltják ezt az üzenetet.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Self-destruct" xml:space="preserve">
@@ -5484,6 +5574,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Send errors" xml:space="preserve">
<source>Send errors</source>
<target>Üzenetküldési hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send link previews" xml:space="preserve">
@@ -5598,6 +5689,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Sent directly" xml:space="preserve">
<source>Sent directly</source>
<target>Közvetlenül küldött</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent file event" xml:space="preserve">
@@ -5612,6 +5704,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Sent messages" xml:space="preserve">
<source>Sent messages</source>
<target>Elküldött üzenetek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent messages will be deleted after set time." xml:space="preserve">
@@ -5621,18 +5714,22 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Sent reply" xml:space="preserve">
<source>Sent reply</source>
<target>Elküldött válasz</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent total" xml:space="preserve">
<source>Sent total</source>
<target>Összes elküldött</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent via proxy" xml:space="preserve">
<source>Sent via proxy</source>
<target>Proxyn keresztül küldve</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Kiszolgáló címe</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings." xml:space="preserve">
@@ -5661,6 +5758,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Server type" xml:space="preserve">
<source>Server type</source>
<target>Kiszolgáló típusa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings." xml:space="preserve">
@@ -5668,6 +5766,10 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
<target>A kiszolgáló verziója nem kompatibilis a hálózati beállításokkal.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5679,10 +5781,12 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Servers info" xml:space="preserve">
<source>Servers info</source>
<target>információk a kiszolgálókról</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers statistics will be reset - this cannot be undone!" xml:space="preserve">
<source>Servers statistics will be reset - this cannot be undone!</source>
<target>A kiszolgálók statisztikái visszaállnak - ez nem vonható vissza!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Session code" xml:space="preserve">
@@ -5702,6 +5806,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Set default theme" xml:space="preserve">
<source>Set default theme</source>
<target>Alapértelmezett téma beállítása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set group preferences" xml:space="preserve">
@@ -5809,6 +5914,10 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
<target>Üzenet állapot megjelenítése</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Előnézet megjelenítése</target>
@@ -5826,6 +5935,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="SimpleX" xml:space="preserve">
<source>SimpleX</source>
<target>SimpleX</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX Address" xml:space="preserve">
@@ -5905,6 +6015,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Size" xml:space="preserve">
<source>Size</source>
<target>Méret</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Skip" xml:space="preserve">
@@ -5954,10 +6065,12 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Starting from %@." xml:space="preserve">
<source>Starting from %@.</source>
<target>Kezdve ettől %@.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Statistics" xml:space="preserve">
<source>Statistics</source>
<target>Statisztikák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stop" xml:space="preserve">
@@ -6027,18 +6140,17 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Subscribed" xml:space="preserve">
<source>Subscribed</source>
<target>Feliratkozva</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription errors" xml:space="preserve">
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<target>Feliratkozási hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<target>Elutasított feliratkozások</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Support SimpleX Chat" xml:space="preserve">
@@ -6123,6 +6235,7 @@ Engedélyezze a beállításokban a *Hálózat és kiszolgálók* menüpontban.<
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<target>Ideiglenes fájlhiba</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Test failed at step %@." xml:space="preserve">
@@ -6264,6 +6377,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő.</target>
</trans-unit>
<trans-unit id="Themes" xml:space="preserve">
<source>Themes</source>
<target>Témák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="These settings are for your current profile **%@**." xml:space="preserve">
@@ -6333,6 +6447,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő.</target>
</trans-unit>
<trans-unit id="This link was used with another mobile device, please create a new link on the desktop." xml:space="preserve">
<source>This link was used with another mobile device, please create a new link on the desktop.</source>
<target>Ezt a hivatkozást egy másik mobilleszközön már használták, hozzon létre egy új hivatkozást az asztali számítógépén.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This setting applies to messages in your current chat profile **%@**." xml:space="preserve">
@@ -6342,6 +6457,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő.</target>
</trans-unit>
<trans-unit id="Title" xml:space="preserve">
<source>Title</source>
<target>Cím</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To ask any questions and to receive updates:" xml:space="preserve">
@@ -6413,6 +6529,7 @@ A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befej
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<target>Összesen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Transport isolation" xml:space="preserve">
@@ -6422,6 +6539,7 @@ A funkció engedélyezése előtt a rendszer felszólítja a hitelesítés befej
</trans-unit>
<trans-unit id="Transport sessions" xml:space="preserve">
<source>Transport sessions</source>
<target>Munkamenetek átvitele</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Trying to connect to the server used to receive messages from this contact (error: %@)." xml:space="preserve">
@@ -6618,6 +6736,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol
</trans-unit>
<trans-unit id="Upload errors" xml:space="preserve">
<source>Upload errors</source>
<target>Feltöltési hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upload failed" xml:space="preserve">
@@ -6632,10 +6751,12 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol
</trans-unit>
<trans-unit id="Uploaded" xml:space="preserve">
<source>Uploaded</source>
<target>Feltöltve</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Uploaded files" xml:space="preserve">
<source>Uploaded files</source>
<target>Feltöltött fájlok</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Uploading archive" xml:space="preserve">
@@ -6715,6 +6836,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Felhasználó kiválasztása</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
@@ -6854,10 +6976,12 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol
</trans-unit>
<trans-unit id="Wallpaper accent" xml:space="preserve">
<source>Wallpaper accent</source>
<target>Háttérkép kiemelés</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wallpaper background" xml:space="preserve">
<source>Wallpaper background</source>
<target>Háttérkép háttérszíne</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Warning: starting chat on multiple devices is not supported and will cause message delivery failures" xml:space="preserve">
@@ -6967,6 +7091,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol
</trans-unit>
<trans-unit id="Wrong key or unknown file chunk address - most likely file is deleted." xml:space="preserve">
<source>Wrong key or unknown file chunk address - most likely file is deleted.</source>
<target>Hibás kulcs vagy ismeretlen fájltöredék cím - valószínűleg a fájl törlődött.</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="Wrong passphrase!" xml:space="preserve">
@@ -6976,6 +7101,7 @@ A kapcsolódáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsol
</trans-unit>
<trans-unit id="XFTP server" xml:space="preserve">
<source>XFTP server</source>
<target>XFTP-kiszolgáló</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
@@ -7062,6 +7188,7 @@ Csatlakozási kérés megismétlése?</target>
</trans-unit>
<trans-unit id="You are not connected to these servers. Private routing is used to deliver messages to them." xml:space="preserve">
<source>You are not connected to these servers. Private routing is used to deliver messages to them.</source>
<target>Ön nem kapcsolódik ezekhez a kiszolgálókhoz. A privát útválasztás az üzenetek kézbesítésére szolgál.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can accept calls from lock screen, without device and app authentication." xml:space="preserve">
@@ -7099,8 +7226,8 @@ Csatlakozási kérés megismétlése?</target>
<target>Láthatóvá teheti SimpleX ismerősök számára a Beállításokban.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Mostantól küldhet üzeneteket %@ számára</target>
<note>notification body</note>
</trans-unit>
@@ -7472,6 +7599,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
</trans-unit>
<trans-unit id="attempts" xml:space="preserve">
<source>attempts</source>
<target>próbálkozások</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
@@ -7507,7 +7635,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>letiltva az admin által</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7666,6 +7794,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
</trans-unit>
<trans-unit id="decryption errors" xml:space="preserve">
<source>decryption errors</source>
<target>visszafejtési hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="default (%@)" xml:space="preserve">
@@ -7720,6 +7849,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
</trans-unit>
<trans-unit id="duplicates" xml:space="preserve">
<source>duplicates</source>
<target>duplikációk</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="e2e encrypted" xml:space="preserve">
@@ -7804,6 +7934,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
</trans-unit>
<trans-unit id="expired" xml:space="preserve">
<source>expired</source>
<target>lejárt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
@@ -7838,6 +7969,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<target>inaktív</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
@@ -8019,10 +8151,12 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
</trans-unit>
<trans-unit id="other" xml:space="preserve">
<source>other</source>
<target>egyéb</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="other errors" xml:space="preserve">
<source>other errors</source>
<target>egyéb hibák</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="owner" xml:space="preserve">
@@ -554,6 +554,7 @@
</trans-unit>
<trans-unit id="Accent" xml:space="preserve">
<source>Accent</source>
<target>Principale</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Accept" xml:space="preserve">
@@ -579,10 +580,12 @@
</trans-unit>
<trans-unit id="Acknowledged" xml:space="preserve">
<source>Acknowledged</source>
<target>Riconosciuto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Acknowledgement errors" xml:space="preserve">
<source>Acknowledgement errors</source>
<target>Errori di riconoscimento</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
@@ -631,14 +634,17 @@
</trans-unit>
<trans-unit id="Additional accent" xml:space="preserve">
<source>Additional accent</source>
<target>Principale aggiuntivo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Additional accent 2" xml:space="preserve">
<source>Additional accent 2</source>
<target>Principale aggiuntivo 2</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Additional secondary" xml:space="preserve">
<source>Additional secondary</source>
<target>Secondario aggiuntivo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Address" xml:space="preserve">
@@ -668,6 +674,7 @@
</trans-unit>
<trans-unit id="Advanced settings" xml:space="preserve">
<source>Advanced settings</source>
<target>Impostazioni avanzate</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All app data is deleted." xml:space="preserve">
@@ -687,6 +694,7 @@
</trans-unit>
<trans-unit id="All data is private to your device." xml:space="preserve">
<source>All data is private to your device.</source>
<target>Tutti i dati sono privati, nel tuo dispositivo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All group members will remain connected." xml:space="preserve">
@@ -709,8 +717,9 @@
<target>Tutti i nuovi messaggi da %@ verrranno nascosti!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>Tutti gli profili</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -915,6 +924,7 @@
</trans-unit>
<trans-unit id="Apply to" xml:space="preserve">
<source>Apply to</source>
<target>Applica a</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Archive and upload" xml:space="preserve">
@@ -994,6 +1004,7 @@
</trans-unit>
<trans-unit id="Background" xml:space="preserve">
<source>Background</source>
<target>Sfondo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Bad desktop address" xml:space="preserve">
@@ -1023,6 +1034,7 @@
</trans-unit>
<trans-unit id="Black" xml:space="preserve">
<source>Black</source>
<target>Nero</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Block" xml:space="preserve">
@@ -1137,6 +1149,7 @@
</trans-unit>
<trans-unit id="Cannot forward message" xml:space="preserve">
<source>Cannot forward message</source>
<target>Impossibile inoltrare il messaggio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cannot receive file" xml:space="preserve">
@@ -1212,6 +1225,7 @@
</trans-unit>
<trans-unit id="Chat colors" xml:space="preserve">
<source>Chat colors</source>
<target>Colori della chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat console" xml:space="preserve">
@@ -1261,6 +1275,7 @@
</trans-unit>
<trans-unit id="Chat theme" xml:space="preserve">
<source>Chat theme</source>
<target>Tema della chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
@@ -1295,14 +1310,17 @@
</trans-unit>
<trans-unit id="Chunks deleted" xml:space="preserve">
<source>Chunks deleted</source>
<target>Blocchi eliminati</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chunks downloaded" xml:space="preserve">
<source>Chunks downloaded</source>
<target>Blocchi scaricati</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chunks uploaded" xml:space="preserve">
<source>Chunks uploaded</source>
<target>Blocchi inviati</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Clear" xml:space="preserve">
@@ -1332,6 +1350,7 @@
</trans-unit>
<trans-unit id="Color mode" xml:space="preserve">
<source>Color mode</source>
<target>Modalità di colore</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Compare file" xml:space="preserve">
@@ -1346,6 +1365,7 @@
</trans-unit>
<trans-unit id="Completed" xml:space="preserve">
<source>Completed</source>
<target>Completato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure ICE servers" xml:space="preserve">
@@ -1463,6 +1483,7 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Connected" xml:space="preserve">
<source>Connected</source>
<target>Connesso</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connected desktop" xml:space="preserve">
@@ -1472,6 +1493,7 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Connected servers" xml:space="preserve">
<source>Connected servers</source>
<target>Server connessi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connected to desktop" xml:space="preserve">
@@ -1481,6 +1503,7 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Connecting" xml:space="preserve">
<source>Connecting</source>
<target>In connessione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connecting server…" xml:space="preserve">
@@ -1530,10 +1553,12 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Connection with desktop stopped" xml:space="preserve">
<source>Connection with desktop stopped</source>
<target>Connessione con il desktop fermata</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections" xml:space="preserve">
<source>Connections</source>
<target>Connessioni</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
@@ -1593,6 +1618,7 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Copy error" xml:space="preserve">
<source>Copy error</source>
<target>Copia errore</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Core version: v%@" xml:space="preserve">
@@ -1672,6 +1698,7 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Created" xml:space="preserve">
<source>Created</source>
<target>Creato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Created at" xml:space="preserve">
@@ -1709,8 +1736,9 @@ Questo è il tuo link una tantum!</target>
<target>Password attuale…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<target>Profilo attuale</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1725,6 +1753,7 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Customize theme" xml:space="preserve">
<source>Customize theme</source>
<target>Personalizza il tema</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Dark" xml:space="preserve">
@@ -1734,6 +1763,7 @@ Questo è il tuo link una tantum!</target>
</trans-unit>
<trans-unit id="Dark mode colors" xml:space="preserve">
<source>Dark mode colors</source>
<target>Colori modalità scura</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Database ID" xml:space="preserve">
@@ -2043,6 +2073,7 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Deleted" xml:space="preserve">
<source>Deleted</source>
<target>Eliminato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Deleted at" xml:space="preserve">
@@ -2057,6 +2088,7 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Deletion errors" xml:space="preserve">
<source>Deletion errors</source>
<target>Errori di eliminazione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Delivery" xml:space="preserve">
@@ -2101,10 +2133,12 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Detailed statistics" xml:space="preserve">
<source>Detailed statistics</source>
<target>Statistiche dettagliate</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Details" xml:space="preserve">
<source>Details</source>
<target>Dettagli</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Develop" xml:space="preserve">
@@ -2264,6 +2298,7 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Download errors" xml:space="preserve">
<source>Download errors</source>
<target>Errori di scaricamento</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Download failed" xml:space="preserve">
@@ -2278,10 +2313,12 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Downloaded" xml:space="preserve">
<source>Downloaded</source>
<target>Scaricato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Downloaded files" xml:space="preserve">
<source>Downloaded files</source>
<target>File scaricati</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Downloading archive" xml:space="preserve">
@@ -2656,6 +2693,7 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Error exporting theme: %@" xml:space="preserve">
<source>Error exporting theme: %@</source>
<target>Errore di esportazione del tema: %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error importing chat database" xml:space="preserve">
@@ -2685,10 +2723,12 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Error reconnecting server" xml:space="preserve">
<source>Error reconnecting server</source>
<target>Errore di riconnessione al server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error reconnecting servers" xml:space="preserve">
<source>Error reconnecting servers</source>
<target>Errore di riconnessione ai server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error removing member" xml:space="preserve">
@@ -2698,6 +2738,7 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Error resetting statistics" xml:space="preserve">
<source>Error resetting statistics</source>
<target>Errore di azzeramento statistiche</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error saving %@ servers" xml:space="preserve">
@@ -2833,6 +2874,7 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Errors" xml:space="preserve">
<source>Errors</source>
<target>Errori</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Even when disabled in the conversation." xml:space="preserve">
@@ -2862,6 +2904,7 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="Export theme" xml:space="preserve">
<source>Export theme</source>
<target>Esporta tema</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Exported database archive." xml:space="preserve">
@@ -2901,22 +2944,27 @@ Non è reversibile!</target>
</trans-unit>
<trans-unit id="File error" xml:space="preserve">
<source>File error</source>
<target>Errore del file</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="File not found - most likely file was deleted or cancelled." xml:space="preserve">
<source>File not found - most likely file was deleted or cancelled.</source>
<target>File non trovato - probabilmente è stato eliminato o annullato.</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="File server error: %@" xml:space="preserve">
<source>File server error: %@</source>
<target>Errore del server dei file: %@</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="File status" xml:space="preserve">
<source>File status</source>
<target>Stato del file</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="File status: %@" xml:space="preserve">
<source>File status: %@</source>
<target>Stato del file: %@</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="File will be deleted from servers." xml:space="preserve">
@@ -3110,10 +3158,12 @@ Errore: %2$@</target>
</trans-unit>
<trans-unit id="Good afternoon!" xml:space="preserve">
<source>Good afternoon!</source>
<target>Buon pomeriggio!</target>
<note>message preview</note>
</trans-unit>
<trans-unit id="Good morning!" xml:space="preserve">
<source>Good morning!</source>
<target>Buongiorno!</target>
<note>message preview</note>
</trans-unit>
<trans-unit id="Group" xml:space="preserve">
@@ -3398,6 +3448,7 @@ Errore: %2$@</target>
</trans-unit>
<trans-unit id="Import theme" xml:space="preserve">
<source>Import theme</source>
<target>Importa tema</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Importing archive" xml:space="preserve">
@@ -3524,6 +3575,7 @@ Errore: %2$@</target>
</trans-unit>
<trans-unit id="Interface colors" xml:space="preserve">
<source>Interface colors</source>
<target>Colori dell'interfaccia</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invalid QR code" xml:space="preserve">
@@ -3871,6 +3923,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Membro inattivo</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
@@ -3890,6 +3943,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Menus" xml:space="preserve">
<source>Menus</source>
<target>Menu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message delivery error" xml:space="preserve">
@@ -3914,10 +3968,12 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<target>Messaggio inoltrato</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<target>Il messaggio può essere consegnato più tardi se il membro diventa attivo.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
@@ -3961,10 +4017,12 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Message status" xml:space="preserve">
<source>Message status</source>
<target>Stato del messaggio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message status: %@" xml:space="preserve">
<source>Message status: %@</source>
<target>Stato del messaggio: %@</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
@@ -3994,10 +4052,12 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Messages received" xml:space="preserve">
<source>Messages received</source>
<target>Messaggi ricevuti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Messages sent" xml:space="preserve">
<source>Messages sent</source>
<target>Messaggi inviati</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." xml:space="preserve">
@@ -4237,6 +4297,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<target>Ancora nessuna connessione diretta, il messaggio viene inoltrato dall'amministratore.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
@@ -4256,6 +4317,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="No info, try to reload" xml:space="preserve">
<source>No info, try to reload</source>
<target>Nessuna informazione, prova a ricaricare</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No network connection" xml:space="preserve">
@@ -4444,6 +4506,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Open server settings" xml:space="preserve">
<source>Open server settings</source>
<target>Apri impostazioni server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Open user profiles" xml:space="preserve">
@@ -4557,6 +4620,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Pending" xml:space="preserve">
<source>Pending</source>
<target>In attesa</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
@@ -4587,6 +4651,8 @@ Questo è il tuo link per il gruppo %@!</target>
<trans-unit id="Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.&#10;Please share any other issues with the developers." xml:space="preserve">
<source>Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.
Please share any other issues with the developers.</source>
<target>Controlla che mobile e desktop siano collegati alla stessa rete locale e che il firewall del desktop consenta la connessione.
Si prega di condividere qualsiasi altro problema con gli sviluppatori.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please check that you used the correct link or ask your contact to send you another one." xml:space="preserve">
@@ -4692,6 +4758,7 @@ Errore: %@</target>
</trans-unit>
<trans-unit id="Previously connected servers" xml:space="preserve">
<source>Previously connected servers</source>
<target>Server precedentemente connessi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Privacy &amp; security" xml:space="preserve">
@@ -4765,6 +4832,7 @@ Errore: %@</target>
</trans-unit>
<trans-unit id="Profile theme" xml:space="preserve">
<source>Profile theme</source>
<target>Tema del profilo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile update will be sent to your contacts." xml:space="preserve">
@@ -4851,10 +4919,12 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Proxied" xml:space="preserve">
<source>Proxied</source>
<target>Via proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Proxied servers" xml:space="preserve">
<source>Proxied servers</source>
<target>Server via proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Push notifications" xml:space="preserve">
@@ -4924,6 +4994,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Receive errors" xml:space="preserve">
<source>Receive errors</source>
<target>Errori di ricezione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received at" xml:space="preserve">
@@ -4948,14 +5019,17 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Received messages" xml:space="preserve">
<source>Received messages</source>
<target>Messaggi ricevuti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received reply" xml:space="preserve">
<source>Received reply</source>
<target>Risposta ricevuta</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Received total" xml:space="preserve">
<source>Received total</source>
<target>Totale ricevuto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receiving address will be changed to a different server. Address change will complete after sender comes online." xml:space="preserve">
@@ -4990,6 +5064,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Reconnect" xml:space="preserve">
<source>Reconnect</source>
<target>Riconnetti</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect all connected servers to force message delivery. It uses additional traffic." xml:space="preserve">
@@ -4999,18 +5074,22 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Reconnect all servers" xml:space="preserve">
<source>Reconnect all servers</source>
<target>Riconnetti tutti i server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect all servers?" xml:space="preserve">
<source>Reconnect all servers?</source>
<target>Riconnettere tutti i server?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect server to force message delivery. It uses additional traffic." xml:space="preserve">
<source>Reconnect server to force message delivery. It uses additional traffic.</source>
<target>Riconnetti il server per forzare la consegna dei messaggi. Usa traffico aggiuntivo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect server?" xml:space="preserve">
<source>Reconnect server?</source>
<target>Riconnettere il server?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reconnect servers?" xml:space="preserve">
@@ -5065,6 +5144,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Remove image" xml:space="preserve">
<source>Remove image</source>
<target>Rimuovi immagine</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove member" xml:space="preserve">
@@ -5139,10 +5219,12 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Reset all statistics" xml:space="preserve">
<source>Reset all statistics</source>
<target>Azzera tutte le statistiche</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset all statistics?" xml:space="preserve">
<source>Reset all statistics?</source>
<target>Azzerare tutte le statistiche?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset colors" xml:space="preserve">
@@ -5152,6 +5234,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Reset to app theme" xml:space="preserve">
<source>Reset to app theme</source>
<target>Ripristina al tema dell'app</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset to defaults" xml:space="preserve">
@@ -5161,6 +5244,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Reset to user theme" xml:space="preserve">
<source>Reset to user theme</source>
<target>Ripristina al tema dell'utente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Restart the app to create a new chat profile" xml:space="preserve">
@@ -5235,6 +5319,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="SMP server" xml:space="preserve">
<source>SMP server</source>
<target>Server SMP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SMP servers" xml:space="preserve">
@@ -5354,10 +5439,12 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Scale" xml:space="preserve">
<source>Scale</source>
<target>Scala</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scan / Paste link" xml:space="preserve">
<source>Scan / Paste link</source>
<target>Scansiona / Incolla link</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Scan QR code" xml:space="preserve">
@@ -5402,6 +5489,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Secondary" xml:space="preserve">
<source>Secondary</source>
<target>Secondario</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Secure queue" xml:space="preserve">
@@ -5411,6 +5499,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Secured" xml:space="preserve">
<source>Secured</source>
<target>Protetto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Security assessment" xml:space="preserve">
@@ -5430,6 +5519,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Selected chat preferences prohibit this message." xml:space="preserve">
<source>Selected chat preferences prohibit this message.</source>
<target>Le preferenze della chat selezionata vietano questo messaggio.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Self-destruct" xml:space="preserve">
@@ -5484,6 +5574,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Send errors" xml:space="preserve">
<source>Send errors</source>
<target>Errori di invio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send link previews" xml:space="preserve">
@@ -5598,6 +5689,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Sent directly" xml:space="preserve">
<source>Sent directly</source>
<target>Inviato direttamente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent file event" xml:space="preserve">
@@ -5612,6 +5704,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Sent messages" xml:space="preserve">
<source>Sent messages</source>
<target>Messaggi inviati</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent messages will be deleted after set time." xml:space="preserve">
@@ -5621,18 +5714,22 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Sent reply" xml:space="preserve">
<source>Sent reply</source>
<target>Risposta inviata</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent total" xml:space="preserve">
<source>Sent total</source>
<target>Totale inviato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sent via proxy" xml:space="preserve">
<source>Sent via proxy</source>
<target>Inviato via proxy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address" xml:space="preserve">
<source>Server address</source>
<target>Indirizzo server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings." xml:space="preserve">
@@ -5661,6 +5758,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Server type" xml:space="preserve">
<source>Server type</source>
<target>Tipo server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings." xml:space="preserve">
@@ -5668,6 +5766,10 @@ Attivalo nelle impostazioni *Rete e server*.</target>
<target>La versione del server non è compatibile con le impostazioni di rete.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5679,10 +5781,12 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Servers info" xml:space="preserve">
<source>Servers info</source>
<target>Info dei server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers statistics will be reset - this cannot be undone!" xml:space="preserve">
<source>Servers statistics will be reset - this cannot be undone!</source>
<target>Le statistiche dei server verranno azzerate - è irreversibile!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Session code" xml:space="preserve">
@@ -5702,6 +5806,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Set default theme" xml:space="preserve">
<source>Set default theme</source>
<target>Imposta tema predefinito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set group preferences" xml:space="preserve">
@@ -5809,6 +5914,10 @@ Attivalo nelle impostazioni *Rete e server*.</target>
<target>Mostra stato del messaggio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Mostra anteprima</target>
@@ -5826,6 +5935,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="SimpleX" xml:space="preserve">
<source>SimpleX</source>
<target>SimpleX</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="SimpleX Address" xml:space="preserve">
@@ -5905,6 +6015,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Size" xml:space="preserve">
<source>Size</source>
<target>Dimensione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Skip" xml:space="preserve">
@@ -5954,10 +6065,12 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Starting from %@." xml:space="preserve">
<source>Starting from %@.</source>
<target>Inizio da %@.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Statistics" xml:space="preserve">
<source>Statistics</source>
<target>Statistiche</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Stop" xml:space="preserve">
@@ -6027,18 +6140,17 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Subscribed" xml:space="preserve">
<source>Subscribed</source>
<target>Iscritto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription errors" xml:space="preserve">
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<target>Errori di iscrizione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<target>Iscrizioni ignorate</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Support SimpleX Chat" xml:space="preserve">
@@ -6123,6 +6235,7 @@ Attivalo nelle impostazioni *Rete e server*.</target>
</trans-unit>
<trans-unit id="Temporary file error" xml:space="preserve">
<source>Temporary file error</source>
<target>Errore del file temporaneo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Test failed at step %@." xml:space="preserve">
@@ -6264,6 +6377,7 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.</ta
</trans-unit>
<trans-unit id="Themes" xml:space="preserve">
<source>Themes</source>
<target>Temi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="These settings are for your current profile **%@**." xml:space="preserve">
@@ -6333,6 +6447,7 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.</ta
</trans-unit>
<trans-unit id="This link was used with another mobile device, please create a new link on the desktop." xml:space="preserve">
<source>This link was used with another mobile device, please create a new link on the desktop.</source>
<target>Questo link è stato usato con un altro dispositivo mobile, creane uno nuovo sul desktop.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This setting applies to messages in your current chat profile **%@**." xml:space="preserve">
@@ -6342,6 +6457,7 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.</ta
</trans-unit>
<trans-unit id="Title" xml:space="preserve">
<source>Title</source>
<target>Titoli</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To ask any questions and to receive updates:" xml:space="preserve">
@@ -6413,6 +6529,7 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio
</trans-unit>
<trans-unit id="Total" xml:space="preserve">
<source>Total</source>
<target>Totale</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Transport isolation" xml:space="preserve">
@@ -6422,6 +6539,7 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio
</trans-unit>
<trans-unit id="Transport sessions" xml:space="preserve">
<source>Transport sessions</source>
<target>Sessioni di trasporto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Trying to connect to the server used to receive messages from this contact (error: %@)." xml:space="preserve">
@@ -6618,6 +6736,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit>
<trans-unit id="Upload errors" xml:space="preserve">
<source>Upload errors</source>
<target>Errori di invio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Upload failed" xml:space="preserve">
@@ -6632,10 +6751,12 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit>
<trans-unit id="Uploaded" xml:space="preserve">
<source>Uploaded</source>
<target>Inviato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Uploaded files" xml:space="preserve">
<source>Uploaded files</source>
<target>File inviati</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Uploading archive" xml:space="preserve">
@@ -6715,6 +6836,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit>
<trans-unit id="User selection" xml:space="preserve">
<source>User selection</source>
<target>Selezione utente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
@@ -6854,10 +6976,12 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit>
<trans-unit id="Wallpaper accent" xml:space="preserve">
<source>Wallpaper accent</source>
<target>Tinta dello sfondo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Wallpaper background" xml:space="preserve">
<source>Wallpaper background</source>
<target>Retro dello sfondo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Warning: starting chat on multiple devices is not supported and will cause message delivery failures" xml:space="preserve">
@@ -6967,6 +7091,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit>
<trans-unit id="Wrong key or unknown file chunk address - most likely file is deleted." xml:space="preserve">
<source>Wrong key or unknown file chunk address - most likely file is deleted.</source>
<target>Chiave sbagliata o indirizzo sconosciuto per frammento del file - probabilmente il file è stato eliminato.</target>
<note>file error text</note>
</trans-unit>
<trans-unit id="Wrong passphrase!" xml:space="preserve">
@@ -6976,6 +7101,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
</trans-unit>
<trans-unit id="XFTP server" xml:space="preserve">
<source>XFTP server</source>
<target>Server XFTP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="XFTP servers" xml:space="preserve">
@@ -7062,6 +7188,7 @@ Ripetere la richiesta di ingresso?</target>
</trans-unit>
<trans-unit id="You are not connected to these servers. Private routing is used to deliver messages to them." xml:space="preserve">
<source>You are not connected to these servers. Private routing is used to deliver messages to them.</source>
<target>Non sei connesso/a a questi server. L'instradamento privato è usato per consegnare loro i messaggi.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can accept calls from lock screen, without device and app authentication." xml:space="preserve">
@@ -7099,8 +7226,8 @@ Ripetere la richiesta di ingresso?</target>
<target>Puoi renderlo visibile ai tuoi contatti SimpleX nelle impostazioni.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Ora puoi inviare messaggi a %@</target>
<note>notification body</note>
</trans-unit>
@@ -7472,6 +7599,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
</trans-unit>
<trans-unit id="attempts" xml:space="preserve">
<source>attempts</source>
<target>tentativi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="audio call (not e2e encrypted)" xml:space="preserve">
@@ -7507,7 +7635,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>bloccato dall'amministratore</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7666,6 +7794,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
</trans-unit>
<trans-unit id="decryption errors" xml:space="preserve">
<source>decryption errors</source>
<target>errori di decifrazione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="default (%@)" xml:space="preserve">
@@ -7720,6 +7849,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
</trans-unit>
<trans-unit id="duplicates" xml:space="preserve">
<source>duplicates</source>
<target>doppi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="e2e encrypted" xml:space="preserve">
@@ -7804,6 +7934,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
</trans-unit>
<trans-unit id="expired" xml:space="preserve">
<source>expired</source>
<target>scaduto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="forwarded" xml:space="preserve">
@@ -7838,6 +7969,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<target>inattivo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
@@ -8019,10 +8151,12 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
</trans-unit>
<trans-unit id="other" xml:space="preserve">
<source>other</source>
<target>altro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="other errors" xml:space="preserve">
<source>other errors</source>
<target>altri errori</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="owner" xml:space="preserve">
@@ -699,8 +699,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1652,8 +1652,8 @@ This is your own one-time link!</source>
<target>現在の暗証フレーズ…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5442,6 +5442,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5577,6 +5581,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>プレビューを表示</target>
@@ -5794,10 +5802,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6796,8 +6800,8 @@ Repeat join request?</source>
<source>You can make it visible to your SimpleX contacts via Settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>%@ にメッセージを送信できるようになりました</target>
<note>notification body</note>
</trans-unit>
@@ -7190,7 +7194,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
</trans-unit>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -709,8 +709,8 @@
<target>Alle nieuwe berichten van %@ worden verborgen!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1709,8 +1709,8 @@ Dit is uw eigen eenmalige link!</target>
<target>Huidige wachtwoord…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5668,6 +5668,10 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
<target>Serverversie is incompatibel met netwerkinstellingen.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5809,6 +5813,10 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
<target>Toon berichtstatus</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Toon voorbeeld</target>
@@ -6033,10 +6041,6 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7099,8 +7103,8 @@ Deelnameverzoek herhalen?</target>
<target>Je kunt het via Instellingen zichtbaar maken voor je SimpleX contacten.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Je kunt nu berichten sturen naar %@</target>
<note>notification body</note>
</trans-unit>
@@ -7507,7 +7511,7 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>geblokkeerd door beheerder</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -709,8 +709,8 @@
<target>Wszystkie nowe wiadomości z %@ zostaną ukryte!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1709,8 +1709,8 @@ To jest twój jednorazowy link!</target>
<target>Obecne hasło…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5668,6 +5668,10 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
<target>Wersja serwera jest niekompatybilna z ustawieniami sieciowymi.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5809,6 +5813,10 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
<target>Pokaż status wiadomości</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Pokaż podgląd</target>
@@ -6033,10 +6041,6 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7099,8 +7103,8 @@ Powtórzyć prośbę dołączenia?</target>
<target>Możesz ustawić go jako widoczny dla swoich kontaktów SimpleX w Ustawieniach.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Możesz teraz wysyłać wiadomości do %@</target>
<note>notification body</note>
</trans-unit>
@@ -7507,7 +7511,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>zablokowany przez admina</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -709,8 +709,8 @@
<target>Все новые сообщения от %@ будут скрыты!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1709,8 +1709,8 @@ This is your own one-time link!</source>
<target>Текущий пароль…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5666,6 +5666,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Версия сервера несовместима с настройками сети.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5807,6 +5811,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Показать статус сообщения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Показывать уведомления</target>
@@ -6031,10 +6039,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7097,9 +7101,9 @@ Repeat join request?</source>
<target>Вы можете сделать его видимым для ваших контактов в SimpleX через Настройки.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<target>Вы теперь можете отправлять сообщения %@</target>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Вы теперь можете общаться с %@</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
@@ -7505,7 +7509,7 @@ SimpleX серверы не могут получить доступ к Ваше
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>заблокировано администратором</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -674,8 +674,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1617,8 +1617,8 @@ This is your own one-time link!</source>
<target>รหัสผ่านปัจจุบัน…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5401,6 +5401,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5535,6 +5539,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>แสดงตัวอย่าง</target>
@@ -5750,10 +5758,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6750,8 +6754,8 @@ Repeat join request?</source>
<source>You can make it visible to your SimpleX contacts via Settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>ตอนนี้คุณสามารถส่งข้อความถึง %@</target>
<note>notification body</note>
</trans-unit>
@@ -7142,7 +7146,7 @@ SimpleX servers cannot see your profile.</source>
</trans-unit>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -709,8 +709,8 @@
<target>%@ 'den gelen bütün yeni mesajlar saklı olacak!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1709,8 +1709,8 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Şu anki parola…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5668,6 +5668,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Sunucu sürümü ağ ayarlarıyla uyumlu değil.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5809,6 +5813,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Mesaj durumunu göster</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Ön gösterimi göser</target>
@@ -6033,10 +6041,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7099,8 +7103,8 @@ Katılma isteği tekrarlansın mı?</target>
<target>Ayarlardan SimpleX kişilerinize görünür yapabilirsiniz.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Artık %@ adresine mesaj gönderebilirsin</target>
<note>notification body</note>
</trans-unit>
@@ -7507,7 +7511,7 @@ SimpleX sunucuları profilinizi göremez.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>yönetici tarafından engellendi</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -709,8 +709,8 @@
<target>Всі нові повідомлення від %@ будуть приховані!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1709,8 +1709,8 @@ This is your own one-time link!</source>
<target>Поточна парольна фраза…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5668,6 +5668,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Серверна версія несумісна з мережевими налаштуваннями.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5809,6 +5813,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Показати статус повідомлення</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Показати попередній перегляд</target>
@@ -6033,10 +6041,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7099,8 +7103,8 @@ Repeat join request?</source>
<target>Ви можете зробити його видимим для ваших контактів у SimpleX за допомогою налаштувань.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Тепер ви можете надсилати повідомлення на адресу %@</target>
<note>notification body</note>
</trans-unit>
@@ -7507,7 +7511,7 @@ SimpleX servers cannot see your profile.</source>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>заблоковано адміністратором</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -696,8 +696,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1683,8 +1683,8 @@ This is your own one-time link!</source>
<target>现有密码……</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -5589,6 +5589,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
@@ -5729,6 +5733,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>显示预览</target>
@@ -5952,10 +5960,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7000,8 +7004,8 @@ Repeat join request?</source>
<target>你可以通过设置让它对你的 SimpleX 联系人可见。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>您现在可以给 %@ 发送消息</target>
<note>notification body</note>
</trans-unit>
@@ -7403,7 +7407,7 @@ SimpleX 服务器无法看到您的资料。</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>由管理员封禁</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
+22 -14
View File
@@ -119,7 +119,7 @@ class NotificationService: UNNotificationServiceExtension {
var threadId: UUID? = NSEThreads.shared.newThread()
var notificationInfo: NtfMessages?
var receiveEntityId: String?
var expectedMessages: Set<String> = []
var expectedMessage: String?
// return true if the message is taken - it prevents sending it to another NotificationService instance for processing
var shouldProcessNtf = false
var appSubscriber: AppSubscriber?
@@ -191,7 +191,7 @@ class NotificationService: UNNotificationServiceExtension {
let dbStatus = startChat()
if case .ok = dbStatus,
let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessages.count))")
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessage_ == nil ? 0 : 1))")
if let connEntity = ntfInfo.connEntity_ {
setBestAttemptNtf(
ntfInfo.ntfsEnabled
@@ -201,7 +201,7 @@ class NotificationService: UNNotificationServiceExtension {
if let id = connEntity.id, ntfInfo.msgTs != nil {
notificationInfo = ntfInfo
receiveEntityId = id
expectedMessages = Set(ntfInfo.ntfMessages.map { $0.msgId })
expectedMessage = ntfInfo.ntfMessage_.flatMap { $0.msgId }
shouldProcessNtf = true
return
}
@@ -224,12 +224,10 @@ class NotificationService: UNNotificationServiceExtension {
self.setBestAttemptNtf(.empty)
}
if case let .msgInfo(info) = ntf {
let found = expectedMessages.remove(info.msgId)
if found != nil {
logger.debug("NotificationService processNtf: msgInfo, last: \(self.expectedMessages.isEmpty)")
if expectedMessages.isEmpty {
self.deliverBestAttemptNtf()
}
if info.msgId == expectedMessage {
expectedMessage = nil
logger.debug("NotificationService processNtf: msgInfo")
self.deliverBestAttemptNtf()
return true
} else if info.msgTs > msgTs {
logger.debug("NotificationService processNtf: unexpected msgInfo, let other instance to process it, stopping this one")
@@ -392,6 +390,16 @@ func appStateSubscriber(onState: @escaping (AppState) -> Void) -> AppSubscriber
}
}
let seSubscriber = seMessageSubscriber {
switch $0 {
case let .state(state):
if state == .sendingMessage && NSEChatState.shared.value.canSuspend {
logger.debug("NotificationService: seSubscriber app state \(state.rawValue), suspending")
suspendChat(fastNSESuspendSchedule.timeout)
}
}
}
var receiverStarted = false
let startLock = DispatchSemaphore(value: 1)
let suspendLock = DispatchSemaphore(value: 1)
@@ -636,7 +644,7 @@ func apiGetActiveUser() -> User? {
}
func apiStartChat() throws -> Bool {
let r = sendSimpleXCmd(.startChat(mainApp: false))
let r = sendSimpleXCmd(.startChat(mainApp: false, enableSndFiles: false))
switch r {
case .chatStarted: return true
case .chatRunning: return false
@@ -677,9 +685,9 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
return nil
}
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
if case let .ntfMessages(user, connEntity_, msgTs, ntfMessages) = r, let user = user {
logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessages.count)")
return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessages: ntfMessages)
if case let .ntfMessages(user, connEntity_, msgTs, ntfMessage_) = r, let user = user {
logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessage_ == nil ? 0 : 1)")
return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessage_: ntfMessage_)
} else if case let .chatCmdError(_, error) = r {
logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))")
} else {
@@ -726,7 +734,7 @@ struct NtfMessages {
var user: User
var connEntity_: ConnectionEntity?
var msgTs: Date?
var ntfMessages: [NtfMsgInfo]
var ntfMessage_: NtfMsgInfo?
var ntfsEnabled: Bool {
user.showNotifications && (connEntity_?.ntfsEnabled ?? false)
+35
View File
@@ -0,0 +1,35 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>NSExtension</key>
<dict>
<key>NSExtensionAttributes</key>
<dict>
<key>NSExtensionActivationRule</key>
<dict>
<key>NSExtensionActivationSupportsText</key>
<true/>
<key>NSExtensionActivationSupportsAttachmentsWithMinCount</key>
<integer>0</integer>
<key>NSExtensionActivationSupportsAttachmentsWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebPageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsWebURLWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsFileWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsImageWithMaxCount</key>
<integer>1</integer>
<key>NSExtensionActivationSupportsMovieWithMaxCount</key>
<integer>1</integer>
</dict>
</dict>
<key>NSExtensionPointIdentifier</key>
<string>com.apple.share-services</string>
<key>NSExtensionPrincipalClass</key>
<string>ShareViewController</string>
</dict>
</dict>
</plist>
+39
View File
@@ -0,0 +1,39 @@
//
// SEChatState.swift
// SimpleX SE
//
// Created by User on 18/07/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
// SEStateGroupDefault must not be used in the share extension directly, only via this singleton
class SEChatState {
static let shared = SEChatState()
private var value_ = seStateGroupDefault.get()
var value: SEState {
value_
}
func set(_ state: SEState) {
seStateGroupDefault.set(state)
sendSEState(state)
value_ = state
}
}
/// Waits for other processes to set their state to suspended
/// Will wait for maximum of two seconds, since they might not be running
func waitForOtherProcessesToSuspend() async {
let startTime = CFAbsoluteTimeGetCurrent()
while CFAbsoluteTimeGetCurrent() - startTime < 2 {
try? await Task.sleep(nanoseconds: 100 * NSEC_PER_MSEC)
if appStateGroupDefault.get() == .suspended &&
nseStateGroupDefault.get() == .suspended {
break
}
}
}
+115
View File
@@ -0,0 +1,115 @@
//
// ShareAPI.swift
// SimpleX SE
//
// Created by User on 15/07/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import OSLog
import Foundation
import SimpleXChat
let logger = Logger()
func apiGetActiveUser() throws -> User? {
let r = sendSimpleXCmd(.showActiveUser)
switch r {
case let .activeUser(user): return user
case .chatCmdError(_, .error(.noActiveUser)): return nil
default: throw r
}
}
func apiStartChat() throws -> Bool {
let r = sendSimpleXCmd(.startChat(mainApp: false, enableSndFiles: true))
switch r {
case .chatStarted: return true
case .chatRunning: return false
default: throw r
}
}
func apiSetNetworkConfig(_ cfg: NetCfg) throws {
let r = sendSimpleXCmd(.apiSetNetworkConfig(networkConfig: cfg))
if case .cmdOk = r { return }
throw r
}
func apiSetAppFilePaths(filesFolder: String, tempFolder: String, assetsFolder: String) throws {
let r = sendSimpleXCmd(.apiSetAppFilePaths(filesFolder: filesFolder, tempFolder: tempFolder, assetsFolder: assetsFolder))
if case .cmdOk = r { return }
throw r
}
func apiSetEncryptLocalFiles(_ enable: Bool) throws {
let r = sendSimpleXCmd(.apiSetEncryptLocalFiles(enable: enable))
if case .cmdOk = r { return }
throw r
}
func apiGetChats(userId: User.ID) throws -> Array<ChatData> {
let r = sendSimpleXCmd(.apiGetChats(userId: userId))
if case let .apiChats(user: _, chats: chats) = r { return chats }
throw r
}
func apiSendMessage(
chatInfo: ChatInfo,
cryptoFile: CryptoFile?,
msgContent: MsgContent
) throws -> AChatItem {
let r = sendSimpleXCmd(
chatInfo.chatType == .local
? .apiCreateChatItem(
noteFolderId: chatInfo.apiId,
file: cryptoFile,
msg: msgContent
)
: .apiSendMessage(
type: chatInfo.chatType,
id: chatInfo.apiId,
file: cryptoFile,
quotedItemId: nil,
msg: msgContent,
live: false,
ttl: nil
)
)
if case let .newChatItem(_, chatItem) = r {
return chatItem
} else {
if let filePath = cryptoFile?.filePath { removeFile(filePath) }
throw r
}
}
func apiActivateChat() throws {
chatReopenStore()
let r = sendSimpleXCmd(.apiActivateChat(restoreChat: false))
if case .cmdOk = r { return }
throw r
}
func apiSuspendChat(expired: Bool) {
let r = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: expired ? 0 : 3_000000))
// Block until `chatSuspended` received or 3 seconds has passed
var suspended = false
if case .cmdOk = r, !expired {
let startTime = CFAbsoluteTimeGetCurrent()
while CFAbsoluteTimeGetCurrent() - startTime < 3 {
switch recvSimpleXMsg(messageTimeout: 3_500000) {
case .chatSuspended:
suspended = false
break
default: continue
}
}
}
if !suspended {
_ = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: 0))
}
logger.debug("close store")
chatCloseStore()
SEChatState.shared.set(.inactive)
}
+500
View File
@@ -0,0 +1,500 @@
//
// ShareModel.swift
// SimpleX SE
//
// Created by Levitating Pineapple on 09/07/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import UniformTypeIdentifiers
import AVFoundation
import SwiftUI
import SimpleXChat
/// Maximum size of hex encoded media previews
private let MAX_DATA_SIZE: Int64 = 14000
/// Maximum dimension (width or height) of an image, before passed for processing
private let MAX_DOWNSAMPLE_SIZE: Int64 = 2000
class ShareModel: ObservableObject {
@Published var sharedContent: SharedContent?
@Published var chats = Array<ChatData>()
@Published var profileImages = Dictionary<ChatInfo.ID, UIImage>()
@Published var search = String()
@Published var comment = String()
@Published var selected: ChatData?
@Published var isLoaded = false
@Published var bottomBar: BottomBar = .loadingSpinner
@Published var errorAlert: ErrorAlert?
enum BottomBar {
case sendButton
case loadingSpinner
case loadingBar(progress: Double)
var isLoading: Bool {
switch self {
case .sendButton: false
case .loadingSpinner: true
case .loadingBar: true
}
}
}
var completion: () -> Void = {
fatalError("completion has not been set")
}
private var itemProvider: NSItemProvider?
var isSendDisbled: Bool { sharedContent == nil || selected == nil }
var filteredChats: Array<ChatData> {
search.isEmpty
? filterChatsToForwardTo(chats: chats)
: filterChatsToForwardTo(chats: chats)
.filter { foundChat($0, search.localizedLowercase) }
}
func setup(context: NSExtensionContext) {
if let item = context.inputItems.first as? NSExtensionItem,
let itemProvider = item.attachments?.first {
self.itemProvider = itemProvider
self.completion = {
ShareModel.CompletionHandler.isEventLoopEnabled = false
context.completeRequest(returningItems: [item]) {
apiSuspendChat(expired: $0)
}
}
// Init Chat
Task {
if let e = initChat() {
await MainActor.run { errorAlert = e }
} else {
// Load Chats
Task {
switch fetchChats() {
case let .success(chats):
// Decode base64 images on background thread
let profileImages = chats.reduce(into: Dictionary<ChatInfo.ID, UIImage>()) { dict, chatData in
if let profileImage = chatData.chatInfo.image,
let uiImage = UIImage(base64Encoded: profileImage) {
dict[chatData.id] = uiImage
}
}
await MainActor.run {
self.chats = chats
self.profileImages = profileImages
withAnimation { isLoaded = true }
}
case let .failure(error):
await MainActor.run { errorAlert = error }
}
}
// Process Attachment
Task {
switch await self.itemProvider!.sharedContent() {
case let .success(chatItemContent):
await MainActor.run {
self.sharedContent = chatItemContent
self.bottomBar = .sendButton
if case let .text(string) = chatItemContent { comment = string }
}
case let .failure(errorAlert):
await MainActor.run { self.errorAlert = errorAlert }
}
}
}
}
}
}
func send() {
if let sharedContent, let selected {
Task {
await MainActor.run { self.bottomBar = .loadingSpinner }
do {
SEChatState.shared.set(.sendingMessage)
await waitForOtherProcessesToSuspend()
let ci = try apiSendMessage(
chatInfo: selected.chatInfo,
cryptoFile: sharedContent.cryptoFile,
msgContent: sharedContent.msgContent(comment: self.comment)
)
if selected.chatInfo.chatType == .local {
completion()
} else {
await MainActor.run { self.bottomBar = .loadingBar(progress: .zero) }
if let e = await handleEvents(
isGroupChat: ci.chatInfo.chatType == .group,
isWithoutFile: sharedContent.cryptoFile == nil,
chatItemId: ci.chatItem.id
) {
await MainActor.run { errorAlert = e }
} else {
completion()
}
}
} catch {
if let e = error as? ErrorAlert {
await MainActor.run { errorAlert = e }
}
}
}
}
}
private func initChat() -> ErrorAlert? {
do {
if hasChatCtrl() {
try apiActivateChat()
} else {
registerGroupDefaults()
haskell_init_se()
let (_, result) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation())
if let e = migrationError(result) { return e }
try apiSetAppFilePaths(
filesFolder: getAppFilesDirectory().path,
tempFolder: getTempFilesDirectory().path,
assetsFolder: getWallpaperDirectory().deletingLastPathComponent().path
)
let isRunning = try apiStartChat()
logger.log(level: .debug, "chat started, running: \(isRunning)")
}
try apiSetNetworkConfig(getNetCfg())
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
} catch { return ErrorAlert(error) }
return nil
}
private func migrationError(_ r: DBMigrationResult) -> ErrorAlert? {
let useKeychain = storeDBPassphraseGroupDefault.get()
let storedDBKey = kcDatabasePassword.get()
// This switch duplicates DatabaseErrorView.
// TODO allow entering passphrase and make messages the same as in DatabaseErrorView.
return switch r {
case .errorNotADatabase:
if useKeychain && storedDBKey != nil && storedDBKey != "" {
ErrorAlert(
title: "Wrong database passphrase",
message: "Database passphrase is different from saved in the keychain."
)
} else {
ErrorAlert(
title: "Encrypted database",
message: "Sharing is not supported when passphrase is not stored in KeyChain."
)
}
case let .errorMigration(_, migrationError):
switch migrationError {
case .upgrade:
ErrorAlert(
title: "Database upgrade required",
message: "Open the app to upgrade the database."
)
case .downgrade:
ErrorAlert(
title: "Database downgrade required",
message: "Open the app to downgrade the database."
)
case let .migrationError(mtrError):
ErrorAlert(
title: "Incompatible database version",
message: mtrErrorDescription(mtrError)
)
}
case let .errorSQL(_, migrationSQLError):
ErrorAlert(
title: "Database error",
message: "Error: \(migrationSQLError)"
)
case .errorKeychain:
ErrorAlert(
title: "Keychain error",
message: "Cannot access keychain to save database password"
)
case .invalidConfirmation:
ErrorAlert("Invalid migration confirmation")
case let .unknown(json):
ErrorAlert(
title: "Database error",
message: "Unknown database error: \(json)"
)
case .ok: nil
}
}
private func fetchChats() -> Result<Array<ChatData>, ErrorAlert> {
do {
guard let user = try apiGetActiveUser() else {
return .failure(
ErrorAlert(
title: "No active profile",
message: "Please create a profile in the SimpleX app"
)
)
}
return .success(try apiGetChats(userId: user.id))
} catch {
return .failure(ErrorAlert(error))
}
}
actor CompletionHandler {
static var isEventLoopEnabled = false
private var fileCompleted = false
private var messageCompleted = false
func completeFile() { fileCompleted = true }
func completeMessage() { messageCompleted = true }
var isRunning: Bool {
Self.isEventLoopEnabled && !(fileCompleted && messageCompleted)
}
}
/// Polls and processes chat events
/// Returns when message sending has completed optionally returning and error.
private func handleEvents(isGroupChat: Bool, isWithoutFile: Bool, chatItemId: ChatItem.ID) async -> ErrorAlert? {
func isMessage(for item: AChatItem?) -> Bool {
item.map { $0.chatItem.id == chatItemId } ?? false
}
CompletionHandler.isEventLoopEnabled = true
let ch = CompletionHandler()
if isWithoutFile { await ch.completeFile() }
var networkTimeout = CFAbsoluteTimeGetCurrent()
while await ch.isRunning {
if CFAbsoluteTimeGetCurrent() - networkTimeout > 30 {
networkTimeout = CFAbsoluteTimeGetCurrent()
await MainActor.run {
self.errorAlert = ErrorAlert(title: "No network connection") {
Button("Keep Trying", role: .cancel) { }
Button("Dismiss Sheet", role: .destructive) { self.completion() }
}
}
}
switch recvSimpleXMsg(messageTimeout: 1_000_000) {
case let .sndFileProgressXFTP(_, ci, _, sentSize, totalSize):
guard isMessage(for: ci) else { continue }
networkTimeout = CFAbsoluteTimeGetCurrent()
await MainActor.run {
withAnimation {
let progress = Double(sentSize) / Double(totalSize)
bottomBar = .loadingBar(progress: progress)
}
}
case let .sndFileCompleteXFTP(_, ci, _):
guard isMessage(for: ci) else { continue }
if isGroupChat {
await MainActor.run { bottomBar = .loadingSpinner }
}
await ch.completeFile()
if await !ch.isRunning { break }
case let .chatItemStatusUpdated(_, ci):
guard isMessage(for: ci) else { continue }
if let (title, message) = ci.chatItem.meta.itemStatus.statusInfo {
// `title` and `message` already localized and interpolated
return ErrorAlert(
title: "\(title)",
message: "\(message)"
)
} else if case let .sndSent(sndProgress) = ci.chatItem.meta.itemStatus {
switch sndProgress {
case .complete:
await ch.completeMessage()
case .partial:
if isGroupChat {
Task {
try? await Task.sleep(nanoseconds: 5 * NSEC_PER_SEC)
await ch.completeMessage()
}
}
}
}
case let .sndFileError(_, ci, _, errorMessage):
guard isMessage(for: ci) else { continue }
if let ci { cleanupFile(ci) }
return ErrorAlert(title: "File error", message: "\(fileErrorInfo(ci) ?? errorMessage)")
case let .sndFileWarning(_, ci, _, errorMessage):
guard isMessage(for: ci) else { continue }
if let ci { cleanupFile(ci) }
return ErrorAlert(title: "File error", message: "\(fileErrorInfo(ci) ?? errorMessage)")
case let .chatError(_, chatError):
return ErrorAlert(chatError)
case let .chatCmdError(_, chatError):
return ErrorAlert(chatError)
default: continue
}
}
return nil
}
private func fileErrorInfo(_ ci: AChatItem?) -> String? {
switch ci?.chatItem.file?.fileStatus {
case let .sndError(e): e.errorInfo
case let .sndWarning(e): e.errorInfo
default: nil
}
}
}
/// Chat Item Content extracted from `NSItemProvider` without the comment
enum SharedContent {
case image(preview: String, cryptoFile: CryptoFile)
case movie(preview: String, duration: Int, cryptoFile: CryptoFile)
case url(preview: LinkPreview)
case text(string: String)
case data(cryptoFile: CryptoFile)
var cryptoFile: CryptoFile? {
switch self {
case let .image(_, cryptoFile): cryptoFile
case let .movie(_, _, cryptoFile): cryptoFile
case .url: nil
case .text: nil
case let .data(cryptoFile): cryptoFile
}
}
func msgContent(comment: String) -> MsgContent {
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: comment, preview: preview)
case .text: .text(comment)
case .data: .file(comment)
}
}
}
extension NSItemProvider {
fileprivate func sharedContent() async -> Result<SharedContent, ErrorAlert> {
if let type = firstMatching(of: [.image, .movie, .fileURL, .url, .text]) {
switch type {
// Prepare Image message
case .image:
// Animated
return if hasItemConformingToTypeIdentifier(UTType.gif.identifier) {
if let url = try? await inPlaceUrl(type: type),
let data = try? Data(contentsOf: url),
let image = UIImage(data: data),
let cryptoFile = saveFile(data, generateNewFileName("IMG", "gif"), encrypted: privacyEncryptLocalFilesGroupDefault.get()),
let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) {
.success(.image(preview: preview, cryptoFile: cryptoFile))
} else { .failure(ErrorAlert("Error preparing message")) }
// Static
} else {
if let url = try? await inPlaceUrl(type: type),
let image = downsampleImage(at: url, to: MAX_DOWNSAMPLE_SIZE),
let cryptoFile = saveImage(image),
let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE) {
.success(.image(preview: preview, cryptoFile: cryptoFile))
} else { .failure(ErrorAlert("Error preparing message")) }
}
// Prepare Movie message
case .movie:
if let url = try? await inPlaceUrl(type: type),
let trancodedUrl = await transcodeVideo(from: url),
let (image, duration) = AVAsset(url: trancodedUrl).generatePreview(),
let preview = resizeImageToStrSize(image, maxDataSize: MAX_DATA_SIZE),
let cryptoFile = moveTempFileFromURL(trancodedUrl) {
try? FileManager.default.removeItem(at: trancodedUrl)
return .success(.movie(preview: preview, duration: duration, cryptoFile: cryptoFile))
} else { return .failure(ErrorAlert("Error preparing message")) }
// Prepare Data message
case .fileURL:
if let url = try? await inPlaceUrl(type: .data) {
if isFileTooLarge(for: url) {
let sizeString = ByteCountFormatter.string(
fromByteCount: Int64(getMaxFileSize(.xftp)),
countStyle: .binary
)
return .failure(
ErrorAlert(
title: "Large file!",
message: "Currently maximum supported file size is \(sizeString)."
)
)
}
if let file = saveFileFromURL(url) {
return .success(.data(cryptoFile: file))
}
}
return .failure(ErrorAlert("Error preparing file"))
// Prepare Link message
case .url:
if let url = try? await loadItem(forTypeIdentifier: type.identifier) as? URL {
let content: SharedContent =
// Option to disable previews needs to be taken into account
// if let linkPreview = await getLinkPreview(for: url) {
// .url(preview: linkPreview)
// } else {
.text(string: url.absoluteString)
// }
return .success(content)
} else { return .failure(ErrorAlert("Error preparing message")) }
// Prepare Text message
case .text:
return if let text = try? await loadItem(forTypeIdentifier: type.identifier) as? String {
.success(.text(string: text))
} else { .failure(ErrorAlert("Error preparing message")) }
default: return .failure(ErrorAlert("Unsupported format"))
}
} else {
return .failure(ErrorAlert("Unsupported format"))
}
}
private func inPlaceUrl(type: UTType) async throws -> URL {
try await withCheckedThrowingContinuation { cont in
let _ = loadInPlaceFileRepresentation(forTypeIdentifier: type.identifier) { url, bool, error in
if let url = url {
cont.resume(returning: url)
} else if let error = error {
cont.resume(throwing: error)
} else {
fatalError("Either `url` or `error` must be present")
}
}
}
}
private func firstMatching(of types: Array<UTType>) -> UTType? {
for type in types {
if hasItemConformingToTypeIdentifier(type.identifier) { return type }
}
return nil
}
}
fileprivate func transcodeVideo(from input: URL) async -> URL? {
let outputUrl = URL(
fileURLWithPath: generateNewFileName(
getTempFilesDirectory().path + "/" + "video", "mp4",
fullPath: true
)
)
if await makeVideoQualityLower(input, outputUrl: outputUrl) {
return outputUrl
} else {
try? FileManager.default.removeItem(at: outputUrl)
return nil
}
}
fileprivate func isFileTooLarge(for url: URL) -> Bool {
fileSize(url)
.map { $0 > getMaxFileSize(.xftp) }
?? false
}
+180
View File
@@ -0,0 +1,180 @@
//
// ShareView.swift
// SimpleX SE
//
// Created by Levitating Pineapple on 09/07/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
import SimpleXChat
struct ShareView: View {
@ObservedObject var model: ShareModel
@Environment(\.colorScheme) var colorScheme
var body: some View {
NavigationView {
ZStack(alignment: .bottom) {
if model.isLoaded {
List(model.filteredChats) { chat in
HStack {
profileImage(
chatInfoId: chat.chatInfo.id,
systemFallback: chatIconName(chat.chatInfo),
size: 30
)
Text(chat.chatInfo.displayName)
Spacer()
radioButton(selected: chat == model.selected)
}
.contentShape(Rectangle())
.onTapGesture { model.selected = model.selected == chat ? nil : chat }
.tag(chat)
}
} else {
ProgressView().frame(maxHeight: .infinity)
}
}
.navigationTitle("Share")
.safeAreaInset(edge: .bottom) {
switch model.bottomBar {
case .sendButton:
compose(isLoading: false)
case .loadingSpinner:
compose(isLoading: true)
case .loadingBar(let progress):
loadingBar(progress: progress)
}
}
}
.searchable(
text: $model.search,
placement: .navigationBarDrawer(displayMode: .always)
)
.alert($model.errorAlert) { alert in
Button("Ok") { model.completion() }
}
}
private func compose(isLoading: Bool) -> some View {
VStack(spacing: .zero) {
Divider()
if let content = model.sharedContent {
itemPreview(content)
}
HStack {
Group {
if #available(iOSApplicationExtension 16.0, *) {
TextField("Comment", text: $model.comment, axis: .vertical)
} else {
TextField("Comment", text: $model.comment)
}
}
.contentShape(Rectangle())
.disabled(isLoading)
.padding(.horizontal, 12)
.padding(.vertical, 4)
Group {
if isLoading {
ProgressView()
} else {
Button(action: model.send) {
Image(systemName: "arrow.up.circle.fill")
.resizable()
}
.disabled(model.isSendDisbled)
}
}
.frame(width: 28, height: 28)
.padding(6)
}
.background(Color(.systemBackground))
.clipShape(RoundedRectangle(cornerRadius: 20))
.overlay(
RoundedRectangle(cornerRadius: 20)
.strokeBorder(.secondary, lineWidth: 0.5).opacity(0.7)
)
.padding(8)
}
.background(.thinMaterial)
}
@ViewBuilder private func itemPreview(_ content: SharedContent) -> some View {
switch content {
case let .image(preview, _): imagePreview(preview)
case let .movie(preview, _, _): imagePreview(preview)
case let .url(linkPreview): imagePreview(linkPreview.image)
case let .data(cryptoFile):
previewArea {
Image(systemName: "doc.fill")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.foregroundColor(Color(uiColor: .tertiaryLabel))
.padding(.leading, 4)
Text(cryptoFile.filePath)
}
case .text: EmptyView()
}
}
@ViewBuilder private func imagePreview(_ img: String) -> some View {
if let img = UIImage(base64Encoded: img) {
previewArea {
Image(uiImage: img)
.resizable()
.scaledToFit()
.frame(minHeight: 40, maxHeight: 60)
}
} else {
EmptyView()
}
}
@ViewBuilder private func previewArea<V: View>(@ViewBuilder content: @escaping () -> V) -> some View {
HStack(alignment: .center, spacing: 8) {
content()
Spacer()
}
.padding(.vertical, 1)
.frame(minHeight: 54)
.background {
switch colorScheme {
case .light: LightColorPaletteApp.sentMessage
case .dark: DarkColorPaletteApp.sentMessage
@unknown default: Color(.tertiarySystemBackground)
}
}
Divider()
}
private func loadingBar(progress: Double) -> some View {
VStack {
Text("Sending File")
ProgressView(value: progress)
}
.padding()
.background(Material.ultraThin)
}
private func profileImage(chatInfoId: ChatInfo.ID, systemFallback: String, size: Double) -> some View {
Group {
if let uiImage = model.profileImages[chatInfoId] {
Image(uiImage: uiImage).resizable()
} else {
Image(systemName: systemFallback).resizable()
}
}
.foregroundStyle(Color(.tertiaryLabel))
.frame(width: size, height: size)
.clipShape(RoundedRectangle(cornerRadius: size * 0.225, style: .continuous))
}
private func radioButton(selected: Bool) -> some View {
Image(systemName: selected ? "checkmark.circle.fill" : "circle")
.imageScale(.large)
.foregroundStyle(selected ? Color.accentColor : Color(.tertiaryLabel))
}
}
@@ -0,0 +1,46 @@
//
// ShareViewController.swift
// SimpleX SE
//
// Created by Levitating Pineapple on 08/07/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import UIKit
import SwiftUI
import SimpleXChat
/// Extension Entry point
/// System will create this controller each time share sheet is invoked
/// using `NSExtensionPrincipalClass` in the info.plist
@objc(ShareViewController)
class ShareViewController: UIHostingController<ShareView> {
private let model = ShareModel()
// Assuming iOS continues to only allow single share sheet to be presented at once
static var isVisible: Bool = false
@objc init(nibName nibNameOrNil: String?, bundle nibBundleOrNil: Bundle?) {
super.init(rootView: ShareView(model: model))
}
@available(*, unavailable)
required init?(coder aDecoder: NSCoder) { fatalError() }
override func viewDidLoad() {
ShareModel.CompletionHandler.isEventLoopEnabled = false
model.setup(context: extensionContext!)
}
override func viewWillAppear(_ animated: Bool) {
logger.debug("ShareSheet will appear")
super.viewWillAppear(animated)
Self.isVisible = true
}
override func viewWillDisappear(_ animated: Bool) {
logger.debug("ShareSheet will dissappear")
super.viewWillDisappear(animated)
ShareModel.CompletionHandler.isEventLoopEnabled = false
Self.isVisible = false
}
}
@@ -0,0 +1,14 @@
<?xml version="1.0" encoding="UTF-8"?>
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
<plist version="1.0">
<dict>
<key>com.apple.security.application-groups</key>
<array>
<string>group.chat.simplex.app</string>
</array>
<key>keychain-access-groups</key>
<array>
<string>$(AppIdentifierPrefix)chat.simplex.app</string>
</array>
</dict>
</plist>
+216 -29
View File
@@ -178,7 +178,6 @@
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; };
64EEB0F72C353F1C00972D62 /* ServersSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */; };
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; };
8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; };
8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */; };
8C74C3E72C1B901900039E77 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C852B072C1086D100BA61E8 /* Color.swift */; };
@@ -194,9 +193,17 @@
8C9BC2652C240D5200875A27 /* ThemeModeEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */; };
8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */; };
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; };
CE1EB0E42C459A660099D896 /* ShareAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EB0E32C459A660099D896 /* ShareAPI.swift */; };
CE2AD9CE2C452A4D00E844E3 /* ChatUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */; };
CE3097FB2C4C0C9F00180898 /* ErrorAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */; };
CE38A29A2C3FCA54005ED185 /* ImageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */; };
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = CE38A29B2C3FCD72005ED185 /* SwiftyGif */; };
CE984D4B2C36C5D500E3AEFF /* ChatItemClipShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */; };
CEDE70222C48FD9500233B1F /* SEChatState.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEDE70212C48FD9500233B1F /* SEChatState.swift */; };
CEE723AA2C3BD3D70009AE93 /* ShareViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */; };
CEE723B12C3BD3D70009AE93 /* SimpleX SE.appex in Embed App Extensions */ = {isa = PBXBuildFile; fileRef = CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */; settings = {ATTRIBUTES = (RemoveHeadersOnCopy, ); }; };
CEE723F02C3D25C70009AE93 /* ShareView.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723EF2C3D25C70009AE93 /* ShareView.swift */; };
CEE723F22C3D25ED0009AE93 /* ShareModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEE723F12C3D25ED0009AE93 /* ShareModel.swift */; };
CEEA861D2C2ABCB50084E1EA /* ReverseList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEA861C2C2ABCB50084E1EA /* ReverseList.swift */; };
D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; };
D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; };
@@ -205,11 +212,12 @@
D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; };
D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; };
E50581062C3DDD9D009C3F71 /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = E50581052C3DDD9D009C3F71 /* Yams */; };
E505810C2C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E50581072C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4-ghc9.6.3.a */; };
E505810D2C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E50581082C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4.a */; };
E505810E2C4656A4009C3F71 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E50581092C4656A4009C3F71 /* libgmpxx.a */; };
E505810F2C4656A4009C3F71 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E505810A2C4656A4009C3F71 /* libffi.a */; };
E50581102C4656A4009C3F71 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E505810B2C4656A4009C3F71 /* libgmp.a */; };
E5DCF8D52C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8D02C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp-ghc9.6.3.a */; };
E5DCF8D62C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8D12C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp.a */; };
E5DCF8D72C56F7EF007928CC /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8D22C56F7EF007928CC /* libffi.a */; };
E5DCF8D82C56F7EF007928CC /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8D32C56F7EF007928CC /* libgmp.a */; };
E5DCF8D92C56F7EF007928CC /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5DCF8D42C56F7EF007928CC /* libgmpxx.a */; };
E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -241,6 +249,20 @@
remoteGlobalIDString = 5CE2BA672845308900EC33A6;
remoteInfo = SimpleXChat;
};
CEE723AF2C3BD3D70009AE93 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5CA059BE279559F40002BEB4 /* Project object */;
proxyType = 1;
remoteGlobalIDString = CEE723A62C3BD3D70009AE93;
remoteInfo = "SimpleX SE";
};
CEE723D12C3C21C90009AE93 /* PBXContainerItemProxy */ = {
isa = PBXContainerItemProxy;
containerPortal = 5CA059BE279559F40002BEB4 /* Project object */;
proxyType = 1;
remoteGlobalIDString = 5CE2BA672845308900EC33A6;
remoteInfo = SimpleXChat;
};
/* End PBXContainerItemProxy section */
/* Begin PBXCopyFilesBuildPhase section */
@@ -261,6 +283,7 @@
dstPath = "";
dstSubfolderSpec = 13;
files = (
CEE723B12C3BD3D70009AE93 /* SimpleX SE.appex in Embed App Extensions */,
5CE2BA9D284555F500EC33A6 /* SimpleX NSE.appex in Embed App Extensions */,
);
name = "Embed App Extensions";
@@ -489,7 +512,6 @@
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = "<group>"; };
64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersSummaryView.swift; sourceTree = "<group>"; };
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = "<group>"; };
8C05382D2B39887E006436DC /* VideoUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VideoUtils.swift; sourceTree = "<group>"; };
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
8C74C3EB2C1B92A900039E77 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = "<group>"; };
@@ -504,17 +526,27 @@
8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ThemeModeEditor.swift; sourceTree = "<group>"; };
8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallAudioDeviceManager.swift; sourceTree = "<group>"; };
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = "<group>"; };
CE1EB0E32C459A660099D896 /* ShareAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAPI.swift; sourceTree = "<group>"; };
CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUtils.swift; sourceTree = "<group>"; };
CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorAlert.swift; sourceTree = "<group>"; };
CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemClipShape.swift; sourceTree = "<group>"; };
CEDE70212C48FD9500233B1F /* SEChatState.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SEChatState.swift; sourceTree = "<group>"; };
CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX SE.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareViewController.swift; sourceTree = "<group>"; };
CEE723AE2C3BD3D70009AE93 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
CEE723D42C3C21F50009AE93 /* SimpleX SE.entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SimpleX SE.entitlements"; sourceTree = "<group>"; };
CEE723EF2C3D25C70009AE93 /* ShareView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareView.swift; sourceTree = "<group>"; };
CEE723F12C3D25ED0009AE93 /* ShareModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareModel.swift; sourceTree = "<group>"; };
CEEA861C2C2ABCB50084E1EA /* ReverseList.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = ReverseList.swift; sourceTree = "<group>"; };
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; };
D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; };
D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
E50581072C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4-ghc9.6.3.a"; sourceTree = "<group>"; };
E50581082C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4.a"; sourceTree = "<group>"; };
E50581092C4656A4009C3F71 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
E505810A2C4656A4009C3F71 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
E505810B2C4656A4009C3F71 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
E5DCF8D02C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp-ghc9.6.3.a"; sourceTree = "<group>"; };
E5DCF8D12C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp.a"; sourceTree = "<group>"; };
E5DCF8D22C56F7EF007928CC /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
E5DCF8D32C56F7EF007928CC /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
E5DCF8D42C56F7EF007928CC /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -554,14 +586,22 @@
buildActionMask = 2147483647;
files = (
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
E50581102C4656A4009C3F71 /* libgmp.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
E505810C2C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4-ghc9.6.3.a in Frameworks */,
E505810F2C4656A4009C3F71 /* libffi.a in Frameworks */,
E5DCF8D82C56F7EF007928CC /* libgmp.a in Frameworks */,
E50581062C3DDD9D009C3F71 /* Yams in Frameworks */,
E505810D2C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4.a in Frameworks */,
E5DCF8D62C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp.a in Frameworks */,
E5DCF8D72C56F7EF007928CC /* libffi.a in Frameworks */,
E5DCF8D92C56F7EF007928CC /* libgmpxx.a in Frameworks */,
E5DCF8D52C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp-ghc9.6.3.a in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
E505810E2C4656A4009C3F71 /* libgmpxx.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
E5DCF8DA2C56FABA007928CC /* Frameworks */ = {
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E5DCF8DB2C56FAC1007928CC /* SimpleXChat.framework in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -628,11 +668,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
E505810A2C4656A4009C3F71 /* libffi.a */,
E505810B2C4656A4009C3F71 /* libgmp.a */,
E50581092C4656A4009C3F71 /* libgmpxx.a */,
E50581072C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4-ghc9.6.3.a */,
E50581082C4656A4009C3F71 /* libHSsimplex-chat-6.0.0.1-6hE2sENjTY0J94Us5F3bT4.a */,
E5DCF8D22C56F7EF007928CC /* libffi.a */,
E5DCF8D32C56F7EF007928CC /* libgmp.a */,
E5DCF8D42C56F7EF007928CC /* libgmpxx.a */,
E5DCF8D02C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp-ghc9.6.3.a */,
E5DCF8D12C56F7EF007928CC /* libHSsimplex-chat-6.0.0.2-B4oiZFZeYN0AY2321yyqdp.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -684,7 +724,6 @@
64466DCB29FFE3E800E3D48D /* MailView.swift */,
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */,
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */,
8C05382D2B39887E006436DC /* VideoUtils.swift */,
8C7F8F0D2C19C0C100D16888 /* ViewModifiers.swift */,
8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */,
8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */,
@@ -703,6 +742,7 @@
5C764E5C279C70B7000C6508 /* Libraries */,
5CA059C2279559F40002BEB4 /* Shared */,
5CDCAD462818589900503DA2 /* SimpleX NSE */,
CEE723A82C3BD3D70009AE93 /* SimpleX SE */,
5CA059DA279559F40002BEB4 /* Tests iOS */,
5CE2BA692845308900EC33A6 /* SimpleXChat */,
5CA059CB279559F40002BEB4 /* Products */,
@@ -733,6 +773,7 @@
5CA059D7279559F40002BEB4 /* Tests iOS.xctest */,
5CDCAD452818589900503DA2 /* SimpleX NSE.appex */,
5CE2BA682845308900EC33A6 /* SimpleXChat.framework */,
CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */,
);
name = Products;
sourceTree = "<group>";
@@ -856,10 +897,12 @@
5CDCAD7228188CFF00503DA2 /* ChatTypes.swift */,
5CDCAD7428188D2900503DA2 /* APITypes.swift */,
5C5E5D3C282447AB00B0488A /* CallTypes.swift */,
CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */,
5C9FD96A27A56D4D0075386C /* JSON.swift */,
5CDCAD7D2818941F00503DA2 /* API.swift */,
5CDCAD80281A7E2700503DA2 /* Notifications.swift */,
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */,
64DAE1502809D9F5000DA960 /* FileUtils.swift */,
5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */,
5C00168028C4FE760094D739 /* KeyChain.swift */,
@@ -974,6 +1017,20 @@
path = Theme;
sourceTree = "<group>";
};
CEE723A82C3BD3D70009AE93 /* SimpleX SE */ = {
isa = PBXGroup;
children = (
CEE723D42C3C21F50009AE93 /* SimpleX SE.entitlements */,
CEE723AE2C3BD3D70009AE93 /* Info.plist */,
CEDE70212C48FD9500233B1F /* SEChatState.swift */,
CE1EB0E32C459A660099D896 /* ShareAPI.swift */,
CEE723F12C3D25ED0009AE93 /* ShareModel.swift */,
CEE723EF2C3D25C70009AE93 /* ShareView.swift */,
CEE723A92C3BD3D70009AE93 /* ShareViewController.swift */,
);
path = "SimpleX SE";
sourceTree = "<group>";
};
/* End PBXGroup section */
/* Begin PBXHeadersBuildPhase section */
@@ -1005,6 +1062,7 @@
dependencies = (
5CE2BA6F2845308900EC33A6 /* PBXTargetDependency */,
5CE2BA9F284555F500EC33A6 /* PBXTargetDependency */,
CEE723B02C3BD3D70009AE93 /* PBXTargetDependency */,
);
name = "SimpleX (iOS)";
packageProductDependencies = (
@@ -1076,6 +1134,24 @@
productReference = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */;
productType = "com.apple.product-type.framework";
};
CEE723A62C3BD3D70009AE93 /* SimpleX SE */ = {
isa = PBXNativeTarget;
buildConfigurationList = CEE723B42C3BD3D70009AE93 /* Build configuration list for PBXNativeTarget "SimpleX SE" */;
buildPhases = (
CEE723A32C3BD3D70009AE93 /* Sources */,
CEE723A52C3BD3D70009AE93 /* Resources */,
E5DCF8DA2C56FABA007928CC /* Frameworks */,
);
buildRules = (
);
dependencies = (
CEE723D22C3C21C90009AE93 /* PBXTargetDependency */,
);
name = "SimpleX SE";
productName = "SimpleX SE";
productReference = CEE723A72C3BD3D70009AE93 /* SimpleX SE.appex */;
productType = "com.apple.product-type.app-extension";
};
/* End PBXNativeTarget section */
/* Begin PBXProject section */
@@ -1083,7 +1159,7 @@
isa = PBXProject;
attributes = {
BuildIndependentTargetsInParallel = 1;
LastSwiftUpdateCheck = 1330;
LastSwiftUpdateCheck = 1540;
LastUpgradeCheck = 1340;
ORGANIZATIONNAME = "SimpleX Chat";
TargetAttributes = {
@@ -1103,6 +1179,9 @@
CreatedOnToolsVersion = 13.3;
LastSwiftMigration = 1330;
};
CEE723A62C3BD3D70009AE93 = {
CreatedOnToolsVersion = 15.4;
};
};
};
buildConfigurationList = 5CA059C1279559F40002BEB4 /* Build configuration list for PBXProject "SimpleX" */;
@@ -1144,6 +1223,7 @@
5CA059C9279559F40002BEB4 /* SimpleX (iOS) */,
5CA059D6279559F40002BEB4 /* Tests iOS */,
5CDCAD442818589900503DA2 /* SimpleX NSE */,
CEE723A62C3BD3D70009AE93 /* SimpleX SE */,
5CE2BA672845308900EC33A6 /* SimpleXChat */,
);
};
@@ -1183,6 +1263,13 @@
);
runOnlyForDeploymentPostprocessing = 0;
};
CEE723A52C3BD3D70009AE93 /* Resources */ = {
isa = PBXResourcesBuildPhase;
buildActionMask = 2147483647;
files = (
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXResourcesBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@@ -1299,7 +1386,6 @@
5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */,
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */,
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */,
8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */,
5CADE79C292131E900072E13 /* ContactPreferencesView.swift in Sources */,
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */,
5C9C2DA52894777E00CC63B1 /* GroupProfileView.swift in Sources */,
@@ -1368,6 +1454,7 @@
buildActionMask = 2147483647;
files = (
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */,
CE3097FB2C4C0C9F00180898 /* ErrorAlert.swift in Sources */,
5C00168128C4FE760094D739 /* KeyChain.swift in Sources */,
5CE2BA97284537A800EC33A6 /* dummy.m in Sources */,
5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */,
@@ -1384,10 +1471,23 @@
8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */,
5CE2BA8D284533A300EC33A6 /* CallTypes.swift in Sources */,
8C74C3E82C1B905B00039E77 /* ChatWallpaperTypes.swift in Sources */,
CE2AD9CE2C452A4D00E844E3 /* ChatUtils.swift in Sources */,
5CE2BA8E284533A300EC33A6 /* API.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
CEE723A32C3BD3D70009AE93 /* Sources */ = {
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
CEDE70222C48FD9500233B1F /* SEChatState.swift in Sources */,
CEE723F02C3D25C70009AE93 /* ShareView.swift in Sources */,
CE1EB0E42C459A660099D896 /* ShareAPI.swift in Sources */,
CEE723F22C3D25ED0009AE93 /* ShareModel.swift in Sources */,
CEE723AA2C3BD3D70009AE93 /* ShareViewController.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
};
/* End PBXSourcesBuildPhase section */
/* Begin PBXTargetDependency section */
@@ -1412,6 +1512,16 @@
target = 5CE2BA672845308900EC33A6 /* SimpleXChat */;
targetProxy = 5CE2BAA82845617C00EC33A6 /* PBXContainerItemProxy */;
};
CEE723B02C3BD3D70009AE93 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = CEE723A62C3BD3D70009AE93 /* SimpleX SE */;
targetProxy = CEE723AF2C3BD3D70009AE93 /* PBXContainerItemProxy */;
};
CEE723D22C3C21C90009AE93 /* PBXTargetDependency */ = {
isa = PBXTargetDependency;
target = 5CE2BA672845308900EC33A6 /* SimpleXChat */;
targetProxy = CEE723D12C3C21C90009AE93 /* PBXContainerItemProxy */;
};
/* End PBXTargetDependency section */
/* Begin PBXVariantGroup section */
@@ -1922,6 +2032,74 @@
};
name = Release;
};
CEE723B22C3BD3D70009AE93 /* Debug */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "SimpleX SE/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "SimpleX SE";
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 SimpleX Chat. All rights reserved.";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_ACTIVE_COMPILATION_CONDITIONS = "DEBUG $(inherited)";
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
};
name = Debug;
};
CEE723B32C3BD3D70009AE93 /* Release */ = {
isa = XCBuildConfiguration;
buildSettings = {
ASSETCATALOG_COMPILER_GENERATE_SWIFT_ASSET_SYMBOL_EXTENSIONS = YES;
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 1;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
GENERATE_INFOPLIST_FILE = YES;
INFOPLIST_FILE = "SimpleX SE/Info.plist";
INFOPLIST_KEY_CFBundleDisplayName = "SimpleX SE";
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2024 SimpleX Chat. All rights reserved.";
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
"@executable_path/../../Frameworks",
);
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
MARKETING_VERSION = 1.0;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
PRODUCT_NAME = "$(TARGET_NAME)";
SDKROOT = iphoneos;
SKIP_INSTALL = YES;
SWIFT_EMIT_LOC_STRINGS = YES;
SWIFT_VERSION = 5.0;
TARGETED_DEVICE_FAMILY = "1,2";
VALIDATE_PRODUCT = YES;
};
name = Release;
};
/* End XCBuildConfiguration section */
/* Begin XCConfigurationList section */
@@ -1970,6 +2148,15 @@
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
CEE723B42C3BD3D70009AE93 /* Build configuration list for PBXNativeTarget "SimpleX SE" */ = {
isa = XCConfigurationList;
buildConfigurations = (
CEE723B22C3BD3D70009AE93 /* Debug */,
CEE723B32C3BD3D70009AE93 /* Release */,
);
defaultConfigurationIsVisible = 0;
defaultConfigurationName = Release;
};
/* End XCConfigurationList section */
/* Begin XCRemoteSwiftPackageReference section */
@@ -1977,8 +2164,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/twostraws/CodeScanner";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.0.0;
kind = exactVersion;
version = 2.1.1;
};
};
8C73C1162C21E17B00892670 /* XCRemoteSwiftPackageReference "Yams" */ = {
@@ -2001,8 +2188,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kirualex/SwiftyGif";
requirement = {
branch = master;
kind = branch;
kind = revision;
revision = 5e8619335d394901379c9add5c4c1c2f420b3800;
};
};
D7F0E33729964E7D0068AF69 /* XCRemoteSwiftPackageReference "lzstring-swift" */ = {
@@ -0,0 +1,97 @@
<?xml version="1.0" encoding="UTF-8"?>
<Scheme
LastUpgradeVersion = "1540"
wasCreatedForAppExtension = "YES"
version = "2.0">
<BuildAction
parallelizeBuildables = "YES"
buildImplicitDependencies = "YES"
buildArchitectures = "Automatic">
<BuildActionEntries>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "CEE723A62C3BD3D70009AE93"
BuildableName = "SimpleX SE.appex"
BlueprintName = "SimpleX SE"
ReferencedContainer = "container:SimpleX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
<BuildActionEntry
buildForTesting = "YES"
buildForRunning = "YES"
buildForProfiling = "YES"
buildForArchiving = "YES"
buildForAnalyzing = "YES">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5CA059C9279559F40002BEB4"
BuildableName = "SimpleX.app"
BlueprintName = "SimpleX (iOS)"
ReferencedContainer = "container:SimpleX.xcodeproj">
</BuildableReference>
</BuildActionEntry>
</BuildActionEntries>
</BuildAction>
<TestAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
shouldUseLaunchSchemeArgsEnv = "YES"
shouldAutocreateTestPlan = "YES">
</TestAction>
<LaunchAction
buildConfiguration = "Debug"
selectedDebuggerIdentifier = ""
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
launchStyle = "0"
askForAppToLaunch = "Yes"
useCustomWorkingDirectory = "NO"
ignoresPersistentStateOnLaunch = "NO"
debugDocumentVersioning = "YES"
debugServiceExtension = "internal"
allowLocationSimulation = "YES"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5CA059C9279559F40002BEB4"
BuildableName = "SimpleX.app"
BlueprintName = "SimpleX (iOS)"
ReferencedContainer = "container:SimpleX.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</LaunchAction>
<ProfileAction
buildConfiguration = "Release"
shouldUseLaunchSchemeArgsEnv = "YES"
savedToolIdentifier = ""
useCustomWorkingDirectory = "NO"
debugDocumentVersioning = "YES"
askForAppToLaunch = "Yes"
launchAutomaticallySubstyle = "2">
<BuildableProductRunnable
runnableDebuggingMode = "0">
<BuildableReference
BuildableIdentifier = "primary"
BlueprintIdentifier = "5CA059C9279559F40002BEB4"
BuildableName = "SimpleX.app"
BlueprintName = "SimpleX (iOS)"
ReferencedContainer = "container:SimpleX.xcodeproj">
</BuildableReference>
</BuildableProductRunnable>
</ProfileAction>
<AnalyzeAction
buildConfiguration = "Debug">
</AnalyzeAction>
<ArchiveAction
buildConfiguration = "Release"
revealArchiveInOrganizer = "YES">
</ArchiveAction>
</Scheme>
+18 -10
View File
@@ -117,10 +117,10 @@ public func sendSimpleXCmd(_ cmd: ChatCommand, _ ctrl: chat_ctrl? = nil) -> Chat
}
// in microseconds
let MESSAGE_TIMEOUT: Int32 = 15_000_000
public let MESSAGE_TIMEOUT: Int32 = 15_000_000
public func recvSimpleXMsg(_ ctrl: chat_ctrl? = nil) -> ChatResponse? {
if let cjson = chat_recv_msg_wait(ctrl ?? getChatCtrl(), MESSAGE_TIMEOUT) {
public func recvSimpleXMsg(_ ctrl: chat_ctrl? = nil, messageTimeout: Int32 = MESSAGE_TIMEOUT) -> ChatResponse? {
if let cjson = chat_recv_msg_wait(ctrl ?? getChatCtrl(), messageTimeout) {
let s = fromCString(cjson)
return s == "" ? nil : chatResponse(s)
}
@@ -205,7 +205,7 @@ public func chatResponse(_ s: String) -> ChatResponse {
if let chatData = try? parseChatData(jChat) {
return chatData
}
return ChatData.invalidJSON(prettyJSON(jChat) ?? "")
return ChatData.invalidJSON(serializeJSON(jChat, options: .prettyPrinted) ?? "")
}
return .apiChats(user: user, chats: chats)
}
@@ -218,15 +218,15 @@ public func chatResponse(_ s: String) -> ChatResponse {
}
} else if type == "chatCmdError" {
if let jError = jResp["chatCmdError"] as? NSDictionary {
return .chatCmdError(user_: decodeUser_(jError), chatError: .invalidJSON(json: prettyJSON(jError) ?? ""))
return .chatCmdError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
}
} else if type == "chatError" {
if let jError = jResp["chatError"] as? NSDictionary {
return .chatError(user_: decodeUser_(jError), chatError: .invalidJSON(json: prettyJSON(jError) ?? ""))
return .chatError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
}
}
}
json = prettyJSON(j)
json = serializeJSON(j, options: .prettyPrinted)
}
return ChatResponse.response(type: type ?? "invalid", json: json ?? s)
}
@@ -239,6 +239,14 @@ private func decodeUser_(_ jDict: NSDictionary) -> UserRef? {
}
}
private func errorJson(_ jDict: NSDictionary) -> String? {
if let chatError = jDict["chatError"] {
serializeJSON(chatError)
} else {
serializeJSON(jDict)
}
}
func parseChatData(_ jChat: Any) throws -> ChatData {
let jChatDict = jChat as! NSDictionary
let chatInfo: ChatInfo = try decodeObject(jChatDict["chatInfo"]!)
@@ -251,7 +259,7 @@ func parseChatData(_ jChat: Any) throws -> ChatData {
return ChatItem.invalidJSON(
chatDir: decodeProperty(jCI, "chatDir"),
meta: decodeProperty(jCI, "meta"),
json: prettyJSON(jCI) ?? ""
json: serializeJSON(jCI, options: .prettyPrinted) ?? ""
)
}
return ChatData(chatInfo: chatInfo, chatItems: chatItems, chatStats: chatStats)
@@ -268,8 +276,8 @@ func decodeProperty<T: Decodable>(_ obj: Any, _ prop: NSString) -> T? {
return nil
}
func prettyJSON(_ obj: Any) -> String? {
if let d = try? JSONSerialization.data(withJSONObject: obj, options: .prettyPrinted) {
func serializeJSON(_ obj: Any, options: JSONSerialization.WritingOptions = []) -> String? {
if let d = try? JSONSerialization.data(withJSONObject: obj, options: options) {
return String(decoding: d, as: UTF8.self)
}
return nil
+35 -5
View File
@@ -26,7 +26,8 @@ public enum ChatCommand {
case apiMuteUser(userId: Int64)
case apiUnmuteUser(userId: Int64)
case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?)
case startChat(mainApp: Bool)
case startChat(mainApp: Bool, enableSndFiles: Bool)
case checkChatRunning
case apiStopChat
case apiActivateChat(restoreChat: Bool)
case apiSuspendChat(timeoutMicroseconds: Int)
@@ -146,6 +147,7 @@ public enum ChatCommand {
case apiStandaloneFileInfo(url: String)
// misc
case showVersion
case getAgentSubsTotal(userId: Int64)
case getAgentServersSummary(userId: Int64)
case resetAgentServersStats
case string(String)
@@ -171,7 +173,8 @@ public enum ChatCommand {
case let .apiMuteUser(userId): return "/_mute user \(userId)"
case let .apiUnmuteUser(userId): return "/_unmute user \(userId)"
case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))"
case let .startChat(mainApp): return "/_start main=\(onOff(mainApp))"
case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))"
case .checkChatRunning: return "/_check running"
case .apiStopChat: return "/_stop"
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
@@ -309,6 +312,7 @@ public enum ChatCommand {
case let .apiDownloadStandaloneFile(userId, link, file): return "/_download \(userId) \(link) \(file.filePath)"
case let .apiStandaloneFileInfo(link): return "/_download info \(link)"
case .showVersion: return "/version"
case let .getAgentSubsTotal(userId): return "/get subs total \(userId)"
case let .getAgentServersSummary(userId): return "/get servers summary \(userId)"
case .resetAgentServersStats: return "/reset servers stats"
case let .string(str): return str
@@ -332,6 +336,7 @@ public enum ChatCommand {
case .apiUnmuteUser: return "apiUnmuteUser"
case .apiDeleteUser: return "apiDeleteUser"
case .startChat: return "startChat"
case .checkChatRunning: return "checkChatRunning"
case .apiStopChat: return "apiStopChat"
case .apiActivateChat: return "apiActivateChat"
case .apiSuspendChat: return "apiSuspendChat"
@@ -447,6 +452,7 @@ public enum ChatCommand {
case .apiDownloadStandaloneFile: return "apiDownloadStandaloneFile"
case .apiStandaloneFileInfo: return "apiStandaloneFileInfo"
case .showVersion: return "showVersion"
case .getAgentSubsTotal: return "getAgentSubsTotal"
case .getAgentServersSummary: return "getAgentServersSummary"
case .resetAgentServersStats: return "resetAgentServersStats"
case .string: return "console command"
@@ -575,6 +581,7 @@ public enum ChatResponse: Decodable, Error {
case userContactLinkDeleted(user: User)
case contactConnected(user: UserRef, contact: Contact, userCustomProfile: Profile?)
case contactConnecting(user: UserRef, contact: Contact)
case contactSndReady(user: UserRef, contact: Contact)
case receivedContactRequest(user: UserRef, contactRequest: UserContactRequest)
case acceptingContactRequest(user: UserRef, contact: Contact)
case contactRequestRejected(user: UserRef)
@@ -661,7 +668,7 @@ public enum ChatResponse: Decodable, Error {
case callInvitations(callInvitations: [RcvCallInvitation])
case ntfTokenStatus(status: NtfTknStatus)
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode, ntfServer: String)
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessage_: NtfMsgInfo?)
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo)
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
case contactDisabled(user: UserRef, contact: Contact)
@@ -677,6 +684,7 @@ public enum ChatResponse: Decodable, Error {
// misc
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
case cmdOk(user: UserRef?)
case agentSubsTotal(user: UserRef, subsTotal: SMPServerSubs, hasSession: Bool)
case agentServersSummary(user: UserRef, serversSummary: PresentedServersSummary)
case agentSubsSummary(user: UserRef, subsSummary: SMPServerSubs)
case chatCmdError(user_: UserRef?, chatError: ChatError)
@@ -742,6 +750,7 @@ public enum ChatResponse: Decodable, Error {
case .userContactLinkDeleted: return "userContactLinkDeleted"
case .contactConnected: return "contactConnected"
case .contactConnecting: return "contactConnecting"
case .contactSndReady: return "contactSndReady"
case .receivedContactRequest: return "receivedContactRequest"
case .acceptingContactRequest: return "acceptingContactRequest"
case .contactRequestRejected: return "contactRequestRejected"
@@ -837,6 +846,7 @@ public enum ChatResponse: Decodable, Error {
case .contactPQEnabled: return "contactPQEnabled"
case .versionInfo: return "versionInfo"
case .cmdOk: return "cmdOk"
case .agentSubsTotal: return "agentSubsTotal"
case .agentServersSummary: return "agentServersSummary"
case .agentSubsSummary: return "agentSubsSummary"
case .chatCmdError: return "chatCmdError"
@@ -907,6 +917,7 @@ public enum ChatResponse: Decodable, Error {
case .userContactLinkDeleted: return noDetails
case let .contactConnected(u, contact, _): return withUser(u, String(describing: contact))
case let .contactConnecting(u, contact): return withUser(u, String(describing: contact))
case let .contactSndReady(u, contact): return withUser(u, String(describing: contact))
case let .receivedContactRequest(u, contactRequest): return withUser(u, String(describing: contactRequest))
case let .acceptingContactRequest(u, contact): return withUser(u, String(describing: contact))
case .contactRequestRejected: return noDetails
@@ -1002,6 +1013,7 @@ public enum ChatResponse: Decodable, Error {
case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)")
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
case .cmdOk: return noDetails
case let .agentSubsTotal(u, subsTotal, hasSession): return withUser(u, "subsTotal: \(String(describing: subsTotal))\nhasSession: \(hasSession)")
case let .agentServersSummary(u, serversSummary): return withUser(u, String(describing: serversSummary))
case let .agentSubsSummary(u, subsSummary): return withUser(u, String(describing: subsSummary))
case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError))
@@ -1228,7 +1240,7 @@ public struct ProtocolTestFailure: Decodable, Error, Equatable {
public var localizedDescription: String {
let err = String.localizedStringWithFormat(NSLocalizedString("Test failed at step %@.", comment: "server test failure"), testStep.text)
switch testError {
case .SMP(.AUTH):
case .SMP(_, .AUTH):
return err + " " + NSLocalizedString("Server requires authorization to create queues, check password", comment: "server test error")
case .XFTP(.AUTH):
return err + " " + NSLocalizedString("Server requires authorization to upload, check password", comment: "server test error")
@@ -1887,7 +1899,7 @@ public enum SQLiteError: Decodable, Hashable {
public enum AgentErrorType: Decodable, Hashable {
case CMD(cmdErr: CommandErrorType)
case CONN(connErr: ConnectionErrorType)
case SMP(smpErr: ProtocolErrorType)
case SMP(serverAddress: String, smpErr: ProtocolErrorType)
case NTF(ntfErr: ProtocolErrorType)
case XFTP(xftpErr: XFTPErrorType)
case PROXY(proxyServer: String, relayServer: String, proxyErr: ProxyClientError)
@@ -2087,6 +2099,7 @@ public struct AppSettings: Codable, Equatable, Hashable {
public var privacyShowChatPreviews: Bool? = nil
public var privacySaveLastDraft: Bool? = nil
public var privacyProtectScreen: Bool? = nil
public var privacyMediaBlurRadius: Int? = nil
public var notificationMode: AppSettingsNotificationMode? = nil
public var notificationPreviewMode: NotificationPreviewMode? = nil
public var webrtcPolicyRelay: Bool? = nil
@@ -2116,6 +2129,7 @@ public struct AppSettings: Codable, Equatable, Hashable {
if privacyShowChatPreviews != def.privacyShowChatPreviews { empty.privacyShowChatPreviews = privacyShowChatPreviews }
if privacySaveLastDraft != def.privacySaveLastDraft { empty.privacySaveLastDraft = privacySaveLastDraft }
if privacyProtectScreen != def.privacyProtectScreen { empty.privacyProtectScreen = privacyProtectScreen }
if privacyMediaBlurRadius != def.privacyMediaBlurRadius { empty.privacyMediaBlurRadius = privacyMediaBlurRadius }
if notificationMode != def.notificationMode { empty.notificationMode = notificationMode }
if notificationPreviewMode != def.notificationPreviewMode { empty.notificationPreviewMode = notificationPreviewMode }
if webrtcPolicyRelay != def.webrtcPolicyRelay { empty.webrtcPolicyRelay = webrtcPolicyRelay }
@@ -2146,6 +2160,7 @@ public struct AppSettings: Codable, Equatable, Hashable {
privacyShowChatPreviews: true,
privacySaveLastDraft: true,
privacyProtectScreen: false,
privacyMediaBlurRadius: 0,
notificationMode: AppSettingsNotificationMode.instant,
notificationPreviewMode: NotificationPreviewMode.message,
webrtcPolicyRelay: true,
@@ -2321,6 +2336,8 @@ public struct ServerSessions: Codable {
ssErrors: 0,
ssConnecting: 0
)
public var hasSess: Bool { ssConnected > 0 }
}
public struct SMPServerSubs: Codable {
@@ -2374,6 +2391,10 @@ public struct AgentSMPServerStatsData: Codable {
public var _connSubAttempts: Int
public var _connSubIgnored: Int
public var _connSubErrs: Int
public var _ntfKey: Int
public var _ntfKeyAttempts: Int
public var _ntfKeyDeleted: Int
public var _ntfKeyDeleteAttempts: Int
}
public struct XFTPServersSummary: Codable {
@@ -2413,3 +2434,12 @@ public struct AgentXFTPServerStatsData: Codable {
public var _deleteAttempts: Int
public var _deleteErrs: Int
}
public struct AgentNtfServerStatsData: Codable {
public var _ntfCreated: Int
public var _ntfCreateAttempts: Int
public var _ntfChecked: Int
public var _ntfCheckAttempts: Int
public var _ntfDeleted: Int
public var _ntfDelAttempts: Int
}
+12
View File
@@ -13,6 +13,7 @@ public let appSuspendTimeout: Int = 15 // seconds
let GROUP_DEFAULT_APP_STATE = "appState"
let GROUP_DEFAULT_NSE_STATE = "nseState"
let GROUP_DEFAULT_SE_STATE = "seState"
let GROUP_DEFAULT_DB_CONTAINER = "dbContainer"
public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart"
public let GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN = "chatLastBackgroundRun"
@@ -136,6 +137,11 @@ public enum NSEState: String, Codable {
}
}
public enum SEState: String, Codable {
case inactive
case sendingMessage
}
public enum DBContainer: String {
case documents
case group
@@ -155,6 +161,12 @@ public let nseStateGroupDefault = EnumDefault<NSEState>(
withDefault: .suspended // so that NSE that was never launched does not delay the app from resuming
)
public let seStateGroupDefault = EnumDefault<SEState>(
defaults: groupDefaults,
forKey: GROUP_DEFAULT_SE_STATE,
withDefault: .inactive
)
// inactive app states do not include "stopped" state
public func allowBackgroundRefresh() -> Bool {
appStateGroupDefault.get().inactive && nseStateGroupDefault.get().inactive
+26 -3
View File
@@ -1463,7 +1463,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat, Hashable {
)
}
public struct ChatData: Decodable, Identifiable, Hashable {
public struct ChatData: Decodable, Identifiable, Hashable, ChatLike {
public var chatInfo: ChatInfo
public var chatItems: [ChatItem]
public var chatStats: ChatStats
@@ -1512,10 +1512,11 @@ public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
public var id: ChatId { get { "@\(contactId)" } }
public var apiId: Int64 { get { contactId } }
public var ready: Bool { get { activeConn?.connStatus == .ready } }
public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } }
public var active: Bool { get { contactStatus == .active } }
public var sendMsgEnabled: Bool { get {
(
ready
sndReady
&& active
&& !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false)
&& !(activeConn?.connDisabled ?? true)
@@ -1824,7 +1825,7 @@ public enum ConnStatus: String, Decodable, Hashable {
case .joined: return false
case .requested: return true
case .accepted: return true
case .sndReady: return false
case .sndReady: return nil
case .ready: return nil
case .deleted: return nil
}
@@ -3293,6 +3294,28 @@ public struct CIFile: Decodable, Hashable {
}
}
}
public var showStatusIconInSmallView: Bool {
get {
switch fileStatus {
case .sndStored: fileProtocol != .local
case .sndTransfer: true
case .sndComplete: false
case .sndCancelled: true
case .sndError: true
case .sndWarning: true
case .rcvInvitation: false
case .rcvAccepted: true
case .rcvTransfer: true
case .rcvAborted: true
case .rcvCancelled: true
case .rcvComplete: false
case .rcvError: true
case .rcvWarning: true
case .invalid: true
}
}
}
}
public struct CryptoFile: Codable, Hashable {
+62
View File
@@ -0,0 +1,62 @@
//
// ChatUtils.swift
// SimpleXChat
//
// Created by Levitating Pineapple on 15/07/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import Foundation
public protocol ChatLike {
var chatInfo: ChatInfo { get}
var chatItems: [ChatItem] { get }
var chatStats: ChatStats { get }
}
public func filterChatsToForwardTo<C: ChatLike>(chats: [C]) -> [C] {
var filteredChats = chats.filter { c in
c.chatInfo.chatType != .local && canForwardToChat(c.chatInfo)
}
if let privateNotes = chats.first(where: { $0.chatInfo.chatType == .local }) {
filteredChats.insert(privateNotes, at: 0)
}
return filteredChats
}
public func foundChat(_ chat: ChatLike, _ searchStr: String) -> Bool {
let cInfo = chat.chatInfo
return switch cInfo {
case let .direct(contact):
viewNameContains(cInfo, searchStr) ||
contact.profile.displayName.localizedLowercase.contains(searchStr) ||
contact.fullName.localizedLowercase.contains(searchStr)
default:
viewNameContains(cInfo, searchStr)
}
func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool {
cInfo.chatViewName.localizedLowercase.contains(s)
}
}
private func canForwardToChat(_ cInfo: ChatInfo) -> Bool {
switch cInfo {
case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv
case let .group(groupInfo): groupInfo.sendMsgEnabled
case let .local(noteFolder): noteFolder.sendMsgEnabled
case .contactRequest: false
case .contactConnection: false
case .invalidJSON: false
}
}
public func chatIconName(_ cInfo: ChatInfo) -> String {
switch cInfo {
case .direct: "person.crop.circle.fill"
case .group: "person.2.circle.fill"
case .local: "folder.circle.fill"
case .contactRequest: "person.crop.circle.fill"
default: "circle.fill"
}
}
+159
View File
@@ -0,0 +1,159 @@
//
// ErrorAlert.swift
// SimpleXChat
//
// Created by Levitating Pineapple on 20/07/2024.
// Copyright © 2024 SimpleX Chat. All rights reserved.
//
import SwiftUI
public struct ErrorAlert: Error {
public let title: LocalizedStringKey
public let message: LocalizedStringKey?
public let actions: Optional<() -> AnyView>
public init(
title: LocalizedStringKey,
message: LocalizedStringKey? = nil
) {
self.title = title
self.message = message
self.actions = nil
}
public init<A: View>(
title: LocalizedStringKey,
message: LocalizedStringKey? = nil,
@ViewBuilder actions: @escaping () -> A
) {
self.title = title
self.message = message
self.actions = { AnyView(actions()) }
}
public init(_ title: LocalizedStringKey) {
self = ErrorAlert(title: title)
}
public init(_ error: any Error) {
self = if let chatResponse = error as? ChatResponse {
ErrorAlert(chatResponse)
} else {
ErrorAlert(LocalizedStringKey(error.localizedDescription))
}
}
public init(_ chatError: ChatError) {
self = ErrorAlert("\(chatErrorString(chatError))")
}
public init(_ chatResponse: ChatResponse) {
self = if let networkErrorAlert = getNetworkErrorAlert(chatResponse) {
networkErrorAlert
} else {
ErrorAlert("\(responseError(chatResponse))")
}
}
}
extension LocalizedStringKey: @unchecked Sendable { }
extension View {
/// Bridges ``ErrorAlert`` to the generic alert API.
/// - Parameters:
/// - errorAlert: Binding to the Error, which is rendered in the alert
/// - actions: View Builder containing action buttons.
/// System defaults to `Ok` dismiss error action, when no actions are provided.
/// System implicitly adds `Cancel` action, if a destructive action is present
///
/// - Returns: View, which displays ErrorAlert?, when set.
@ViewBuilder public func alert<A: View>(
_ errorAlert: Binding<ErrorAlert?>,
@ViewBuilder actions: (ErrorAlert) -> A = { _ in EmptyView() }
) -> some View {
if let alert = errorAlert.wrappedValue {
self.alert(
alert.title,
isPresented: Binding<Bool>(
get: { errorAlert.wrappedValue != nil },
set: { if !$0 { errorAlert.wrappedValue = nil } }
),
actions: {
if let actions_ = alert.actions {
actions_()
} else {
actions(alert)
}
},
message: {
if let message = alert.message { Text(message) }
}
)
} else { self }
}
}
public func getNetworkErrorAlert(_ r: ChatResponse) -> ErrorAlert? {
switch r {
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))):
return ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))):
return ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .HOST))):
return ErrorAlert(title: "Connection error", message: "Server address is incompatible with network settings: \(serverHostname(addr)).")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TRANSPORT(.version)))):
return ErrorAlert(title: "Connection error", message: "Server version is incompatible with your app: \(serverHostname(addr)).")
case let .chatCmdError(_, .errorAgent(.SMP(serverAddress, .PROXY(proxyErr)))):
return smpProxyErrorAlert(proxyErr, serverAddress)
case let .chatCmdError(_, .errorAgent(.PROXY(proxyServer, relayServer, .protocolError(.PROXY(proxyErr))))):
return proxyDestinationErrorAlert(proxyErr, proxyServer, relayServer)
default:
return nil
}
}
private func smpProxyErrorAlert(_ proxyErr: ProxyError, _ srvAddr: String) -> ErrorAlert? {
switch proxyErr {
case .BROKER(brokerErr: .TIMEOUT):
return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.")
case .BROKER(brokerErr: .NETWORK):
return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.")
case .BROKER(brokerErr: .HOST):
return ErrorAlert(title: "Private routing error", message: "Forwarding server address is incompatible with network settings: \(serverHostname(srvAddr)).")
case .BROKER(brokerErr: .TRANSPORT(.version)):
return ErrorAlert(title: "Private routing error", message: "Forwarding server version is incompatible with network settings: \(serverHostname(srvAddr)).")
default:
return nil
}
}
private func proxyDestinationErrorAlert(_ proxyErr: ProxyError, _ proxyServer: String, _ relayServer: String) -> ErrorAlert? {
switch proxyErr {
case .BROKER(brokerErr: .TIMEOUT):
return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.")
case .BROKER(brokerErr: .NETWORK):
return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.")
case .NO_SESSION:
return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.")
case .BROKER(brokerErr: .HOST):
return ErrorAlert(title: "Private routing error", message: "Destination server address of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)) settings.")
case .BROKER(brokerErr: .TRANSPORT(.version)):
return ErrorAlert(title: "Private routing error", message: "Destination server version of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)).")
default:
return nil
}
}
public func serverHostname(_ srv: String) -> String {
parseServerAddress(srv)?.hostnames.first ?? srv
}
public func mtrErrorDescription(_ err: MTRError) -> LocalizedStringKey {
switch err {
case let .noDown(dbMigrations):
"database version is newer than the app, but no down migration for: \(dbMigrations.joined(separator: ", "))"
case let .different(appMigration, dbMigration):
"different migration in the app/database: \(appMigration) / \(dbMigration)"
}
}
+80
View File
@@ -10,6 +10,7 @@ import Foundation
import SwiftUI
import AVKit
import SwiftyGif
import LinkPresentation
public func getLoadedFileSource(_ file: CIFile?) -> CryptoFile? {
if let file = file, file.loaded {
@@ -158,6 +159,34 @@ public func imageHasAlpha(_ img: UIImage) -> Bool {
return false
}
/// Reduces image size, while consuming less RAM
///
/// Used by ShareExtension to downsize large images
/// before passing them to regular image processing pipeline
/// to avoid exceeding 120MB memory
///
/// - Parameters:
/// - url: Location of the image data
/// - size: Maximum dimension (width or height)
/// - Returns: Downsampled image or `nil`, if the image can't be located
public func downsampleImage(at url: URL, to size: Int64) -> UIImage? {
autoreleasepool {
if let source = CGImageSourceCreateWithURL(url as CFURL, nil) {
CGImageSourceCreateThumbnailAtIndex(
source,
Int.zero,
[
kCGImageSourceCreateThumbnailFromImageAlways: true,
kCGImageSourceShouldCacheImmediately: true,
kCGImageSourceCreateThumbnailWithTransform: true,
kCGImageSourceThumbnailMaxPixelSize: String(size) as CFString
] as CFDictionary
)
.map { UIImage(cgImage: $0) }
} else { nil }
}
}
public func saveFileFromURL(_ url: URL) -> CryptoFile? {
let encrypted = privacyEncryptLocalFilesGroupDefault.get()
let savedFile: CryptoFile?
@@ -281,6 +310,21 @@ private func dropPrefix(_ s: String, _ prefix: String) -> String {
s.hasPrefix(prefix) ? String(s.dropFirst(prefix.count)) : s
}
public func makeVideoQualityLower(_ input: URL, outputUrl: URL) async -> Bool {
let asset: AVURLAsset = AVURLAsset(url: input, options: nil)
if let s = AVAssetExportSession(asset: asset, presetName: AVAssetExportPreset640x480) {
s.outputURL = outputUrl
s.outputFileType = .mp4
s.metadataItemFilter = AVMetadataItemFilter.forSharing()
await s.export()
if let err = s.error {
logger.error("Failed to export video with error: \(err)")
}
return s.status == .completed
}
return false
}
extension AVAsset {
public func generatePreview() -> (UIImage, Int)? {
let generator = AVAssetImageGenerator(asset: self)
@@ -348,3 +392,39 @@ extension UIImage {
}
}
}
public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) {
logger.debug("getLinkMetadata: fetching URL preview")
LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in
if let e = error {
logger.error("Error retrieving link metadata: \(e.localizedDescription)")
}
if let metadata = metadata,
let imageProvider = metadata.imageProvider,
imageProvider.canLoadObject(ofClass: UIImage.self) {
imageProvider.loadObject(ofClass: UIImage.self){ object, error in
var linkPreview: LinkPreview? = nil
if let error = error {
logger.error("Couldn't load image preview from link metadata with error: \(error.localizedDescription)")
} else {
if let image = object as? UIImage,
let resized = resizeImageToStrSize(image, maxDataSize: 14000),
let title = metadata.title,
let uri = metadata.originalURL {
linkPreview = LinkPreview(uri: uri, title: title, image: resized)
}
}
cb(linkPreview)
}
} else {
logger.error("Could not load link preview image")
cb(nil)
}
}
}
public func getLinkPreview(for url: URL) async -> LinkPreview? {
await withCheckedContinuation { cont in
getLinkPreview(url: url) { cont.resume(returning: $0) }
}
}
+1 -1
View File
@@ -47,7 +47,7 @@ public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact)
hideContent ? NSLocalizedString("A new contact", comment: "notification title") : contact.displayName
),
body: String.localizedStringWithFormat(
NSLocalizedString("You can now send messages to %@", comment: "notification body"),
NSLocalizedString("You can now chat with %@", comment: "notification body"),
hideContent ? NSLocalizedString("this contact", comment: "notification title") : contact.chatViewName
),
targetContentIdentifier: contact.id,
@@ -12,6 +12,8 @@ public typealias AppSubscriber = SharedFileSubscriber<ProcessMessage<AppProcessM
public typealias NSESubscriber = SharedFileSubscriber<ProcessMessage<NSEProcessMessage>>
public typealias SESubscriber = SharedFileSubscriber<ProcessMessage<SEProcessMessage>>
public class SharedFileSubscriber<Message: Codable>: NSObject, NSFilePresenter {
var fileURL: URL
public var presentedItemURL: URL?
@@ -57,6 +59,8 @@ let appMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent(
let nseMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.SimpleX-NSE.messages", isDirectory: false)
let seMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.SimpleX-SE.messages", isDirectory: false)
public struct ProcessMessage<Message: Codable>: Codable {
var createdAt: Date = Date.now
var message: Message
@@ -70,6 +74,10 @@ public enum NSEProcessMessage: Codable {
case state(state: NSEState)
}
public enum SEProcessMessage: Codable {
case state(state: SEState)
}
public func sendAppProcessMessage(_ message: AppProcessMessage) {
SharedFileSubscriber.notify(url: appMessagesSharedFile, message: ProcessMessage(message: message))
}
@@ -78,6 +86,10 @@ public func sendNSEProcessMessage(_ message: NSEProcessMessage) {
SharedFileSubscriber.notify(url: nseMessagesSharedFile, message: ProcessMessage(message: message))
}
public func sendSEProcessMessage(_ message: SEProcessMessage) {
SharedFileSubscriber.notify(url: seMessagesSharedFile, message: ProcessMessage(message: message))
}
public func appMessageSubscriber(onMessage: @escaping (AppProcessMessage) -> Void) -> AppSubscriber {
SharedFileSubscriber(fileURL: appMessagesSharedFile) { (msg: ProcessMessage<AppProcessMessage>) in
onMessage(msg.message)
@@ -90,6 +102,12 @@ public func nseMessageSubscriber(onMessage: @escaping (NSEProcessMessage) -> Voi
}
}
public func seMessageSubscriber(onMessage: @escaping (SEProcessMessage) -> Void) -> SESubscriber {
SharedFileSubscriber(fileURL: seMessagesSharedFile) { (msg: ProcessMessage<SEProcessMessage>) in
onMessage(msg.message)
}
}
public func sendAppState(_ state: AppState) {
sendAppProcessMessage(.state(state: state))
}
@@ -97,3 +115,7 @@ public func sendAppState(_ state: AppState) {
public func sendNSEState(_ state: NSEState) {
sendNSEProcessMessage(.state(state: state))
}
public func sendSEState(_ state: SEState) {
sendSEProcessMessage(.state(state: state))
}
+16
View File
@@ -39,3 +39,19 @@ void haskell_init_nse(void) {
char **pargv = argv;
hs_init_with_rtsopts(&argc, &pargv);
}
void haskell_init_se(void) {
int argc = 7;
char *argv[] = {
"simplex",
"+RTS", // requires `hs_init_with_rtsopts`
"-A1m", // chunk size for new allocations
"-H1m", // initial heap size
"-F0.5", // heap growth triggering GC
"-Fd1", // memory return
"-c", // compacting garbage collector
0
};
char **pargv = argv;
hs_init_with_rtsopts(&argc, &pargv);
}
+2
View File
@@ -13,4 +13,6 @@ void haskell_init(void);
void haskell_init_nse(void);
void haskell_init_se(void);
#endif /* hs_init_h */
+2 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ е потвърдено";
/* No comment provided by engineer. */
"%@ servers" = "%@ сървъри";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ качено";
@@ -644,7 +641,7 @@
/* rcv group event chat item */
"blocked %@" = "блокиран %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "блокиран от админ";
/* No comment provided by engineer. */
@@ -4297,7 +4294,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Можете да го направите видим за вашите контакти в SimpleX чрез Настройки.";
/* notification body */
"You can now send messages to %@" = "Вече можете да изпращате съобщения до %@";
"You can now chat with %@" = "Вече можете да изпращате съобщения до %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Можете да зададете визуализация на известията на заключен екран през настройките.";
+1 -4
View File
@@ -130,9 +130,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ je ověřený";
/* No comment provided by engineer. */
"%@ servers" = "%@ servery";
/* notification title */
"%@ wants to connect!" = "%@ se chce připojit!";
@@ -3439,7 +3436,7 @@
"You can hide or mute a user profile - swipe it to the right." = "Profil uživatele můžete skrýt nebo ztlumit - přejeďte prstem doprava.";
/* notification body */
"You can now send messages to %@" = "Nyní můžete posílat zprávy %@";
"You can now chat with %@" = "Nyní můžete posílat zprávy %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Náhled oznámení na zamykací obrazovce můžete změnit v nastavení.";
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@
"CFBundleName" = "SimpleX";
/* Privacy - Camera Usage Description */
"NSCameraUsageDescription" = "SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Nutzern zu scannen und Videoanrufe durchzuführen.";
"NSCameraUsageDescription" = "SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Benutzern zu scannen und Videoanrufe durchzuführen.";
/* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "Face ID wird von SimpleX für die lokale Authentifizierung genutzt";
+2 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ está verificado";
/* No comment provided by engineer. */
"%@ servers" = "Servidores %@";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ subido";
@@ -650,7 +647,7 @@
/* rcv group event chat item */
"blocked %@" = "ha bloqueado a %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "bloqueado por administrador";
/* No comment provided by engineer. */
@@ -4417,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Puedes hacerlo visible para tus contactos de SimpleX en Configuración.";
/* notification body */
"You can now send messages to %@" = "Ya puedes enviar mensajes a %@";
"You can now chat with %@" = "Ya puedes enviar mensajes a %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Puedes configurar las notificaciones de la pantalla de bloqueo desde Configuración.";
+1 -4
View File
@@ -121,9 +121,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ on vahvistettu";
/* No comment provided by engineer. */
"%@ servers" = "%@ palvelimet";
/* notification title */
"%@ wants to connect!" = "%@ haluaa muodostaa yhteyden!";
@@ -3397,7 +3394,7 @@
"You can hide or mute a user profile - swipe it to the right." = "Voit piilottaa tai mykistää käyttäjäprofiilin pyyhkäisemällä sitä oikealle.";
/* notification body */
"You can now send messages to %@" = "Voit nyt lähettää viestejä %@:lle";
"You can now chat with %@" = "Voit nyt lähettää viestejä %@:lle";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Voit määrittää lukitusnäytön ilmoituksen esikatselun asetuksista.";
+2 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ est vérifié·e";
/* No comment provided by engineer. */
"%@ servers" = "Serveurs %@";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ envoyé";
@@ -650,7 +647,7 @@
/* rcv group event chat item */
"blocked %@" = "%@ bloqué";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "bloqué par l'administrateur";
/* No comment provided by engineer. */
@@ -4417,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Vous pouvez le rendre visible à vos contacts SimpleX via Paramètres.";
/* notification body */
"You can now send messages to %@" = "Vous pouvez maintenant envoyer des messages à %@";
"You can now chat with %@" = "Vous pouvez maintenant envoyer des messages à %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Vous pouvez configurer l'aperçu des notifications sur l'écran de verrouillage via les paramètres.";
+389 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ ellenőrizve";
/* No comment provided by engineer. */
"%@ servers" = "%@ kiszolgáló";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ feltöltve";
@@ -337,6 +334,9 @@
/* No comment provided by engineer. */
"above, then choose:" = "gombra fent, majd válassza ki:";
/* No comment provided by engineer. */
"Accent" = "Kiemelés";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Elfogadás";
@@ -353,6 +353,12 @@
/* call status */
"accepted call" = "elfogadott hívás";
/* No comment provided by engineer. */
"Acknowledged" = "Nyugtázva";
/* No comment provided by engineer. */
"Acknowledgement errors" = "Nyugtázott hibák";
/* No comment provided by engineer. */
"Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Cím hozzáadása a profilhoz, hogy az ismerősei megoszthassák másokkal. A profilfrissítés elküldésre kerül az ismerősei számára.";
@@ -377,6 +383,15 @@
/* No comment provided by engineer. */
"Add welcome message" = "Üdvözlő üzenet hozzáadása";
/* No comment provided by engineer. */
"Additional accent" = "További kiemelés";
/* No comment provided by engineer. */
"Additional accent 2" = "További kiemelés 2";
/* No comment provided by engineer. */
"Additional secondary" = "További másodlagos";
/* No comment provided by engineer. */
"Address" = "Cím";
@@ -398,6 +413,9 @@
/* No comment provided by engineer. */
"Advanced network settings" = "Speciális hálózati beállítások";
/* No comment provided by engineer. */
"Advanced settings" = "Haladó beállítások";
/* chat item text */
"agreeing encryption for %@…" = "titkosítás jóváhagyása %@ számára…";
@@ -413,6 +431,9 @@
/* No comment provided by engineer. */
"All data is erased when it is entered." = "A jelkód megadása után minden adat törlésre kerül.";
/* No comment provided by engineer. */
"All data is private to your device." = "Minden adat biztonságban van a készülékén.";
/* No comment provided by engineer. */
"All group members will remain connected." = "Minden csoporttag kapcsolódva marad.";
@@ -428,6 +449,9 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Minden új üzenet elrejtésre kerül tőle: %@!";
/* No comment provided by engineer. */
"All profiles" = "Minden profil";
/* No comment provided by engineer. */
"All your contacts will remain connected." = "Minden ismerős kapcsolódva marad.";
@@ -554,6 +578,9 @@
/* No comment provided by engineer. */
"Apply" = "Alkalmaz";
/* No comment provided by engineer. */
"Apply to" = "Alkalmazás erre";
/* No comment provided by engineer. */
"Archive and upload" = "Archiválás és feltöltés";
@@ -563,6 +590,9 @@
/* No comment provided by engineer. */
"Attach" = "Csatolás";
/* No comment provided by engineer. */
"attempts" = "próbálkozások";
/* No comment provided by engineer. */
"Audio & video calls" = "Hang- és videóhívások";
@@ -605,6 +635,9 @@
/* No comment provided by engineer. */
"Back" = "Vissza";
/* No comment provided by engineer. */
"Background" = "Háttér";
/* No comment provided by engineer. */
"Bad desktop address" = "Hibás számítógép cím";
@@ -626,6 +659,9 @@
/* No comment provided by engineer. */
"Better messages" = "Jobb üzenetek";
/* No comment provided by engineer. */
"Black" = "Fekete";
/* No comment provided by engineer. */
"Block" = "Blokkolás";
@@ -650,7 +686,7 @@
/* rcv group event chat item */
"blocked %@" = "letiltotta őt: %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "letiltva az admin által";
/* No comment provided by engineer. */
@@ -716,6 +752,9 @@
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Nem lehet hozzáférni a kulcstartóhoz az adatbázis jelszavának mentéséhez";
/* No comment provided by engineer. */
"Cannot forward message" = "Nem lehet továbbítani az üzenetet";
/* No comment provided by engineer. */
"Cannot receive file" = "Nem lehet fogadni a fájlt";
@@ -774,6 +813,9 @@
/* No comment provided by engineer. */
"Chat archive" = "Csevegési archívum";
/* No comment provided by engineer. */
"Chat colors" = "Csevegés színei";
/* No comment provided by engineer. */
"Chat console" = "Csevegési konzol";
@@ -801,6 +843,9 @@
/* No comment provided by engineer. */
"Chat preferences" = "Csevegési beállítások";
/* No comment provided by engineer. */
"Chat theme" = "Csevegés témája";
/* No comment provided by engineer. */
"Chats" = "Csevegések";
@@ -819,6 +864,15 @@
/* No comment provided by engineer. */
"Choose from library" = "Választás a könyvtárból";
/* No comment provided by engineer. */
"Chunks deleted" = "Törölt fájltöredékek";
/* No comment provided by engineer. */
"Chunks downloaded" = "Letöltött fájltöredékek";
/* No comment provided by engineer. */
"Chunks uploaded" = "Feltöltött fájltöredékek";
/* No comment provided by engineer. */
"Clear" = "Kiürítés";
@@ -834,6 +888,9 @@
/* No comment provided by engineer. */
"Clear verification" = "Hitelesítés törlése";
/* No comment provided by engineer. */
"Color mode" = "Színmód";
/* No comment provided by engineer. */
"colored" = "színes";
@@ -846,6 +903,9 @@
/* No comment provided by engineer. */
"complete" = "befejezett";
/* No comment provided by engineer. */
"Completed" = "Elkészült";
/* No comment provided by engineer. */
"Configure ICE servers" = "ICE kiszolgálók beállítása";
@@ -915,18 +975,27 @@
/* No comment provided by engineer. */
"connected" = "kapcsolódva";
/* No comment provided by engineer. */
"Connected" = "Kapcsolódva";
/* No comment provided by engineer. */
"Connected desktop" = "Csatlakoztatott számítógép";
/* rcv group event chat item */
"connected directly" = "közvetlenül kapcsolódva";
/* No comment provided by engineer. */
"Connected servers" = "Kapcsolódott kiszolgálók";
/* No comment provided by engineer. */
"Connected to desktop" = "Kapcsolódva a számítógéphez";
/* No comment provided by engineer. */
"connecting" = "kapcsolódás";
/* No comment provided by engineer. */
"Connecting" = "Kapcsolódás";
/* No comment provided by engineer. */
"connecting (accepted)" = "kapcsolódás (elfogadva)";
@@ -975,9 +1044,15 @@
/* No comment provided by engineer. */
"Connection timeout" = "Kapcsolat időtúllépés";
/* No comment provided by engineer. */
"Connection with desktop stopped" = "A kapcsolat a számítógéppel megszakadt";
/* connection information */
"connection:%@" = "kapcsolat: %@";
/* No comment provided by engineer. */
"Connections" = "Kapcsolatok";
/* profile update event chat item */
"contact %@ changed to %@" = "%1$@ megváltoztatta a nevét erre: %2$@";
@@ -1020,6 +1095,9 @@
/* No comment provided by engineer. */
"Copy" = "Másolás";
/* No comment provided by engineer. */
"Copy error" = "Másolási hiba";
/* No comment provided by engineer. */
"Core version: v%@" = "Alapverziószám: v%@";
@@ -1065,6 +1143,9 @@
/* No comment provided by engineer. */
"Create your profile" = "Saját profil létrehozása";
/* No comment provided by engineer. */
"Created" = "Létrehozva";
/* No comment provided by engineer. */
"Created at" = "Létrehozva ekkor:";
@@ -1089,6 +1170,9 @@
/* No comment provided by engineer. */
"Current passphrase…" = "Jelenlegi jelmondat…";
/* No comment provided by engineer. */
"Current profile" = "Jelenlegi profil";
/* No comment provided by engineer. */
"Currently maximum supported file size is %@." = "Jelenleg a maximális támogatott fájlméret %@.";
@@ -1098,9 +1182,15 @@
/* No comment provided by engineer. */
"Custom time" = "Személyreszabott idő";
/* No comment provided by engineer. */
"Customize theme" = "Téma személyre szabása";
/* No comment provided by engineer. */
"Dark" = "Sötét";
/* No comment provided by engineer. */
"Dark mode colors" = "Sötét mód színei";
/* No comment provided by engineer. */
"Database downgrade" = "Visszatérés a korábbi adatbázis verzióra";
@@ -1170,6 +1260,9 @@
/* message decrypt error item */
"Decryption error" = "Titkosítás visszafejtési hiba";
/* No comment provided by engineer. */
"decryption errors" = "visszafejtési hibák";
/* pref value */
"default (%@)" = "alapértelmezett (%@)";
@@ -1296,6 +1389,9 @@
/* deleted chat item */
"deleted" = "törölve";
/* No comment provided by engineer. */
"Deleted" = "Törölve";
/* No comment provided by engineer. */
"Deleted at" = "Törölve ekkor:";
@@ -1308,6 +1404,9 @@
/* rcv group event chat item */
"deleted group" = "törölt csoport";
/* No comment provided by engineer. */
"Deletion errors" = "Törlési hibák";
/* No comment provided by engineer. */
"Delivery" = "Kézbesítés";
@@ -1332,6 +1431,12 @@
/* snd error text */
"Destination server error: %@" = "Célkiszolgáló hiba: %@";
/* No comment provided by engineer. */
"Detailed statistics" = "Részletes statisztikák";
/* No comment provided by engineer. */
"Details" = "Részletek";
/* No comment provided by engineer. */
"Develop" = "Fejlesztés";
@@ -1434,12 +1539,21 @@
/* chat item action */
"Download" = "Letöltés";
/* No comment provided by engineer. */
"Download errors" = "Letöltési hibák";
/* No comment provided by engineer. */
"Download failed" = "Sikertelen letöltés";
/* server test step */
"Download file" = "Fájl letöltése";
/* No comment provided by engineer. */
"Downloaded" = "Letöltve";
/* No comment provided by engineer. */
"Downloaded files" = "Letöltött fájlok";
/* No comment provided by engineer. */
"Downloading archive" = "Archívum letöltése";
@@ -1452,6 +1566,9 @@
/* integrity error chat item */
"duplicate message" = "duplikált üzenet";
/* No comment provided by engineer. */
"duplicates" = "duplikációk";
/* No comment provided by engineer. */
"Duration" = "Időtartam";
@@ -1710,6 +1827,9 @@
/* No comment provided by engineer. */
"Error exporting chat database" = "Hiba a csevegési adatbázis exportálásakor";
/* No comment provided by engineer. */
"Error exporting theme: %@" = "Hiba a téma exportálásakor: %@";
/* No comment provided by engineer. */
"Error importing chat database" = "Hiba a csevegési adatbázis importálásakor";
@@ -1725,9 +1845,18 @@
/* No comment provided by engineer. */
"Error receiving file" = "Hiba a fájl fogadásakor";
/* No comment provided by engineer. */
"Error reconnecting server" = "Hiba a kiszolgálóhoz való újrakapcsolódáskor";
/* No comment provided by engineer. */
"Error reconnecting servers" = "Hiba a kiszolgálókhoz való újrakapcsolódáskor";
/* No comment provided by engineer. */
"Error removing member" = "Hiba a tag eltávolításakor";
/* No comment provided by engineer. */
"Error resetting statistics" = "Hiba a statisztikák visszaállításakor";
/* No comment provided by engineer. */
"Error saving %@ servers" = "Hiba történt a %@ kiszolgálók mentése közben";
@@ -1807,6 +1936,9 @@
/* No comment provided by engineer. */
"Error: URL is invalid" = "Hiba: az URL érvénytelen";
/* No comment provided by engineer. */
"Errors" = "Hibák";
/* No comment provided by engineer. */
"Even when disabled in the conversation." = "Akkor is, ha le van tiltva a beszélgetésben.";
@@ -1819,12 +1951,18 @@
/* chat item action */
"Expand" = "Kibontás";
/* No comment provided by engineer. */
"expired" = "lejárt";
/* No comment provided by engineer. */
"Export database" = "Adatbázis exportálása";
/* No comment provided by engineer. */
"Export error:" = "Exportálási hiba:";
/* No comment provided by engineer. */
"Export theme" = "Téma exportálása";
/* No comment provided by engineer. */
"Exported database archive." = "Exportált adatbázis-archívum.";
@@ -1846,6 +1984,21 @@
/* No comment provided by engineer. */
"Favorite" = "Kedvenc";
/* No comment provided by engineer. */
"File error" = "Fájlhiba";
/* file error text */
"File not found - most likely file was deleted or cancelled." = "A fájl nem található - valószínűleg a fájlt törölték vagy visszavonták.";
/* file error text */
"File server error: %@" = "Fájlkiszolgáló hiba: %@";
/* No comment provided by engineer. */
"File status" = "Fájlállapot";
/* copied message info */
"File status: %@" = "Fájlállapot: %@";
/* No comment provided by engineer. */
"File will be deleted from servers." = "A fájl törölve lesz a kiszolgálóról.";
@@ -1960,6 +2113,12 @@
/* No comment provided by engineer. */
"GIFs and stickers" = "GIF-ek és matricák";
/* message preview */
"Good afternoon!" = "Jó napot!";
/* message preview */
"Good morning!" = "Jó reggelt!";
/* No comment provided by engineer. */
"Group" = "Csoport";
@@ -2137,6 +2296,9 @@
/* No comment provided by engineer. */
"Import failed" = "Sikertelen importálás";
/* No comment provided by engineer. */
"Import theme" = "Téma importálása";
/* No comment provided by engineer. */
"Importing archive" = "Archívum importálása";
@@ -2158,6 +2320,9 @@
/* No comment provided by engineer. */
"In-call sounds" = "Bejövő hívás csengőhangja";
/* No comment provided by engineer. */
"inactive" = "inaktív";
/* No comment provided by engineer. */
"Incognito" = "Inkognitó";
@@ -2221,6 +2386,9 @@
/* No comment provided by engineer. */
"Interface" = "Felület";
/* No comment provided by engineer. */
"Interface colors" = "Kezelőfelület színei";
/* invalid chat data */
"invalid chat" = "érvénytelen csevegés";
@@ -2473,6 +2641,9 @@
/* rcv group event chat item */
"member connected" = "kapcsolódott";
/* item status text */
"Member inactive" = "Inaktív tag";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". All group members will be notified." = "A tag szerepköre meg fog változni erre: „%@”. A csoport minden tagja értesítést kap róla.";
@@ -2482,6 +2653,9 @@
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "A tag eltávolítása a csoportból - ez a művelet nem vonható vissza!";
/* No comment provided by engineer. */
"Menus" = "Menük";
/* item status text */
"Message delivery error" = "Üzenetkézbesítési hiba";
@@ -2494,6 +2668,12 @@
/* No comment provided by engineer. */
"Message draft" = "Üzenetvázlat";
/* item status text */
"Message forwarded" = "Továbbított üzenet";
/* item status description */
"Message may be delivered later if member becomes active." = "Az üzenet később is kézbesíthető, ha a tag aktívvá válik.";
/* No comment provided by engineer. */
"Message queue info" = "Üzenet-várakoztatási információ";
@@ -2518,6 +2698,12 @@
/* No comment provided by engineer. */
"Message source remains private." = "Az üzenet forrása titokban marad.";
/* No comment provided by engineer. */
"Message status" = "Üzenetállapot";
/* copied message info */
"Message status: %@" = "Üzenetállapot: %@";
/* No comment provided by engineer. */
"Message text" = "Üzenet szövege";
@@ -2533,6 +2719,12 @@
/* No comment provided by engineer. */
"Messages from %@ will be shown!" = "A(z) %@ által írt üzenetek megjelennek!";
/* No comment provided by engineer. */
"Messages received" = "Fogadott üzenetek";
/* No comment provided by engineer. */
"Messages sent" = "Elküldött üzenetek";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "Az üzeneteket, fájlokat és hívásokat **végpontok közötti titkosítással**, sérülés utáni titkosság-védelemmel és -helyreállítással, továbbá visszautasítással védi.";
@@ -2698,6 +2890,9 @@
/* No comment provided by engineer. */
"No device token!" = "Nincs eszköztoken!";
/* item status description */
"No direct connection yet, message is forwarded by admin." = "Még nincs közvetlen kapcsolat, az üzenetet az admin továbbítja.";
/* No comment provided by engineer. */
"no e2e encryption" = "nincs e2e titkosítás";
@@ -2710,6 +2905,9 @@
/* No comment provided by engineer. */
"No history" = "Nincsenek előzmények";
/* No comment provided by engineer. */
"No info, try to reload" = "Nincs információ, próbálja meg újratölteni";
/* No comment provided by engineer. */
"No network connection" = "Nincs hálózati kapcsolat";
@@ -2835,6 +3033,9 @@
/* authentication reason */
"Open migration to another device" = "Átköltöztetés megkezdése egy másik eszközre";
/* No comment provided by engineer. */
"Open server settings" = "Kiszolgáló beállításainak megnyitása";
/* No comment provided by engineer. */
"Open Settings" = "Beállítások megnyitása";
@@ -2859,9 +3060,15 @@
/* No comment provided by engineer. */
"Or show this code" = "Vagy mutassa meg ezt a kódot";
/* No comment provided by engineer. */
"other" = "egyéb";
/* No comment provided by engineer. */
"Other" = "További";
/* No comment provided by engineer. */
"other errors" = "egyéb hibák";
/* member role */
"owner" = "tulajdonos";
@@ -2904,6 +3111,9 @@
/* No comment provided by engineer. */
"peer-to-peer" = "ponttól-pontig";
/* No comment provided by engineer. */
"Pending" = "Függő";
/* No comment provided by engineer. */
"People can connect to you only via the links you share." = "Az emberek csak az ön által megosztott hivatkozáson keresztül kapcsolódhatnak.";
@@ -2925,6 +3135,9 @@
/* No comment provided by engineer. */
"Please ask your contact to enable sending voice messages." = "Ismerős felkérése, hogy engedélyezze a hangüzenetek küldését.";
/* No comment provided by engineer. */
"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Ellenőrizze, hogy a mobil és az asztali számítógép ugyanahhoz a helyi hálózathoz csatlakozik-e, valamint az asztali számítógép tűzfalában engedélyezve van-e a kapcsolat.\nMinden további problémát osszon meg a fejlesztőkkel.";
/* No comment provided by engineer. */
"Please check that you used the correct link or ask your contact to send you another one." = "Ellenőrizze, hogy a megfelelő hivatkozást használta-e, vagy kérje meg ismerősét, hogy küldjön egy másikat.";
@@ -2982,6 +3195,9 @@
/* No comment provided by engineer. */
"Preview" = "Előnézet";
/* No comment provided by engineer. */
"Previously connected servers" = "Korábban kapcsolódott kiszolgálók";
/* No comment provided by engineer. */
"Privacy & security" = "Adatvédelem és biztonság";
@@ -3021,6 +3237,9 @@
/* No comment provided by engineer. */
"Profile password" = "Profiljelszó";
/* No comment provided by engineer. */
"Profile theme" = "Profiltéma";
/* No comment provided by engineer. */
"Profile update will be sent to your contacts." = "A profilfrissítés elküldésre került az ismerősök számára.";
@@ -3069,6 +3288,12 @@
/* No comment provided by engineer. */
"Protocol timeout per KB" = "Protokoll időkorlát KB-onként";
/* No comment provided by engineer. */
"Proxied" = "Proxyzott";
/* No comment provided by engineer. */
"Proxied servers" = "Proxyzott kiszolgálók";
/* No comment provided by engineer. */
"Push notifications" = "Push értesítések";
@@ -3111,6 +3336,9 @@
/* No comment provided by engineer. */
"Receipts are disabled" = "Üzenet kézbesítési jelentés letiltva";
/* No comment provided by engineer. */
"Receive errors" = "Üzenetfogadási hibák";
/* No comment provided by engineer. */
"received answer…" = "fogadott válasz…";
@@ -3129,6 +3357,15 @@
/* message info title */
"Received message" = "Fogadott üzenet";
/* No comment provided by engineer. */
"Received messages" = "Fogadott üzenetek";
/* No comment provided by engineer. */
"Received reply" = "Fogadott válasz";
/* No comment provided by engineer. */
"Received total" = "Összes fogadott";
/* No comment provided by engineer. */
"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "A fogadó cím egy másik kiszolgálóra változik. A címváltoztatás a feladó online állapotba kerülése után fejeződik be.";
@@ -3147,9 +3384,24 @@
/* No comment provided by engineer. */
"Recipients see updates as you type them." = "A címzettek a beírás közben látják a frissítéseket.";
/* No comment provided by engineer. */
"Reconnect" = "Újrakapcsolás";
/* No comment provided by engineer. */
"Reconnect all connected servers to force message delivery. It uses additional traffic." = "Újrakapcsolódás az összes kiszolgálóhoz az üzenetek kézbesítésének kikényszerítéséhez. Ez további forgalmat használ.";
/* No comment provided by engineer. */
"Reconnect all servers" = "Újrakapcsolódás minden kiszolgálóhoz";
/* No comment provided by engineer. */
"Reconnect all servers?" = "Újrakapcsolódás minden kiszolgálóhoz?";
/* No comment provided by engineer. */
"Reconnect server to force message delivery. It uses additional traffic." = "A kiszolgálóhoz való újrakapcsolódás az üzenet kézbesítésének kikényszerítéséhez. Ez további adatforgalmat használ.";
/* No comment provided by engineer. */
"Reconnect server?" = "Újrakapcsolódás a kiszolgálóhoz?";
/* No comment provided by engineer. */
"Reconnect servers?" = "Újrakapcsolódás a kiszolgálókhoz?";
@@ -3183,6 +3435,9 @@
/* No comment provided by engineer. */
"Remove" = "Eltávolítás";
/* No comment provided by engineer. */
"Remove image" = "Kép eltávolítása";
/* No comment provided by engineer. */
"Remove member" = "Eltávolítás";
@@ -3240,12 +3495,24 @@
/* No comment provided by engineer. */
"Reset" = "Alaphelyzetbe állítás";
/* No comment provided by engineer. */
"Reset all statistics" = "Minden statisztika visszaállítása";
/* No comment provided by engineer. */
"Reset all statistics?" = "Minden statisztika visszaállítása?";
/* No comment provided by engineer. */
"Reset colors" = "Színek alaphelyzetbe állítása";
/* No comment provided by engineer. */
"Reset to app theme" = "Alkalmazás témájának visszaállítása";
/* No comment provided by engineer. */
"Reset to defaults" = "Alaphelyzetbe állítás";
/* No comment provided by engineer. */
"Reset to user theme" = "Felhasználó által létrehozott téma visszaállítása";
/* No comment provided by engineer. */
"Restart the app to create a new chat profile" = "Új csevegési profil létrehozásához indítsa újra az alkalmazást";
@@ -3360,6 +3627,12 @@
/* No comment provided by engineer. */
"Saved WebRTC ICE servers will be removed" = "A mentett WebRTC ICE kiszolgálók eltávolításra kerülnek";
/* No comment provided by engineer. */
"Scale" = "Méretezés";
/* No comment provided by engineer. */
"Scan / Paste link" = "Hivatkozás beolvasása / beillesztése";
/* No comment provided by engineer. */
"Scan code" = "Beolvasás";
@@ -3387,6 +3660,9 @@
/* network option */
"sec" = "mp";
/* No comment provided by engineer. */
"Secondary" = "Másodlagos";
/* time unit */
"seconds" = "másodperc";
@@ -3396,6 +3672,9 @@
/* server test step */
"Secure queue" = "Biztonságos várólista";
/* No comment provided by engineer. */
"Secured" = "Biztosítva";
/* No comment provided by engineer. */
"Security assessment" = "Biztonsági kiértékelés";
@@ -3408,6 +3687,9 @@
/* No comment provided by engineer. */
"Select" = "Választás";
/* No comment provided by engineer. */
"Selected chat preferences prohibit this message." = "A kiválasztott csevegési beállítások tiltják ezt az üzenetet.";
/* No comment provided by engineer. */
"Self-destruct" = "Önmegsemmisítés";
@@ -3441,6 +3723,9 @@
/* No comment provided by engineer. */
"Send disappearing message" = "Eltűnő üzenet küldése";
/* No comment provided by engineer. */
"Send errors" = "Üzenetküldési hibák";
/* No comment provided by engineer. */
"Send link previews" = "Hivatkozás előnézetek küldése";
@@ -3507,15 +3792,33 @@
/* copied message info */
"Sent at: %@" = "Elküldve ekkor: %@";
/* No comment provided by engineer. */
"Sent directly" = "Közvetlenül küldött";
/* notification */
"Sent file event" = "Elküldött fájl esemény";
/* message info title */
"Sent message" = "Elküldött üzenet";
/* No comment provided by engineer. */
"Sent messages" = "Elküldött üzenetek";
/* No comment provided by engineer. */
"Sent messages will be deleted after set time." = "Az elküldött üzenetek törlésre kerülnek a beállított idő után.";
/* No comment provided by engineer. */
"Sent reply" = "Elküldött válasz";
/* No comment provided by engineer. */
"Sent total" = "Összes elküldött";
/* No comment provided by engineer. */
"Sent via proxy" = "Proxyn keresztül küldve";
/* No comment provided by engineer. */
"Server address" = "Kiszolgáló címe";
/* srv error text. */
"Server address is incompatible with network settings." = "A kiszolgáló címe nem kompatibilis a hálózati beállításokkal.";
@@ -3531,12 +3834,21 @@
/* No comment provided by engineer. */
"Server test failed!" = "Sikertelen kiszolgáló-teszt!";
/* No comment provided by engineer. */
"Server type" = "Kiszolgáló típusa";
/* srv error text */
"Server version is incompatible with network settings." = "A kiszolgáló verziója nem kompatibilis a hálózati beállításokkal.";
/* No comment provided by engineer. */
"Servers" = "Kiszolgálók";
/* No comment provided by engineer. */
"Servers info" = "információk a kiszolgálókról";
/* No comment provided by engineer. */
"Servers statistics will be reset - this cannot be undone!" = "A kiszolgálók statisztikái visszaállnak - ez nem vonható vissza!";
/* No comment provided by engineer. */
"Session code" = "Munkamenet kód";
@@ -3546,6 +3858,9 @@
/* No comment provided by engineer. */
"Set contact name…" = "Ismerős nevének beállítása…";
/* No comment provided by engineer. */
"Set default theme" = "Alapértelmezett téma beállítása";
/* No comment provided by engineer. */
"Set group preferences" = "Csoportbeállítások megadása";
@@ -3624,6 +3939,9 @@
/* No comment provided by engineer. */
"Show:" = "Megjelenítés:";
/* No comment provided by engineer. */
"SimpleX" = "SimpleX";
/* No comment provided by engineer. */
"SimpleX address" = "SimpleX cím";
@@ -3669,6 +3987,9 @@
/* No comment provided by engineer. */
"Simplified incognito mode" = "Egyszerűsített inkognító mód";
/* No comment provided by engineer. */
"Size" = "Méret";
/* No comment provided by engineer. */
"Skip" = "Kihagyás";
@@ -3678,6 +3999,9 @@
/* No comment provided by engineer. */
"Small groups (max 20)" = "Kis csoportok (max. 20 tag)";
/* No comment provided by engineer. */
"SMP server" = "SMP-kiszolgáló";
/* No comment provided by engineer. */
"SMP servers" = "SMP kiszolgálók";
@@ -3702,9 +4026,15 @@
/* No comment provided by engineer. */
"Start migration" = "Átköltöztetés indítása";
/* No comment provided by engineer. */
"Starting from %@." = "Kezdve ettől %@.";
/* No comment provided by engineer. */
"starting…" = "indítás…";
/* No comment provided by engineer. */
"Statistics" = "Statisztikák";
/* No comment provided by engineer. */
"Stop" = "Megállítás";
@@ -3747,6 +4077,15 @@
/* No comment provided by engineer. */
"Submit" = "Elküldés";
/* No comment provided by engineer. */
"Subscribed" = "Feliratkozva";
/* No comment provided by engineer. */
"Subscription errors" = "Feliratkozási hibák";
/* No comment provided by engineer. */
"Subscriptions ignored" = "Elutasított feliratkozások";
/* No comment provided by engineer. */
"Support SimpleX Chat" = "Támogassa a SimpleX Chatet";
@@ -3795,6 +4134,9 @@
/* No comment provided by engineer. */
"TCP_KEEPINTVL" = "TCP_KEEPINTVL";
/* No comment provided by engineer. */
"Temporary file error" = "Ideiglenes fájlhiba";
/* server test failure */
"Test failed at step %@." = "A teszt sikertelen volt a(z) %@ lépésnél.";
@@ -3876,6 +4218,9 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "A beillesztett szöveg nem egy SimpleX hivatkozás.";
/* No comment provided by engineer. */
"Themes" = "Témák";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Ezek a beállítások a jelenlegi **%@** profiljára vonatkoznak.";
@@ -3918,9 +4263,15 @@
/* No comment provided by engineer. */
"This is your own SimpleX address!" = "Ez az ön SimpleX címe!";
/* No comment provided by engineer. */
"This link was used with another mobile device, please create a new link on the desktop." = "Ezt a hivatkozást egy másik mobilleszközön már használták, hozzon létre egy új hivatkozást az asztali számítógépén.";
/* No comment provided by engineer. */
"This setting applies to messages in your current chat profile **%@**." = "Ez a beállítás a jelenlegi **%@** profiljában lévő üzenetekre érvényes.";
/* No comment provided by engineer. */
"Title" = "Cím";
/* No comment provided by engineer. */
"To ask any questions and to receive updates:" = "Bármilyen kérdés feltevéséhez és a frissítésekért:";
@@ -3960,9 +4311,15 @@
/* No comment provided by engineer. */
"Toggle incognito when connecting." = "Inkognitó mód kapcsolódáskor.";
/* No comment provided by engineer. */
"Total" = "Összesen";
/* No comment provided by engineer. */
"Transport isolation" = "Kapcsolat izolációs mód";
/* No comment provided by engineer. */
"Transport sessions" = "Munkamenetek átvitele";
/* No comment provided by engineer. */
"Trying to connect to the server used to receive messages from this contact (error: %@)." = "Kapcsolódási kísérlet ahhoz a kiszolgálóhoz, amely az adott ismerőstől érkező üzenetek fogadására szolgál (hiba: %@).";
@@ -4098,12 +4455,21 @@
/* No comment provided by engineer. */
"Upgrade and open chat" = "A csevegés frissítése és megnyitása";
/* No comment provided by engineer. */
"Upload errors" = "Feltöltési hibák";
/* No comment provided by engineer. */
"Upload failed" = "Sikertelen feltöltés";
/* server test step */
"Upload file" = "Fájl feltöltése";
/* No comment provided by engineer. */
"Uploaded" = "Feltöltve";
/* No comment provided by engineer. */
"Uploaded files" = "Feltöltött fájlok";
/* No comment provided by engineer. */
"Uploading archive" = "Archívum feltöltése";
@@ -4149,6 +4515,9 @@
/* No comment provided by engineer. */
"User profile" = "Felhasználói profil";
/* No comment provided by engineer. */
"User selection" = "Felhasználó kiválasztása";
/* No comment provided by engineer. */
"Using .onion hosts requires compatible VPN provider." = "A .onion kiszolgálók használatához kompatibilis VPN szolgáltatóra van szükség.";
@@ -4257,6 +4626,12 @@
/* No comment provided by engineer. */
"Waiting for video" = "Videóra várakozás";
/* No comment provided by engineer. */
"Wallpaper accent" = "Háttérkép kiemelés";
/* No comment provided by engineer. */
"Wallpaper background" = "Háttérkép háttérszíne";
/* No comment provided by engineer. */
"wants to connect to you!" = "kapcsolatba akar lépni önnel!";
@@ -4329,9 +4704,15 @@
/* snd error text */
"Wrong key or unknown connection - most likely this connection is deleted." = "Rossz kulcs vagy ismeretlen kapcsolat - valószínűleg ez a kapcsolat törlődött.";
/* file error text */
"Wrong key or unknown file chunk address - most likely file is deleted." = "Hibás kulcs vagy ismeretlen fájltöredék cím - valószínűleg a fájl törlődött.";
/* No comment provided by engineer. */
"Wrong passphrase!" = "Téves jelmondat!";
/* No comment provided by engineer. */
"XFTP server" = "XFTP-kiszolgáló";
/* No comment provided by engineer. */
"XFTP servers" = "XFTP kiszolgálók";
@@ -4389,6 +4770,9 @@
/* No comment provided by engineer. */
"You are invited to group" = "Meghívást kapott a csoportba";
/* No comment provided by engineer. */
"You are not connected to these servers. Private routing is used to deliver messages to them." = "Ön nem kapcsolódik ezekhez a kiszolgálókhoz. A privát útválasztás az üzenetek kézbesítésére szolgál.";
/* No comment provided by engineer. */
"you are observer" = "megfigyelő szerep";
@@ -4417,7 +4801,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Láthatóvá teheti SimpleX ismerősök számára a Beállításokban.";
/* notification body */
"You can now send messages to %@" = "Mostantól küldhet üzeneteket %@ számára";
"You can now chat with %@" = "Mostantól küldhet üzeneteket %@ számára";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "A beállításokon keresztül beállíthatja a lezárási képernyő értesítési előnézetét.";
+389 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ è verificato/a";
/* No comment provided by engineer. */
"%@ servers" = "Server %@";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ caricati";
@@ -337,6 +334,9 @@
/* No comment provided by engineer. */
"above, then choose:" = "sopra, quindi scegli:";
/* No comment provided by engineer. */
"Accent" = "Principale";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Accetta";
@@ -353,6 +353,12 @@
/* call status */
"accepted call" = "chiamata accettata";
/* No comment provided by engineer. */
"Acknowledged" = "Riconosciuto";
/* No comment provided by engineer. */
"Acknowledgement errors" = "Errori di riconoscimento";
/* No comment provided by engineer. */
"Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Aggiungi l'indirizzo al tuo profilo, in modo che i tuoi contatti possano condividerlo con altre persone. L'aggiornamento del profilo verrà inviato ai tuoi contatti.";
@@ -377,6 +383,15 @@
/* No comment provided by engineer. */
"Add welcome message" = "Aggiungi messaggio di benvenuto";
/* No comment provided by engineer. */
"Additional accent" = "Principale aggiuntivo";
/* No comment provided by engineer. */
"Additional accent 2" = "Principale aggiuntivo 2";
/* No comment provided by engineer. */
"Additional secondary" = "Secondario aggiuntivo";
/* No comment provided by engineer. */
"Address" = "Indirizzo";
@@ -398,6 +413,9 @@
/* No comment provided by engineer. */
"Advanced network settings" = "Impostazioni di rete avanzate";
/* No comment provided by engineer. */
"Advanced settings" = "Impostazioni avanzate";
/* chat item text */
"agreeing encryption for %@…" = "concordando la crittografia per %@…";
@@ -413,6 +431,9 @@
/* No comment provided by engineer. */
"All data is erased when it is entered." = "Tutti i dati vengono cancellati quando inserito.";
/* No comment provided by engineer. */
"All data is private to your device." = "Tutti i dati sono privati, nel tuo dispositivo.";
/* No comment provided by engineer. */
"All group members will remain connected." = "Tutti i membri del gruppo resteranno connessi.";
@@ -428,6 +449,9 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Tutti i nuovi messaggi da %@ verrranno nascosti!";
/* No comment provided by engineer. */
"All profiles" = "Tutti gli profili";
/* No comment provided by engineer. */
"All your contacts will remain connected." = "Tutti i tuoi contatti resteranno connessi.";
@@ -554,6 +578,9 @@
/* No comment provided by engineer. */
"Apply" = "Applica";
/* No comment provided by engineer. */
"Apply to" = "Applica a";
/* No comment provided by engineer. */
"Archive and upload" = "Archivia e carica";
@@ -563,6 +590,9 @@
/* No comment provided by engineer. */
"Attach" = "Allega";
/* No comment provided by engineer. */
"attempts" = "tentativi";
/* No comment provided by engineer. */
"Audio & video calls" = "Chiamate audio e video";
@@ -605,6 +635,9 @@
/* No comment provided by engineer. */
"Back" = "Indietro";
/* No comment provided by engineer. */
"Background" = "Sfondo";
/* No comment provided by engineer. */
"Bad desktop address" = "Indirizzo desktop errato";
@@ -626,6 +659,9 @@
/* No comment provided by engineer. */
"Better messages" = "Messaggi migliorati";
/* No comment provided by engineer. */
"Black" = "Nero";
/* No comment provided by engineer. */
"Block" = "Blocca";
@@ -650,7 +686,7 @@
/* rcv group event chat item */
"blocked %@" = "ha bloccato %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "bloccato dall'amministratore";
/* No comment provided by engineer. */
@@ -716,6 +752,9 @@
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Impossibile accedere al portachiavi per salvare la password del database";
/* No comment provided by engineer. */
"Cannot forward message" = "Impossibile inoltrare il messaggio";
/* No comment provided by engineer. */
"Cannot receive file" = "Impossibile ricevere il file";
@@ -774,6 +813,9 @@
/* No comment provided by engineer. */
"Chat archive" = "Archivio chat";
/* No comment provided by engineer. */
"Chat colors" = "Colori della chat";
/* No comment provided by engineer. */
"Chat console" = "Console della chat";
@@ -801,6 +843,9 @@
/* No comment provided by engineer. */
"Chat preferences" = "Preferenze della chat";
/* No comment provided by engineer. */
"Chat theme" = "Tema della chat";
/* No comment provided by engineer. */
"Chats" = "Chat";
@@ -819,6 +864,15 @@
/* No comment provided by engineer. */
"Choose from library" = "Scegli dalla libreria";
/* No comment provided by engineer. */
"Chunks deleted" = "Blocchi eliminati";
/* No comment provided by engineer. */
"Chunks downloaded" = "Blocchi scaricati";
/* No comment provided by engineer. */
"Chunks uploaded" = "Blocchi inviati";
/* No comment provided by engineer. */
"Clear" = "Svuota";
@@ -834,6 +888,9 @@
/* No comment provided by engineer. */
"Clear verification" = "Annulla la verifica";
/* No comment provided by engineer. */
"Color mode" = "Modalità di colore";
/* No comment provided by engineer. */
"colored" = "colorato";
@@ -846,6 +903,9 @@
/* No comment provided by engineer. */
"complete" = "completo";
/* No comment provided by engineer. */
"Completed" = "Completato";
/* No comment provided by engineer. */
"Configure ICE servers" = "Configura server ICE";
@@ -915,18 +975,27 @@
/* No comment provided by engineer. */
"connected" = "connesso/a";
/* No comment provided by engineer. */
"Connected" = "Connesso";
/* No comment provided by engineer. */
"Connected desktop" = "Desktop connesso";
/* rcv group event chat item */
"connected directly" = "si è connesso/a direttamente";
/* No comment provided by engineer. */
"Connected servers" = "Server connessi";
/* No comment provided by engineer. */
"Connected to desktop" = "Connesso al desktop";
/* No comment provided by engineer. */
"connecting" = "in connessione";
/* No comment provided by engineer. */
"Connecting" = "In connessione";
/* No comment provided by engineer. */
"connecting (accepted)" = "in connessione (accettato)";
@@ -975,9 +1044,15 @@
/* No comment provided by engineer. */
"Connection timeout" = "Connessione scaduta";
/* No comment provided by engineer. */
"Connection with desktop stopped" = "Connessione con il desktop fermata";
/* connection information */
"connection:%@" = "connessione:% @";
/* No comment provided by engineer. */
"Connections" = "Connessioni";
/* profile update event chat item */
"contact %@ changed to %@" = "contatto %1$@ cambiato in %2$@";
@@ -1020,6 +1095,9 @@
/* No comment provided by engineer. */
"Copy" = "Copia";
/* No comment provided by engineer. */
"Copy error" = "Copia errore";
/* No comment provided by engineer. */
"Core version: v%@" = "Versione core: v%@";
@@ -1065,6 +1143,9 @@
/* No comment provided by engineer. */
"Create your profile" = "Crea il tuo profilo";
/* No comment provided by engineer. */
"Created" = "Creato";
/* No comment provided by engineer. */
"Created at" = "Creato il";
@@ -1089,6 +1170,9 @@
/* No comment provided by engineer. */
"Current passphrase…" = "Password attuale…";
/* No comment provided by engineer. */
"Current profile" = "Profilo attuale";
/* No comment provided by engineer. */
"Currently maximum supported file size is %@." = "Attualmente la dimensione massima supportata è di %@.";
@@ -1098,9 +1182,15 @@
/* No comment provided by engineer. */
"Custom time" = "Tempo personalizzato";
/* No comment provided by engineer. */
"Customize theme" = "Personalizza il tema";
/* No comment provided by engineer. */
"Dark" = "Scuro";
/* No comment provided by engineer. */
"Dark mode colors" = "Colori modalità scura";
/* No comment provided by engineer. */
"Database downgrade" = "Downgrade del database";
@@ -1170,6 +1260,9 @@
/* message decrypt error item */
"Decryption error" = "Errore di decifrazione";
/* No comment provided by engineer. */
"decryption errors" = "errori di decifrazione";
/* pref value */
"default (%@)" = "predefinito (%@)";
@@ -1296,6 +1389,9 @@
/* deleted chat item */
"deleted" = "eliminato";
/* No comment provided by engineer. */
"Deleted" = "Eliminato";
/* No comment provided by engineer. */
"Deleted at" = "Eliminato il";
@@ -1308,6 +1404,9 @@
/* rcv group event chat item */
"deleted group" = "gruppo eliminato";
/* No comment provided by engineer. */
"Deletion errors" = "Errori di eliminazione";
/* No comment provided by engineer. */
"Delivery" = "Consegna";
@@ -1332,6 +1431,12 @@
/* snd error text */
"Destination server error: %@" = "Errore del server di destinazione: %@";
/* No comment provided by engineer. */
"Detailed statistics" = "Statistiche dettagliate";
/* No comment provided by engineer. */
"Details" = "Dettagli";
/* No comment provided by engineer. */
"Develop" = "Sviluppa";
@@ -1434,12 +1539,21 @@
/* chat item action */
"Download" = "Scarica";
/* No comment provided by engineer. */
"Download errors" = "Errori di scaricamento";
/* No comment provided by engineer. */
"Download failed" = "Scaricamento fallito";
/* server test step */
"Download file" = "Scarica file";
/* No comment provided by engineer. */
"Downloaded" = "Scaricato";
/* No comment provided by engineer. */
"Downloaded files" = "File scaricati";
/* No comment provided by engineer. */
"Downloading archive" = "Scaricamento archivio";
@@ -1452,6 +1566,9 @@
/* integrity error chat item */
"duplicate message" = "messaggio duplicato";
/* No comment provided by engineer. */
"duplicates" = "doppi";
/* No comment provided by engineer. */
"Duration" = "Durata";
@@ -1710,6 +1827,9 @@
/* No comment provided by engineer. */
"Error exporting chat database" = "Errore nell'esportazione del database della chat";
/* No comment provided by engineer. */
"Error exporting theme: %@" = "Errore di esportazione del tema: %@";
/* No comment provided by engineer. */
"Error importing chat database" = "Errore nell'importazione del database della chat";
@@ -1725,9 +1845,18 @@
/* No comment provided by engineer. */
"Error receiving file" = "Errore nella ricezione del file";
/* No comment provided by engineer. */
"Error reconnecting server" = "Errore di riconnessione al server";
/* No comment provided by engineer. */
"Error reconnecting servers" = "Errore di riconnessione ai server";
/* No comment provided by engineer. */
"Error removing member" = "Errore nella rimozione del membro";
/* No comment provided by engineer. */
"Error resetting statistics" = "Errore di azzeramento statistiche";
/* No comment provided by engineer. */
"Error saving %@ servers" = "Errore nel salvataggio dei server %@";
@@ -1807,6 +1936,9 @@
/* No comment provided by engineer. */
"Error: URL is invalid" = "Errore: l'URL non è valido";
/* No comment provided by engineer. */
"Errors" = "Errori";
/* No comment provided by engineer. */
"Even when disabled in the conversation." = "Anche quando disattivato nella conversazione.";
@@ -1819,12 +1951,18 @@
/* chat item action */
"Expand" = "Espandi";
/* No comment provided by engineer. */
"expired" = "scaduto";
/* No comment provided by engineer. */
"Export database" = "Esporta database";
/* No comment provided by engineer. */
"Export error:" = "Errore di esportazione:";
/* No comment provided by engineer. */
"Export theme" = "Esporta tema";
/* No comment provided by engineer. */
"Exported database archive." = "Archivio database esportato.";
@@ -1846,6 +1984,21 @@
/* No comment provided by engineer. */
"Favorite" = "Preferito";
/* No comment provided by engineer. */
"File error" = "Errore del file";
/* file error text */
"File not found - most likely file was deleted or cancelled." = "File non trovato - probabilmente è stato eliminato o annullato.";
/* file error text */
"File server error: %@" = "Errore del server dei file: %@";
/* No comment provided by engineer. */
"File status" = "Stato del file";
/* copied message info */
"File status: %@" = "Stato del file: %@";
/* No comment provided by engineer. */
"File will be deleted from servers." = "Il file verrà eliminato dai server.";
@@ -1960,6 +2113,12 @@
/* No comment provided by engineer. */
"GIFs and stickers" = "GIF e adesivi";
/* message preview */
"Good afternoon!" = "Buon pomeriggio!";
/* message preview */
"Good morning!" = "Buongiorno!";
/* No comment provided by engineer. */
"Group" = "Gruppo";
@@ -2137,6 +2296,9 @@
/* No comment provided by engineer. */
"Import failed" = "Importazione fallita";
/* No comment provided by engineer. */
"Import theme" = "Importa tema";
/* No comment provided by engineer. */
"Importing archive" = "Importazione archivio";
@@ -2158,6 +2320,9 @@
/* No comment provided by engineer. */
"In-call sounds" = "Suoni nelle chiamate";
/* No comment provided by engineer. */
"inactive" = "inattivo";
/* No comment provided by engineer. */
"Incognito" = "Incognito";
@@ -2221,6 +2386,9 @@
/* No comment provided by engineer. */
"Interface" = "Interfaccia";
/* No comment provided by engineer. */
"Interface colors" = "Colori dell'interfaccia";
/* invalid chat data */
"invalid chat" = "chat non valida";
@@ -2473,6 +2641,9 @@
/* rcv group event chat item */
"member connected" = "si è connesso/a";
/* item status text */
"Member inactive" = "Membro inattivo";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". All group members will be notified." = "Il ruolo del membro verrà cambiato in \"%@\". Tutti i membri del gruppo verranno avvisati.";
@@ -2482,6 +2653,9 @@
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "Il membro verrà rimosso dal gruppo, non è reversibile!";
/* No comment provided by engineer. */
"Menus" = "Menu";
/* item status text */
"Message delivery error" = "Errore di recapito del messaggio";
@@ -2494,6 +2668,12 @@
/* No comment provided by engineer. */
"Message draft" = "Bozza dei messaggi";
/* item status text */
"Message forwarded" = "Messaggio inoltrato";
/* item status description */
"Message may be delivered later if member becomes active." = "Il messaggio può essere consegnato più tardi se il membro diventa attivo.";
/* No comment provided by engineer. */
"Message queue info" = "Info coda messaggi";
@@ -2518,6 +2698,12 @@
/* No comment provided by engineer. */
"Message source remains private." = "La fonte del messaggio resta privata.";
/* No comment provided by engineer. */
"Message status" = "Stato del messaggio";
/* copied message info */
"Message status: %@" = "Stato del messaggio: %@";
/* No comment provided by engineer. */
"Message text" = "Testo del messaggio";
@@ -2533,6 +2719,12 @@
/* No comment provided by engineer. */
"Messages from %@ will be shown!" = "I messaggi da %@ verranno mostrati!";
/* No comment provided by engineer. */
"Messages received" = "Messaggi ricevuti";
/* No comment provided by engineer. */
"Messages sent" = "Messaggi inviati";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "I messaggi, i file e le chiamate sono protetti da **crittografia end-to-end** con perfect forward secrecy, ripudio e recupero da intrusione.";
@@ -2698,6 +2890,9 @@
/* No comment provided by engineer. */
"No device token!" = "Nessun token del dispositivo!";
/* item status description */
"No direct connection yet, message is forwarded by admin." = "Ancora nessuna connessione diretta, il messaggio viene inoltrato dall'amministratore.";
/* No comment provided by engineer. */
"no e2e encryption" = "nessuna crittografia e2e";
@@ -2710,6 +2905,9 @@
/* No comment provided by engineer. */
"No history" = "Nessuna cronologia";
/* No comment provided by engineer. */
"No info, try to reload" = "Nessuna informazione, prova a ricaricare";
/* No comment provided by engineer. */
"No network connection" = "Nessuna connessione di rete";
@@ -2835,6 +3033,9 @@
/* authentication reason */
"Open migration to another device" = "Apri migrazione ad un altro dispositivo";
/* No comment provided by engineer. */
"Open server settings" = "Apri impostazioni server";
/* No comment provided by engineer. */
"Open Settings" = "Apri le impostazioni";
@@ -2859,9 +3060,15 @@
/* No comment provided by engineer. */
"Or show this code" = "O mostra questo codice";
/* No comment provided by engineer. */
"other" = "altro";
/* No comment provided by engineer. */
"Other" = "Altro";
/* No comment provided by engineer. */
"other errors" = "altri errori";
/* member role */
"owner" = "proprietario";
@@ -2904,6 +3111,9 @@
/* No comment provided by engineer. */
"peer-to-peer" = "peer-to-peer";
/* No comment provided by engineer. */
"Pending" = "In attesa";
/* No comment provided by engineer. */
"People can connect to you only via the links you share." = "Le persone possono connettersi a te solo tramite i link che condividi.";
@@ -2925,6 +3135,9 @@
/* No comment provided by engineer. */
"Please ask your contact to enable sending voice messages." = "Chiedi al tuo contatto di attivare l'invio dei messaggi vocali.";
/* No comment provided by engineer. */
"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Controlla che mobile e desktop siano collegati alla stessa rete locale e che il firewall del desktop consenta la connessione.\nSi prega di condividere qualsiasi altro problema con gli sviluppatori.";
/* No comment provided by engineer. */
"Please check that you used the correct link or ask your contact to send you another one." = "Controlla di aver usato il link giusto o chiedi al tuo contatto di inviartene un altro.";
@@ -2982,6 +3195,9 @@
/* No comment provided by engineer. */
"Preview" = "Anteprima";
/* No comment provided by engineer. */
"Previously connected servers" = "Server precedentemente connessi";
/* No comment provided by engineer. */
"Privacy & security" = "Privacy e sicurezza";
@@ -3021,6 +3237,9 @@
/* No comment provided by engineer. */
"Profile password" = "Password del profilo";
/* No comment provided by engineer. */
"Profile theme" = "Tema del profilo";
/* No comment provided by engineer. */
"Profile update will be sent to your contacts." = "L'aggiornamento del profilo verrà inviato ai tuoi contatti.";
@@ -3069,6 +3288,12 @@
/* No comment provided by engineer. */
"Protocol timeout per KB" = "Scadenza del protocollo per KB";
/* No comment provided by engineer. */
"Proxied" = "Via proxy";
/* No comment provided by engineer. */
"Proxied servers" = "Server via proxy";
/* No comment provided by engineer. */
"Push notifications" = "Notifiche push";
@@ -3111,6 +3336,9 @@
/* No comment provided by engineer. */
"Receipts are disabled" = "Le ricevute sono disattivate";
/* No comment provided by engineer. */
"Receive errors" = "Errori di ricezione";
/* No comment provided by engineer. */
"received answer…" = "risposta ricevuta…";
@@ -3129,6 +3357,15 @@
/* message info title */
"Received message" = "Messaggio ricevuto";
/* No comment provided by engineer. */
"Received messages" = "Messaggi ricevuti";
/* No comment provided by engineer. */
"Received reply" = "Risposta ricevuta";
/* No comment provided by engineer. */
"Received total" = "Totale ricevuto";
/* No comment provided by engineer. */
"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "L'indirizzo di ricezione verrà cambiato in un server diverso. La modifica dell'indirizzo verrà completata dopo che il mittente sarà in linea.";
@@ -3147,9 +3384,24 @@
/* No comment provided by engineer. */
"Recipients see updates as you type them." = "I destinatari vedono gli aggiornamenti mentre li digiti.";
/* No comment provided by engineer. */
"Reconnect" = "Riconnetti";
/* No comment provided by engineer. */
"Reconnect all connected servers to force message delivery. It uses additional traffic." = "Riconnetti tutti i server connessi per imporre il recapito dei messaggi. Utilizza traffico aggiuntivo.";
/* No comment provided by engineer. */
"Reconnect all servers" = "Riconnetti tutti i server";
/* No comment provided by engineer. */
"Reconnect all servers?" = "Riconnettere tutti i server?";
/* No comment provided by engineer. */
"Reconnect server to force message delivery. It uses additional traffic." = "Riconnetti il server per forzare la consegna dei messaggi. Usa traffico aggiuntivo.";
/* No comment provided by engineer. */
"Reconnect server?" = "Riconnettere il server?";
/* No comment provided by engineer. */
"Reconnect servers?" = "Riconnettere i server?";
@@ -3183,6 +3435,9 @@
/* No comment provided by engineer. */
"Remove" = "Rimuovi";
/* No comment provided by engineer. */
"Remove image" = "Rimuovi immagine";
/* No comment provided by engineer. */
"Remove member" = "Rimuovi membro";
@@ -3240,12 +3495,24 @@
/* No comment provided by engineer. */
"Reset" = "Ripristina";
/* No comment provided by engineer. */
"Reset all statistics" = "Azzera tutte le statistiche";
/* No comment provided by engineer. */
"Reset all statistics?" = "Azzerare tutte le statistiche?";
/* No comment provided by engineer. */
"Reset colors" = "Ripristina i colori";
/* No comment provided by engineer. */
"Reset to app theme" = "Ripristina al tema dell'app";
/* No comment provided by engineer. */
"Reset to defaults" = "Ripristina i predefiniti";
/* No comment provided by engineer. */
"Reset to user theme" = "Ripristina al tema dell'utente";
/* No comment provided by engineer. */
"Restart the app to create a new chat profile" = "Riavvia l'app per creare un nuovo profilo di chat";
@@ -3360,6 +3627,12 @@
/* No comment provided by engineer. */
"Saved WebRTC ICE servers will be removed" = "I server WebRTC ICE salvati verranno rimossi";
/* No comment provided by engineer. */
"Scale" = "Scala";
/* No comment provided by engineer. */
"Scan / Paste link" = "Scansiona / Incolla link";
/* No comment provided by engineer. */
"Scan code" = "Scansiona codice";
@@ -3387,6 +3660,9 @@
/* network option */
"sec" = "sec";
/* No comment provided by engineer. */
"Secondary" = "Secondario";
/* time unit */
"seconds" = "secondi";
@@ -3396,6 +3672,9 @@
/* server test step */
"Secure queue" = "Coda sicura";
/* No comment provided by engineer. */
"Secured" = "Protetto";
/* No comment provided by engineer. */
"Security assessment" = "Valutazione della sicurezza";
@@ -3408,6 +3687,9 @@
/* No comment provided by engineer. */
"Select" = "Seleziona";
/* No comment provided by engineer. */
"Selected chat preferences prohibit this message." = "Le preferenze della chat selezionata vietano questo messaggio.";
/* No comment provided by engineer. */
"Self-destruct" = "Autodistruzione";
@@ -3441,6 +3723,9 @@
/* No comment provided by engineer. */
"Send disappearing message" = "Invia messaggio a tempo";
/* No comment provided by engineer. */
"Send errors" = "Errori di invio";
/* No comment provided by engineer. */
"Send link previews" = "Invia anteprime dei link";
@@ -3507,15 +3792,33 @@
/* copied message info */
"Sent at: %@" = "Inviato il: %@";
/* No comment provided by engineer. */
"Sent directly" = "Inviato direttamente";
/* notification */
"Sent file event" = "Evento file inviato";
/* message info title */
"Sent message" = "Messaggio inviato";
/* No comment provided by engineer. */
"Sent messages" = "Messaggi inviati";
/* No comment provided by engineer. */
"Sent messages will be deleted after set time." = "I messaggi inviati verranno eliminati dopo il tempo impostato.";
/* No comment provided by engineer. */
"Sent reply" = "Risposta inviata";
/* No comment provided by engineer. */
"Sent total" = "Totale inviato";
/* No comment provided by engineer. */
"Sent via proxy" = "Inviato via proxy";
/* No comment provided by engineer. */
"Server address" = "Indirizzo server";
/* srv error text. */
"Server address is incompatible with network settings." = "L'indirizzo del server non è compatibile con le impostazioni di rete.";
@@ -3531,12 +3834,21 @@
/* No comment provided by engineer. */
"Server test failed!" = "Test del server fallito!";
/* No comment provided by engineer. */
"Server type" = "Tipo server";
/* srv error text */
"Server version is incompatible with network settings." = "La versione del server non è compatibile con le impostazioni di rete.";
/* No comment provided by engineer. */
"Servers" = "Server";
/* No comment provided by engineer. */
"Servers info" = "Info dei server";
/* No comment provided by engineer. */
"Servers statistics will be reset - this cannot be undone!" = "Le statistiche dei server verranno azzerate - è irreversibile!";
/* No comment provided by engineer. */
"Session code" = "Codice di sessione";
@@ -3546,6 +3858,9 @@
/* No comment provided by engineer. */
"Set contact name…" = "Imposta nome del contatto…";
/* No comment provided by engineer. */
"Set default theme" = "Imposta tema predefinito";
/* No comment provided by engineer. */
"Set group preferences" = "Imposta le preferenze del gruppo";
@@ -3624,6 +3939,9 @@
/* No comment provided by engineer. */
"Show:" = "Mostra:";
/* No comment provided by engineer. */
"SimpleX" = "SimpleX";
/* No comment provided by engineer. */
"SimpleX address" = "Indirizzo SimpleX";
@@ -3669,6 +3987,9 @@
/* No comment provided by engineer. */
"Simplified incognito mode" = "Modalità incognito semplificata";
/* No comment provided by engineer. */
"Size" = "Dimensione";
/* No comment provided by engineer. */
"Skip" = "Salta";
@@ -3678,6 +3999,9 @@
/* No comment provided by engineer. */
"Small groups (max 20)" = "Piccoli gruppi (max 20)";
/* No comment provided by engineer. */
"SMP server" = "Server SMP";
/* No comment provided by engineer. */
"SMP servers" = "Server SMP";
@@ -3702,9 +4026,15 @@
/* No comment provided by engineer. */
"Start migration" = "Avvia la migrazione";
/* No comment provided by engineer. */
"Starting from %@." = "Inizio da %@.";
/* No comment provided by engineer. */
"starting…" = "avvio…";
/* No comment provided by engineer. */
"Statistics" = "Statistiche";
/* No comment provided by engineer. */
"Stop" = "Ferma";
@@ -3747,6 +4077,15 @@
/* No comment provided by engineer. */
"Submit" = "Invia";
/* No comment provided by engineer. */
"Subscribed" = "Iscritto";
/* No comment provided by engineer. */
"Subscription errors" = "Errori di iscrizione";
/* No comment provided by engineer. */
"Subscriptions ignored" = "Iscrizioni ignorate";
/* No comment provided by engineer. */
"Support SimpleX Chat" = "Supporta SimpleX Chat";
@@ -3795,6 +4134,9 @@
/* No comment provided by engineer. */
"TCP_KEEPINTVL" = "TCP_KEEPINTVL";
/* No comment provided by engineer. */
"Temporary file error" = "Errore del file temporaneo";
/* server test failure */
"Test failed at step %@." = "Test fallito al passo %@.";
@@ -3876,6 +4218,9 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "Il testo che hai incollato non è un link SimpleX.";
/* No comment provided by engineer. */
"Themes" = "Temi";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Queste impostazioni sono per il tuo profilo attuale **%@**.";
@@ -3918,9 +4263,15 @@
/* No comment provided by engineer. */
"This is your own SimpleX address!" = "Questo è il tuo indirizzo SimpleX!";
/* No comment provided by engineer. */
"This link was used with another mobile device, please create a new link on the desktop." = "Questo link è stato usato con un altro dispositivo mobile, creane uno nuovo sul desktop.";
/* No comment provided by engineer. */
"This setting applies to messages in your current chat profile **%@**." = "Questa impostazione si applica ai messaggi del profilo di chat attuale **%@**.";
/* No comment provided by engineer. */
"Title" = "Titoli";
/* No comment provided by engineer. */
"To ask any questions and to receive updates:" = "Per porre domande e ricevere aggiornamenti:";
@@ -3960,9 +4311,15 @@
/* No comment provided by engineer. */
"Toggle incognito when connecting." = "Attiva/disattiva l'incognito quando ti colleghi.";
/* No comment provided by engineer. */
"Total" = "Totale";
/* No comment provided by engineer. */
"Transport isolation" = "Isolamento del trasporto";
/* No comment provided by engineer. */
"Transport sessions" = "Sessioni di trasporto";
/* No comment provided by engineer. */
"Trying to connect to the server used to receive messages from this contact (error: %@)." = "Tentativo di connessione al server usato per ricevere messaggi da questo contatto (errore: %@).";
@@ -4098,12 +4455,21 @@
/* No comment provided by engineer. */
"Upgrade and open chat" = "Aggiorna e apri chat";
/* No comment provided by engineer. */
"Upload errors" = "Errori di invio";
/* No comment provided by engineer. */
"Upload failed" = "Invio fallito";
/* server test step */
"Upload file" = "Invia file";
/* No comment provided by engineer. */
"Uploaded" = "Inviato";
/* No comment provided by engineer. */
"Uploaded files" = "File inviati";
/* No comment provided by engineer. */
"Uploading archive" = "Invio dell'archivio";
@@ -4149,6 +4515,9 @@
/* No comment provided by engineer. */
"User profile" = "Profilo utente";
/* No comment provided by engineer. */
"User selection" = "Selezione utente";
/* No comment provided by engineer. */
"Using .onion hosts requires compatible VPN provider." = "L'uso di host .onion richiede un fornitore di VPN compatibile.";
@@ -4257,6 +4626,12 @@
/* No comment provided by engineer. */
"Waiting for video" = "In attesa del video";
/* No comment provided by engineer. */
"Wallpaper accent" = "Tinta dello sfondo";
/* No comment provided by engineer. */
"Wallpaper background" = "Retro dello sfondo";
/* No comment provided by engineer. */
"wants to connect to you!" = "vuole connettersi con te!";
@@ -4329,9 +4704,15 @@
/* snd error text */
"Wrong key or unknown connection - most likely this connection is deleted." = "Chiave sbagliata o connessione sconosciuta - molto probabilmente questa connessione è stata eliminata.";
/* file error text */
"Wrong key or unknown file chunk address - most likely file is deleted." = "Chiave sbagliata o indirizzo sconosciuto per frammento del file - probabilmente il file è stato eliminato.";
/* No comment provided by engineer. */
"Wrong passphrase!" = "Password sbagliata!";
/* No comment provided by engineer. */
"XFTP server" = "Server XFTP";
/* No comment provided by engineer. */
"XFTP servers" = "Server XFTP";
@@ -4389,6 +4770,9 @@
/* No comment provided by engineer. */
"You are invited to group" = "Sei stato/a invitato/a al gruppo";
/* No comment provided by engineer. */
"You are not connected to these servers. Private routing is used to deliver messages to them." = "Non sei connesso/a a questi server. L'instradamento privato è usato per consegnare loro i messaggi.";
/* No comment provided by engineer. */
"you are observer" = "sei un osservatore";
@@ -4417,7 +4801,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Puoi renderlo visibile ai tuoi contatti SimpleX nelle impostazioni.";
/* notification body */
"You can now send messages to %@" = "Ora puoi inviare messaggi a %@";
"You can now chat with %@" = "Ora puoi inviare messaggi a %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Puoi impostare l'anteprima della notifica nella schermata di blocco tramite le impostazioni.";
+1 -4
View File
@@ -148,9 +148,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ は検証されています";
/* No comment provided by engineer. */
"%@ servers" = "%@ サーバー";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ アップロード済";
@@ -3451,7 +3448,7 @@
"You can hide or mute a user profile - swipe it to the right." = "ユーザープロファイルを右にスワイプすると、非表示またはミュートにすることができます。";
/* notification body */
"You can now send messages to %@" = "%@ にメッセージを送信できるようになりました";
"You can now chat with %@" = "%@ にメッセージを送信できるようになりました";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "設定からロック画面の通知プレビューを設定できます。";
+2 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ is geverifieerd";
/* No comment provided by engineer. */
"%@ servers" = "%@ servers";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ geüpload";
@@ -650,7 +647,7 @@
/* rcv group event chat item */
"blocked %@" = "geblokkeerd %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "geblokkeerd door beheerder";
/* No comment provided by engineer. */
@@ -4417,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Je kunt het via Instellingen zichtbaar maken voor je SimpleX contacten.";
/* notification body */
"You can now send messages to %@" = "Je kunt nu berichten sturen naar %@";
"You can now chat with %@" = "Je kunt nu berichten sturen naar %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "U kunt een voorbeeld van een melding op het vergrendeld scherm instellen via instellingen.";
+2 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ jest zweryfikowany";
/* No comment provided by engineer. */
"%@ servers" = "%@ serwery";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ wgrane";
@@ -650,7 +647,7 @@
/* rcv group event chat item */
"blocked %@" = "zablokowany %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "zablokowany przez admina";
/* No comment provided by engineer. */
@@ -4417,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Możesz ustawić go jako widoczny dla swoich kontaktów SimpleX w Ustawieniach.";
/* notification body */
"You can now send messages to %@" = "Możesz teraz wysyłać wiadomości do %@";
"You can now chat with %@" = "Możesz teraz wysyłać wiadomości do %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Podgląd powiadomień na ekranie blokady można ustawić w ustawieniach.";
+2 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ подтверждён";
/* No comment provided by engineer. */
"%@ servers" = "%@ серверы";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ загружено";
@@ -650,7 +647,7 @@
/* rcv group event chat item */
"blocked %@" = "%@ заблокирован";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "заблокировано администратором";
/* No comment provided by engineer. */
@@ -4408,7 +4405,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Вы можете сделать его видимым для ваших контактов в SimpleX через Настройки.";
/* notification body */
"You can now send messages to %@" = "Вы теперь можете отправлять сообщения %@";
"You can now chat with %@" = "Вы теперь можете общаться с %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Вы можете установить просмотр уведомлений на экране блокировки в настройках.";
+1 -4
View File
@@ -109,9 +109,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ ได้รับการตรวจสอบแล้ว";
/* No comment provided by engineer. */
"%@ servers" = "%@ เซิร์ฟเวอร์";
/* notification title */
"%@ wants to connect!" = "%@ อยากเชื่อมต่อ!";
@@ -3304,7 +3301,7 @@
"You can hide or mute a user profile - swipe it to the right." = "คุณสามารถซ่อนหรือปิดเสียงโปรไฟล์ผู้ใช้ - ปัดไปทางขวา";
/* notification body */
"You can now send messages to %@" = "ตอนนี้คุณสามารถส่งข้อความถึง %@";
"You can now chat with %@" = "ตอนนี้คุณสามารถส่งข้อความถึง %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "คุณสามารถตั้งค่าแสดงตัวอย่างการแจ้งเตือนบนหน้าจอล็อคผ่านการตั้งค่า";
+2 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ onaylandı";
/* No comment provided by engineer. */
"%@ servers" = "%@ sunucuları";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ yüklendi";
@@ -650,7 +647,7 @@
/* rcv group event chat item */
"blocked %@" = "engellendi %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "yönetici tarafından engellendi";
/* No comment provided by engineer. */
@@ -4417,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Ayarlardan SimpleX kişilerinize görünür yapabilirsiniz.";
/* notification body */
"You can now send messages to %@" = "Artık %@ adresine mesaj gönderebilirsin";
"You can now chat with %@" = "Artık %@ adresine mesaj gönderebilirsin";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Kilit ekranı bildirim önizlemesini ayarlar üzerinden ayarlayabilirsiniz.";
+2 -5
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ перевірено";
/* No comment provided by engineer. */
"%@ servers" = "%@ сервери";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ завантажено";
@@ -650,7 +647,7 @@
/* rcv group event chat item */
"blocked %@" = "заблоковано %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "заблоковано адміністратором";
/* No comment provided by engineer. */
@@ -4417,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Ви можете зробити його видимим для ваших контактів у SimpleX за допомогою налаштувань.";
/* notification body */
"You can now send messages to %@" = "Тепер ви можете надсилати повідомлення на адресу %@";
"You can now chat with %@" = "Тепер ви можете надсилати повідомлення на адресу %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Ви можете налаштувати попередній перегляд сповіщень на екрані блокування за допомогою налаштувань.";
+2 -5
View File
@@ -133,9 +133,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ 已认证";
/* No comment provided by engineer. */
"%@ servers" = "%@ 服务器";
/* notification title */
"%@ wants to connect!" = "%@ 要连接!";
@@ -602,7 +599,7 @@
/* rcv group event chat item */
"blocked %@" = "已封禁 %@";
/* blocked chat item */
/* marked deleted chat item preview text */
"blocked by admin" = "由管理员封禁";
/* No comment provided by engineer. */
@@ -4114,7 +4111,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "你可以通过设置让它对你的 SimpleX 联系人可见。";
/* notification body */
"You can now send messages to %@" = "您现在可以给 %@ 发送消息";
"You can now chat with %@" = "您现在可以给 %@ 发送消息";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "您可以通过设置来设置锁屏通知预览。";
@@ -28,3 +28,5 @@ actual fun Modifier.desktopOnExternalDrag(
actual fun Modifier.onRightClick(action: () -> Unit): Modifier = this
actual fun Modifier.desktopPointerHoverIconHand(): Modifier = this
actual fun Modifier.desktopOnHovered(action: (Boolean) -> Unit): Modifier = Modifier

Some files were not shown because too many files have changed in this diff Show More