Compare commits
166 Commits
jh/split-g
...
av/android
| Author | SHA1 | Date | |
|---|---|---|---|
|
|
677eedfa8d | ||
|
|
25a4719414 | ||
|
|
f802ec75fe | ||
|
|
045b195483 | ||
|
|
283c90f5ae | ||
|
|
99a9fb2e1f | ||
|
|
ce9d583b39 | ||
|
|
53414608db | ||
|
|
c7cf206585 | ||
|
|
6067ac3c93 | ||
|
|
fc56873f1c | ||
|
|
a30da38af7 | ||
|
|
0bf3d054c6 | ||
|
|
a55a8b116a | ||
|
|
7a207fd641 | ||
|
|
4508e0dfc1 | ||
|
|
a853ba3a15 | ||
|
|
a2f190a6c6 | ||
|
|
267178dddb | ||
|
|
fadce0c140 | ||
|
|
58ad97fe6d | ||
|
|
3ccd9903a7 | ||
|
|
e294999044 | ||
|
|
2bbc687f4a | ||
|
|
61c507e7da | ||
|
|
64230f3545 | ||
|
|
bb61b9c658 | ||
|
|
3428f4d2ee | ||
|
|
fe865c5e11 | ||
|
|
9e87fe73a5 | ||
|
|
0ef2c55983 | ||
|
|
8882284fb7 | ||
|
|
767522e701 | ||
|
|
575d899f5a | ||
|
|
d009777901 | ||
|
|
f758a5526a | ||
|
|
e6b5727003 | ||
|
|
c9b1d54f13 | ||
|
|
05065e919b | ||
|
|
5399212e48 | ||
|
|
809040c7bc | ||
|
|
4ab078bd18 | ||
|
|
825257e898 | ||
|
|
644169b835 | ||
|
|
78eefee6cc | ||
|
|
e253c55ba4 | ||
|
|
478bb32cdb | ||
|
|
1438fd00e2 | ||
|
|
05b55d3fb5 | ||
|
|
9c061508a4 | ||
|
|
2bacc00a06 | ||
|
|
d637714963 | ||
|
|
5ff6bd15f6 | ||
|
|
5b52d0e173 | ||
|
|
b4dd6941d7 | ||
|
|
07334b057d | ||
|
|
51bf2f413c | ||
|
|
e3a69b12ba | ||
|
|
b220b5f6ec | ||
|
|
0b8346701c | ||
|
|
efc873b09b | ||
|
|
9f71502b51 | ||
|
|
bbde6d81ee | ||
|
|
58906e1a60 | ||
|
|
ed3d234826 | ||
|
|
dded56d8b8 | ||
|
|
4d5aefa82c | ||
|
|
9ac99ec2d9 | ||
|
|
37d033c7a5 | ||
|
|
5798efcf71 | ||
|
|
e086719f27 | ||
|
|
bb4293eb5e | ||
|
|
c3c66182f2 | ||
|
|
5b90d92ca2 | ||
|
|
af22348bf8 | ||
|
|
5a6670998c | ||
|
|
700f6fa663 | ||
|
|
d474cae705 | ||
|
|
2834b192ce | ||
|
|
b8cb954882 | ||
|
|
6aeef6f132 | ||
|
|
fa1702a566 | ||
|
|
95d6df926c | ||
|
|
cccd517277 | ||
|
|
12d1ada25e | ||
|
|
f93f68e425 | ||
|
|
23989aca57 | ||
|
|
57a6e85668 | ||
|
|
67590f3258 | ||
|
|
c83238c35a | ||
|
|
8b0d2dede7 | ||
|
|
c4855313b6 | ||
|
|
2bff3b9c97 | ||
|
|
aa037c0662 | ||
|
|
d198d6a8db | ||
|
|
7bcda7e54b | ||
|
|
4a4d470859 | ||
|
|
6ba3100d34 | ||
|
|
7b073ba9f8 | ||
|
|
5e042d222e | ||
|
|
26a189917b | ||
|
|
ce9218b186 | ||
|
|
f0338a03d1 | ||
|
|
6fa0001ea7 | ||
|
|
974fa448b4 | ||
|
|
8cec5428ee | ||
|
|
73130bf321 | ||
|
|
67241ff65c | ||
|
|
b6b041490f | ||
|
|
7f9f9a674c | ||
|
|
ae94bb6f87 | ||
|
|
7ec39d1ffa | ||
|
|
ca6dfb5ea1 | ||
|
|
a5048db6fa | ||
|
|
aca3a71b38 | ||
|
|
d358390e3d | ||
|
|
f9a125bc32 | ||
|
|
35c1975d66 | ||
|
|
0bfe37137c | ||
|
|
666903ae76 | ||
|
|
8a41a4c214 | ||
|
|
79a954336c | ||
|
|
f65b8a9e78 | ||
|
|
e8016adfdc | ||
|
|
d3059afc99 | ||
|
|
2f7632a70f | ||
|
|
27c14f32f1 | ||
|
|
13a32f7864 | ||
|
|
b1652b8930 | ||
|
|
a9b36e8e39 | ||
|
|
ee163a6540 | ||
|
|
4fd6405113 | ||
|
|
ccc62274ee | ||
|
|
4c6d52ba75 | ||
|
|
9df63160e5 | ||
|
|
c8e9788c29 | ||
|
|
7099776357 | ||
|
|
3481d379c6 | ||
|
|
85c1e871dc | ||
|
|
fec5ff3f15 | ||
|
|
acaa597c90 | ||
|
|
6a9a67db14 | ||
|
|
f94c0311c1 | ||
|
|
e1ff7c88d7 | ||
|
|
9a1c7f41f7 | ||
|
|
40e69ae713 | ||
|
|
b74e33b958 | ||
|
|
540c8883a0 | ||
|
|
0e18b13bea | ||
|
|
a4b44254bc | ||
|
|
5819e42305 | ||
|
|
9580b4110d | ||
|
|
05a64c99a2 | ||
|
|
6a21d5c7f1 | ||
|
|
950bbe19da | ||
|
|
f31054de4f | ||
|
|
05278e5a06 | ||
|
|
7a54d74517 | ||
|
|
bfcb2ac230 | ||
|
|
3073c4a1d5 | ||
|
|
d4ac1c0cf2 | ||
|
|
d29f1bb0cf | ||
|
|
75c2de8a12 | ||
|
|
f20ac33e67 | ||
|
|
8cc0954430 | ||
|
|
8f6a31ca07 |
62
.github/workflows/build.yml
vendored
62
.github/workflows/build.yml
vendored
@@ -42,7 +42,7 @@ jobs:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
|
||||
build:
|
||||
name: build-${{ matrix.os }}
|
||||
name: build-${{ matrix.os }}-${{ matrix.ghc }}
|
||||
if: always()
|
||||
needs: prepare-release
|
||||
runs-on: ${{ matrix.os }}
|
||||
@@ -51,18 +51,25 @@ jobs:
|
||||
matrix:
|
||||
include:
|
||||
- os: ubuntu-20.04
|
||||
ghc: "8.10.7"
|
||||
cache_path: ~/.cabal/store
|
||||
- os: ubuntu-20.04
|
||||
ghc: "9.6.3"
|
||||
cache_path: ~/.cabal/store
|
||||
asset_name: simplex-chat-ubuntu-20_04-x86-64
|
||||
desktop_asset_name: simplex-desktop-ubuntu-20_04-x86_64.deb
|
||||
- os: ubuntu-22.04
|
||||
ghc: "9.6.3"
|
||||
cache_path: ~/.cabal/store
|
||||
asset_name: simplex-chat-ubuntu-22_04-x86-64
|
||||
desktop_asset_name: simplex-desktop-ubuntu-22_04-x86_64.deb
|
||||
- os: macos-latest
|
||||
ghc: "9.6.3"
|
||||
cache_path: ~/.cabal/store
|
||||
asset_name: simplex-chat-macos-x86-64
|
||||
desktop_asset_name: simplex-desktop-macos-x86_64.dmg
|
||||
- os: windows-latest
|
||||
ghc: "9.6.3"
|
||||
cache_path: C:/cabal
|
||||
asset_name: simplex-chat-windows-x86-64
|
||||
desktop_asset_name: simplex-desktop-windows-x86_64.msi
|
||||
@@ -81,16 +88,17 @@ jobs:
|
||||
- name: Setup Haskell
|
||||
uses: haskell-actions/setup@v2
|
||||
with:
|
||||
ghc-version: "9.6.3"
|
||||
ghc-version: ${{ matrix.ghc }}
|
||||
cabal-version: "3.10.1.0"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v3
|
||||
- name: Restore cached build
|
||||
id: restore_cache
|
||||
uses: actions/cache/restore@v3
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.cache_path }}
|
||||
dist-newstyle
|
||||
key: ${{ matrix.os }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}
|
||||
key: ${{ matrix.os }}-ghc${{ matrix.ghc }}-${{ hashFiles('cabal.project', 'simplex-chat.cabal') }}
|
||||
|
||||
# / Unix
|
||||
|
||||
@@ -105,7 +113,7 @@ jobs:
|
||||
echo " flags: +openssl" >> cabal.project.local
|
||||
|
||||
- name: Install AppImage dependencies
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'ubuntu-20.04'
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
|
||||
run: sudo apt install -y desktop-file-utils
|
||||
|
||||
- name: Install pkg-config for Mac
|
||||
@@ -131,7 +139,7 @@ jobs:
|
||||
echo "bin_hash=$(echo SHA2-512\(${{ matrix.asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Unix upload CLI binary to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os != 'windows-latest'
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os != 'windows-latest'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -140,7 +148,7 @@ jobs:
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- name: Unix update CLI binary hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os != 'windows-latest'
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os != 'windows-latest'
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -150,7 +158,7 @@ jobs:
|
||||
${{ steps.unix_cli_build.outputs.bin_hash }}
|
||||
|
||||
- name: Setup Java
|
||||
if: startsWith(github.ref, 'refs/tags/v')
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name
|
||||
uses: actions/setup-java@v3
|
||||
with:
|
||||
distribution: 'corretto'
|
||||
@@ -159,7 +167,7 @@ jobs:
|
||||
|
||||
- name: Linux build desktop
|
||||
id: linux_desktop_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
shell: bash
|
||||
run: |
|
||||
scripts/desktop/build-lib-linux.sh
|
||||
@@ -168,10 +176,10 @@ jobs:
|
||||
path=$(echo $PWD/release/main/deb/simplex_*_amd64.deb)
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- name: Linux make AppImage
|
||||
id: linux_appimage_build
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'ubuntu-20.04'
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
|
||||
shell: bash
|
||||
run: |
|
||||
scripts/desktop/make-appimage-linux.sh
|
||||
@@ -194,7 +202,7 @@ jobs:
|
||||
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
- name: Linux upload desktop package to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -203,7 +211,7 @@ jobs:
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- name: Linux update desktop package hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && (matrix.os == 'ubuntu-20.04' || matrix.os == 'ubuntu-22.04')
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -213,7 +221,7 @@ jobs:
|
||||
${{ steps.linux_desktop_build.outputs.package_hash }}
|
||||
|
||||
- name: Linux upload AppImage to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'ubuntu-20.04'
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
with:
|
||||
repo_token: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -222,7 +230,7 @@ jobs:
|
||||
tag: ${{ github.ref }}
|
||||
|
||||
- name: Linux update AppImage hash
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'ubuntu-20.04'
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.asset_name && matrix.os == 'ubuntu-20.04'
|
||||
uses: softprops/action-gh-release@v1
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
@@ -250,6 +258,15 @@ jobs:
|
||||
body: |
|
||||
${{ steps.mac_desktop_build.outputs.package_hash }}
|
||||
|
||||
- name: Cache unix build
|
||||
uses: actions/cache/save@v3
|
||||
if: matrix.os != 'windows-latest'
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.cache_path }}
|
||||
dist-newstyle
|
||||
key: ${{ steps.restore_cache.outputs.cache-primary-key }}
|
||||
|
||||
- name: Unix test
|
||||
if: matrix.os != 'windows-latest'
|
||||
timeout-minutes: 30
|
||||
@@ -262,7 +279,7 @@ jobs:
|
||||
# rm -rf dist-newstyle/src/direct-sq* is here because of the bug in cabal's dependency which prevents second build from finishing
|
||||
|
||||
- name: 'Setup MSYS2'
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
|
||||
if: matrix.os == 'windows-latest'
|
||||
uses: msys2/setup-msys2@v2
|
||||
with:
|
||||
msystem: ucrt64
|
||||
@@ -330,7 +347,7 @@ jobs:
|
||||
path=$(echo $PWD/release/main/msi/*imple*.msi | sed 's#/\([a-z]\)#\1:#' | sed 's#/#\\#g')
|
||||
echo "package_path=$path" >> $GITHUB_OUTPUT
|
||||
echo "package_hash=$(echo SHA2-512\(${{ matrix.desktop_asset_name }}\)= $(openssl sha512 $path | cut -d' ' -f 2))" >> $GITHUB_OUTPUT
|
||||
|
||||
|
||||
- name: Windows upload desktop package to release
|
||||
if: startsWith(github.ref, 'refs/tags/v') && matrix.os == 'windows-latest'
|
||||
uses: svenstaro/upload-release-action@v2
|
||||
@@ -350,4 +367,13 @@ jobs:
|
||||
body: |
|
||||
${{ steps.windows_desktop_build.outputs.package_hash }}
|
||||
|
||||
- name: Cache windows build
|
||||
uses: actions/cache/save@v3
|
||||
if: matrix.os == 'windows-latest'
|
||||
with:
|
||||
path: |
|
||||
${{ matrix.cache_path }}
|
||||
dist-newstyle
|
||||
key: ${{ steps.restore_cache.outputs.cache-primary-key }}
|
||||
|
||||
# Windows /
|
||||
|
||||
@@ -15,6 +15,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
logger.debug("AppDelegate: didFinishLaunchingWithOptions")
|
||||
application.registerForRemoteNotifications()
|
||||
if #available(iOS 17.0, *) { trackKeyboard() }
|
||||
NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil)
|
||||
return true
|
||||
}
|
||||
|
||||
@@ -36,12 +37,17 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
ChatModel.shared.keyboardHeight = 0
|
||||
}
|
||||
|
||||
@objc func pasteboardChanged() {
|
||||
ChatModel.shared.pasteboardHasStrings = UIPasteboard.general.hasStrings
|
||||
}
|
||||
|
||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
|
||||
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
|
||||
let m = ChatModel.shared
|
||||
let deviceToken = DeviceToken(pushProvider: PushProvider(env: pushEnvironment), token: token)
|
||||
m.deviceToken = deviceToken
|
||||
// savedToken is set in startChat, when it is started before this method is called
|
||||
if m.savedToken != nil {
|
||||
registerToken(token: deviceToken)
|
||||
}
|
||||
@@ -80,7 +86,7 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
||||
}
|
||||
} else if let checkMessages = ntfData["checkMessages"] as? Bool, checkMessages {
|
||||
logger.debug("AppDelegate: didReceiveRemoteNotification: checkMessages")
|
||||
if appStateGroupDefault.get().inactive && m.ntfEnablePeriodic {
|
||||
if m.ntfEnablePeriodic && allowBackgroundRefresh() && BGManager.shared.lastRanLongAgo {
|
||||
receiveMessages(completionHandler)
|
||||
} else {
|
||||
completionHandler(.noData)
|
||||
|
||||
@@ -14,11 +14,14 @@ struct ContentView: View {
|
||||
@ObservedObject var alertManager = AlertManager.shared
|
||||
@ObservedObject var callController = CallController.shared
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
@Binding var doAuthenticate: Bool
|
||||
@Binding var userAuthorized: Bool?
|
||||
@Binding var canConnectCall: Bool
|
||||
@Binding var lastSuccessfulUnlock: TimeInterval?
|
||||
@Binding var showInitializationView: Bool
|
||||
|
||||
var contentAccessAuthenticationExtended: Bool
|
||||
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@State private var automaticAuthenticationAttempted = false
|
||||
@State private var canConnectViewCall = false
|
||||
@State private var lastSuccessfulUnlock: TimeInterval? = nil
|
||||
|
||||
@AppStorage(DEFAULT_SHOW_LA_NOTICE) private var prefShowLANotice = false
|
||||
@AppStorage(DEFAULT_LA_NOTICE_SHOWN) private var prefLANoticeShown = false
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
@@ -28,6 +31,7 @@ struct ContentView: View {
|
||||
@State private var showWhatsNew = false
|
||||
@State private var showChooseLAMode = false
|
||||
@State private var showSetPasscode = false
|
||||
@State private var waitingForOrPassedAuth = true
|
||||
@State private var chatListActionSheet: ChatListActionSheet? = nil
|
||||
|
||||
private enum ChatListActionSheet: Identifiable {
|
||||
@@ -40,16 +44,31 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private var accessAuthenticated: Bool {
|
||||
chatModel.contentViewAccessAuthenticated || contentAccessAuthenticationExtended
|
||||
}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
contentView()
|
||||
// contentView() has to be in a single branch, so that enabling authentication doesn't trigger re-rendering and close settings.
|
||||
// i.e. with separate branches like this settings are closed: `if prefPerformLA { ... contentView() ... } else { contentView() }
|
||||
if !prefPerformLA || accessAuthenticated {
|
||||
contentView()
|
||||
} else {
|
||||
lockButton()
|
||||
}
|
||||
if chatModel.showCallView, let call = chatModel.activeCall {
|
||||
callView(call)
|
||||
}
|
||||
if !showSettings, let la = chatModel.laRequest {
|
||||
LocalAuthView(authRequest: la)
|
||||
.onDisappear {
|
||||
// this flag is separate from accessAuthenticated to show initializationView while we wait for authentication
|
||||
waitingForOrPassedAuth = accessAuthenticated
|
||||
}
|
||||
} else if showSetPasscode {
|
||||
SetAppPasscodeView {
|
||||
chatModel.contentViewAccessAuthenticated = true
|
||||
prefPerformLA = true
|
||||
showSetPasscode = false
|
||||
privacyLocalAuthModeDefault.set(.passcode)
|
||||
@@ -59,15 +78,10 @@ struct ContentView: View {
|
||||
showSetPasscode = false
|
||||
alertManager.showAlert(laPasscodeNotSetAlert())
|
||||
}
|
||||
} else if chatModel.chatDbStatus == nil && AppChatState.shared.value != .stopped && waitingForOrPassedAuth {
|
||||
initializationView()
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if prefPerformLA { requestNtfAuthorization() }
|
||||
initAuthenticate()
|
||||
}
|
||||
.onChange(of: doAuthenticate) { _ in
|
||||
initAuthenticate()
|
||||
}
|
||||
.alert(isPresented: $alertManager.presentAlert) { alertManager.alertView! }
|
||||
.sheet(isPresented: $showSettings) {
|
||||
SettingsView(showSettings: $showSettings)
|
||||
@@ -76,14 +90,44 @@ struct ContentView: View {
|
||||
Button("System authentication") { initialEnableLA() }
|
||||
Button("Passcode entry") { showSetPasscode = true }
|
||||
}
|
||||
.onChange(of: scenePhase) { phase in
|
||||
logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))")
|
||||
switch (phase) {
|
||||
case .background:
|
||||
// also see .onChange(of: scenePhase) in SimpleXApp: on entering background
|
||||
// it remembers enteredBackgroundAuthenticated and sets chatModel.contentViewAccessAuthenticated to false
|
||||
automaticAuthenticationAttempted = false
|
||||
canConnectViewCall = false
|
||||
case .active:
|
||||
canConnectViewCall = !prefPerformLA || contentAccessAuthenticationExtended || unlockedRecently()
|
||||
|
||||
// condition `!chatModel.contentViewAccessAuthenticated` is required for when authentication is enabled in settings or on initial notice
|
||||
if prefPerformLA && !chatModel.contentViewAccessAuthenticated {
|
||||
if AppChatState.shared.value != .stopped {
|
||||
if contentAccessAuthenticationExtended {
|
||||
chatModel.contentViewAccessAuthenticated = true
|
||||
} else {
|
||||
if !automaticAuthenticationAttempted {
|
||||
automaticAuthenticationAttempted = true
|
||||
// authenticate if call kit call is not in progress
|
||||
if !(CallController.useCallKit() && chatModel.showCallView && chatModel.activeCall != nil) {
|
||||
authenticateContentViewAccess()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
// when app is stopped automatic authentication is not attempted
|
||||
chatModel.contentViewAccessAuthenticated = contentAccessAuthenticationExtended
|
||||
}
|
||||
}
|
||||
default:
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func contentView() -> some View {
|
||||
if prefPerformLA && userAuthorized != true {
|
||||
lockButton()
|
||||
} else if chatModel.chatDbStatus == nil && showInitializationView {
|
||||
initializationView()
|
||||
} else if let status = chatModel.chatDbStatus, status != .ok {
|
||||
if let status = chatModel.chatDbStatus, status != .ok {
|
||||
DatabaseErrorView(status: status)
|
||||
} else if !chatModel.v3DBMigration.startChat {
|
||||
MigrateToAppGroupView()
|
||||
@@ -106,11 +150,11 @@ struct ContentView: View {
|
||||
if CallController.useCallKit() {
|
||||
ActiveCallView(call: call, canConnectCall: Binding.constant(true))
|
||||
.onDisappear {
|
||||
if userAuthorized == false && doAuthenticate { runAuthenticate() }
|
||||
if prefPerformLA && !accessAuthenticated { authenticateContentViewAccess() }
|
||||
}
|
||||
} else {
|
||||
ActiveCallView(call: call, canConnectCall: $canConnectCall)
|
||||
if prefPerformLA && userAuthorized != true {
|
||||
ActiveCallView(call: call, canConnectCall: $canConnectViewCall)
|
||||
if prefPerformLA && !accessAuthenticated {
|
||||
Rectangle()
|
||||
.fill(colorScheme == .dark ? .black : .white)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
@@ -120,22 +164,27 @@ struct ContentView: View {
|
||||
}
|
||||
|
||||
private func lockButton() -> some View {
|
||||
Button(action: runAuthenticate) { Label("Unlock", systemImage: "lock") }
|
||||
Button(action: authenticateContentViewAccess) { Label("Unlock", systemImage: "lock") }
|
||||
}
|
||||
|
||||
private func initializationView() -> some View {
|
||||
VStack {
|
||||
ProgressView().scaleEffect(2)
|
||||
Text("Opening database…")
|
||||
Text("Opening app…")
|
||||
.padding()
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity )
|
||||
.background(
|
||||
Rectangle()
|
||||
.fill(.background)
|
||||
)
|
||||
}
|
||||
|
||||
private func mainView() -> some View {
|
||||
ZStack(alignment: .top) {
|
||||
ChatListView(showSettings: $showSettings).privacySensitive(protectScreen)
|
||||
.onAppear {
|
||||
if !prefPerformLA { requestNtfAuthorization() }
|
||||
requestNtfAuthorization()
|
||||
// Local Authentication notice is to be shown on next start after onboarding is complete
|
||||
if (!prefLANoticeShown && prefShowLANotice && !chatModel.chats.isEmpty) {
|
||||
prefLANoticeShown = true
|
||||
@@ -187,48 +236,37 @@ struct ContentView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func initAuthenticate() {
|
||||
logger.debug("initAuthenticate")
|
||||
if CallController.useCallKit() && chatModel.showCallView && chatModel.activeCall != nil {
|
||||
userAuthorized = false
|
||||
} else if doAuthenticate {
|
||||
runAuthenticate()
|
||||
}
|
||||
}
|
||||
|
||||
private func runAuthenticate() {
|
||||
logger.debug("DEBUGGING: runAuthenticate")
|
||||
if !prefPerformLA {
|
||||
userAuthorized = true
|
||||
private func unlockedRecently() -> Bool {
|
||||
if let lastSuccessfulUnlock = lastSuccessfulUnlock {
|
||||
return ProcessInfo.processInfo.systemUptime - lastSuccessfulUnlock < 2
|
||||
} else {
|
||||
logger.debug("DEBUGGING: before dismissAllSheets")
|
||||
dismissAllSheets(animated: false) {
|
||||
logger.debug("DEBUGGING: in dismissAllSheets callback")
|
||||
chatModel.chatId = nil
|
||||
justAuthenticate()
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private func justAuthenticate() {
|
||||
userAuthorized = false
|
||||
let laMode = privacyLocalAuthModeDefault.get()
|
||||
authenticate(reason: NSLocalizedString("Unlock app", comment: "authentication reason"), selfDestruct: true) { laResult in
|
||||
logger.debug("DEBUGGING: authenticate callback: \(String(describing: laResult))")
|
||||
switch (laResult) {
|
||||
case .success:
|
||||
userAuthorized = true
|
||||
canConnectCall = true
|
||||
lastSuccessfulUnlock = ProcessInfo.processInfo.systemUptime
|
||||
case .failed:
|
||||
if laMode == .passcode {
|
||||
AlertManager.shared.showAlert(laFailedAlert())
|
||||
private func authenticateContentViewAccess() {
|
||||
logger.debug("DEBUGGING: authenticateContentViewAccess")
|
||||
dismissAllSheets(animated: false) {
|
||||
logger.debug("DEBUGGING: authenticateContentViewAccess, in dismissAllSheets callback")
|
||||
chatModel.chatId = nil
|
||||
|
||||
authenticate(reason: NSLocalizedString("Unlock app", comment: "authentication reason"), selfDestruct: true) { laResult in
|
||||
logger.debug("DEBUGGING: authenticate callback: \(String(describing: laResult))")
|
||||
switch (laResult) {
|
||||
case .success:
|
||||
chatModel.contentViewAccessAuthenticated = true
|
||||
canConnectViewCall = true
|
||||
lastSuccessfulUnlock = ProcessInfo.processInfo.systemUptime
|
||||
case .failed:
|
||||
chatModel.contentViewAccessAuthenticated = false
|
||||
if privacyLocalAuthModeDefault.get() == .passcode {
|
||||
AlertManager.shared.showAlert(laFailedAlert())
|
||||
}
|
||||
case .unavailable:
|
||||
prefPerformLA = false
|
||||
canConnectViewCall = true
|
||||
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
|
||||
}
|
||||
case .unavailable:
|
||||
userAuthorized = true
|
||||
prefPerformLA = false
|
||||
canConnectCall = true
|
||||
AlertManager.shared.showAlert(laUnavailableTurningOffAlert())
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -259,6 +297,7 @@ struct ContentView: View {
|
||||
authenticate(reason: NSLocalizedString("Enable SimpleX Lock", comment: "authentication reason")) { laResult in
|
||||
switch laResult {
|
||||
case .success:
|
||||
chatModel.contentViewAccessAuthenticated = true
|
||||
prefPerformLA = true
|
||||
alertManager.showAlert(laTurnedOnAlert())
|
||||
case .failed:
|
||||
|
||||
@@ -15,7 +15,13 @@ private let receiveTaskId = "chat.simplex.app.receive"
|
||||
// TCP timeout + 2 sec
|
||||
private let waitForMessages: TimeInterval = 6
|
||||
|
||||
private let bgRefreshInterval: TimeInterval = 450
|
||||
// This is the smallest interval between refreshes, and also target interval in "off" mode
|
||||
private let bgRefreshInterval: TimeInterval = 600 // 10 minutes
|
||||
|
||||
// This intervals are used for background refresh in instant and periodic modes
|
||||
private let periodicBgRefreshInterval: TimeInterval = 1200 // 20 minutes
|
||||
|
||||
private let maxBgRefreshInterval: TimeInterval = 2400 // 40 minutes
|
||||
|
||||
private let maxTimerCount = 9
|
||||
|
||||
@@ -33,14 +39,14 @@ class BGManager {
|
||||
}
|
||||
}
|
||||
|
||||
func schedule() {
|
||||
func schedule(interval: TimeInterval? = nil) {
|
||||
if !ChatModel.shared.ntfEnableLocal {
|
||||
logger.debug("BGManager.schedule: disabled")
|
||||
return
|
||||
}
|
||||
logger.debug("BGManager.schedule")
|
||||
let request = BGAppRefreshTaskRequest(identifier: receiveTaskId)
|
||||
request.earliestBeginDate = Date(timeIntervalSinceNow: bgRefreshInterval)
|
||||
request.earliestBeginDate = Date(timeIntervalSinceNow: interval ?? runInterval)
|
||||
do {
|
||||
try BGTaskScheduler.shared.submit(request)
|
||||
} catch {
|
||||
@@ -48,20 +54,34 @@ class BGManager {
|
||||
}
|
||||
}
|
||||
|
||||
var runInterval: TimeInterval {
|
||||
switch ChatModel.shared.notificationMode {
|
||||
case .instant: maxBgRefreshInterval
|
||||
case .periodic: periodicBgRefreshInterval
|
||||
case .off: bgRefreshInterval
|
||||
}
|
||||
}
|
||||
|
||||
var lastRanLongAgo: Bool {
|
||||
Date.now.timeIntervalSince(chatLastBackgroundRunGroupDefault.get()) > runInterval
|
||||
}
|
||||
|
||||
private func handleRefresh(_ task: BGAppRefreshTask) {
|
||||
if !ChatModel.shared.ntfEnableLocal {
|
||||
logger.debug("BGManager.handleRefresh: disabled")
|
||||
return
|
||||
}
|
||||
logger.debug("BGManager.handleRefresh")
|
||||
schedule()
|
||||
if appStateGroupDefault.get().inactive {
|
||||
let shouldRun_ = lastRanLongAgo
|
||||
if allowBackgroundRefresh() && shouldRun_ {
|
||||
schedule()
|
||||
let completeRefresh = completionHandler {
|
||||
task.setTaskCompleted(success: true)
|
||||
}
|
||||
task.expirationHandler = { completeRefresh("expirationHandler") }
|
||||
receiveMessages(completeRefresh)
|
||||
} else {
|
||||
schedule(interval: shouldRun_ ? bgRefreshInterval : runInterval)
|
||||
logger.debug("BGManager.completionHandler: already active, not started")
|
||||
task.setTaskCompleted(success: true)
|
||||
}
|
||||
@@ -90,20 +110,22 @@ class BGManager {
|
||||
}
|
||||
self.completed = false
|
||||
DispatchQueue.main.async {
|
||||
chatLastBackgroundRunGroupDefault.set(Date.now)
|
||||
let m = ChatModel.shared
|
||||
if (!m.chatInitialized) {
|
||||
setAppState(.bgRefresh)
|
||||
do {
|
||||
try initializeChat(start: true)
|
||||
} catch let error {
|
||||
fatalError("Failed to start or load chats: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
activateChat(appState: .bgRefresh)
|
||||
if m.currentUser == nil {
|
||||
completeReceiving("no current user")
|
||||
return
|
||||
}
|
||||
logger.debug("BGManager.receiveMessages: starting chat")
|
||||
activateChat(appState: .bgRefresh)
|
||||
let cr = ChatReceiver()
|
||||
self.chatReceiver = cr
|
||||
cr.start()
|
||||
|
||||
@@ -54,6 +54,9 @@ final class ChatModel: ObservableObject {
|
||||
@Published var chatDbChanged = false
|
||||
@Published var chatDbEncrypted: Bool?
|
||||
@Published var chatDbStatus: DBMigrationResult?
|
||||
@Published var ctrlInitInProgress: Bool = false
|
||||
// local authentication
|
||||
@Published var contentViewAccessAuthenticated: Bool = false
|
||||
@Published var laRequest: LocalAuthRequest?
|
||||
// list of chat "previews"
|
||||
@Published var chats: [Chat] = []
|
||||
@@ -83,18 +86,19 @@ final class ChatModel: ObservableObject {
|
||||
// current WebRTC call
|
||||
@Published var callInvitations: Dictionary<ChatId, RcvCallInvitation> = [:]
|
||||
@Published var activeCall: Call?
|
||||
@Published var callCommand: WCallCommand?
|
||||
let callCommand: WebRTCCommandProcessor = WebRTCCommandProcessor()
|
||||
@Published var showCallView = false
|
||||
// remote desktop
|
||||
@Published var remoteCtrlSession: RemoteCtrlSession?
|
||||
// currently showing QR code
|
||||
@Published var connReqInv: String?
|
||||
// currently showing invitation
|
||||
@Published var showingInvitation: ShowingInvitation?
|
||||
// audio recording and playback
|
||||
@Published var stopPreviousRecPlay: URL? = nil // coordinates currently playing source
|
||||
@Published var draft: ComposeState?
|
||||
@Published var draftChatId: String?
|
||||
// tracks keyboard height via subscription in AppDelegate
|
||||
@Published var keyboardHeight: CGFloat = 0
|
||||
@Published var pasteboardHasStrings: Bool = UIPasteboard.general.hasStrings
|
||||
|
||||
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
|
||||
|
||||
@@ -104,12 +108,10 @@ final class ChatModel: ObservableObject {
|
||||
|
||||
static var ok: Bool { ChatModel.shared.chatDbStatus == .ok }
|
||||
|
||||
var ntfEnableLocal: Bool {
|
||||
notificationMode == .off || ntfEnableLocalGroupDefault.get()
|
||||
}
|
||||
let ntfEnableLocal = true
|
||||
|
||||
var ntfEnablePeriodic: Bool {
|
||||
notificationMode == .periodic || ntfEnablePeriodicGroupDefault.get()
|
||||
notificationMode != .off
|
||||
}
|
||||
|
||||
var activeRemoteCtrl: Bool {
|
||||
@@ -267,7 +269,20 @@ final class ChatModel: ObservableObject {
|
||||
func addChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
|
||||
// update previews
|
||||
if let i = getChatIndex(cInfo.id) {
|
||||
chats[i].chatItems = [cItem]
|
||||
chats[i].chatItems = switch cInfo {
|
||||
case .group:
|
||||
if let currentPreviewItem = chats[i].chatItems.first {
|
||||
if cItem.meta.itemTs >= currentPreviewItem.meta.itemTs {
|
||||
[cItem]
|
||||
} else {
|
||||
[currentPreviewItem]
|
||||
}
|
||||
} else {
|
||||
[cItem]
|
||||
}
|
||||
default:
|
||||
[cItem]
|
||||
}
|
||||
if case .rcvNew = cItem.meta.itemStatus {
|
||||
chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount + 1
|
||||
increaseUnreadCounter(user: currentUser!)
|
||||
@@ -607,14 +622,16 @@ final class ChatModel: ObservableObject {
|
||||
}
|
||||
|
||||
func dismissConnReqView(_ id: String) {
|
||||
if let connReqInv = connReqInv,
|
||||
let c = getChat(id),
|
||||
case let .contactConnection(contactConnection) = c.chatInfo,
|
||||
connReqInv == contactConnection.connReqInv {
|
||||
if id == showingInvitation?.connId {
|
||||
markShowingInvitationUsed()
|
||||
dismissAllSheets()
|
||||
}
|
||||
}
|
||||
|
||||
func markShowingInvitationUsed() {
|
||||
showingInvitation?.connChatUsed = true
|
||||
}
|
||||
|
||||
func removeChat(_ id: String) {
|
||||
withAnimation {
|
||||
chats.removeAll(where: { $0.id == id })
|
||||
@@ -691,6 +708,11 @@ final class ChatModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
struct ShowingInvitation {
|
||||
var connId: String
|
||||
var connChatUsed: Bool
|
||||
}
|
||||
|
||||
struct NTFContactRequest {
|
||||
var incognito: Bool
|
||||
var chatId: String
|
||||
|
||||
@@ -195,18 +195,18 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
|
||||
}
|
||||
}
|
||||
|
||||
func generateNewFileName(_ prefix: String, _ ext: String) -> String {
|
||||
uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)")
|
||||
func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String {
|
||||
uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)", fullPath: fullPath)
|
||||
}
|
||||
|
||||
private func uniqueCombine(_ fileName: String) -> String {
|
||||
private func uniqueCombine(_ fileName: String, fullPath: Bool = false) -> String {
|
||||
func tryCombine(_ fileName: String, _ n: Int) -> String {
|
||||
let ns = fileName as NSString
|
||||
let name = ns.deletingPathExtension
|
||||
let ext = ns.pathExtension
|
||||
let suffix = (n == 0) ? "" : "_\(n)"
|
||||
let f = "\(name)\(suffix).\(ext)"
|
||||
return (FileManager.default.fileExists(atPath: getAppFilePath(f).path)) ? tryCombine(fileName, n + 1) : f
|
||||
return (FileManager.default.fileExists(atPath: fullPath ? f : getAppFilePath(f).path)) ? tryCombine(fileName, n + 1) : f
|
||||
}
|
||||
return tryCombine(fileName, 0)
|
||||
}
|
||||
|
||||
83
apps/ios/Shared/Model/NSESubscriber.swift
Normal file
83
apps/ios/Shared/Model/NSESubscriber.swift
Normal file
@@ -0,0 +1,83 @@
|
||||
//
|
||||
// NSESubscriber.swift
|
||||
// SimpleXChat
|
||||
//
|
||||
// Created by Evgeny on 09/12/2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import SimpleXChat
|
||||
|
||||
private var nseSubscribers: [UUID:NSESubscriber] = [:]
|
||||
|
||||
// timeout for active notification service extension going into "suspending" state.
|
||||
// If in two seconds the state does not change, we assume that it was not running and proceed with app activation/answering call.
|
||||
private let SUSPENDING_TIMEOUT: TimeInterval = 2
|
||||
|
||||
// timeout should be larger than SUSPENDING_TIMEOUT
|
||||
func waitNSESuspended(timeout: TimeInterval, suspended: @escaping (Bool) -> Void) {
|
||||
if timeout <= SUSPENDING_TIMEOUT {
|
||||
logger.warning("waitNSESuspended: small timeout \(timeout), using \(SUSPENDING_TIMEOUT + 1)")
|
||||
}
|
||||
var state = nseStateGroupDefault.get()
|
||||
if case .suspended = state {
|
||||
DispatchQueue.main.async { suspended(true) }
|
||||
return
|
||||
}
|
||||
let id = UUID()
|
||||
var suspendedCalled = false
|
||||
checkTimeout()
|
||||
nseSubscribers[id] = nseMessageSubscriber { msg in
|
||||
if case let .state(newState) = msg {
|
||||
state = newState
|
||||
logger.debug("waitNSESuspended state: \(state.rawValue)")
|
||||
if case .suspended = newState {
|
||||
notifySuspended(true)
|
||||
}
|
||||
}
|
||||
}
|
||||
return
|
||||
|
||||
func notifySuspended(_ ok: Bool) {
|
||||
logger.debug("waitNSESuspended notifySuspended: \(ok)")
|
||||
if !suspendedCalled {
|
||||
logger.debug("waitNSESuspended notifySuspended: calling suspended(\(ok))")
|
||||
suspendedCalled = true
|
||||
nseSubscribers.removeValue(forKey: id)
|
||||
DispatchQueue.main.async { suspended(ok) }
|
||||
}
|
||||
}
|
||||
|
||||
func checkTimeout() {
|
||||
if !suspending() {
|
||||
checkSuspendingTimeout()
|
||||
} else if state == .suspending {
|
||||
checkSuspendedTimeout()
|
||||
}
|
||||
}
|
||||
|
||||
func suspending() -> Bool {
|
||||
suspendedCalled || state == .suspended || state == .suspending
|
||||
}
|
||||
|
||||
func checkSuspendingTimeout() {
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + SUSPENDING_TIMEOUT) {
|
||||
logger.debug("waitNSESuspended check suspending timeout")
|
||||
if !suspending() {
|
||||
notifySuspended(false)
|
||||
} else if state != .suspended {
|
||||
checkSuspendedTimeout()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func checkSuspendedTimeout() {
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + min(timeout - SUSPENDING_TIMEOUT, 1)) {
|
||||
logger.debug("waitNSESuspended check suspended timeout")
|
||||
if state != .suspended {
|
||||
notifySuspended(false)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -211,7 +211,7 @@ func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) asyn
|
||||
}
|
||||
|
||||
func apiStartChat() throws -> Bool {
|
||||
let r = chatSendCmdSync(.startChat(subscribe: true, expire: true, xftp: true))
|
||||
let r = chatSendCmdSync(.startChat(mainApp: true))
|
||||
switch r {
|
||||
case .chatStarted: return true
|
||||
case .chatRunning: return false
|
||||
@@ -228,7 +228,8 @@ func apiStopChat() async throws {
|
||||
}
|
||||
|
||||
func apiActivateChat() {
|
||||
let r = chatSendCmdSync(.apiActivateChat)
|
||||
chatReopenStore()
|
||||
let r = chatSendCmdSync(.apiActivateChat(restoreChat: true))
|
||||
if case .cmdOk = r { return }
|
||||
logger.error("apiActivateChat error: \(String(describing: r))")
|
||||
}
|
||||
@@ -402,7 +403,7 @@ func apiGetNtfToken() -> (DeviceToken?, NtfTknStatus?, NotificationsMode) {
|
||||
case let .ntfToken(token, status, ntfMode): return (token, status, ntfMode)
|
||||
case .chatCmdError(_, .errorAgent(.CMD(.PROHIBITED))): return (nil, nil, .off)
|
||||
default:
|
||||
logger.debug("apiGetNtfToken response: \(String(describing: r), privacy: .public)")
|
||||
logger.debug("apiGetNtfToken response: \(String(describing: r))")
|
||||
return (nil, nil, .off)
|
||||
}
|
||||
}
|
||||
@@ -580,15 +581,15 @@ func apiVerifyGroupMember(_ groupId: Int64, _ groupMemberId: Int64, connectionCo
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiAddContact(incognito: Bool) async -> (String, PendingContactConnection)? {
|
||||
func apiAddContact(incognito: Bool) async -> ((String, PendingContactConnection)?, Alert?) {
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else {
|
||||
logger.error("apiAddContact: no current user")
|
||||
return nil
|
||||
return (nil, nil)
|
||||
}
|
||||
let r = await chatSendCmd(.apiAddContact(userId: userId, incognito: incognito), bgTask: false)
|
||||
if case let .invitation(_, connReqInvitation, connection) = r { return (connReqInvitation, connection) }
|
||||
AlertManager.shared.showAlert(connectionErrorAlert(r))
|
||||
return nil
|
||||
if case let .invitation(_, connReqInvitation, connection) = r { return ((connReqInvitation, connection), nil) }
|
||||
let alert = connectionErrorAlert(r)
|
||||
return (nil, alert)
|
||||
}
|
||||
|
||||
func apiSetConnectionIncognito(connId: Int64, incognito: Bool) async throws -> PendingContactConnection? {
|
||||
@@ -605,27 +606,29 @@ func apiConnectPlan(connReq: String) async throws -> ConnectionPlan {
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiConnect(incognito: Bool, connReq: String) async -> ConnReqType? {
|
||||
let (connReqType, alert) = await apiConnect_(incognito: incognito, connReq: connReq)
|
||||
func apiConnect(incognito: Bool, connReq: String) async -> (ConnReqType, PendingContactConnection)? {
|
||||
let (r, alert) = await apiConnect_(incognito: incognito, connReq: connReq)
|
||||
if let alert = alert {
|
||||
AlertManager.shared.showAlert(alert)
|
||||
return nil
|
||||
} else {
|
||||
return connReqType
|
||||
return r
|
||||
}
|
||||
}
|
||||
|
||||
func apiConnect_(incognito: Bool, connReq: String) async -> (ConnReqType?, Alert?) {
|
||||
func apiConnect_(incognito: Bool, connReq: String) async -> ((ConnReqType, PendingContactConnection)?, Alert?) {
|
||||
guard let userId = ChatModel.shared.currentUser?.userId else {
|
||||
logger.error("apiConnect: no current user")
|
||||
return (nil, nil)
|
||||
}
|
||||
let r = await chatSendCmd(.apiConnect(userId: userId, incognito: incognito, connReq: connReq))
|
||||
let m = ChatModel.shared
|
||||
switch r {
|
||||
case .sentConfirmation: return (.invitation, nil)
|
||||
case .sentInvitation: return (.contact, nil)
|
||||
case let .sentConfirmation(_, connection):
|
||||
return ((.invitation, connection), nil)
|
||||
case let .sentInvitation(_, connection):
|
||||
return ((.contact, connection), nil)
|
||||
case let .contactAlreadyExists(_, contact):
|
||||
let m = ChatModel.shared
|
||||
if let c = m.getContactChat(contact.contactId) {
|
||||
await MainActor.run { m.chatId = c.id }
|
||||
}
|
||||
@@ -1209,9 +1212,11 @@ private func currentUserId(_ funcName: String) throws -> Int64 {
|
||||
throw RuntimeError("\(funcName): no current user")
|
||||
}
|
||||
|
||||
func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool = true, confirmMigrations: MigrationConfirmation? = nil) throws {
|
||||
func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = nil, refreshInvitations: Bool = true, confirmMigrations: MigrationConfirmation? = nil) throws {
|
||||
logger.debug("initializeChat")
|
||||
let m = ChatModel.shared
|
||||
m.ctrlInitInProgress = true
|
||||
defer { m.ctrlInitInProgress = false }
|
||||
(m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey, confirmMigrations: confirmMigrations)
|
||||
if m.chatDbStatus != .ok { return }
|
||||
// If we migrated successfully means previous re-encryption process on database level finished successfully too
|
||||
@@ -1228,10 +1233,43 @@ func initializeChat(start: Bool, dbKey: String? = nil, refreshInvitations: Bool
|
||||
onboardingStageDefault.set(.step1_SimpleXInfo)
|
||||
privacyDeliveryReceiptsSet.set(true)
|
||||
m.onboardingStage = .step1_SimpleXInfo
|
||||
} else if start {
|
||||
} else if confirmStart {
|
||||
showStartChatAfterRestartAlert { start in
|
||||
do {
|
||||
if start { AppChatState.shared.set(.active) }
|
||||
try chatInitialized(start: start, refreshInvitations: refreshInvitations)
|
||||
} catch let error {
|
||||
logger.error("ChatInitialized error: \(error)")
|
||||
}
|
||||
}
|
||||
} else {
|
||||
try chatInitialized(start: start, refreshInvitations: refreshInvitations)
|
||||
}
|
||||
}
|
||||
|
||||
func showStartChatAfterRestartAlert(result: @escaping (_ start: Bool) -> Void) {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Start chat?"),
|
||||
message: Text("Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat."),
|
||||
primaryButton: .default(Text("Ok")) {
|
||||
result(true)
|
||||
},
|
||||
secondaryButton: .cancel {
|
||||
result(false)
|
||||
}
|
||||
))
|
||||
}
|
||||
|
||||
private func chatInitialized(start: Bool, refreshInvitations: Bool) throws {
|
||||
let m = ChatModel.shared
|
||||
if m.currentUser == nil { return }
|
||||
if start {
|
||||
try startChat(refreshInvitations: refreshInvitations)
|
||||
} else {
|
||||
m.chatRunning = false
|
||||
try getUserChatData()
|
||||
NtfManager.shared.setNtfBadgeCount(m.totalUnreadCountForAllUsers())
|
||||
m.onboardingStage = onboardingStageDefault.get()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1248,6 +1286,8 @@ func startChat(refreshInvitations: Bool = true) throws {
|
||||
try refreshCallInvitations()
|
||||
}
|
||||
(m.savedToken, m.tokenStatus, m.notificationMode) = apiGetNtfToken()
|
||||
// deviceToken is set when AppDelegate.application(didRegisterForRemoteNotificationsWithDeviceToken:) is called,
|
||||
// when it is called before startChat
|
||||
if let token = m.deviceToken {
|
||||
registerToken(token: token)
|
||||
}
|
||||
@@ -1362,18 +1402,6 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
let m = ChatModel.shared
|
||||
logger.debug("processReceivedMsg: \(res.responseType)")
|
||||
switch res {
|
||||
case let .newContactConnection(user, connection):
|
||||
if active(user) {
|
||||
await MainActor.run {
|
||||
m.updateContactConnection(connection)
|
||||
}
|
||||
}
|
||||
case let .contactConnectionDeleted(user, connection):
|
||||
if active(user) {
|
||||
await MainActor.run {
|
||||
m.removeChat(connection.id)
|
||||
}
|
||||
}
|
||||
case let .contactDeletedByContact(user, contact):
|
||||
if active(user) && contact.directOrUsed {
|
||||
await MainActor.run {
|
||||
@@ -1666,36 +1694,40 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
activateCall(invitation)
|
||||
case let .callOffer(_, contact, callType, offer, sharedKey, _):
|
||||
await withCall(contact) { call in
|
||||
call.callState = .offerReceived
|
||||
call.peerMedia = callType.media
|
||||
call.sharedKey = sharedKey
|
||||
await MainActor.run {
|
||||
call.callState = .offerReceived
|
||||
call.peerMedia = callType.media
|
||||
call.sharedKey = sharedKey
|
||||
}
|
||||
let useRelay = UserDefaults.standard.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
|
||||
let iceServers = getIceServers()
|
||||
logger.debug(".callOffer useRelay \(useRelay)")
|
||||
logger.debug(".callOffer iceServers \(String(describing: iceServers))")
|
||||
m.callCommand = .offer(
|
||||
await m.callCommand.processCommand(.offer(
|
||||
offer: offer.rtcSession,
|
||||
iceCandidates: offer.rtcIceCandidates,
|
||||
media: callType.media, aesKey: sharedKey,
|
||||
iceServers: iceServers,
|
||||
relay: useRelay
|
||||
)
|
||||
))
|
||||
}
|
||||
case let .callAnswer(_, contact, answer):
|
||||
await withCall(contact) { call in
|
||||
call.callState = .answerReceived
|
||||
m.callCommand = .answer(answer: answer.rtcSession, iceCandidates: answer.rtcIceCandidates)
|
||||
await MainActor.run {
|
||||
call.callState = .answerReceived
|
||||
}
|
||||
await m.callCommand.processCommand(.answer(answer: answer.rtcSession, iceCandidates: answer.rtcIceCandidates))
|
||||
}
|
||||
case let .callExtraInfo(_, contact, extraInfo):
|
||||
await withCall(contact) { _ in
|
||||
m.callCommand = .ice(iceCandidates: extraInfo.rtcIceCandidates)
|
||||
await m.callCommand.processCommand(.ice(iceCandidates: extraInfo.rtcIceCandidates))
|
||||
}
|
||||
case let .callEnded(_, contact):
|
||||
if let invitation = await MainActor.run(body: { m.callInvitations.removeValue(forKey: contact.id) }) {
|
||||
CallController.shared.reportCallRemoteEnded(invitation: invitation)
|
||||
}
|
||||
await withCall(contact) { call in
|
||||
m.callCommand = .end
|
||||
await m.callCommand.processCommand(.end)
|
||||
CallController.shared.reportCallRemoteEnded(call: call)
|
||||
}
|
||||
case .chatSuspended:
|
||||
@@ -1742,7 +1774,9 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
// This delay is needed to cancel the session that fails on network failure,
|
||||
// e.g. when user did not grant permission to access local network yet.
|
||||
if let sess = m.remoteCtrlSession {
|
||||
m.remoteCtrlSession = nil
|
||||
await MainActor.run {
|
||||
m.remoteCtrlSession = nil
|
||||
}
|
||||
if case .connected = sess.sessionState {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
switchToLocalSession()
|
||||
@@ -1753,9 +1787,9 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
logger.debug("unsupported event: \(res.responseType)")
|
||||
}
|
||||
|
||||
func withCall(_ contact: Contact, _ perform: (Call) -> Void) async {
|
||||
func withCall(_ contact: Contact, _ perform: (Call) async -> Void) async {
|
||||
if let call = m.activeCall, call.contact.apiId == contact.apiId {
|
||||
await MainActor.run { perform(call) }
|
||||
await perform(call)
|
||||
} else {
|
||||
logger.debug("processReceivedMsg: ignoring \(res.responseType), not in call with the contact \(contact.id)")
|
||||
}
|
||||
|
||||
@@ -9,27 +9,30 @@
|
||||
import Foundation
|
||||
import UIKit
|
||||
import SimpleXChat
|
||||
import SwiftUI
|
||||
|
||||
private let suspendLockQueue = DispatchQueue(label: "chat.simplex.app.suspend.lock")
|
||||
|
||||
let appSuspendTimeout: Int = 15 // seconds
|
||||
|
||||
let bgSuspendTimeout: Int = 5 // seconds
|
||||
|
||||
let terminationTimeout: Int = 3 // seconds
|
||||
|
||||
let activationDelay: TimeInterval = 1.5
|
||||
|
||||
let nseSuspendTimeout: TimeInterval = 5
|
||||
|
||||
private func _suspendChat(timeout: Int) {
|
||||
// this is a redundant check to prevent logical errors, like the one fixed in this PR
|
||||
let state = appStateGroupDefault.get()
|
||||
let state = AppChatState.shared.value
|
||||
if !state.canSuspend {
|
||||
logger.error("_suspendChat called, current state: \(state.rawValue, privacy: .public)")
|
||||
logger.error("_suspendChat called, current state: \(state.rawValue)")
|
||||
} else if ChatModel.ok {
|
||||
appStateGroupDefault.set(.suspending)
|
||||
AppChatState.shared.set(.suspending)
|
||||
apiSuspendChat(timeoutMicroseconds: timeout * 1000000)
|
||||
let endTask = beginBGTask(chatSuspended)
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + Double(timeout) + 1, execute: endTask)
|
||||
} else {
|
||||
appStateGroupDefault.set(.suspended)
|
||||
AppChatState.shared.set(.suspended)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -41,18 +44,16 @@ func suspendChat() {
|
||||
|
||||
func suspendBgRefresh() {
|
||||
suspendLockQueue.sync {
|
||||
if case .bgRefresh = appStateGroupDefault.get() {
|
||||
if case .bgRefresh = AppChatState.shared.value {
|
||||
_suspendChat(timeout: bgSuspendTimeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var terminating = false
|
||||
|
||||
func terminateChat() {
|
||||
logger.debug("terminateChat")
|
||||
suspendLockQueue.sync {
|
||||
switch appStateGroupDefault.get() {
|
||||
switch AppChatState.shared.value {
|
||||
case .suspending:
|
||||
// suspend instantly if already suspending
|
||||
_chatSuspended()
|
||||
@@ -64,7 +65,6 @@ func terminateChat() {
|
||||
case .stopped:
|
||||
chatCloseStore()
|
||||
default:
|
||||
terminating = true
|
||||
// the store will be closed in _chatSuspended when event is received
|
||||
_suspendChat(timeout: terminationTimeout)
|
||||
}
|
||||
@@ -73,7 +73,7 @@ func terminateChat() {
|
||||
|
||||
func chatSuspended() {
|
||||
suspendLockQueue.sync {
|
||||
if case .suspending = appStateGroupDefault.get() {
|
||||
if case .suspending = AppChatState.shared.value {
|
||||
_chatSuspended()
|
||||
}
|
||||
}
|
||||
@@ -81,48 +81,111 @@ func chatSuspended() {
|
||||
|
||||
private func _chatSuspended() {
|
||||
logger.debug("_chatSuspended")
|
||||
appStateGroupDefault.set(.suspended)
|
||||
AppChatState.shared.set(.suspended)
|
||||
if ChatModel.shared.chatRunning == true {
|
||||
ChatReceiver.shared.stop()
|
||||
}
|
||||
if terminating {
|
||||
chatCloseStore()
|
||||
chatCloseStore()
|
||||
}
|
||||
|
||||
func setAppState(_ appState: AppState) {
|
||||
suspendLockQueue.sync {
|
||||
AppChatState.shared.set(appState)
|
||||
}
|
||||
}
|
||||
|
||||
func activateChat(appState: AppState = .active) {
|
||||
logger.debug("DEBUGGING: activateChat")
|
||||
terminating = false
|
||||
suspendLockQueue.sync {
|
||||
appStateGroupDefault.set(appState)
|
||||
AppChatState.shared.set(appState)
|
||||
if ChatModel.ok { apiActivateChat() }
|
||||
logger.debug("DEBUGGING: activateChat: after apiActivateChat")
|
||||
}
|
||||
}
|
||||
|
||||
func initChatAndMigrate(refreshInvitations: Bool = true) {
|
||||
terminating = false
|
||||
let m = ChatModel.shared
|
||||
if (!m.chatInitialized) {
|
||||
m.v3DBMigration = v3DBMigrationDefault.get()
|
||||
if AppChatState.shared.value == .stopped && storeDBPassphraseGroupDefault.get() && kcDatabasePassword.get() != nil {
|
||||
initialize(start: true, confirmStart: true)
|
||||
} else {
|
||||
initialize(start: true)
|
||||
}
|
||||
}
|
||||
|
||||
func initialize(start: Bool, confirmStart: Bool = false) {
|
||||
do {
|
||||
m.v3DBMigration = v3DBMigrationDefault.get()
|
||||
try initializeChat(start: m.v3DBMigration.startChat, refreshInvitations: refreshInvitations)
|
||||
try initializeChat(start: m.v3DBMigration.startChat && start, confirmStart: m.v3DBMigration.startChat && confirmStart, refreshInvitations: refreshInvitations)
|
||||
} catch let error {
|
||||
fatalError("Failed to start or load chats: \(responseError(error))")
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: start ? "Error starting chat" : "Error opening chat",
|
||||
message: "Please contact developers.\nError: \(responseError(error))"
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startChatAndActivate() {
|
||||
terminating = false
|
||||
func startChatForCall() {
|
||||
logger.debug("DEBUGGING: startChatForCall")
|
||||
if ChatModel.shared.chatRunning == true {
|
||||
ChatReceiver.shared.start()
|
||||
logger.debug("DEBUGGING: startChatForCall: after ChatReceiver.shared.start")
|
||||
}
|
||||
if .active != AppChatState.shared.value {
|
||||
logger.debug("DEBUGGING: startChatForCall: before activateChat")
|
||||
activateChat()
|
||||
logger.debug("DEBUGGING: startChatForCall: after activateChat")
|
||||
}
|
||||
}
|
||||
|
||||
func startChatAndActivate(_ completion: @escaping () -> Void) {
|
||||
logger.debug("DEBUGGING: startChatAndActivate")
|
||||
if ChatModel.shared.chatRunning == true {
|
||||
ChatReceiver.shared.start()
|
||||
logger.debug("DEBUGGING: startChatAndActivate: after ChatReceiver.shared.start")
|
||||
}
|
||||
if .active != appStateGroupDefault.get() {
|
||||
if case .active = AppChatState.shared.value {
|
||||
completion()
|
||||
} else if nseStateGroupDefault.get().inactive {
|
||||
activate()
|
||||
} else {
|
||||
// setting app state to "activating" to notify NSE that it should suspend
|
||||
setAppState(.activating)
|
||||
waitNSESuspended(timeout: nseSuspendTimeout) { ok in
|
||||
if !ok {
|
||||
// if for some reason NSE failed to suspend,
|
||||
// e.g., it crashed previously without setting its state to "suspended",
|
||||
// set it to "suspended" state anyway, so that next time app
|
||||
// does not have to wait when activating.
|
||||
nseStateGroupDefault.set(.suspended)
|
||||
}
|
||||
if AppChatState.shared.value == .activating {
|
||||
activate()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func activate() {
|
||||
logger.debug("DEBUGGING: startChatAndActivate: before activateChat")
|
||||
activateChat()
|
||||
completion()
|
||||
logger.debug("DEBUGGING: startChatAndActivate: after activateChat")
|
||||
}
|
||||
}
|
||||
|
||||
// appStateGroupDefault must not be used in the app directly, only via this singleton
|
||||
class AppChatState {
|
||||
static let shared = AppChatState()
|
||||
private var value_ = appStateGroupDefault.get()
|
||||
|
||||
var value: AppState {
|
||||
value_
|
||||
}
|
||||
|
||||
func set(_ state: AppState) {
|
||||
appStateGroupDefault.set(state)
|
||||
sendAppState(state)
|
||||
value_ = state
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,14 +16,9 @@ struct SimpleXApp: App {
|
||||
@UIApplicationDelegateAdaptor(AppDelegate.self) var appDelegate
|
||||
@StateObject private var chatModel = ChatModel.shared
|
||||
@ObservedObject var alertManager = AlertManager.shared
|
||||
|
||||
@Environment(\.scenePhase) var scenePhase
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
@State private var userAuthorized: Bool?
|
||||
@State private var doAuthenticate = false
|
||||
@State private var enteredBackground: TimeInterval? = nil
|
||||
@State private var canConnectCall = false
|
||||
@State private var lastSuccessfulUnlock: TimeInterval? = nil
|
||||
@State private var showInitializationView = false
|
||||
@State private var enteredBackgroundAuthenticated: TimeInterval? = nil
|
||||
|
||||
init() {
|
||||
DispatchQueue.global(qos: .background).sync {
|
||||
@@ -39,53 +34,55 @@ struct SimpleXApp: App {
|
||||
}
|
||||
|
||||
var body: some Scene {
|
||||
return WindowGroup {
|
||||
ContentView(
|
||||
doAuthenticate: $doAuthenticate,
|
||||
userAuthorized: $userAuthorized,
|
||||
canConnectCall: $canConnectCall,
|
||||
lastSuccessfulUnlock: $lastSuccessfulUnlock,
|
||||
showInitializationView: $showInitializationView
|
||||
)
|
||||
WindowGroup {
|
||||
// contentAccessAuthenticationExtended has to be passed to ContentView on view initialization,
|
||||
// so that it's computed by the time view renders, and not on event after rendering
|
||||
ContentView(contentAccessAuthenticationExtended: !authenticationExpired())
|
||||
.environmentObject(chatModel)
|
||||
.onOpenURL { url in
|
||||
logger.debug("ContentView.onOpenURL: \(url)")
|
||||
chatModel.appOpenUrl = url
|
||||
}
|
||||
.onAppear() {
|
||||
showInitializationView = true
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.1) {
|
||||
initChatAndMigrate()
|
||||
if kcAppPassword.get() == nil || kcSelfDestructPassword.get() == nil {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.15) {
|
||||
initChatAndMigrate()
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: scenePhase) { phase in
|
||||
logger.debug("scenePhase was \(String(describing: scenePhase)), now \(String(describing: phase))")
|
||||
switch (phase) {
|
||||
case .background:
|
||||
// --- authentication
|
||||
// see ContentView .onChange(of: scenePhase) for remaining authentication logic
|
||||
if chatModel.contentViewAccessAuthenticated {
|
||||
enteredBackgroundAuthenticated = ProcessInfo.processInfo.systemUptime
|
||||
}
|
||||
chatModel.contentViewAccessAuthenticated = false
|
||||
// authentication ---
|
||||
|
||||
if CallController.useCallKit() && chatModel.activeCall != nil {
|
||||
CallController.shared.shouldSuspendChat = true
|
||||
} else {
|
||||
suspendChat()
|
||||
BGManager.shared.schedule()
|
||||
}
|
||||
if userAuthorized == true {
|
||||
enteredBackground = ProcessInfo.processInfo.systemUptime
|
||||
}
|
||||
doAuthenticate = false
|
||||
canConnectCall = false
|
||||
NtfManager.shared.setNtfBadgeCount(chatModel.totalUnreadCountForAllUsers())
|
||||
case .active:
|
||||
CallController.shared.shouldSuspendChat = false
|
||||
let appState = appStateGroupDefault.get()
|
||||
startChatAndActivate()
|
||||
if appState.inactive && chatModel.chatRunning == true {
|
||||
updateChats()
|
||||
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
|
||||
updateCallInvitations()
|
||||
let appState = AppChatState.shared.value
|
||||
|
||||
if appState != .stopped {
|
||||
startChatAndActivate {
|
||||
if appState.inactive && chatModel.chatRunning == true {
|
||||
updateChats()
|
||||
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
|
||||
updateCallInvitations()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
doAuthenticate = authenticationExpired()
|
||||
canConnectCall = !(doAuthenticate && prefPerformLA) || unlockedRecently()
|
||||
default:
|
||||
break
|
||||
}
|
||||
@@ -103,12 +100,12 @@ struct SimpleXApp: App {
|
||||
if legacyDatabase, case .documents = dbContainerGroupDefault.get() {
|
||||
dbContainerGroupDefault.set(.documents)
|
||||
setMigrationState(.offer)
|
||||
logger.debug("SimpleXApp init: using legacy DB in documents folder: \(getAppDatabasePath(), privacy: .public)*.db")
|
||||
logger.debug("SimpleXApp init: using legacy DB in documents folder: \(getAppDatabasePath())*.db")
|
||||
} else {
|
||||
dbContainerGroupDefault.set(.group)
|
||||
setMigrationState(.ready)
|
||||
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath(), privacy: .public)*.db")
|
||||
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not", privacy: .public) present")
|
||||
logger.debug("SimpleXApp init: using DB in app group container: \(getAppDatabasePath())*.db")
|
||||
logger.debug("SimpleXApp init: legacy DB\(legacyDatabase ? "" : " not") present")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -118,22 +115,14 @@ struct SimpleXApp: App {
|
||||
}
|
||||
|
||||
private func authenticationExpired() -> Bool {
|
||||
if let enteredBackground = enteredBackground {
|
||||
if let enteredBackgroundAuthenticated = enteredBackgroundAuthenticated {
|
||||
let delay = Double(UserDefaults.standard.integer(forKey: DEFAULT_LA_LOCK_DELAY))
|
||||
return ProcessInfo.processInfo.systemUptime - enteredBackground >= delay
|
||||
return ProcessInfo.processInfo.systemUptime - enteredBackgroundAuthenticated >= delay
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
private func unlockedRecently() -> Bool {
|
||||
if let lastSuccessfulUnlock = lastSuccessfulUnlock {
|
||||
return ProcessInfo.processInfo.systemUptime - lastSuccessfulUnlock < 2
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
private func updateChats() {
|
||||
do {
|
||||
let chats = try apiGetChats()
|
||||
|
||||
@@ -38,21 +38,21 @@ struct ActiveCallView: View {
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase), privacy: .public), canConnectCall \(canConnectCall)")
|
||||
logger.debug("ActiveCallView: appear client is nil \(client == nil), scenePhase \(String(describing: scenePhase)), canConnectCall \(canConnectCall)")
|
||||
AppDelegate.keepScreenOn(true)
|
||||
createWebRTCClient()
|
||||
dismissAllSheets()
|
||||
}
|
||||
.onChange(of: canConnectCall) { _ in
|
||||
logger.debug("ActiveCallView: canConnectCall changed to \(canConnectCall, privacy: .public)")
|
||||
logger.debug("ActiveCallView: canConnectCall changed to \(canConnectCall)")
|
||||
createWebRTCClient()
|
||||
}
|
||||
.onDisappear {
|
||||
logger.debug("ActiveCallView: disappear")
|
||||
Task { await m.callCommand.setClient(nil) }
|
||||
AppDelegate.keepScreenOn(false)
|
||||
client?.endCall()
|
||||
}
|
||||
.onChange(of: m.callCommand) { _ in sendCommandToClient()}
|
||||
.background(.black)
|
||||
.preferredColorScheme(.dark)
|
||||
}
|
||||
@@ -60,19 +60,8 @@ struct ActiveCallView: View {
|
||||
private func createWebRTCClient() {
|
||||
if client == nil && canConnectCall {
|
||||
client = WebRTCClient($activeCall, { msg in await MainActor.run { processRtcMessage(msg: msg) } }, $localRendererAspectRatio)
|
||||
sendCommandToClient()
|
||||
}
|
||||
}
|
||||
|
||||
private func sendCommandToClient() {
|
||||
if call == m.activeCall,
|
||||
m.activeCall != nil,
|
||||
let client = client,
|
||||
let cmd = m.callCommand {
|
||||
m.callCommand = nil
|
||||
logger.debug("sendCallCommand: \(cmd.cmdType)")
|
||||
Task {
|
||||
await client.sendCallCommand(command: cmd)
|
||||
await m.callCommand.setClient(client)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -168,8 +157,10 @@ struct ActiveCallView: View {
|
||||
}
|
||||
case let .error(message):
|
||||
logger.debug("ActiveCallView: command error: \(message)")
|
||||
AlertManager.shared.showAlert(Alert(title: Text("Error"), message: Text(message)))
|
||||
case let .invalid(type):
|
||||
logger.debug("ActiveCallView: invalid response: \(type)")
|
||||
AlertManager.shared.showAlert(Alert(title: Text("Invalid response"), message: Text(type)))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -255,7 +246,6 @@ struct ActiveCallOverlay: View {
|
||||
HStack {
|
||||
Text(call.encryptionStatus)
|
||||
if let connInfo = call.connectionInfo {
|
||||
// Text("(") + Text(connInfo.text) + Text(", \(connInfo.protocolText))")
|
||||
Text("(") + Text(connInfo.text) + Text(")")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -130,7 +130,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
// The delay allows to accept the second call before suspending a chat
|
||||
// see `.onChange(of: scenePhase)` in SimpleXApp
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 3) { [weak self] in
|
||||
logger.debug("CallController: shouldSuspendChat \(String(describing: self?.shouldSuspendChat), privacy: .public)")
|
||||
logger.debug("CallController: shouldSuspendChat \(String(describing: self?.shouldSuspendChat))")
|
||||
if ChatModel.shared.activeCall == nil && self?.shouldSuspendChat == true {
|
||||
self?.shouldSuspendChat = false
|
||||
suspendChat()
|
||||
@@ -142,33 +142,46 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
|
||||
@objc(pushRegistry:didUpdatePushCredentials:forType:)
|
||||
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
|
||||
logger.debug("CallController: didUpdate push credentials for type \(type.rawValue, privacy: .public)")
|
||||
logger.debug("CallController: didUpdate push credentials for type \(type.rawValue)")
|
||||
}
|
||||
|
||||
func pushRegistry(_ registry: PKPushRegistry, didReceiveIncomingPushWith payload: PKPushPayload, for type: PKPushType, completion: @escaping () -> Void) {
|
||||
logger.debug("CallController: did receive push with type \(type.rawValue, privacy: .public)")
|
||||
logger.debug("CallController: did receive push with type \(type.rawValue)")
|
||||
if type != .voIP {
|
||||
completion()
|
||||
return
|
||||
}
|
||||
logger.debug("CallController: initializing chat")
|
||||
if (!ChatModel.shared.chatInitialized) {
|
||||
initChatAndMigrate(refreshInvitations: false)
|
||||
if AppChatState.shared.value == .stopped {
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
return
|
||||
}
|
||||
startChatAndActivate()
|
||||
shouldSuspendChat = true
|
||||
if (!ChatModel.shared.chatInitialized) {
|
||||
logger.debug("CallController: initializing chat")
|
||||
do {
|
||||
try initializeChat(start: true, refreshInvitations: false)
|
||||
} catch let error {
|
||||
logger.error("CallController: initializing chat error: \(error)")
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
return
|
||||
}
|
||||
}
|
||||
logger.debug("CallController: initialized chat")
|
||||
startChatForCall()
|
||||
logger.debug("CallController: started chat")
|
||||
self.shouldSuspendChat = true
|
||||
// There are no invitations in the model, as it was processed by NSE
|
||||
_ = try? justRefreshCallInvitations()
|
||||
logger.debug("CallController: updated call invitations chat")
|
||||
// logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))")
|
||||
// Extract the call information from the push notification payload
|
||||
let m = ChatModel.shared
|
||||
if let contactId = payload.dictionaryPayload["contactId"] as? String,
|
||||
let invitation = m.callInvitations[contactId] {
|
||||
let update = cxCallUpdate(invitation: invitation)
|
||||
let update = self.cxCallUpdate(invitation: invitation)
|
||||
if let uuid = invitation.callkitUUID {
|
||||
logger.debug("CallController: report pushkit call via CallKit")
|
||||
let update = cxCallUpdate(invitation: invitation)
|
||||
provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
||||
let update = self.cxCallUpdate(invitation: invitation)
|
||||
self.provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
||||
if error != nil {
|
||||
m.callInvitations.removeValue(forKey: contactId)
|
||||
}
|
||||
@@ -176,10 +189,10 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
completion()
|
||||
}
|
||||
} else {
|
||||
reportExpiredCall(update: update, completion)
|
||||
self.reportExpiredCall(update: update, completion)
|
||||
}
|
||||
} else {
|
||||
reportExpiredCall(payload: payload, completion)
|
||||
self.reportExpiredCall(payload: payload, completion)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -210,7 +223,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
}
|
||||
|
||||
func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) {
|
||||
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID), privacy: .public)")
|
||||
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID))")
|
||||
if CallController.useCallKit(), let uuid = invitation.callkitUUID {
|
||||
if invitation.callTs.timeIntervalSinceNow >= -180 {
|
||||
let update = cxCallUpdate(invitation: invitation)
|
||||
@@ -350,7 +363,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
||||
private func requestTransaction(with action: CXAction, onSuccess: @escaping () -> Void = {}) {
|
||||
controller.request(CXTransaction(action: action)) { error in
|
||||
if let error = error {
|
||||
logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription, privacy: .public)")
|
||||
logger.error("CallController.requestTransaction error requesting transaction: \(error.localizedDescription)")
|
||||
} else {
|
||||
logger.debug("CallController.requestTransaction requested transaction successfully")
|
||||
onSuccess()
|
||||
|
||||
@@ -22,7 +22,7 @@ class CallManager {
|
||||
let m = ChatModel.shared
|
||||
if let call = m.activeCall, call.callkitUUID == callUUID {
|
||||
m.showCallView = true
|
||||
m.callCommand = .capabilities(media: call.localMedia)
|
||||
Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) }
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -57,19 +57,21 @@ class CallManager {
|
||||
m.activeCall = call
|
||||
m.showCallView = true
|
||||
|
||||
m.callCommand = .start(
|
||||
Task {
|
||||
await m.callCommand.processCommand(.start(
|
||||
media: invitation.callType.media,
|
||||
aesKey: invitation.sharedKey,
|
||||
iceServers: iceServers,
|
||||
relay: useRelay
|
||||
)
|
||||
))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func enableMedia(media: CallMediaType, enable: Bool, callUUID: UUID) -> Bool {
|
||||
if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID {
|
||||
let m = ChatModel.shared
|
||||
m.callCommand = .media(media: media, enable: enable)
|
||||
Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) }
|
||||
return true
|
||||
}
|
||||
return false
|
||||
@@ -94,11 +96,13 @@ class CallManager {
|
||||
completed()
|
||||
} else {
|
||||
logger.debug("CallManager.endCall: ending call...")
|
||||
m.callCommand = .end
|
||||
m.activeCall = nil
|
||||
m.showCallView = false
|
||||
completed()
|
||||
Task {
|
||||
await m.callCommand.processCommand(.end)
|
||||
await MainActor.run {
|
||||
m.activeCall = nil
|
||||
m.showCallView = false
|
||||
completed()
|
||||
}
|
||||
do {
|
||||
try await apiEndCall(call.contact)
|
||||
} catch {
|
||||
|
||||
@@ -335,6 +335,50 @@ extension WCallResponse: Encodable {
|
||||
}
|
||||
}
|
||||
|
||||
actor WebRTCCommandProcessor {
|
||||
private var client: WebRTCClient? = nil
|
||||
private var commands: [WCallCommand] = []
|
||||
private var running: Bool = false
|
||||
|
||||
func setClient(_ client: WebRTCClient?) async {
|
||||
logger.debug("WebRTC: setClient, commands count \(self.commands.count)")
|
||||
self.client = client
|
||||
if client != nil {
|
||||
await processAllCommands()
|
||||
} else {
|
||||
commands.removeAll()
|
||||
}
|
||||
}
|
||||
|
||||
func processCommand(_ c: WCallCommand) async {
|
||||
// logger.debug("WebRTC: process command \(c.cmdType)")
|
||||
commands.append(c)
|
||||
if !running && client != nil {
|
||||
await processAllCommands()
|
||||
}
|
||||
}
|
||||
|
||||
func processAllCommands() async {
|
||||
logger.debug("WebRTC: process all commands, commands count \(self.commands.count), client == nil \(self.client == nil)")
|
||||
if let client = client {
|
||||
running = true
|
||||
while let c = commands.first, shouldRunCommand(client, c) {
|
||||
commands.remove(at: 0)
|
||||
await client.sendCallCommand(command: c)
|
||||
logger.debug("WebRTC: processed cmd \(c.cmdType)")
|
||||
}
|
||||
running = false
|
||||
}
|
||||
}
|
||||
|
||||
func shouldRunCommand(_ client: WebRTCClient, _ c: WCallCommand) -> Bool {
|
||||
switch c {
|
||||
case .capabilities, .start, .offer, .end: true
|
||||
default: client.activeCall.wrappedValue != nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectionState: Codable, Equatable {
|
||||
var connectionState: String
|
||||
var iceConnectionState: String
|
||||
@@ -358,26 +402,12 @@ struct ConnectionInfo: Codable, Equatable {
|
||||
return "\(local?.rawValue ?? unknown) / \(remote?.rawValue ?? unknown)"
|
||||
}
|
||||
}
|
||||
|
||||
var protocolText: String {
|
||||
let unknown = NSLocalizedString("unknown", comment: "connection info")
|
||||
let local = localCandidate?.protocol?.uppercased() ?? unknown
|
||||
let localRelay = localCandidate?.relayProtocol?.uppercased() ?? unknown
|
||||
let remote = remoteCandidate?.protocol?.uppercased() ?? unknown
|
||||
let localText = localRelay == local || localCandidate?.relayProtocol == nil
|
||||
? local
|
||||
: "\(local) (\(localRelay))"
|
||||
return local == remote
|
||||
? localText
|
||||
: "\(localText) / \(remote)"
|
||||
}
|
||||
}
|
||||
|
||||
// https://developer.mozilla.org/en-US/docs/Web/API/RTCIceCandidate
|
||||
struct RTCIceCandidate: Codable, Equatable {
|
||||
var candidateType: RTCIceCandidateType?
|
||||
var `protocol`: String?
|
||||
var relayProtocol: String?
|
||||
var sdpMid: String?
|
||||
var sdpMLineIndex: Int?
|
||||
var candidate: String
|
||||
|
||||
@@ -18,10 +18,11 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
}()
|
||||
private static let ivTagBytes: Int = 28
|
||||
private static let enableEncryption: Bool = true
|
||||
private var chat_ctrl = getChatCtrl()
|
||||
|
||||
struct Call {
|
||||
var connection: RTCPeerConnection
|
||||
var iceCandidates: [RTCIceCandidate]
|
||||
var iceCandidates: IceCandidates
|
||||
var localMedia: CallMediaType
|
||||
var localCamera: RTCVideoCapturer?
|
||||
var localVideoSource: RTCVideoSource?
|
||||
@@ -33,10 +34,24 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
var frameDecryptor: RTCFrameDecryptor?
|
||||
}
|
||||
|
||||
actor IceCandidates {
|
||||
private var candidates: [RTCIceCandidate] = []
|
||||
|
||||
func getAndClear() async -> [RTCIceCandidate] {
|
||||
let cs = candidates
|
||||
candidates = []
|
||||
return cs
|
||||
}
|
||||
|
||||
func append(_ c: RTCIceCandidate) async {
|
||||
candidates.append(c)
|
||||
}
|
||||
}
|
||||
|
||||
private let rtcAudioSession = RTCAudioSession.sharedInstance()
|
||||
private let audioQueue = DispatchQueue(label: "audio")
|
||||
private var sendCallResponse: (WVAPIMessage) async -> Void
|
||||
private var activeCall: Binding<Call?>
|
||||
var activeCall: Binding<Call?>
|
||||
private var localRendererAspectRatio: Binding<CGFloat?>
|
||||
|
||||
@available(*, unavailable)
|
||||
@@ -60,7 +75,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=tcp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"),
|
||||
]
|
||||
|
||||
func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ remoteIceCandidates: [RTCIceCandidate], _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call {
|
||||
func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call {
|
||||
let connection = createPeerConnection(iceServers ?? getWebRTCIceServers() ?? defaultIceServers, relay)
|
||||
connection.delegate = self
|
||||
createAudioSender(connection)
|
||||
@@ -87,7 +102,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
}
|
||||
return Call(
|
||||
connection: connection,
|
||||
iceCandidates: remoteIceCandidates,
|
||||
iceCandidates: IceCandidates(),
|
||||
localMedia: mediaType,
|
||||
localCamera: localCamera,
|
||||
localVideoSource: localVideoSource,
|
||||
@@ -144,26 +159,18 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
logger.debug("starting incoming call - create webrtc session")
|
||||
if activeCall.wrappedValue != nil { endCall() }
|
||||
let encryption = WebRTCClient.enableEncryption
|
||||
let call = initializeCall(iceServers?.toWebRTCIceServers(), [], media, encryption ? aesKey : nil, relay)
|
||||
let call = initializeCall(iceServers?.toWebRTCIceServers(), media, encryption ? aesKey : nil, relay)
|
||||
activeCall.wrappedValue = call
|
||||
call.connection.offer { answer in
|
||||
Task {
|
||||
let gotCandidates = await self.waitWithTimeout(10_000, stepMs: 1000, until: { self.activeCall.wrappedValue?.iceCandidates.count ?? 0 > 0 })
|
||||
if gotCandidates {
|
||||
await self.sendCallResponse(.init(
|
||||
corrId: nil,
|
||||
resp: .offer(
|
||||
offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))),
|
||||
iceCandidates: compressToBase64(input: encodeJSON(self.activeCall.wrappedValue?.iceCandidates ?? [])),
|
||||
capabilities: CallCapabilities(encryption: encryption)
|
||||
),
|
||||
command: command)
|
||||
)
|
||||
} else {
|
||||
self.endCall()
|
||||
}
|
||||
}
|
||||
|
||||
let (offer, error) = await call.connection.offer()
|
||||
if let offer = offer {
|
||||
resp = .offer(
|
||||
offer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: offer.type.toSdpType(), sdp: offer.sdp))),
|
||||
iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates())),
|
||||
capabilities: CallCapabilities(encryption: encryption)
|
||||
)
|
||||
self.waitForMoreIceCandidates()
|
||||
} else {
|
||||
resp = .error(message: "offer error: \(error?.localizedDescription ?? "unknown error")")
|
||||
}
|
||||
case let .offer(offer, iceCandidates, media, aesKey, iceServers, relay):
|
||||
if activeCall.wrappedValue != nil {
|
||||
@@ -172,26 +179,21 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
resp = .error(message: "accept: encryption is not supported")
|
||||
} else if let offer: CustomRTCSessionDescription = decodeJSON(decompressFromBase64(input: offer)),
|
||||
let remoteIceCandidates: [RTCIceCandidate] = decodeJSON(decompressFromBase64(input: iceCandidates)) {
|
||||
let call = initializeCall(iceServers?.toWebRTCIceServers(), remoteIceCandidates, media, WebRTCClient.enableEncryption ? aesKey : nil, relay)
|
||||
let call = initializeCall(iceServers?.toWebRTCIceServers(), media, WebRTCClient.enableEncryption ? aesKey : nil, relay)
|
||||
activeCall.wrappedValue = call
|
||||
let pc = call.connection
|
||||
if let type = offer.type, let sdp = offer.sdp {
|
||||
if (try? await pc.setRemoteDescription(RTCSessionDescription(type: type.toWebRTCSdpType(), sdp: sdp))) != nil {
|
||||
pc.answer { answer in
|
||||
let (answer, error) = await pc.answer()
|
||||
if let answer = answer {
|
||||
self.addIceCandidates(pc, remoteIceCandidates)
|
||||
// Task {
|
||||
// try? await Task.sleep(nanoseconds: 32_000 * 1000000)
|
||||
Task {
|
||||
await self.sendCallResponse(.init(
|
||||
corrId: nil,
|
||||
resp: .answer(
|
||||
answer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))),
|
||||
iceCandidates: compressToBase64(input: encodeJSON(call.iceCandidates))
|
||||
),
|
||||
command: command)
|
||||
)
|
||||
}
|
||||
// }
|
||||
resp = .answer(
|
||||
answer: compressToBase64(input: encodeJSON(CustomRTCSessionDescription(type: answer.type.toSdpType(), sdp: answer.sdp))),
|
||||
iceCandidates: compressToBase64(input: encodeJSON(await self.getInitialIceCandidates()))
|
||||
)
|
||||
self.waitForMoreIceCandidates()
|
||||
} else {
|
||||
resp = .error(message: "answer error: \(error?.localizedDescription ?? "unknown error")")
|
||||
}
|
||||
} else {
|
||||
resp = .error(message: "accept: remote description is not set")
|
||||
@@ -234,6 +236,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
resp = .ok
|
||||
}
|
||||
case .end:
|
||||
// TODO possibly, endCall should be called before returning .ok
|
||||
await sendCallResponse(.init(corrId: nil, resp: .ok, command: command))
|
||||
endCall()
|
||||
}
|
||||
@@ -242,6 +245,33 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
}
|
||||
}
|
||||
|
||||
func getInitialIceCandidates() async -> [RTCIceCandidate] {
|
||||
await untilIceComplete(timeoutMs: 750, stepMs: 150) {}
|
||||
let candidates = await activeCall.wrappedValue?.iceCandidates.getAndClear() ?? []
|
||||
logger.debug("WebRTCClient: sending initial ice candidates: \(candidates.count)")
|
||||
return candidates
|
||||
}
|
||||
|
||||
func waitForMoreIceCandidates() {
|
||||
Task {
|
||||
await untilIceComplete(timeoutMs: 12000, stepMs: 1500) {
|
||||
let candidates = await self.activeCall.wrappedValue?.iceCandidates.getAndClear() ?? []
|
||||
if candidates.count > 0 {
|
||||
logger.debug("WebRTCClient: sending more ice candidates: \(candidates.count)")
|
||||
await self.sendIceCandidates(candidates)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sendIceCandidates(_ candidates: [RTCIceCandidate]) async {
|
||||
await self.sendCallResponse(.init(
|
||||
corrId: nil,
|
||||
resp: .ice(iceCandidates: compressToBase64(input: encodeJSON(candidates))),
|
||||
command: nil)
|
||||
)
|
||||
}
|
||||
|
||||
func enableMedia(_ media: CallMediaType, _ enable: Bool) {
|
||||
logger.debug("WebRTCClient: enabling media \(media.rawValue) \(enable)")
|
||||
media == .video ? setVideoEnabled(enable) : setAudioEnabled(enable)
|
||||
@@ -279,7 +309,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
memcpy(pointer, (unencrypted as NSData).bytes, unencrypted.count)
|
||||
let isKeyFrame = unencrypted[0] & 1 == 0
|
||||
let clearTextBytesSize = mediaType.rawValue == 0 ? 1 : isKeyFrame ? 10 : 3
|
||||
logCrypto("encrypt", chat_encrypt_media(&key, pointer.advanced(by: clearTextBytesSize), Int32(unencrypted.count + WebRTCClient.ivTagBytes - clearTextBytesSize)))
|
||||
logCrypto("encrypt", chat_encrypt_media(chat_ctrl, &key, pointer.advanced(by: clearTextBytesSize), Int32(unencrypted.count + WebRTCClient.ivTagBytes - clearTextBytesSize)))
|
||||
return Data(bytes: pointer, count: unencrypted.count + WebRTCClient.ivTagBytes)
|
||||
} else {
|
||||
return nil
|
||||
@@ -387,12 +417,13 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
audioSessionToDefaults()
|
||||
}
|
||||
|
||||
func waitWithTimeout(_ timeoutMs: UInt64, stepMs: UInt64, until success: () -> Bool) async -> Bool {
|
||||
let startedAt = DispatchTime.now()
|
||||
while !success() && startedAt.uptimeNanoseconds + timeoutMs * 1000000 > DispatchTime.now().uptimeNanoseconds {
|
||||
guard let _ = try? await Task.sleep(nanoseconds: stepMs * 1000000) else { break }
|
||||
}
|
||||
return success()
|
||||
func untilIceComplete(timeoutMs: UInt64, stepMs: UInt64, action: @escaping () async -> Void) async {
|
||||
var t: UInt64 = 0
|
||||
repeat {
|
||||
_ = try? await Task.sleep(nanoseconds: stepMs * 1000000)
|
||||
t += stepMs
|
||||
await action()
|
||||
} while t < timeoutMs && activeCall.wrappedValue?.connection.iceGatheringState != .complete
|
||||
}
|
||||
}
|
||||
|
||||
@@ -405,25 +436,33 @@ extension WebRTC.RTCPeerConnection {
|
||||
optionalConstraints: nil)
|
||||
}
|
||||
|
||||
func offer(_ completion: @escaping (_ sdp: RTCSessionDescription) -> Void) {
|
||||
offer(for: mediaConstraints()) { (sdp, error) in
|
||||
guard let sdp = sdp else {
|
||||
return
|
||||
func offer() async -> (RTCSessionDescription?, Error?) {
|
||||
await withCheckedContinuation { cont in
|
||||
offer(for: mediaConstraints()) { (sdp, error) in
|
||||
self.processSDP(cont, sdp, error)
|
||||
}
|
||||
self.setLocalDescription(sdp, completionHandler: { (error) in
|
||||
completion(sdp)
|
||||
})
|
||||
}
|
||||
}
|
||||
|
||||
func answer(_ completion: @escaping (_ sdp: RTCSessionDescription) -> Void) {
|
||||
answer(for: mediaConstraints()) { (sdp, error) in
|
||||
guard let sdp = sdp else {
|
||||
return
|
||||
func answer() async -> (RTCSessionDescription?, Error?) {
|
||||
await withCheckedContinuation { cont in
|
||||
answer(for: mediaConstraints()) { (sdp, error) in
|
||||
self.processSDP(cont, sdp, error)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func processSDP(_ cont: CheckedContinuation<(RTCSessionDescription?, Error?), Never>, _ sdp: RTCSessionDescription?, _ error: Error?) {
|
||||
if let sdp = sdp {
|
||||
self.setLocalDescription(sdp, completionHandler: { (error) in
|
||||
completion(sdp)
|
||||
if let error = error {
|
||||
cont.resume(returning: (nil, error))
|
||||
} else {
|
||||
cont.resume(returning: (sdp, nil))
|
||||
}
|
||||
})
|
||||
} else {
|
||||
cont.resume(returning: (nil, error))
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -479,6 +518,7 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
|
||||
default: enableSpeaker = false
|
||||
}
|
||||
setSpeakerEnabledAndConfigureSession(enableSpeaker)
|
||||
case .connected: sendConnectedEvent(connection)
|
||||
case .disconnected, .failed: endCall()
|
||||
default: do {}
|
||||
}
|
||||
@@ -491,7 +531,9 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
|
||||
|
||||
func peerConnection(_ connection: RTCPeerConnection, didGenerate candidate: WebRTC.RTCIceCandidate) {
|
||||
// logger.debug("Connection generated candidate \(candidate.debugDescription)")
|
||||
activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil, nil))
|
||||
Task {
|
||||
await self.activeCall.wrappedValue?.iceCandidates.append(candidate.toCandidate(nil, nil))
|
||||
}
|
||||
}
|
||||
|
||||
func peerConnection(_ connection: RTCPeerConnection, didRemove candidates: [WebRTC.RTCIceCandidate]) {
|
||||
@@ -506,10 +548,9 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
|
||||
lastReceivedMs lastDataReceivedMs: Int32,
|
||||
changeReason reason: String) {
|
||||
// logger.debug("Connection changed candidate \(reason) \(remote.debugDescription) \(remote.description)")
|
||||
sendConnectedEvent(connection, local: local, remote: remote)
|
||||
}
|
||||
|
||||
func sendConnectedEvent(_ connection: WebRTC.RTCPeerConnection, local: WebRTC.RTCIceCandidate, remote: WebRTC.RTCIceCandidate) {
|
||||
func sendConnectedEvent(_ connection: WebRTC.RTCPeerConnection) {
|
||||
connection.statistics { (stats: RTCStatisticsReport) in
|
||||
stats.statistics.values.forEach { stat in
|
||||
// logger.debug("Stat \(stat.debugDescription)")
|
||||
@@ -517,24 +558,25 @@ extension WebRTCClient: RTCPeerConnectionDelegate {
|
||||
let localId = stat.values["localCandidateId"] as? String,
|
||||
let remoteId = stat.values["remoteCandidateId"] as? String,
|
||||
let localStats = stats.statistics[localId],
|
||||
let remoteStats = stats.statistics[remoteId],
|
||||
local.sdp.contains("\((localStats.values["ip"] as? String ?? "--")) \((localStats.values["port"] as? String ?? "--"))") &&
|
||||
remote.sdp.contains("\((remoteStats.values["ip"] as? String ?? "--")) \((remoteStats.values["port"] as? String ?? "--"))")
|
||||
let remoteStats = stats.statistics[remoteId]
|
||||
{
|
||||
Task {
|
||||
await self.sendCallResponse(.init(
|
||||
corrId: nil,
|
||||
resp: .connected(connectionInfo: ConnectionInfo(
|
||||
localCandidate: local.toCandidate(
|
||||
RTCIceCandidateType.init(rawValue: localStats.values["candidateType"] as! String),
|
||||
localStats.values["protocol"] as? String,
|
||||
localStats.values["relayProtocol"] as? String
|
||||
localCandidate: RTCIceCandidate(
|
||||
candidateType: RTCIceCandidateType.init(rawValue: localStats.values["candidateType"] as! String),
|
||||
protocol: localStats.values["protocol"] as? String,
|
||||
sdpMid: nil,
|
||||
sdpMLineIndex: nil,
|
||||
candidate: ""
|
||||
),
|
||||
remoteCandidate: remote.toCandidate(
|
||||
RTCIceCandidateType.init(rawValue: remoteStats.values["candidateType"] as! String),
|
||||
remoteStats.values["protocol"] as? String,
|
||||
remoteStats.values["relayProtocol"] as? String
|
||||
))),
|
||||
remoteCandidate: RTCIceCandidate(
|
||||
candidateType: RTCIceCandidateType.init(rawValue: remoteStats.values["candidateType"] as! String),
|
||||
protocol: remoteStats.values["protocol"] as? String,
|
||||
sdpMid: nil,
|
||||
sdpMLineIndex: nil,
|
||||
candidate: ""))),
|
||||
command: nil)
|
||||
)
|
||||
}
|
||||
@@ -634,11 +676,10 @@ extension RTCIceCandidate {
|
||||
}
|
||||
|
||||
extension WebRTC.RTCIceCandidate {
|
||||
func toCandidate(_ candidateType: RTCIceCandidateType?, _ protocol: String?, _ relayProtocol: String?) -> RTCIceCandidate {
|
||||
func toCandidate(_ candidateType: RTCIceCandidateType?, _ protocol: String?) -> RTCIceCandidate {
|
||||
RTCIceCandidate(
|
||||
candidateType: candidateType,
|
||||
protocol: `protocol`,
|
||||
relayProtocol: relayProtocol,
|
||||
sdpMid: sdpMid,
|
||||
sdpMLineIndex: Int(sdpMLineIndex),
|
||||
candidate: sdp
|
||||
|
||||
@@ -28,7 +28,9 @@ struct FramedItemView: View {
|
||||
@State var metaColor = Color.secondary
|
||||
@State var showFullScreenImage = false
|
||||
@Binding var allowMenu: Bool
|
||||
|
||||
@State private var showSecrets = false
|
||||
@State private var showQuoteSecrets = false
|
||||
|
||||
@Binding var audioPlayer: AudioPlayer?
|
||||
@Binding var playbackState: VoiceMessagePlaybackState
|
||||
@Binding var playbackTime: TimeInterval?
|
||||
@@ -252,10 +254,12 @@ struct FramedItemView: View {
|
||||
}
|
||||
|
||||
private func ciQuotedMsgTextView(_ qi: CIQuote, lines: Int) -> some View {
|
||||
MsgContentView(chat: chat, text: qi.text, formattedText: qi.formattedText)
|
||||
.lineLimit(lines)
|
||||
.font(.subheadline)
|
||||
.padding(.bottom, 6)
|
||||
toggleSecrets(qi.formattedText, $showQuoteSecrets,
|
||||
MsgContentView(chat: chat, text: qi.text, formattedText: qi.formattedText, showSecrets: showQuoteSecrets)
|
||||
.lineLimit(lines)
|
||||
.font(.subheadline)
|
||||
.padding(.bottom, 6)
|
||||
)
|
||||
}
|
||||
|
||||
private func ciQuoteIconView(_ image: String) -> some View {
|
||||
@@ -278,13 +282,15 @@ struct FramedItemView: View {
|
||||
@ViewBuilder private func ciMsgContentView(_ ci: ChatItem) -> some View {
|
||||
let text = ci.meta.isLive ? ci.content.msgContent?.text ?? ci.text : ci.text
|
||||
let rtl = isRightToLeft(text)
|
||||
let v = MsgContentView(
|
||||
let ft = text == "" ? [] : ci.formattedText
|
||||
let v = toggleSecrets(ft, $showSecrets, MsgContentView(
|
||||
chat: chat,
|
||||
text: text,
|
||||
formattedText: text == "" ? [] : ci.formattedText,
|
||||
formattedText: ft,
|
||||
meta: ci.meta,
|
||||
rightToLeft: rtl
|
||||
)
|
||||
rightToLeft: rtl,
|
||||
showSecrets: showSecrets
|
||||
))
|
||||
.multilineTextAlignment(rtl ? .trailing : .leading)
|
||||
.padding(.vertical, 6)
|
||||
.padding(.horizontal, 12)
|
||||
@@ -298,7 +304,7 @@ struct FramedItemView: View {
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@ViewBuilder private func ciFileView(_ ci: ChatItem, _ text: String) -> some View {
|
||||
CIFileView(file: chatItem.file, edited: chatItem.meta.itemEdited)
|
||||
.overlay(DetermineWidth())
|
||||
@@ -318,6 +324,14 @@ struct FramedItemView: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func toggleSecrets<V: View>(_ ft: [FormattedText]?, _ showSecrets: Binding<Bool>, _ v: V) -> some View {
|
||||
if let ft = ft, ft.contains(where: { $0.isSecret }) {
|
||||
v.onTapGesture { showSecrets.wrappedValue.toggle() }
|
||||
} else {
|
||||
v
|
||||
}
|
||||
}
|
||||
|
||||
func isRightToLeft(_ s: String) -> Bool {
|
||||
if let lang = CFStringTokenizerCopyBestStringLanguage(s as CFString, CFRange(location: 0, length: min(s.count, 80))) {
|
||||
return NSLocale.characterDirection(forLanguage: lang as String) == .rightToLeft
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
private let uiLinkColor = UIColor(red: 0, green: 0.533, blue: 1, alpha: 1)
|
||||
let uiLinkColor = UIColor(red: 0, green: 0.533, blue: 1, alpha: 1)
|
||||
|
||||
private let noTyping = Text(" ")
|
||||
|
||||
@@ -31,6 +31,7 @@ struct MsgContentView: View {
|
||||
var sender: String? = nil
|
||||
var meta: CIMeta? = nil
|
||||
var rightToLeft = false
|
||||
var showSecrets: Bool
|
||||
@State private var typingIdx = 0
|
||||
@State private var timer: Timer?
|
||||
|
||||
@@ -62,7 +63,7 @@ struct MsgContentView: View {
|
||||
}
|
||||
|
||||
private func msgContentView() -> Text {
|
||||
var v = messageText(text, formattedText, sender)
|
||||
var v = messageText(text, formattedText, sender, showSecrets: showSecrets)
|
||||
if let mt = meta {
|
||||
if mt.isLive {
|
||||
v = v + typingIndicator(mt.recent)
|
||||
@@ -84,14 +85,14 @@ struct MsgContentView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func messageText(_ text: String, _ formattedText: [FormattedText]?, _ sender: String?, icon: String? = nil, preview: Bool = false) -> Text {
|
||||
func messageText(_ text: String, _ formattedText: [FormattedText]?, _ sender: String?, icon: String? = nil, preview: Bool = false, showSecrets: Bool) -> Text {
|
||||
let s = text
|
||||
var res: Text
|
||||
if let ft = formattedText, ft.count > 0 && ft.count <= 200 {
|
||||
res = formatText(ft[0], preview)
|
||||
res = formatText(ft[0], preview, showSecret: showSecrets)
|
||||
var i = 1
|
||||
while i < ft.count {
|
||||
res = res + formatText(ft[i], preview)
|
||||
res = res + formatText(ft[i], preview, showSecret: showSecrets)
|
||||
i = i + 1
|
||||
}
|
||||
} else {
|
||||
@@ -110,7 +111,7 @@ func messageText(_ text: String, _ formattedText: [FormattedText]?, _ sender: St
|
||||
}
|
||||
}
|
||||
|
||||
private func formatText(_ ft: FormattedText, _ preview: Bool) -> Text {
|
||||
private func formatText(_ ft: FormattedText, _ preview: Bool, showSecret: Bool) -> Text {
|
||||
let t = ft.text
|
||||
if let f = ft.format {
|
||||
switch (f) {
|
||||
@@ -118,7 +119,13 @@ private func formatText(_ ft: FormattedText, _ preview: Bool) -> Text {
|
||||
case .italic: return Text(t).italic()
|
||||
case .strikeThrough: return Text(t).strikethrough()
|
||||
case .snippet: return Text(t).font(.body.monospaced())
|
||||
case .secret: return Text(t).foregroundColor(.clear).underline(color: .primary)
|
||||
case .secret: return
|
||||
showSecret
|
||||
? Text(t)
|
||||
: Text(AttributedString(t, attributes: AttributeContainer([
|
||||
.foregroundColor: UIColor.clear as Any,
|
||||
.backgroundColor: UIColor.secondarySystemFill as Any
|
||||
])))
|
||||
case let .colored(color): return Text(t).foregroundColor(color.uiColor)
|
||||
case .uri: return linkText(t, t, preview, prefix: "")
|
||||
case let .simplexLink(linkType, simplexUri, smpHosts):
|
||||
@@ -144,7 +151,7 @@ private func linkText(_ s: String, _ link: String, _ preview: Bool, prefix: Stri
|
||||
]))).underline()
|
||||
}
|
||||
|
||||
private func simplexLinkText(_ linkType: SimplexLinkType, _ smpHosts: [String]) -> String {
|
||||
func simplexLinkText(_ linkType: SimplexLinkType, _ smpHosts: [String]) -> String {
|
||||
linkType.description + " " + "(via \(smpHosts.first ?? "?"))"
|
||||
}
|
||||
|
||||
@@ -156,7 +163,8 @@ struct MsgContentView_Previews: PreviewProvider {
|
||||
text: chatItem.text,
|
||||
formattedText: chatItem.formattedText,
|
||||
sender: chatItem.memberDisplayName,
|
||||
meta: chatItem.meta
|
||||
meta: chatItem.meta,
|
||||
showSecrets: false
|
||||
)
|
||||
.environmentObject(Chat.sampleData)
|
||||
}
|
||||
|
||||
@@ -168,7 +168,6 @@ struct ChatItemInfoView: View {
|
||||
@ViewBuilder private func itemVersionView(_ itemVersion: ChatItemVersion, _ maxWidth: CGFloat, current: Bool) -> some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
textBubble(itemVersion.msgContent.text, itemVersion.formattedText, nil)
|
||||
.allowsHitTesting(false)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(chatItemFrameColor(ci, colorScheme))
|
||||
@@ -198,7 +197,7 @@ struct ChatItemInfoView: View {
|
||||
|
||||
@ViewBuilder private func textBubble(_ text: String, _ formattedText: [FormattedText]?, _ sender: String? = nil) -> some View {
|
||||
if text != "" {
|
||||
messageText(text, formattedText, sender)
|
||||
TextBubble(text: text, formattedText: formattedText, sender: sender)
|
||||
} else {
|
||||
Text("no text")
|
||||
.italic()
|
||||
@@ -206,6 +205,17 @@ struct ChatItemInfoView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct TextBubble: View {
|
||||
var text: String
|
||||
var formattedText: [FormattedText]?
|
||||
var sender: String? = nil
|
||||
@State private var showSecrets = false
|
||||
|
||||
var body: some View {
|
||||
toggleSecrets(formattedText, $showSecrets, messageText(text, formattedText, sender, showSecrets: showSecrets))
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func quoteTab(_ qi: CIQuote) -> some View {
|
||||
GeometryReader { g in
|
||||
let maxWidth = (g.size.width - 32) * 0.84
|
||||
@@ -227,7 +237,6 @@ struct ChatItemInfoView: View {
|
||||
@ViewBuilder private func quotedMsgView(_ qi: CIQuote, _ maxWidth: CGFloat) -> some View {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
textBubble(qi.text, qi.formattedText, qi.getSender(nil))
|
||||
.allowsHitTesting(false)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.vertical, 6)
|
||||
.background(quotedMsgFrameColor(qi, colorScheme))
|
||||
|
||||
@@ -250,8 +250,8 @@ struct ChatView: View {
|
||||
}
|
||||
|
||||
private func searchToolbar() -> some View {
|
||||
HStack {
|
||||
HStack {
|
||||
HStack(spacing: 12) {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "magnifyingglass")
|
||||
TextField("Search", text: $searchText)
|
||||
.focused($searchFocussed)
|
||||
@@ -264,9 +264,9 @@ struct ChatView: View {
|
||||
Image(systemName: "xmark.circle.fill").opacity(searchText == "" ? 0 : 1)
|
||||
}
|
||||
}
|
||||
.padding(EdgeInsets(top: 8, leading: 6, bottom: 8, trailing: 6))
|
||||
.padding(EdgeInsets(top: 7, leading: 7, bottom: 7, trailing: 7))
|
||||
.foregroundColor(.secondary)
|
||||
.background(Color(.secondarySystemBackground))
|
||||
.background(Color(.tertiarySystemFill))
|
||||
.cornerRadius(10.0)
|
||||
|
||||
Button ("Cancel") {
|
||||
@@ -723,9 +723,14 @@ struct ChatView: View {
|
||||
if ci.meta.itemDeleted == nil && !ci.isLiveDummy && !live {
|
||||
menu.append(replyUIAction(ci))
|
||||
}
|
||||
menu.append(shareUIAction(ci))
|
||||
menu.append(copyUIAction(ci))
|
||||
if let fileSource = getLoadedFileSource(ci.file) {
|
||||
let fileSource = getLoadedFileSource(ci.file)
|
||||
let fileExists = if let fs = fileSource, FileManager.default.fileExists(atPath: getAppFilePath(fs.filePath).path) { true } else { false }
|
||||
let copyAndShareAllowed = !ci.content.text.isEmpty || (ci.content.msgContent?.isImage == true && fileExists)
|
||||
if copyAndShareAllowed {
|
||||
menu.append(shareUIAction(ci))
|
||||
menu.append(copyUIAction(ci))
|
||||
}
|
||||
if let fileSource = fileSource, fileExists {
|
||||
if case .image = ci.content.msgContent, let image = getLoadedImage(ci.file) {
|
||||
if image.imageData != nil {
|
||||
menu.append(saveFileAction(fileSource))
|
||||
|
||||
@@ -104,7 +104,7 @@ struct ComposeState {
|
||||
|
||||
var sendEnabled: Bool {
|
||||
switch preview {
|
||||
case .mediaPreviews: return true
|
||||
case let .mediaPreviews(media): return !media.isEmpty
|
||||
case .voicePreview: return voiceMessageRecordingState == .finished
|
||||
case .filePreview: return true
|
||||
default: return !message.isEmpty || liveMessage != nil
|
||||
@@ -384,10 +384,10 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showMediaPicker) {
|
||||
LibraryMediaListPicker(media: $chosenMedia, selectionLimit: 10) { itemsSelected in
|
||||
showMediaPicker = false
|
||||
if itemsSelected {
|
||||
DispatchQueue.main.async {
|
||||
LibraryMediaListPicker(addMedia: addMediaContent, selectionLimit: 10, finishedPreprocessing: finishedPreprocessingMediaContent) { itemsSelected in
|
||||
await MainActor.run {
|
||||
showMediaPicker = false
|
||||
if itemsSelected {
|
||||
composeState = composeState.copy(preview: .mediaPreviews(mediaPreviews: []))
|
||||
}
|
||||
}
|
||||
@@ -488,6 +488,30 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func addMediaContent(_ content: UploadContent) async {
|
||||
if let img = resizeImageToStrSize(content.uiImage, maxDataSize: 14000) {
|
||||
var newMedia: [(String, UploadContent?)] = []
|
||||
if case var .mediaPreviews(media) = composeState.preview {
|
||||
media.append((img, content))
|
||||
newMedia = media
|
||||
} else {
|
||||
newMedia = [(img, content)]
|
||||
}
|
||||
await MainActor.run {
|
||||
composeState = composeState.copy(preview: .mediaPreviews(mediaPreviews: newMedia))
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// When error occurs while converting video, remove media preview
|
||||
private func finishedPreprocessingMediaContent() {
|
||||
if case let .mediaPreviews(media) = composeState.preview, media.isEmpty {
|
||||
DispatchQueue.main.async {
|
||||
composeState = composeState.copy(preview: .noPreview)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private var maxFileSize: Int64 {
|
||||
getMaxFileSize(.xftp)
|
||||
}
|
||||
|
||||
@@ -51,7 +51,8 @@ struct ContextItemView: View {
|
||||
MsgContentView(
|
||||
chat: chat,
|
||||
text: contextItem.text,
|
||||
formattedText: contextItem.formattedText
|
||||
formattedText: contextItem.formattedText,
|
||||
showSecrets: false
|
||||
)
|
||||
.multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading)
|
||||
.lineLimit(lines)
|
||||
|
||||
@@ -116,7 +116,6 @@ struct ContactPreferencesView: View {
|
||||
|
||||
private func featureFooter(_ feature: ChatFeature, _ enabled: FeatureEnabled) -> some View {
|
||||
Text(feature.enabledDescription(enabled))
|
||||
.frame(height: 36, alignment: .topLeading)
|
||||
}
|
||||
|
||||
private func savePreferences() {
|
||||
|
||||
@@ -16,7 +16,6 @@ struct GroupChatInfoView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@ObservedObject var chat: Chat
|
||||
@Binding var groupInfo: GroupInfo
|
||||
@ObservedObject private var alertManager = AlertManager.shared
|
||||
@State private var alert: GroupChatInfoViewAlert? = nil
|
||||
@State private var groupLink: String?
|
||||
@State private var groupLinkMemberRole: GroupMemberRole = .member
|
||||
|
||||
@@ -188,17 +188,19 @@ struct GroupMemberInfoView: View {
|
||||
// this condition prevents re-setting picker
|
||||
if !justOpened { return }
|
||||
}
|
||||
newRole = member.memberRole
|
||||
do {
|
||||
let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
|
||||
let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
|
||||
_ = chatModel.upsertGroupMember(groupInfo, mem)
|
||||
connectionStats = stats
|
||||
connectionCode = code
|
||||
} catch let error {
|
||||
logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
|
||||
}
|
||||
justOpened = false
|
||||
DispatchQueue.main.async {
|
||||
newRole = member.memberRole
|
||||
do {
|
||||
let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
|
||||
let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
|
||||
_ = chatModel.upsertGroupMember(groupInfo, mem)
|
||||
connectionStats = stats
|
||||
connectionCode = code
|
||||
} catch let error {
|
||||
logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: newRole) { newRole in
|
||||
if newRole != member.memberRole {
|
||||
|
||||
@@ -28,6 +28,7 @@ struct GroupPreferencesView: View {
|
||||
featureSection(.reactions, $preferences.reactions.enable)
|
||||
featureSection(.voice, $preferences.voice.enable)
|
||||
featureSection(.files, $preferences.files.enable)
|
||||
featureSection(.history, $preferences.history.enable)
|
||||
|
||||
if groupInfo.canEdit {
|
||||
Section {
|
||||
@@ -96,7 +97,6 @@ struct GroupPreferencesView: View {
|
||||
}
|
||||
} footer: {
|
||||
Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.canEdit))
|
||||
.frame(height: 36, alignment: .topLeading)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -103,8 +103,10 @@ struct GroupProfileView: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
LibraryImagePicker(image: $chosenImage) {
|
||||
didSelectItem in showImagePicker = false
|
||||
LibraryImagePicker(image: $chosenImage) { _ in
|
||||
await MainActor.run {
|
||||
showImagePicker = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: chosenImage) { image in
|
||||
|
||||
@@ -53,8 +53,7 @@ struct GroupWelcomeView: View {
|
||||
}
|
||||
|
||||
private func textPreview() -> some View {
|
||||
messageText(welcomeText, parseSimpleXMarkdown(welcomeText), nil)
|
||||
.allowsHitTesting(false)
|
||||
messageText(welcomeText, parseSimpleXMarkdown(welcomeText), nil, showSecrets: false)
|
||||
.frame(minHeight: 140, alignment: .topLeading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
|
||||
@@ -17,7 +17,7 @@ struct ScanCodeView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
CodeScannerView(codeTypes: [.qr], completion: processQRCode)
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
Text("Scan security code from your contact's app.")
|
||||
|
||||
@@ -11,7 +11,7 @@ import SwiftUI
|
||||
struct ChatHelp: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@Binding var showSettings: Bool
|
||||
@State private var showAddChat = false
|
||||
@State private var newChatMenuOption: NewChatMenuOption? = nil
|
||||
|
||||
var body: some View {
|
||||
ScrollView { chatHelp() }
|
||||
@@ -39,13 +39,12 @@ struct ChatHelp: View {
|
||||
|
||||
HStack(spacing: 8) {
|
||||
Text("Tap button ")
|
||||
NewChatButton(showAddChat: $showAddChat)
|
||||
NewChatMenuButton(newChatMenuOption: $newChatMenuOption)
|
||||
Text("above, then choose:")
|
||||
}
|
||||
|
||||
Text("**Create link / QR code** for your contact to use.")
|
||||
Text("**Paste received link** or open it in the browser and tap **Open in mobile app**.")
|
||||
Text("**Scan QR code**: to connect to your contact in person or via video call.")
|
||||
Text("**Add contact**: to create a new invitation link, or connect via a link you received.")
|
||||
Text("**Create group**: to create a new group.")
|
||||
}
|
||||
.padding(.top, 24)
|
||||
|
||||
|
||||
@@ -12,8 +12,12 @@ import SimpleXChat
|
||||
struct ChatListView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@Binding var showSettings: Bool
|
||||
@State private var searchMode = false
|
||||
@FocusState private var searchFocussed
|
||||
@State private var searchText = ""
|
||||
@State private var showAddChat = false
|
||||
@State private var searchShowingSimplexLink = false
|
||||
@State private var searchChatFilteredBySimplexLink: String? = nil
|
||||
@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
|
||||
@@ -62,11 +66,7 @@ struct ChatListView: View {
|
||||
|
||||
private var chatListView: some View {
|
||||
VStack {
|
||||
if chatModel.chats.count > 0 {
|
||||
chatList.searchable(text: $searchText)
|
||||
} else {
|
||||
chatList
|
||||
}
|
||||
chatList
|
||||
}
|
||||
.onDisappear() { withAnimation { userPickerVisible = false } }
|
||||
.refreshable {
|
||||
@@ -85,9 +85,9 @@ struct ChatListView: View {
|
||||
secondaryButton: .cancel()
|
||||
))
|
||||
}
|
||||
.offset(x: -8)
|
||||
.listStyle(.plain)
|
||||
.navigationBarTitleDisplayMode(.inline)
|
||||
.navigationBarHidden(searchMode)
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
let user = chatModel.currentUser ?? User.sampleData
|
||||
@@ -124,7 +124,7 @@ struct ChatListView: View {
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
switch chatModel.chatRunning {
|
||||
case .some(true): NewChatButton(showAddChat: $showAddChat)
|
||||
case .some(true): NewChatMenuButton(newChatMenuOption: $newChatMenuOption)
|
||||
case .some(false): chatStoppedIcon()
|
||||
case .none: EmptyView()
|
||||
}
|
||||
@@ -144,11 +144,25 @@ struct ChatListView: View {
|
||||
@ViewBuilder private var chatList: some View {
|
||||
let cs = filteredChats()
|
||||
ZStack {
|
||||
List {
|
||||
ForEach(cs, id: \.viewId) { chat in
|
||||
ChatListNavLink(chat: chat)
|
||||
.padding(.trailing, -16)
|
||||
.disabled(chatModel.chatRunning != true)
|
||||
VStack {
|
||||
List {
|
||||
if !chatModel.chats.isEmpty {
|
||||
ChatListSearchBar(
|
||||
searchMode: $searchMode,
|
||||
searchFocussed: $searchFocussed,
|
||||
searchText: $searchText,
|
||||
searchShowingSimplexLink: $searchShowingSimplexLink,
|
||||
searchChatFilteredBySimplexLink: $searchChatFilteredBySimplexLink
|
||||
)
|
||||
.listRowSeparator(.hidden)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
ForEach(cs, id: \.viewId) { chat in
|
||||
ChatListNavLink(chat: chat)
|
||||
.padding(.trailing, -16)
|
||||
.disabled(chatModel.chatRunning != true)
|
||||
}
|
||||
.offset(x: -8)
|
||||
}
|
||||
}
|
||||
.onChange(of: chatModel.chatId) { _ in
|
||||
@@ -182,7 +196,7 @@ struct ChatListView: View {
|
||||
.padding(.trailing, 12)
|
||||
|
||||
connectButton("Tap to start a new chat") {
|
||||
showAddChat = true
|
||||
newChatMenuOption = .newContact
|
||||
}
|
||||
|
||||
Spacer()
|
||||
@@ -214,22 +228,25 @@ struct ChatListView: View {
|
||||
}
|
||||
|
||||
private func filteredChats() -> [Chat] {
|
||||
let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
|
||||
return s == "" && !showUnreadAndFavorites
|
||||
if let linkChatId = searchChatFilteredBySimplexLink {
|
||||
return chatModel.chats.filter { $0.id == linkChatId }
|
||||
} else {
|
||||
let s = searchString()
|
||||
return s == "" && !showUnreadAndFavorites
|
||||
? chatModel.chats
|
||||
: chatModel.chats.filter { chat in
|
||||
let cInfo = chat.chatInfo
|
||||
switch cInfo {
|
||||
case let .direct(contact):
|
||||
return s == ""
|
||||
? filtered(chat)
|
||||
: (viewNameContains(cInfo, s) ||
|
||||
contact.profile.displayName.localizedLowercase.contains(s) ||
|
||||
contact.fullName.localizedLowercase.contains(s))
|
||||
? filtered(chat)
|
||||
: (viewNameContains(cInfo, s) ||
|
||||
contact.profile.displayName.localizedLowercase.contains(s) ||
|
||||
contact.fullName.localizedLowercase.contains(s))
|
||||
case let .group(gInfo):
|
||||
return s == ""
|
||||
? (filtered(chat) || gInfo.membership.memberStatus == .memInvited)
|
||||
: viewNameContains(cInfo, s)
|
||||
? (filtered(chat) || gInfo.membership.memberStatus == .memInvited)
|
||||
: viewNameContains(cInfo, s)
|
||||
case .contactRequest:
|
||||
return s == "" || viewNameContains(cInfo, s)
|
||||
case let .contactConnection(conn):
|
||||
@@ -238,6 +255,11 @@ struct ChatListView: View {
|
||||
return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func searchString() -> String {
|
||||
searchShowingSimplexLink ? "" : searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
|
||||
}
|
||||
|
||||
func filtered(_ chat: Chat) -> Bool {
|
||||
(chat.chatInfo.chatSettings?.favorite ?? false) || chat.chatStats.unreadCount > 0 || chat.chatStats.unreadChat
|
||||
@@ -249,6 +271,121 @@ struct ChatListView: View {
|
||||
}
|
||||
}
|
||||
|
||||
struct ChatListSearchBar: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@Binding var searchMode: Bool
|
||||
@FocusState.Binding var searchFocussed: Bool
|
||||
@Binding var searchText: String
|
||||
@Binding var searchShowingSimplexLink: Bool
|
||||
@Binding var searchChatFilteredBySimplexLink: String?
|
||||
@State private var ignoreSearchTextChange = false
|
||||
@State private var showScanCodeSheet = false
|
||||
@State private var alert: PlanAndConnectAlert?
|
||||
@State private var sheet: PlanAndConnectActionSheet?
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 12) {
|
||||
HStack(spacing: 12) {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "magnifyingglass")
|
||||
TextField("Search or paste SimpleX link", text: $searchText)
|
||||
.foregroundColor(searchShowingSimplexLink ? .secondary : .primary)
|
||||
.disabled(searchShowingSimplexLink)
|
||||
.focused($searchFocussed)
|
||||
.frame(maxWidth: .infinity)
|
||||
if !searchText.isEmpty {
|
||||
Image(systemName: "xmark.circle.fill")
|
||||
.onTapGesture {
|
||||
searchText = ""
|
||||
}
|
||||
} else if !searchFocussed {
|
||||
HStack(spacing: 24) {
|
||||
if m.pasteboardHasStrings {
|
||||
Image(systemName: "doc")
|
||||
.onTapGesture {
|
||||
if let str = UIPasteboard.general.string {
|
||||
searchText = str
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
Image(systemName: "qrcode")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 20, height: 20)
|
||||
.onTapGesture {
|
||||
showScanCodeSheet = true
|
||||
}
|
||||
}
|
||||
.padding(.trailing, 2)
|
||||
}
|
||||
}
|
||||
.padding(EdgeInsets(top: 7, leading: 7, bottom: 7, trailing: 7))
|
||||
.foregroundColor(.secondary)
|
||||
.background(Color(.tertiarySystemFill))
|
||||
.cornerRadius(10.0)
|
||||
|
||||
if searchFocussed {
|
||||
Text("Cancel")
|
||||
.foregroundColor(.accentColor)
|
||||
.onTapGesture {
|
||||
searchText = ""
|
||||
searchFocussed = false
|
||||
}
|
||||
}
|
||||
}
|
||||
Divider()
|
||||
}
|
||||
.sheet(isPresented: $showScanCodeSheet) {
|
||||
NewChatView(selection: .connect, showQRCodeScanner: true)
|
||||
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil) // fixes .refreshable in ChatListView affecting nested view
|
||||
}
|
||||
.onChange(of: searchFocussed) { sf in
|
||||
withAnimation { searchMode = sf }
|
||||
}
|
||||
.onChange(of: searchText) { t in
|
||||
if ignoreSearchTextChange {
|
||||
ignoreSearchTextChange = false
|
||||
} else {
|
||||
if let link = strHasSingleSimplexLink(t.trimmingCharacters(in: .whitespaces)) { // if SimpleX link is pasted, show connection dialogue
|
||||
searchFocussed = false
|
||||
if case let .simplexLink(linkType, _, smpHosts) = link.format {
|
||||
ignoreSearchTextChange = true
|
||||
searchText = simplexLinkText(linkType, smpHosts)
|
||||
}
|
||||
searchShowingSimplexLink = true
|
||||
searchChatFilteredBySimplexLink = nil
|
||||
connect(link.text)
|
||||
} else {
|
||||
if t != "" { // if some other text is pasted, enter search mode
|
||||
searchFocussed = true
|
||||
}
|
||||
searchShowingSimplexLink = false
|
||||
searchChatFilteredBySimplexLink = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(item: $alert) { a in
|
||||
planAndConnectAlert(a, dismiss: true, cleanup: { searchText = "" })
|
||||
}
|
||||
.actionSheet(item: $sheet) { s in
|
||||
planAndConnectActionSheet(s, dismiss: true, cleanup: { searchText = "" })
|
||||
}
|
||||
}
|
||||
|
||||
private func connect(_ link: String) {
|
||||
planAndConnect(
|
||||
link,
|
||||
showAlert: { alert = $0 },
|
||||
showActionSheet: { sheet = $0 },
|
||||
dismiss: false,
|
||||
incognito: nil,
|
||||
filterKnownContact: { searchChatFilteredBySimplexLink = $0.id },
|
||||
filterKnownGroup: { searchChatFilteredBySimplexLink = $0.id }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
func chatStoppedIcon() -> some View {
|
||||
Button {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
|
||||
@@ -150,7 +150,7 @@ struct ChatPreviewView: View {
|
||||
let msg = draft.message
|
||||
return image("rectangle.and.pencil.and.ellipsis", color: .accentColor)
|
||||
+ attachment()
|
||||
+ messageText(msg, parseSimpleXMarkdown(msg), nil, preview: true)
|
||||
+ messageText(msg, parseSimpleXMarkdown(msg), nil, preview: true, showSecrets: false)
|
||||
|
||||
func image(_ s: String, color: Color = Color(uiColor: .tertiaryLabel)) -> Text {
|
||||
Text(Image(systemName: s)).foregroundColor(color) + Text(" ")
|
||||
@@ -169,7 +169,7 @@ struct ChatPreviewView: View {
|
||||
func chatItemPreview(_ cItem: ChatItem) -> Text {
|
||||
let itemText = cItem.meta.itemDeleted == nil ? cItem.text : NSLocalizedString("marked deleted", comment: "marked deleted chat item preview text")
|
||||
let itemFormattedText = cItem.meta.itemDeleted == nil ? cItem.formattedText : nil
|
||||
return messageText(itemText, itemFormattedText, cItem.memberDisplayName, icon: attachment(), preview: true)
|
||||
return messageText(itemText, itemFormattedText, cItem.memberDisplayName, icon: attachment(), preview: true, showSecrets: false)
|
||||
|
||||
func attachment() -> String? {
|
||||
switch cItem.content.msgContent {
|
||||
|
||||
@@ -164,6 +164,28 @@ struct ContactConnectionInfo: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func shareLinkButton(_ connReqInvitation: String) -> some View {
|
||||
Button {
|
||||
showShareSheet(items: [simplexChatLink(connReqInvitation)])
|
||||
} label: {
|
||||
settingsRow("square.and.arrow.up") {
|
||||
Text("Share 1-time link")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func oneTimeLinkLearnMoreButton() -> some View {
|
||||
NavigationLink {
|
||||
AddContactLearnMore(showTitle: false)
|
||||
.navigationTitle("One-time invitation link")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("info.circle") {
|
||||
Text("Learn more")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ContactConnectionInfo_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContactConnectionInfo(contactConnection: PendingContactConnection.getSampleData())
|
||||
|
||||
@@ -149,7 +149,7 @@ struct DatabaseErrorView: View {
|
||||
private func runChatSync(confirmMigrations: MigrationConfirmation? = nil) {
|
||||
do {
|
||||
resetChatCtrl()
|
||||
try initializeChat(start: m.v3DBMigration.startChat, dbKey: useKeychain ? nil : dbKey, confirmMigrations: confirmMigrations)
|
||||
try initializeChat(start: m.v3DBMigration.startChat, confirmStart: m.v3DBMigration.startChat && AppChatState.shared.value == .stopped, dbKey: useKeychain ? nil : dbKey, confirmMigrations: confirmMigrations)
|
||||
if let s = m.chatDbStatus {
|
||||
status = s
|
||||
let am = AlertManager.shared
|
||||
|
||||
@@ -415,7 +415,7 @@ struct DatabaseView: View {
|
||||
do {
|
||||
try initializeChat(start: true)
|
||||
m.chatDbChanged = false
|
||||
appStateGroupDefault.set(.active)
|
||||
AppChatState.shared.set(.active)
|
||||
} catch let error {
|
||||
fatalError("Error starting chat \(responseError(error))")
|
||||
}
|
||||
@@ -427,7 +427,7 @@ struct DatabaseView: View {
|
||||
m.chatRunning = true
|
||||
ChatReceiver.shared.start()
|
||||
chatLastStartGroupDefault.set(Date.now)
|
||||
appStateGroupDefault.set(.active)
|
||||
AppChatState.shared.set(.active)
|
||||
} catch let error {
|
||||
runChat = false
|
||||
alert = .error(title: "Error starting chat", error: responseError(error))
|
||||
@@ -477,13 +477,14 @@ func stopChatAsync() async throws {
|
||||
try await apiStopChat()
|
||||
ChatReceiver.shared.stop()
|
||||
await MainActor.run { ChatModel.shared.chatRunning = false }
|
||||
appStateGroupDefault.set(.stopped)
|
||||
AppChatState.shared.set(.stopped)
|
||||
}
|
||||
|
||||
func deleteChatAsync() async throws {
|
||||
try await apiDeleteStorage()
|
||||
_ = kcDatabasePassword.remove()
|
||||
storeDBPassphraseGroupDefault.set(true)
|
||||
deleteAppDatabaseAndFiles()
|
||||
}
|
||||
|
||||
struct DatabaseView_Previews: PreviewProvider {
|
||||
|
||||
@@ -13,112 +13,130 @@ import SimpleXChat
|
||||
|
||||
struct LibraryImagePicker: View {
|
||||
@Binding var image: UIImage?
|
||||
var didFinishPicking: (_ didSelectItems: Bool) -> Void
|
||||
@State var images: [UploadContent] = []
|
||||
var didFinishPicking: (_ didSelectImage: Bool) async -> Void
|
||||
@State var mediaAdded = false
|
||||
|
||||
var body: some View {
|
||||
LibraryMediaListPicker(media: $images, selectionLimit: 1, didFinishPicking: didFinishPicking)
|
||||
.onChange(of: images) { _ in
|
||||
if let img = images.first {
|
||||
image = img.uiImage
|
||||
}
|
||||
}
|
||||
LibraryMediaListPicker(addMedia: addMedia, selectionLimit: 1, didFinishPicking: didFinishPicking)
|
||||
}
|
||||
|
||||
private func addMedia(_ content: UploadContent) async {
|
||||
if mediaAdded { return }
|
||||
await MainActor.run {
|
||||
mediaAdded = true
|
||||
image = content.uiImage
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct LibraryMediaListPicker: UIViewControllerRepresentable {
|
||||
typealias UIViewControllerType = PHPickerViewController
|
||||
@Binding var media: [UploadContent]
|
||||
var addMedia: (_ content: UploadContent) async -> Void
|
||||
var selectionLimit: Int
|
||||
var didFinishPicking: (_ didSelectItems: Bool) -> Void
|
||||
var finishedPreprocessing: () -> Void = {}
|
||||
var didFinishPicking: (_ didSelectItems: Bool) async -> Void
|
||||
|
||||
class Coordinator: PHPickerViewControllerDelegate {
|
||||
let parent: LibraryMediaListPicker
|
||||
let dispatchQueue = DispatchQueue(label: "chat.simplex.app.LibraryMediaListPicker")
|
||||
var media: [UploadContent] = []
|
||||
var mediaCount: Int = 0
|
||||
|
||||
init(_ parent: LibraryMediaListPicker) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
||||
parent.didFinishPicking(!results.isEmpty)
|
||||
guard !results.isEmpty else {
|
||||
return
|
||||
Task {
|
||||
await parent.didFinishPicking(!results.isEmpty)
|
||||
if results.isEmpty { return }
|
||||
for r in results {
|
||||
await loadItem(r.itemProvider)
|
||||
}
|
||||
parent.finishedPreprocessing()
|
||||
}
|
||||
}
|
||||
|
||||
parent.media = []
|
||||
media = []
|
||||
mediaCount = results.count
|
||||
for result in results {
|
||||
logger.log("LibraryMediaListPicker result")
|
||||
let p = result.itemProvider
|
||||
if p.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
|
||||
p.loadFileRepresentation(forTypeIdentifier: UTType.movie.identifier) { url, error in
|
||||
if let url = url {
|
||||
let tempUrl = URL(fileURLWithPath: getTempFilesDirectory().path + "/" + generateNewFileName("video", url.pathExtension))
|
||||
if ((try? FileManager.default.copyItem(at: url, to: tempUrl)) != nil) {
|
||||
ChatModel.shared.filesToDelete.insert(tempUrl)
|
||||
self.loadVideo(url: tempUrl, error: error)
|
||||
private func loadItem(_ p: NSItemProvider) async {
|
||||
logger.debug("LibraryMediaListPicker result")
|
||||
if p.hasItemConformingToTypeIdentifier(UTType.movie.identifier) {
|
||||
if let video = await loadVideo(p) {
|
||||
await self.parent.addMedia(video)
|
||||
logger.debug("LibraryMediaListPicker: added video")
|
||||
}
|
||||
} else if p.hasItemConformingToTypeIdentifier(UTType.data.identifier) {
|
||||
if let img = await loadImageData(p) {
|
||||
await self.parent.addMedia(img)
|
||||
logger.debug("LibraryMediaListPicker: added image")
|
||||
}
|
||||
} else if p.canLoadObject(ofClass: UIImage.self) {
|
||||
if let img = await loadImage(p) {
|
||||
await self.parent.addMedia(.simpleImage(image: img))
|
||||
logger.debug("LibraryMediaListPicker: added image")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadImageData(_ p: NSItemProvider) async -> UploadContent? {
|
||||
await withCheckedContinuation { cont in
|
||||
loadFileURL(p, type: UTType.data) { url in
|
||||
if let url = url {
|
||||
let img = UploadContent.loadFromURL(url: url)
|
||||
cont.resume(returning: img)
|
||||
} else {
|
||||
cont.resume(returning: nil)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadImage(_ p: NSItemProvider) async -> UIImage? {
|
||||
await withCheckedContinuation { cont in
|
||||
p.loadObject(ofClass: UIImage.self) { obj, err in
|
||||
if let err = err {
|
||||
logger.error("LibraryMediaListPicker result image error: \(err.localizedDescription)")
|
||||
cont.resume(returning: nil)
|
||||
} else {
|
||||
cont.resume(returning: obj as? UIImage)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadVideo(_ p: NSItemProvider) async -> UploadContent? {
|
||||
await withCheckedContinuation { cont in
|
||||
loadFileURL(p, type: UTType.movie) { url in
|
||||
if let url = url {
|
||||
let tempUrl = URL(fileURLWithPath: generateNewFileName(getTempFilesDirectory().path + "/" + "rawvideo", url.pathExtension, fullPath: true))
|
||||
let convertedVideoUrl = URL(fileURLWithPath: generateNewFileName(getTempFilesDirectory().path + "/" + "video", "mp4", fullPath: true))
|
||||
do {
|
||||
// logger.debug("LibraryMediaListPicker copyItem \(url) to \(tempUrl)")
|
||||
try FileManager.default.copyItem(at: url, to: tempUrl)
|
||||
} catch let err {
|
||||
logger.error("LibraryMediaListPicker copyItem error: \(err.localizedDescription)")
|
||||
return cont.resume(returning: nil)
|
||||
}
|
||||
Task {
|
||||
let success = await makeVideoQualityLower(tempUrl, outputUrl: convertedVideoUrl)
|
||||
try? FileManager.default.removeItem(at: tempUrl)
|
||||
if success {
|
||||
_ = ChatModel.shared.filesToDelete.insert(convertedVideoUrl)
|
||||
let video = UploadContent.loadVideoFromURL(url: convertedVideoUrl)
|
||||
return cont.resume(returning: video)
|
||||
}
|
||||
try? FileManager.default.removeItem(at: convertedVideoUrl)
|
||||
cont.resume(returning: nil)
|
||||
}
|
||||
}
|
||||
} else if p.hasItemConformingToTypeIdentifier(UTType.data.identifier) {
|
||||
p.loadFileRepresentation(forTypeIdentifier: UTType.data.identifier) { url, error in
|
||||
self.loadImage(object: url, error: error)
|
||||
}
|
||||
} else if p.canLoadObject(ofClass: UIImage.self) {
|
||||
p.loadObject(ofClass: UIImage.self) { image, error in
|
||||
DispatchQueue.main.async {
|
||||
self.loadImage(object: image, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func loadFileURL(_ p: NSItemProvider, type: UTType, completion: @escaping (URL?) -> Void) {
|
||||
p.loadFileRepresentation(forTypeIdentifier: type.identifier) { url, err in
|
||||
if let err = err {
|
||||
logger.error("LibraryMediaListPicker loadFileURL error: \(err.localizedDescription)")
|
||||
completion(nil)
|
||||
} else {
|
||||
dispatchQueue.sync { self.mediaCount -= 1}
|
||||
}
|
||||
}
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
|
||||
self.dispatchQueue.sync {
|
||||
if self.parent.media.count == 0 {
|
||||
logger.log("LibraryMediaListPicker: added \(self.media.count) images out of \(results.count)")
|
||||
self.parent.media = self.media
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadImage(object: Any?, error: Error? = nil) {
|
||||
if let error = error {
|
||||
logger.error("LibraryMediaListPicker: couldn't load image with error: \(error.localizedDescription)")
|
||||
} else if let image = object as? UIImage {
|
||||
media.append(.simpleImage(image: image))
|
||||
logger.log("LibraryMediaListPicker: added image")
|
||||
} else if let url = object as? URL, let image = UploadContent.loadFromURL(url: url) {
|
||||
media.append(image)
|
||||
}
|
||||
dispatchQueue.sync {
|
||||
self.mediaCount -= 1
|
||||
if self.mediaCount == 0 && self.parent.media.count == 0 {
|
||||
logger.log("LibraryMediaListPicker: added all media")
|
||||
self.parent.media = self.media
|
||||
self.media = []
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadVideo(url: URL?, error: Error? = nil) {
|
||||
if let error = error {
|
||||
logger.error("LibraryMediaListPicker: couldn't load video with error: \(error.localizedDescription)")
|
||||
} else if let url = url as URL?, let video = UploadContent.loadVideoFromURL(url: url) {
|
||||
media.append(video)
|
||||
}
|
||||
dispatchQueue.sync {
|
||||
self.mediaCount -= 1
|
||||
if self.mediaCount == 0 && self.parent.media.count == 0 {
|
||||
logger.log("LibraryMediaListPicker: added all media")
|
||||
self.parent.media = self.media
|
||||
self.media = []
|
||||
completion(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
26
apps/ios/Shared/Views/Helpers/VideoUtils.swift
Normal file
26
apps/ios/Shared/Views/Helpers/VideoUtils.swift
Normal file
@@ -0,0 +1,26 @@
|
||||
//
|
||||
// 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
|
||||
}
|
||||
@@ -13,19 +13,28 @@ struct LocalAuthView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
var authRequest: LocalAuthRequest
|
||||
@State private var password = ""
|
||||
@State private var allowToReact = true
|
||||
|
||||
var body: some View {
|
||||
PasscodeView(passcode: $password, title: authRequest.title ?? "Enter Passcode", reason: authRequest.reason, submitLabel: "Submit") {
|
||||
PasscodeView(passcode: $password, title: authRequest.title ?? "Enter Passcode", reason: authRequest.reason, submitLabel: "Submit",
|
||||
buttonsEnabled: $allowToReact) {
|
||||
if let sdPassword = kcSelfDestructPassword.get(), authRequest.selfDestruct && password == sdPassword {
|
||||
allowToReact = false
|
||||
deleteStorageAndRestart(sdPassword) { r in
|
||||
m.laRequest = nil
|
||||
authRequest.completed(r)
|
||||
}
|
||||
return
|
||||
}
|
||||
let r: LAResult = password == authRequest.password
|
||||
? .success
|
||||
: .failed(authError: NSLocalizedString("Incorrect passcode", comment: "PIN entry"))
|
||||
let r: LAResult
|
||||
if password == authRequest.password {
|
||||
if authRequest.selfDestruct && kcSelfDestructPassword.get() != nil && !m.chatInitialized {
|
||||
initChatAndMigrate()
|
||||
}
|
||||
r = .success
|
||||
} else {
|
||||
r = .failed(authError: NSLocalizedString("Incorrect passcode", comment: "PIN entry"))
|
||||
}
|
||||
m.laRequest = nil
|
||||
authRequest.completed(r)
|
||||
} cancel: {
|
||||
@@ -37,8 +46,27 @@ struct LocalAuthView: View {
|
||||
private func deleteStorageAndRestart(_ password: String, completed: @escaping (LAResult) -> Void) {
|
||||
Task {
|
||||
do {
|
||||
try await stopChatAsync()
|
||||
try await deleteChatAsync()
|
||||
/** Waiting until [initializeChat] finishes */
|
||||
while (m.ctrlInitInProgress) {
|
||||
try await Task.sleep(nanoseconds: 50_000000)
|
||||
}
|
||||
if m.chatRunning == true {
|
||||
try await stopChatAsync()
|
||||
}
|
||||
if m.chatInitialized {
|
||||
/**
|
||||
* The following sequence can bring a user here:
|
||||
* the user opened the app, entered app passcode, went to background, returned back, entered self-destruct code.
|
||||
* In this case database should be closed to prevent possible situation when OS can deny database removal command
|
||||
* */
|
||||
chatCloseStore()
|
||||
}
|
||||
deleteAppDatabaseAndFiles()
|
||||
// Clear sensitive data on screen just in case app fails to hide its views while new database is created
|
||||
m.chatId = nil
|
||||
m.reversedChatItems = []
|
||||
m.chats = []
|
||||
m.users = []
|
||||
_ = kcAppPassword.set(password)
|
||||
_ = kcSelfDestructPassword.remove()
|
||||
await NtfManager.shared.removeAllNotifications()
|
||||
@@ -52,8 +80,8 @@ struct LocalAuthView: View {
|
||||
resetChatCtrl()
|
||||
try initializeChat(start: true)
|
||||
m.chatDbChanged = false
|
||||
appStateGroupDefault.set(.active)
|
||||
if m.currentUser != nil { return }
|
||||
AppChatState.shared.set(.active)
|
||||
if m.currentUser != nil || !m.chatInitialized { return }
|
||||
var profile: Profile? = nil
|
||||
if let displayName = displayName, displayName != "" {
|
||||
profile = Profile(displayName: displayName, fullName: "")
|
||||
|
||||
@@ -14,6 +14,8 @@ struct PasscodeView: View {
|
||||
var reason: String? = nil
|
||||
var submitLabel: LocalizedStringKey
|
||||
var submitEnabled: ((String) -> Bool)?
|
||||
@Binding var buttonsEnabled: Bool
|
||||
|
||||
var submit: () -> Void
|
||||
var cancel: () -> Void
|
||||
|
||||
@@ -70,11 +72,11 @@ struct PasscodeView: View {
|
||||
@ViewBuilder private func buttonsView() -> some View {
|
||||
Button(action: cancel) {
|
||||
Label("Cancel", systemImage: "multiply")
|
||||
}
|
||||
}.disabled(!buttonsEnabled)
|
||||
Button(action: submit) {
|
||||
Label(submitLabel, systemImage: "checkmark")
|
||||
}
|
||||
.disabled(submitEnabled?(passcode) == false || passcode.count < 4)
|
||||
.disabled(submitEnabled?(passcode) == false || passcode.count < 4 || !buttonsEnabled)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -85,6 +87,7 @@ struct PasscodeViewView_Previews: PreviewProvider {
|
||||
title: "Enter Passcode",
|
||||
reason: "Unlock app",
|
||||
submitLabel: "Submit",
|
||||
buttonsEnabled: Binding.constant(true),
|
||||
submit: {},
|
||||
cancel: {}
|
||||
)
|
||||
|
||||
@@ -11,6 +11,7 @@ import SimpleXChat
|
||||
|
||||
struct SetAppPasscodeView: View {
|
||||
var passcodeKeychain: KeyChainItem = kcAppPassword
|
||||
var prohibitedPasscodeKeychain: KeyChainItem = kcSelfDestructPassword
|
||||
var title: LocalizedStringKey = "New Passcode"
|
||||
var reason: String?
|
||||
var submit: () -> Void
|
||||
@@ -41,7 +42,10 @@ struct SetAppPasscodeView: View {
|
||||
}
|
||||
}
|
||||
} else {
|
||||
setPasswordView(title: title, submitLabel: "Save") {
|
||||
setPasswordView(title: title,
|
||||
submitLabel: "Save",
|
||||
// Do not allow to set app passcode == selfDestruct passcode
|
||||
submitEnabled: { pwd in pwd != prohibitedPasscodeKeychain.get() }) {
|
||||
enteredPassword = passcode
|
||||
passcode = ""
|
||||
confirming = true
|
||||
@@ -54,7 +58,7 @@ struct SetAppPasscodeView: View {
|
||||
}
|
||||
|
||||
private func setPasswordView(title: LocalizedStringKey, submitLabel: LocalizedStringKey, submitEnabled: (((String) -> Bool))? = nil, submit: @escaping () -> Void) -> some View {
|
||||
PasscodeView(passcode: $passcode, title: title, reason: reason, submitLabel: submitLabel, submitEnabled: submitEnabled, submit: submit) {
|
||||
PasscodeView(passcode: $passcode, title: title, reason: reason, submitLabel: submitLabel, submitEnabled: submitEnabled, buttonsEnabled: Binding.constant(true), submit: submit) {
|
||||
dismiss()
|
||||
cancel()
|
||||
}
|
||||
|
||||
@@ -9,8 +9,20 @@
|
||||
import SwiftUI
|
||||
|
||||
struct AddContactLearnMore: View {
|
||||
var showTitle: Bool
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
if showTitle {
|
||||
Text("One-time invitation link")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.vertical)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
}
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
Text("To connect, your contact can scan QR code or use the link in the app.")
|
||||
Text("If you can't meet in person, show QR code in a video call, or share the link.")
|
||||
@@ -23,6 +35,6 @@ struct AddContactLearnMore: View {
|
||||
|
||||
struct AddContactLearnMore_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AddContactLearnMore()
|
||||
AddContactLearnMore(showTitle: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,129 +0,0 @@
|
||||
//
|
||||
// AddContactView.swift
|
||||
// SimpleX
|
||||
//
|
||||
// Created by Evgeny Poberezkin on 29/01/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import CoreImage.CIFilterBuiltins
|
||||
import SimpleXChat
|
||||
|
||||
struct AddContactView: View {
|
||||
@EnvironmentObject private var chatModel: ChatModel
|
||||
@Binding var contactConnection: PendingContactConnection?
|
||||
var connReqInvitation: String
|
||||
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
|
||||
|
||||
var body: some View {
|
||||
VStack {
|
||||
List {
|
||||
Section {
|
||||
if connReqInvitation != "" {
|
||||
SimpleXLinkQRCode(uri: connReqInvitation)
|
||||
} else {
|
||||
ProgressView()
|
||||
.progressViewStyle(.circular)
|
||||
.scaleEffect(2)
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding(.vertical)
|
||||
}
|
||||
IncognitoToggle(incognitoEnabled: $incognitoDefault)
|
||||
.disabled(contactConnection == nil)
|
||||
shareLinkButton(connReqInvitation)
|
||||
oneTimeLinkLearnMoreButton()
|
||||
} header: {
|
||||
Text("1-time link")
|
||||
} footer: {
|
||||
sharedProfileInfo(incognitoDefault)
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear { chatModel.connReqInv = connReqInvitation }
|
||||
.onChange(of: incognitoDefault) { incognito in
|
||||
Task {
|
||||
do {
|
||||
if let contactConn = contactConnection,
|
||||
let conn = try await apiSetConnectionIncognito(connId: contactConn.pccConnId, incognito: incognito) {
|
||||
await MainActor.run {
|
||||
contactConnection = conn
|
||||
chatModel.updateContactConnection(conn)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
logger.error("apiSetConnectionIncognito error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct IncognitoToggle: View {
|
||||
@Binding var incognitoEnabled: Bool
|
||||
@State private var showIncognitoSheet = false
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
Image(systemName: incognitoEnabled ? "theatermasks.fill" : "theatermasks")
|
||||
.frame(maxWidth: 24, maxHeight: 24, alignment: .center)
|
||||
.foregroundColor(incognitoEnabled ? Color.indigo : .secondary)
|
||||
.font(.system(size: 14))
|
||||
Toggle(isOn: $incognitoEnabled) {
|
||||
HStack(spacing: 6) {
|
||||
Text("Incognito")
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(.accentColor)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.onTapGesture {
|
||||
showIncognitoSheet = true
|
||||
}
|
||||
}
|
||||
.padding(.leading, 36)
|
||||
}
|
||||
.sheet(isPresented: $showIncognitoSheet) {
|
||||
IncognitoHelp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sharedProfileInfo(_ incognito: Bool) -> Text {
|
||||
let name = ChatModel.shared.currentUser?.displayName ?? ""
|
||||
return Text(
|
||||
incognito
|
||||
? "A new random profile will be shared."
|
||||
: "Your profile **\(name)** will be shared."
|
||||
)
|
||||
}
|
||||
|
||||
func shareLinkButton(_ connReqInvitation: String) -> some View {
|
||||
Button {
|
||||
showShareSheet(items: [simplexChatLink(connReqInvitation)])
|
||||
} label: {
|
||||
settingsRow("square.and.arrow.up") {
|
||||
Text("Share 1-time link")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func oneTimeLinkLearnMoreButton() -> some View {
|
||||
NavigationLink {
|
||||
AddContactLearnMore()
|
||||
.navigationTitle("One-time invitation link")
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
} label: {
|
||||
settingsRow("info.circle") {
|
||||
Text("Learn more")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct AddContactView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
AddContactView(
|
||||
contactConnection: Binding.constant(PendingContactConnection.getSampleData()),
|
||||
connReqInvitation: "https://simplex.chat/invitation#/?v=1&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FFe5ICmvrm4wkrr6X1LTMii-lhBqLeB76%23MCowBQYDK2VuAyEAdhZZsHpuaAk3Hh1q0uNb_6hGTpuwBIrsp2z9U2T0oC0%3D&e2e=v%3D1%26x3dh%3DMEIwBQYDK2VvAzkAcz6jJk71InuxA0bOX7OUhddfB8Ov7xwQIlIDeXBRZaOntUU4brU5Y3rBzroZBdQJi0FKdtt_D7I%3D%2CMEIwBQYDK2VvAzkA-hDvk1duBi1hlOr08VWSI-Ou4JNNSQjseY69QyKm7Kgg1zZjbpGfyBqSZ2eqys6xtoV4ZtoQUXQ%3D"
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -130,8 +130,10 @@ struct AddGroupView: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
LibraryImagePicker(image: $chosenImage) {
|
||||
didSelectItem in showImagePicker = false
|
||||
LibraryImagePicker(image: $chosenImage) { _ in
|
||||
await MainActor.run {
|
||||
showImagePicker = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.alert(isPresented: $showInvalidNameAlert) {
|
||||
@@ -185,6 +187,7 @@ struct AddGroupView: View {
|
||||
hideKeyboard()
|
||||
do {
|
||||
profile.displayName = profile.displayName.trimmingCharacters(in: .whitespaces)
|
||||
profile.groupPreferences = GroupPreferences(history: GroupPreference(enable: .on))
|
||||
let gInfo = try apiNewGroup(incognito: incognitoDefault, groupProfile: profile)
|
||||
Task {
|
||||
let groupMembers = await apiListMembers(gInfo.groupId)
|
||||
|
||||
@@ -1,42 +0,0 @@
|
||||
//
|
||||
// ConnectViaLinkView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Evgeny on 21/09/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum ConnectViaLinkTab: String {
|
||||
case scan
|
||||
case paste
|
||||
}
|
||||
|
||||
struct ConnectViaLinkView: View {
|
||||
@State private var selection: ConnectViaLinkTab = connectViaLinkTabDefault.get()
|
||||
|
||||
var body: some View {
|
||||
TabView(selection: $selection) {
|
||||
ScanToConnectView()
|
||||
.tabItem {
|
||||
Label("Scan QR code", systemImage: "qrcode")
|
||||
}
|
||||
.tag(ConnectViaLinkTab.scan)
|
||||
PasteToConnectView()
|
||||
.tabItem {
|
||||
Label("Paste received link", systemImage: "doc.plaintext")
|
||||
}
|
||||
.tag(ConnectViaLinkTab.paste)
|
||||
}
|
||||
.onChange(of: selection) { _ in
|
||||
connectViaLinkTabDefault.set(selection)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectViaLinkView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ConnectViaLinkView()
|
||||
}
|
||||
}
|
||||
@@ -1,93 +0,0 @@
|
||||
//
|
||||
// CreateLinkView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Evgeny on 21/09/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
enum CreateLinkTab {
|
||||
case oneTime
|
||||
case longTerm
|
||||
|
||||
var title: LocalizedStringKey {
|
||||
switch self {
|
||||
case .oneTime: return "One-time invitation link"
|
||||
case .longTerm: return "Your SimpleX address"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateLinkView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@State var selection: CreateLinkTab
|
||||
@State var connReqInvitation: String = ""
|
||||
@State var contactConnection: PendingContactConnection? = nil
|
||||
@State private var creatingConnReq = false
|
||||
var viaNavLink = false
|
||||
|
||||
var body: some View {
|
||||
if viaNavLink {
|
||||
createLinkView()
|
||||
} else {
|
||||
NavigationView {
|
||||
createLinkView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func createLinkView() -> some View {
|
||||
TabView(selection: $selection) {
|
||||
AddContactView(contactConnection: $contactConnection, connReqInvitation: connReqInvitation)
|
||||
.tabItem {
|
||||
Label(
|
||||
connReqInvitation == ""
|
||||
? "Create one-time invitation link"
|
||||
: "One-time invitation link",
|
||||
systemImage: "1.circle"
|
||||
)
|
||||
}
|
||||
.tag(CreateLinkTab.oneTime)
|
||||
UserAddressView(viaCreateLinkView: true)
|
||||
.tabItem {
|
||||
Label("Your SimpleX address", systemImage: "infinity.circle")
|
||||
}
|
||||
.tag(CreateLinkTab.longTerm)
|
||||
}
|
||||
.onChange(of: selection) { _ in
|
||||
if case .oneTime = selection, connReqInvitation == "", contactConnection == nil && !creatingConnReq {
|
||||
createInvitation()
|
||||
}
|
||||
}
|
||||
.onAppear { m.connReqInv = connReqInvitation }
|
||||
.onDisappear { m.connReqInv = nil }
|
||||
.navigationTitle(selection.title)
|
||||
.navigationBarTitleDisplayMode(.large)
|
||||
}
|
||||
|
||||
private func createInvitation() {
|
||||
creatingConnReq = true
|
||||
Task {
|
||||
if let (connReq, pcc) = await apiAddContact(incognito: incognitoGroupDefault.get()) {
|
||||
await MainActor.run {
|
||||
connReqInvitation = connReq
|
||||
contactConnection = pcc
|
||||
m.connReqInv = connReq
|
||||
}
|
||||
} else {
|
||||
await MainActor.run {
|
||||
creatingConnReq = false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct CreateLinkView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
CreateLinkView(selection: CreateLinkTab.oneTime)
|
||||
}
|
||||
}
|
||||
@@ -1,460 +0,0 @@
|
||||
//
|
||||
// NewChatButton.swift
|
||||
// SimpleX
|
||||
//
|
||||
// Created by Evgeny Poberezkin on 31/01/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
enum NewChatAction: Identifiable {
|
||||
case createLink(link: String, connection: PendingContactConnection)
|
||||
case connectViaLink
|
||||
case createGroup
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .createLink(link, _): return "createLink \(link)"
|
||||
case .connectViaLink: return "connectViaLink"
|
||||
case .createGroup: return "createGroup"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct NewChatButton: View {
|
||||
@Binding var showAddChat: Bool
|
||||
@State private var actionSheet: NewChatAction?
|
||||
|
||||
var body: some View {
|
||||
Button { showAddChat = true } label: {
|
||||
Image(systemName: "square.and.pencil")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 24, height: 24)
|
||||
}
|
||||
.confirmationDialog("Start a new chat", isPresented: $showAddChat, titleVisibility: .visible) {
|
||||
Button("Share one-time invitation link") { addContactAction() }
|
||||
Button("Connect via link / QR code") { actionSheet = .connectViaLink }
|
||||
Button("Create secret group") { actionSheet = .createGroup }
|
||||
}
|
||||
.sheet(item: $actionSheet) { sheet in
|
||||
switch sheet {
|
||||
case let .createLink(link, pcc):
|
||||
CreateLinkView(selection: .oneTime, connReqInvitation: link, contactConnection: pcc)
|
||||
case .connectViaLink: ConnectViaLinkView()
|
||||
case .createGroup: AddGroupView()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func addContactAction() {
|
||||
Task {
|
||||
if let (connReq, pcc) = await apiAddContact(incognito: incognitoGroupDefault.get()) {
|
||||
actionSheet = .createLink(link: connReq, connection: pcc)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum PlanAndConnectAlert: Identifiable {
|
||||
case ownInvitationLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case invitationLinkConnecting(connectionLink: String)
|
||||
case ownContactAddressConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case contactAddressConnectingConfirmReconnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConnectingConfirmReconnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConnecting(connectionLink: String, groupInfo: GroupInfo?)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .ownInvitationLinkConfirmConnect(connectionLink, _, _): return "ownInvitationLinkConfirmConnect \(connectionLink)"
|
||||
case let .invitationLinkConnecting(connectionLink): return "invitationLinkConnecting \(connectionLink)"
|
||||
case let .ownContactAddressConfirmConnect(connectionLink, _, _): return "ownContactAddressConfirmConnect \(connectionLink)"
|
||||
case let .contactAddressConnectingConfirmReconnect(connectionLink, _, _): return "contactAddressConnectingConfirmReconnect \(connectionLink)"
|
||||
case let .groupLinkConfirmConnect(connectionLink, _, _): return "groupLinkConfirmConnect \(connectionLink)"
|
||||
case let .groupLinkConnectingConfirmReconnect(connectionLink, _, _): return "groupLinkConnectingConfirmReconnect \(connectionLink)"
|
||||
case let .groupLinkConnecting(connectionLink, _): return "groupLinkConnecting \(connectionLink)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool) -> Alert {
|
||||
switch alert {
|
||||
case let .ownInvitationLinkConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Connect to yourself?"),
|
||||
message: Text("This is your own one-time link!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito) }
|
||||
),
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
case .invitationLinkConnecting:
|
||||
return Alert(
|
||||
title: Text("Already connecting!"),
|
||||
message: Text("You are already connecting via this one-time link!")
|
||||
)
|
||||
case let .ownContactAddressConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Connect to yourself?"),
|
||||
message: Text("This is your own SimpleX address!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito) }
|
||||
),
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
case let .contactAddressConnectingConfirmReconnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Repeat connection request?"),
|
||||
message: Text("You have already requested connection via this address!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito) }
|
||||
),
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
case let .groupLinkConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Join group?"),
|
||||
message: Text("You will connect to all group members."),
|
||||
primaryButton: .default(
|
||||
Text(incognito ? "Join incognito" : "Join"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito) }
|
||||
),
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
case let .groupLinkConnectingConfirmReconnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Repeat join request?"),
|
||||
message: Text("You are already joining the group via this link!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Join incognito" : "Join"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito) }
|
||||
),
|
||||
secondaryButton: .cancel()
|
||||
)
|
||||
case let .groupLinkConnecting(_, groupInfo):
|
||||
if let groupInfo = groupInfo {
|
||||
return Alert(
|
||||
title: Text("Group already exists!"),
|
||||
message: Text("You are already joining the group \(groupInfo.displayName).")
|
||||
)
|
||||
} else {
|
||||
return Alert(
|
||||
title: Text("Already joining the group!"),
|
||||
message: Text("You are already joining the group via this link.")
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum PlanAndConnectActionSheet: Identifiable {
|
||||
case askCurrentOrIncognitoProfile(connectionLink: String, connectionPlan: ConnectionPlan?, title: LocalizedStringKey)
|
||||
case askCurrentOrIncognitoProfileDestructive(connectionLink: String, connectionPlan: ConnectionPlan, title: LocalizedStringKey)
|
||||
case askCurrentOrIncognitoProfileConnectContactViaAddress(contact: Contact)
|
||||
case ownGroupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool?, groupInfo: GroupInfo)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .askCurrentOrIncognitoProfile(connectionLink, _, _): return "askCurrentOrIncognitoProfile \(connectionLink)"
|
||||
case let .askCurrentOrIncognitoProfileDestructive(connectionLink, _, _): return "askCurrentOrIncognitoProfileDestructive \(connectionLink)"
|
||||
case let .askCurrentOrIncognitoProfileConnectContactViaAddress(contact): return "askCurrentOrIncognitoProfileConnectContactViaAddress \(contact.contactId)"
|
||||
case let .ownGroupLinkConfirmConnect(connectionLink, _, _, _): return "ownGroupLinkConfirmConnect \(connectionLink)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func planAndConnectActionSheet(_ sheet: PlanAndConnectActionSheet, dismiss: Bool) -> ActionSheet {
|
||||
switch sheet {
|
||||
case let .askCurrentOrIncognitoProfile(connectionLink, connectionPlan, title):
|
||||
return ActionSheet(
|
||||
title: Text(title),
|
||||
buttons: [
|
||||
.default(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false) },
|
||||
.default(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true) },
|
||||
.cancel()
|
||||
]
|
||||
)
|
||||
case let .askCurrentOrIncognitoProfileDestructive(connectionLink, connectionPlan, title):
|
||||
return ActionSheet(
|
||||
title: Text(title),
|
||||
buttons: [
|
||||
.destructive(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false) },
|
||||
.destructive(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true) },
|
||||
.cancel()
|
||||
]
|
||||
)
|
||||
case let .askCurrentOrIncognitoProfileConnectContactViaAddress(contact):
|
||||
return ActionSheet(
|
||||
title: Text("Connect with \(contact.chatViewName)"),
|
||||
buttons: [
|
||||
.default(Text("Use current profile")) { connectContactViaAddress_(contact, dismiss: dismiss, incognito: false) },
|
||||
.default(Text("Use new incognito profile")) { connectContactViaAddress_(contact, dismiss: dismiss, incognito: true) },
|
||||
.cancel()
|
||||
]
|
||||
)
|
||||
case let .ownGroupLinkConfirmConnect(connectionLink, connectionPlan, incognito, groupInfo):
|
||||
if let incognito = incognito {
|
||||
return ActionSheet(
|
||||
title: Text("Join your group?\nThis is your link for group \(groupInfo.displayName)!"),
|
||||
buttons: [
|
||||
.default(Text("Open group")) { openKnownGroup(groupInfo, dismiss: dismiss, showAlreadyExistsAlert: nil) },
|
||||
.destructive(Text(incognito ? "Join incognito" : "Join with current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito) },
|
||||
.cancel()
|
||||
]
|
||||
)
|
||||
} else {
|
||||
return ActionSheet(
|
||||
title: Text("Join your group?\nThis is your link for group \(groupInfo.displayName)!"),
|
||||
buttons: [
|
||||
.default(Text("Open group")) { openKnownGroup(groupInfo, dismiss: dismiss, showAlreadyExistsAlert: nil) },
|
||||
.destructive(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false) },
|
||||
.destructive(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true) },
|
||||
.cancel()
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func planAndConnect(
|
||||
_ connectionLink: String,
|
||||
showAlert: @escaping (PlanAndConnectAlert) -> Void,
|
||||
showActionSheet: @escaping (PlanAndConnectActionSheet) -> Void,
|
||||
dismiss: Bool,
|
||||
incognito: Bool?
|
||||
) {
|
||||
Task {
|
||||
do {
|
||||
let connectionPlan = try await apiConnectPlan(connReq: connectionLink)
|
||||
switch connectionPlan {
|
||||
case let .invitationLink(ilp):
|
||||
switch ilp {
|
||||
case .ok:
|
||||
logger.debug("planAndConnect, .invitationLink, .ok, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via one-time link"))
|
||||
}
|
||||
case .ownLink:
|
||||
logger.debug("planAndConnect, .invitationLink, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
showAlert(.ownInvitationLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own one-time link!"))
|
||||
}
|
||||
case let .connecting(contact_):
|
||||
logger.debug("planAndConnect, .invitationLink, .connecting, incognito=\(incognito?.description ?? "nil")")
|
||||
if let contact = contact_ {
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) }
|
||||
} else {
|
||||
showAlert(.invitationLinkConnecting(connectionLink: connectionLink))
|
||||
}
|
||||
case let .known(contact):
|
||||
logger.debug("planAndConnect, .invitationLink, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyExistsAlert(contact)) }
|
||||
}
|
||||
case let .contactAddress(cap):
|
||||
switch cap {
|
||||
case .ok:
|
||||
logger.debug("planAndConnect, .contactAddress, .ok, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via contact address"))
|
||||
}
|
||||
case .ownLink:
|
||||
logger.debug("planAndConnect, .contactAddress, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
showAlert(.ownContactAddressConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own SimpleX address!"))
|
||||
}
|
||||
case .connectingConfirmReconnect:
|
||||
logger.debug("planAndConnect, .contactAddress, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
showAlert(.contactAddressConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You have already requested connection!\nRepeat connection request?"))
|
||||
}
|
||||
case let .connectingProhibit(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .connectingProhibit, incognito=\(incognito?.description ?? "nil")")
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) }
|
||||
case let .known(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyExistsAlert(contact)) }
|
||||
case let .contactViaAddress(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .contactViaAddress, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectContactViaAddress_(contact, dismiss: dismiss, incognito: incognito)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileConnectContactViaAddress(contact: contact))
|
||||
}
|
||||
}
|
||||
case let .groupLink(glp):
|
||||
switch glp {
|
||||
case .ok:
|
||||
if let incognito = incognito {
|
||||
showAlert(.groupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Join group"))
|
||||
}
|
||||
case let .ownLink(groupInfo):
|
||||
logger.debug("planAndConnect, .groupLink, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
showActionSheet(.ownGroupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito, groupInfo: groupInfo))
|
||||
case .connectingConfirmReconnect:
|
||||
logger.debug("planAndConnect, .groupLink, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
showAlert(.groupLinkConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You are already joining the group!\nRepeat join request?"))
|
||||
}
|
||||
case let .connectingProhibit(groupInfo_):
|
||||
logger.debug("planAndConnect, .groupLink, .connectingProhibit, incognito=\(incognito?.description ?? "nil")")
|
||||
showAlert(.groupLinkConnecting(connectionLink: connectionLink, groupInfo: groupInfo_))
|
||||
case let .known(groupInfo):
|
||||
logger.debug("planAndConnect, .groupLink, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
openKnownGroup(groupInfo, dismiss: dismiss) { AlertManager.shared.showAlert(groupAlreadyExistsAlert(groupInfo)) }
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
logger.debug("planAndConnect, plan error")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: nil, dismiss: dismiss, incognito: incognito)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: nil, title: "Connect via link"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func connectContactViaAddress_(_ contact: Contact, dismiss: Bool, incognito: Bool) {
|
||||
Task {
|
||||
if dismiss {
|
||||
DispatchQueue.main.async {
|
||||
dismissAllSheets(animated: true)
|
||||
}
|
||||
}
|
||||
_ = await connectContactViaAddress(contact.contactId, incognito)
|
||||
}
|
||||
}
|
||||
|
||||
private func connectViaLink(_ connectionLink: String, connectionPlan: ConnectionPlan?, dismiss: Bool, incognito: Bool) {
|
||||
Task {
|
||||
if let connReqType = await apiConnect(incognito: incognito, connReq: connectionLink) {
|
||||
let crt: ConnReqType
|
||||
if let plan = connectionPlan {
|
||||
crt = planToConnReqType(plan)
|
||||
} else {
|
||||
crt = connReqType
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
if dismiss {
|
||||
dismissAllSheets(animated: true) {
|
||||
AlertManager.shared.showAlert(connReqSentAlert(crt))
|
||||
}
|
||||
} else {
|
||||
AlertManager.shared.showAlert(connReqSentAlert(crt))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if dismiss {
|
||||
DispatchQueue.main.async {
|
||||
dismissAllSheets(animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openKnownContact(_ contact: Contact, dismiss: Bool, showAlreadyExistsAlert: (() -> Void)?) {
|
||||
Task {
|
||||
let m = ChatModel.shared
|
||||
if let c = m.getContactChat(contact.contactId) {
|
||||
DispatchQueue.main.async {
|
||||
if dismiss {
|
||||
dismissAllSheets(animated: true) {
|
||||
m.chatId = c.id
|
||||
showAlreadyExistsAlert?()
|
||||
}
|
||||
} else {
|
||||
m.chatId = c.id
|
||||
showAlreadyExistsAlert?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openKnownGroup(_ groupInfo: GroupInfo, dismiss: Bool, showAlreadyExistsAlert: (() -> Void)?) {
|
||||
Task {
|
||||
let m = ChatModel.shared
|
||||
if let g = m.getGroupChat(groupInfo.groupId) {
|
||||
DispatchQueue.main.async {
|
||||
if dismiss {
|
||||
dismissAllSheets(animated: true) {
|
||||
m.chatId = g.id
|
||||
showAlreadyExistsAlert?()
|
||||
}
|
||||
} else {
|
||||
m.chatId = g.id
|
||||
showAlreadyExistsAlert?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func contactAlreadyConnectingAlert(_ contact: Contact) -> Alert {
|
||||
mkAlert(
|
||||
title: "Contact already exists",
|
||||
message: "You are already connecting to \(contact.displayName)."
|
||||
)
|
||||
}
|
||||
|
||||
func groupAlreadyExistsAlert(_ groupInfo: GroupInfo) -> Alert {
|
||||
mkAlert(
|
||||
title: "Group already exists",
|
||||
message: "You are already in group \(groupInfo.displayName)."
|
||||
)
|
||||
}
|
||||
|
||||
enum ConnReqType: Equatable {
|
||||
case invitation
|
||||
case contact
|
||||
case groupLink
|
||||
|
||||
var connReqSentText: LocalizedStringKey {
|
||||
switch self {
|
||||
case .invitation: return "You will be connected when your contact's device is online, please wait or check later!"
|
||||
case .contact: return "You will be connected when your connection request is accepted, please wait or check later!"
|
||||
case .groupLink: return "You will be connected when group link host's device is online, please wait or check later!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func planToConnReqType(_ connectionPlan: ConnectionPlan) -> ConnReqType {
|
||||
switch connectionPlan {
|
||||
case .invitationLink: return .invitation
|
||||
case .contactAddress: return .contact
|
||||
case .groupLink: return .groupLink
|
||||
}
|
||||
}
|
||||
|
||||
func connReqSentAlert(_ type: ConnReqType) -> Alert {
|
||||
return mkAlert(
|
||||
title: "Connection request sent!",
|
||||
message: type.connReqSentText
|
||||
)
|
||||
}
|
||||
|
||||
struct NewChatButton_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
NewChatButton(showAddChat: Binding.constant(false))
|
||||
}
|
||||
}
|
||||
52
apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift
Normal file
52
apps/ios/Shared/Views/NewChat/NewChatMenuButton.swift
Normal file
@@ -0,0 +1,52 @@
|
||||
//
|
||||
// NewChatMenuButton.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 28.11.2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
|
||||
enum NewChatMenuOption: Identifiable {
|
||||
case newContact
|
||||
case newGroup
|
||||
|
||||
var id: Self { self }
|
||||
}
|
||||
|
||||
struct NewChatMenuButton: View {
|
||||
@Binding var newChatMenuOption: NewChatMenuOption?
|
||||
|
||||
var body: some View {
|
||||
Menu {
|
||||
Button {
|
||||
newChatMenuOption = .newContact
|
||||
} label: {
|
||||
Text("Add contact")
|
||||
}
|
||||
Button {
|
||||
newChatMenuOption = .newGroup
|
||||
} label: {
|
||||
Text("Create group")
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "square.and.pencil")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 24, height: 24)
|
||||
}
|
||||
.sheet(item: $newChatMenuOption) { opt in
|
||||
switch opt {
|
||||
case .newContact: NewChatView(selection: .invite)
|
||||
case .newGroup: AddGroupView()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NewChatMenuButton(
|
||||
newChatMenuOption: Binding.constant(nil)
|
||||
)
|
||||
}
|
||||
959
apps/ios/Shared/Views/NewChat/NewChatView.swift
Normal file
959
apps/ios/Shared/Views/NewChat/NewChatView.swift
Normal file
@@ -0,0 +1,959 @@
|
||||
//
|
||||
// NewChatView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 28.11.2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
import CodeScanner
|
||||
import AVFoundation
|
||||
|
||||
enum SomeAlert: Identifiable {
|
||||
case someAlert(alert: Alert, id: String)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .someAlert(_, id): return id
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private enum NewChatViewAlert: Identifiable {
|
||||
case planAndConnectAlert(alert: PlanAndConnectAlert)
|
||||
case newChatSomeAlert(alert: SomeAlert)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .planAndConnectAlert(alert): return "planAndConnectAlert \(alert.id)"
|
||||
case let .newChatSomeAlert(alert): return "newChatSomeAlert \(alert.id)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum NewChatOption: Identifiable {
|
||||
case invite
|
||||
case connect
|
||||
|
||||
var id: Self { self }
|
||||
}
|
||||
|
||||
struct NewChatView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@State var selection: NewChatOption
|
||||
@State var showQRCodeScanner = false
|
||||
@State private var invitationUsed: Bool = false
|
||||
@State private var contactConnection: PendingContactConnection? = nil
|
||||
@State private var connReqInvitation: String = ""
|
||||
@State private var creatingConnReq = false
|
||||
@State private var pastedLink: String = ""
|
||||
@State private var alert: NewChatViewAlert?
|
||||
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
HStack {
|
||||
Text("New chat")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
Spacer()
|
||||
InfoSheetButton {
|
||||
AddContactLearnMore(showTitle: true)
|
||||
}
|
||||
}
|
||||
.padding()
|
||||
.padding(.top)
|
||||
|
||||
Picker("New chat", selection: $selection) {
|
||||
Label("Add contact", systemImage: "link")
|
||||
.tag(NewChatOption.invite)
|
||||
Label("Connect via link", systemImage: "qrcode")
|
||||
.tag(NewChatOption.connect)
|
||||
}
|
||||
.pickerStyle(.segmented)
|
||||
.padding()
|
||||
|
||||
VStack {
|
||||
// it seems there's a bug in iOS 15 if several views in switch (or if-else) statement have different transitions
|
||||
// https://developer.apple.com/forums/thread/714977?answerId=731615022#731615022
|
||||
if case .invite = selection {
|
||||
prepareAndInviteView()
|
||||
.transition(.move(edge: .leading))
|
||||
.onAppear {
|
||||
createInvitation()
|
||||
}
|
||||
}
|
||||
if case .connect = selection {
|
||||
ConnectView(showQRCodeScanner: showQRCodeScanner, pastedLink: $pastedLink, alert: $alert)
|
||||
.transition(.move(edge: .trailing))
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.background(
|
||||
// Rectangle is needed for swipe gesture to work on mostly empty views (creatingLinkProgressView and retryButton)
|
||||
Rectangle()
|
||||
.fill(Color(uiColor: .systemGroupedBackground))
|
||||
)
|
||||
.animation(.easeInOut(duration: 0.3333), value: selection)
|
||||
.gesture(DragGesture(minimumDistance: 20.0, coordinateSpace: .local)
|
||||
.onChanged { value in
|
||||
switch(value.translation.width, value.translation.height) {
|
||||
case (...0, -30...30): // left swipe
|
||||
if selection == .invite {
|
||||
selection = .connect
|
||||
}
|
||||
case (0..., -30...30): // right swipe
|
||||
if selection == .connect {
|
||||
selection = .invite
|
||||
}
|
||||
default: ()
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.onChange(of: invitationUsed) { used in
|
||||
if used && !(m.showingInvitation?.connChatUsed ?? true) {
|
||||
m.markShowingInvitationUsed()
|
||||
}
|
||||
}
|
||||
.onDisappear {
|
||||
if !(m.showingInvitation?.connChatUsed ?? true),
|
||||
let conn = contactConnection {
|
||||
AlertManager.shared.showAlert(Alert(
|
||||
title: Text("Keep unused invitation?"),
|
||||
message: Text("You can view invitation link again in connection details."),
|
||||
primaryButton: .default(Text("Keep")) {},
|
||||
secondaryButton: .destructive(Text("Delete")) {
|
||||
Task {
|
||||
await deleteChat(Chat(
|
||||
chatInfo: .contactConnection(contactConnection: conn),
|
||||
chatItems: []
|
||||
))
|
||||
}
|
||||
}
|
||||
))
|
||||
}
|
||||
m.showingInvitation = nil
|
||||
}
|
||||
.alert(item: $alert) { a in
|
||||
switch(a) {
|
||||
case let .planAndConnectAlert(alert):
|
||||
return planAndConnectAlert(alert, dismiss: true, cleanup: { pastedLink = "" })
|
||||
case let .newChatSomeAlert(.someAlert(alert, _)):
|
||||
return alert
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func prepareAndInviteView() -> some View {
|
||||
ZStack { // ZStack is needed for views to not make transitions between each other
|
||||
if connReqInvitation != "" {
|
||||
InviteView(
|
||||
invitationUsed: $invitationUsed,
|
||||
contactConnection: $contactConnection,
|
||||
connReqInvitation: connReqInvitation
|
||||
)
|
||||
} else if creatingConnReq {
|
||||
creatingLinkProgressView()
|
||||
} else {
|
||||
retryButton()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func createInvitation() {
|
||||
if connReqInvitation == "" && contactConnection == nil && !creatingConnReq {
|
||||
creatingConnReq = true
|
||||
Task {
|
||||
_ = try? await Task.sleep(nanoseconds: 250_000000)
|
||||
let (r, apiAlert) = await apiAddContact(incognito: incognitoGroupDefault.get())
|
||||
if let (connReq, pcc) = r {
|
||||
await MainActor.run {
|
||||
m.updateContactConnection(pcc)
|
||||
m.showingInvitation = ShowingInvitation(connId: pcc.id, connChatUsed: false)
|
||||
connReqInvitation = connReq
|
||||
contactConnection = pcc
|
||||
}
|
||||
} else {
|
||||
await MainActor.run {
|
||||
creatingConnReq = false
|
||||
if let apiAlert = apiAlert {
|
||||
alert = .newChatSomeAlert(alert: .someAlert(alert: apiAlert, id: "createInvitation error"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Rectangle here and in retryButton are needed for gesture to work
|
||||
private func creatingLinkProgressView() -> some View {
|
||||
ProgressView("Creating link…")
|
||||
.progressViewStyle(.circular)
|
||||
}
|
||||
|
||||
private func retryButton() -> some View {
|
||||
Button(action: createInvitation) {
|
||||
VStack(spacing: 6) {
|
||||
Image(systemName: "arrow.counterclockwise")
|
||||
Text("Retry")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct InviteView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@Binding var invitationUsed: Bool
|
||||
@Binding var contactConnection: PendingContactConnection?
|
||||
var connReqInvitation: String
|
||||
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section("Share this 1-time invite link") {
|
||||
shareLinkView()
|
||||
}
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10))
|
||||
|
||||
qrCodeView()
|
||||
|
||||
Section {
|
||||
IncognitoToggle(incognitoEnabled: $incognitoDefault)
|
||||
} footer: {
|
||||
sharedProfileInfo(incognitoDefault)
|
||||
}
|
||||
}
|
||||
.onChange(of: incognitoDefault) { incognito in
|
||||
Task {
|
||||
do {
|
||||
if let contactConn = contactConnection,
|
||||
let conn = try await apiSetConnectionIncognito(connId: contactConn.pccConnId, incognito: incognito) {
|
||||
await MainActor.run {
|
||||
contactConnection = conn
|
||||
chatModel.updateContactConnection(conn)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
logger.error("apiSetConnectionIncognito error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
setInvitationUsed()
|
||||
}
|
||||
}
|
||||
|
||||
private func shareLinkView() -> some View {
|
||||
HStack {
|
||||
let link = simplexChatLink(connReqInvitation)
|
||||
linkTextView(link)
|
||||
Button {
|
||||
showShareSheet(items: [link])
|
||||
setInvitationUsed()
|
||||
} label: {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
.padding(.top, -7)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
private func qrCodeView() -> some View {
|
||||
Section("Or show this code") {
|
||||
SimpleXLinkQRCode(uri: connReqInvitation, onShare: setInvitationUsed)
|
||||
.padding()
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12, style: .continuous)
|
||||
.fill(Color(uiColor: .secondarySystemGroupedBackground))
|
||||
)
|
||||
.padding(.horizontal)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
}
|
||||
}
|
||||
|
||||
private func setInvitationUsed() {
|
||||
if !invitationUsed {
|
||||
invitationUsed = true
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private struct ConnectView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@State var showQRCodeScanner = false
|
||||
@State private var cameraAuthorizationStatus: AVAuthorizationStatus?
|
||||
@Binding var pastedLink: String
|
||||
@Binding var alert: NewChatViewAlert?
|
||||
@State private var sheet: PlanAndConnectActionSheet?
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Section("Paste the link you received") {
|
||||
pasteLinkView()
|
||||
}
|
||||
|
||||
scanCodeView()
|
||||
}
|
||||
.actionSheet(item: $sheet) { s in
|
||||
planAndConnectActionSheet(s, dismiss: true, cleanup: { pastedLink = "" })
|
||||
}
|
||||
.onAppear {
|
||||
let status = AVCaptureDevice.authorizationStatus(for: .video)
|
||||
cameraAuthorizationStatus = status
|
||||
if showQRCodeScanner {
|
||||
switch status {
|
||||
case .notDetermined: askCameraAuthorization()
|
||||
case .restricted: showQRCodeScanner = false
|
||||
case .denied: showQRCodeScanner = false
|
||||
case .authorized: ()
|
||||
@unknown default: askCameraAuthorization()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func askCameraAuthorization(_ cb: (() -> Void)? = nil) {
|
||||
AVCaptureDevice.requestAccess(for: .video) { allowed in
|
||||
cameraAuthorizationStatus = AVCaptureDevice.authorizationStatus(for: .video)
|
||||
if allowed { cb?() }
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func pasteLinkView() -> some View {
|
||||
if pastedLink == "" {
|
||||
Button {
|
||||
if let str = UIPasteboard.general.string {
|
||||
if let link = strHasSingleSimplexLink(str.trimmingCharacters(in: .whitespaces)) {
|
||||
pastedLink = link.text
|
||||
// It would be good to hide it, but right now it is not clear how to release camera in CodeScanner
|
||||
// https://github.com/twostraws/CodeScanner/issues/121
|
||||
// No known tricks worked (changing view ID, wrapping it in another view, etc.)
|
||||
// showQRCodeScanner = false
|
||||
connect(pastedLink)
|
||||
} else {
|
||||
alert = .newChatSomeAlert(alert: .someAlert(
|
||||
alert: mkAlert(title: "Invalid link", message: "The text you pasted is not a SimpleX link."),
|
||||
id: "pasteLinkView: code is not a SimpleX link"
|
||||
))
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
Text("Tap to paste link")
|
||||
}
|
||||
.disabled(!ChatModel.shared.pasteboardHasStrings)
|
||||
.frame(maxWidth: .infinity, alignment: .center)
|
||||
} else {
|
||||
linkTextView(pastedLink)
|
||||
}
|
||||
}
|
||||
|
||||
private func scanCodeView() -> some View {
|
||||
Section("Or scan QR code") {
|
||||
if showQRCodeScanner, case .authorized = cameraAuthorizationStatus {
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .continuous, completion: processQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
.padding(.horizontal)
|
||||
} else {
|
||||
Button {
|
||||
switch cameraAuthorizationStatus {
|
||||
case .notDetermined: askCameraAuthorization { showQRCodeScanner = true }
|
||||
case .restricted: ()
|
||||
case .denied: UIApplication.shared.open(appSettingsURL)
|
||||
case .authorized: showQRCodeScanner = true
|
||||
default: askCameraAuthorization { showQRCodeScanner = true }
|
||||
}
|
||||
} label: {
|
||||
ZStack {
|
||||
Rectangle()
|
||||
.aspectRatio(contentMode: .fill)
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
.foregroundColor(Color.clear)
|
||||
switch cameraAuthorizationStatus {
|
||||
case .restricted: Text("Camera not available")
|
||||
case .denied: Label("Enable camera access", systemImage: "camera")
|
||||
default: Label("Tap to scan", systemImage: "qrcode")
|
||||
}
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .center)
|
||||
.padding()
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12, style: .continuous)
|
||||
.fill(Color(uiColor: .secondarySystemGroupedBackground))
|
||||
)
|
||||
.padding(.horizontal)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
.disabled(cameraAuthorizationStatus == .restricted)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func processQRCode(_ resp: Result<ScanResult, ScanError>) {
|
||||
switch resp {
|
||||
case let .success(r):
|
||||
let link = r.string
|
||||
if strIsSimplexLink(r.string) {
|
||||
connect(link)
|
||||
} else {
|
||||
alert = .newChatSomeAlert(alert: .someAlert(
|
||||
alert: mkAlert(title: "Invalid QR code", message: "The code you scanned is not a SimpleX link QR code."),
|
||||
id: "processQRCode: code is not a SimpleX link"
|
||||
))
|
||||
}
|
||||
case let .failure(e):
|
||||
logger.error("processQRCode QR code error: \(e.localizedDescription)")
|
||||
alert = .newChatSomeAlert(alert: .someAlert(
|
||||
alert: mkAlert(title: "Invalid QR code", message: "Error scanning code: \(e.localizedDescription)"),
|
||||
id: "processQRCode: failure"
|
||||
))
|
||||
}
|
||||
}
|
||||
|
||||
private func connect(_ link: String) {
|
||||
planAndConnect(
|
||||
link,
|
||||
showAlert: { alert = .planAndConnectAlert(alert: $0) },
|
||||
showActionSheet: { sheet = $0 },
|
||||
dismiss: true,
|
||||
incognito: nil
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
private func linkTextView(_ link: String) -> some View {
|
||||
Text(link)
|
||||
.lineLimit(1)
|
||||
.font(.caption)
|
||||
.truncationMode(.middle)
|
||||
}
|
||||
|
||||
struct InfoSheetButton<Content: View>: View {
|
||||
@ViewBuilder let content: Content
|
||||
@State private var showInfoSheet = false
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
showInfoSheet = true
|
||||
} label: {
|
||||
Image(systemName: "info.circle")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 24, height: 24)
|
||||
}
|
||||
.sheet(isPresented: $showInfoSheet) {
|
||||
content
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func strIsSimplexLink(_ str: String) -> Bool {
|
||||
if let parsedMd = parseSimpleXMarkdown(str),
|
||||
parsedMd.count == 1,
|
||||
case .simplexLink = parsedMd[0].format {
|
||||
return true
|
||||
} else {
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
func strHasSingleSimplexLink(_ str: String) -> FormattedText? {
|
||||
if let parsedMd = parseSimpleXMarkdown(str) {
|
||||
let parsedLinks = parsedMd.filter({ $0.format?.isSimplexLink ?? false })
|
||||
if parsedLinks.count == 1 {
|
||||
return parsedLinks[0]
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
struct IncognitoToggle: View {
|
||||
@Binding var incognitoEnabled: Bool
|
||||
@State private var showIncognitoSheet = false
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .leading) {
|
||||
Image(systemName: incognitoEnabled ? "theatermasks.fill" : "theatermasks")
|
||||
.frame(maxWidth: 24, maxHeight: 24, alignment: .center)
|
||||
.foregroundColor(incognitoEnabled ? Color.indigo : .secondary)
|
||||
.font(.system(size: 14))
|
||||
Toggle(isOn: $incognitoEnabled) {
|
||||
HStack(spacing: 6) {
|
||||
Text("Incognito")
|
||||
Image(systemName: "info.circle")
|
||||
.foregroundColor(.accentColor)
|
||||
.font(.system(size: 14))
|
||||
}
|
||||
.onTapGesture {
|
||||
showIncognitoSheet = true
|
||||
}
|
||||
}
|
||||
.padding(.leading, 36)
|
||||
}
|
||||
.sheet(isPresented: $showIncognitoSheet) {
|
||||
IncognitoHelp()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func sharedProfileInfo(_ incognito: Bool) -> Text {
|
||||
let name = ChatModel.shared.currentUser?.displayName ?? ""
|
||||
return Text(
|
||||
incognito
|
||||
? "A new random profile will be shared."
|
||||
: "Your profile **\(name)** will be shared."
|
||||
)
|
||||
}
|
||||
|
||||
enum PlanAndConnectAlert: Identifiable {
|
||||
case ownInvitationLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case invitationLinkConnecting(connectionLink: String)
|
||||
case ownContactAddressConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case contactAddressConnectingConfirmReconnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConnectingConfirmReconnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool)
|
||||
case groupLinkConnecting(connectionLink: String, groupInfo: GroupInfo?)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .ownInvitationLinkConfirmConnect(connectionLink, _, _): return "ownInvitationLinkConfirmConnect \(connectionLink)"
|
||||
case let .invitationLinkConnecting(connectionLink): return "invitationLinkConnecting \(connectionLink)"
|
||||
case let .ownContactAddressConfirmConnect(connectionLink, _, _): return "ownContactAddressConfirmConnect \(connectionLink)"
|
||||
case let .contactAddressConnectingConfirmReconnect(connectionLink, _, _): return "contactAddressConnectingConfirmReconnect \(connectionLink)"
|
||||
case let .groupLinkConfirmConnect(connectionLink, _, _): return "groupLinkConfirmConnect \(connectionLink)"
|
||||
case let .groupLinkConnectingConfirmReconnect(connectionLink, _, _): return "groupLinkConnectingConfirmReconnect \(connectionLink)"
|
||||
case let .groupLinkConnecting(connectionLink, _): return "groupLinkConnecting \(connectionLink)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func planAndConnectAlert(_ alert: PlanAndConnectAlert, dismiss: Bool, cleanup: (() -> Void)? = nil) -> Alert {
|
||||
switch alert {
|
||||
case let .ownInvitationLinkConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Connect to yourself?"),
|
||||
message: Text("This is your own one-time link!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case .invitationLinkConnecting:
|
||||
return Alert(
|
||||
title: Text("Already connecting!"),
|
||||
message: Text("You are already connecting via this one-time link!"),
|
||||
dismissButton: .default(Text("OK")) { cleanup?() }
|
||||
)
|
||||
case let .ownContactAddressConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Connect to yourself?"),
|
||||
message: Text("This is your own SimpleX address!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case let .contactAddressConnectingConfirmReconnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Repeat connection request?"),
|
||||
message: Text("You have already requested connection via this address!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Connect incognito" : "Connect"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case let .groupLinkConfirmConnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Join group?"),
|
||||
message: Text("You will connect to all group members."),
|
||||
primaryButton: .default(
|
||||
Text(incognito ? "Join incognito" : "Join"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case let .groupLinkConnectingConfirmReconnect(connectionLink, connectionPlan, incognito):
|
||||
return Alert(
|
||||
title: Text("Repeat join request?"),
|
||||
message: Text("You are already joining the group via this link!"),
|
||||
primaryButton: .destructive(
|
||||
Text(incognito ? "Join incognito" : "Join"),
|
||||
action: { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) }
|
||||
),
|
||||
secondaryButton: .cancel() { cleanup?() }
|
||||
)
|
||||
case let .groupLinkConnecting(_, groupInfo):
|
||||
if let groupInfo = groupInfo {
|
||||
return Alert(
|
||||
title: Text("Group already exists!"),
|
||||
message: Text("You are already joining the group \(groupInfo.displayName)."),
|
||||
dismissButton: .default(Text("OK")) { cleanup?() }
|
||||
)
|
||||
} else {
|
||||
return Alert(
|
||||
title: Text("Already joining the group!"),
|
||||
message: Text("You are already joining the group via this link."),
|
||||
dismissButton: .default(Text("OK")) { cleanup?() }
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum PlanAndConnectActionSheet: Identifiable {
|
||||
case askCurrentOrIncognitoProfile(connectionLink: String, connectionPlan: ConnectionPlan?, title: LocalizedStringKey)
|
||||
case askCurrentOrIncognitoProfileDestructive(connectionLink: String, connectionPlan: ConnectionPlan, title: LocalizedStringKey)
|
||||
case askCurrentOrIncognitoProfileConnectContactViaAddress(contact: Contact)
|
||||
case ownGroupLinkConfirmConnect(connectionLink: String, connectionPlan: ConnectionPlan, incognito: Bool?, groupInfo: GroupInfo)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case let .askCurrentOrIncognitoProfile(connectionLink, _, _): return "askCurrentOrIncognitoProfile \(connectionLink)"
|
||||
case let .askCurrentOrIncognitoProfileDestructive(connectionLink, _, _): return "askCurrentOrIncognitoProfileDestructive \(connectionLink)"
|
||||
case let .askCurrentOrIncognitoProfileConnectContactViaAddress(contact): return "askCurrentOrIncognitoProfileConnectContactViaAddress \(contact.contactId)"
|
||||
case let .ownGroupLinkConfirmConnect(connectionLink, _, _, _): return "ownGroupLinkConfirmConnect \(connectionLink)"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func planAndConnectActionSheet(_ sheet: PlanAndConnectActionSheet, dismiss: Bool, cleanup: (() -> Void)? = nil) -> ActionSheet {
|
||||
switch sheet {
|
||||
case let .askCurrentOrIncognitoProfile(connectionLink, connectionPlan, title):
|
||||
return ActionSheet(
|
||||
title: Text(title),
|
||||
buttons: [
|
||||
.default(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false, cleanup: cleanup) },
|
||||
.default(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
)
|
||||
case let .askCurrentOrIncognitoProfileDestructive(connectionLink, connectionPlan, title):
|
||||
return ActionSheet(
|
||||
title: Text(title),
|
||||
buttons: [
|
||||
.destructive(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false, cleanup: cleanup) },
|
||||
.destructive(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
)
|
||||
case let .askCurrentOrIncognitoProfileConnectContactViaAddress(contact):
|
||||
return ActionSheet(
|
||||
title: Text("Connect with \(contact.chatViewName)"),
|
||||
buttons: [
|
||||
.default(Text("Use current profile")) { connectContactViaAddress_(contact, dismiss: dismiss, incognito: false, cleanup: cleanup) },
|
||||
.default(Text("Use new incognito profile")) { connectContactViaAddress_(contact, dismiss: dismiss, incognito: true, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
)
|
||||
case let .ownGroupLinkConfirmConnect(connectionLink, connectionPlan, incognito, groupInfo):
|
||||
if let incognito = incognito {
|
||||
return ActionSheet(
|
||||
title: Text("Join your group?\nThis is your link for group \(groupInfo.displayName)!"),
|
||||
buttons: [
|
||||
.default(Text("Open group")) { openKnownGroup(groupInfo, dismiss: dismiss, showAlreadyExistsAlert: nil) },
|
||||
.destructive(Text(incognito ? "Join incognito" : "Join with current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
)
|
||||
} else {
|
||||
return ActionSheet(
|
||||
title: Text("Join your group?\nThis is your link for group \(groupInfo.displayName)!"),
|
||||
buttons: [
|
||||
.default(Text("Open group")) { openKnownGroup(groupInfo, dismiss: dismiss, showAlreadyExistsAlert: nil) },
|
||||
.destructive(Text("Use current profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: false, cleanup: cleanup) },
|
||||
.destructive(Text("Use new incognito profile")) { connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: true, cleanup: cleanup) },
|
||||
.cancel() { cleanup?() }
|
||||
]
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func planAndConnect(
|
||||
_ connectionLink: String,
|
||||
showAlert: @escaping (PlanAndConnectAlert) -> Void,
|
||||
showActionSheet: @escaping (PlanAndConnectActionSheet) -> Void,
|
||||
dismiss: Bool,
|
||||
incognito: Bool?,
|
||||
cleanup: (() -> Void)? = nil,
|
||||
filterKnownContact: ((Contact) -> Void)? = nil,
|
||||
filterKnownGroup: ((GroupInfo) -> Void)? = nil
|
||||
) {
|
||||
Task {
|
||||
do {
|
||||
let connectionPlan = try await apiConnectPlan(connReq: connectionLink)
|
||||
switch connectionPlan {
|
||||
case let .invitationLink(ilp):
|
||||
switch ilp {
|
||||
case .ok:
|
||||
logger.debug("planAndConnect, .invitationLink, .ok, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via one-time link"))
|
||||
}
|
||||
case .ownLink:
|
||||
logger.debug("planAndConnect, .invitationLink, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
showAlert(.ownInvitationLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own one-time link!"))
|
||||
}
|
||||
case let .connecting(contact_):
|
||||
logger.debug("planAndConnect, .invitationLink, .connecting, incognito=\(incognito?.description ?? "nil")")
|
||||
if let contact = contact_ {
|
||||
if let f = filterKnownContact {
|
||||
f(contact)
|
||||
} else {
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) }
|
||||
}
|
||||
} else {
|
||||
showAlert(.invitationLinkConnecting(connectionLink: connectionLink))
|
||||
}
|
||||
case let .known(contact):
|
||||
logger.debug("planAndConnect, .invitationLink, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
if let f = filterKnownContact {
|
||||
f(contact)
|
||||
} else {
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyExistsAlert(contact)) }
|
||||
}
|
||||
}
|
||||
case let .contactAddress(cap):
|
||||
switch cap {
|
||||
case .ok:
|
||||
logger.debug("planAndConnect, .contactAddress, .ok, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: connectionPlan, dismiss: dismiss, incognito: incognito, cleanup: cleanup)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect via contact address"))
|
||||
}
|
||||
case .ownLink:
|
||||
logger.debug("planAndConnect, .contactAddress, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
showAlert(.ownContactAddressConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Connect to yourself?\nThis is your own SimpleX address!"))
|
||||
}
|
||||
case .connectingConfirmReconnect:
|
||||
logger.debug("planAndConnect, .contactAddress, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
showAlert(.contactAddressConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You have already requested connection!\nRepeat connection request?"))
|
||||
}
|
||||
case let .connectingProhibit(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .connectingProhibit, incognito=\(incognito?.description ?? "nil")")
|
||||
if let f = filterKnownContact {
|
||||
f(contact)
|
||||
} else {
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyConnectingAlert(contact)) }
|
||||
}
|
||||
case let .known(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
if let f = filterKnownContact {
|
||||
f(contact)
|
||||
} else {
|
||||
openKnownContact(contact, dismiss: dismiss) { AlertManager.shared.showAlert(contactAlreadyExistsAlert(contact)) }
|
||||
}
|
||||
case let .contactViaAddress(contact):
|
||||
logger.debug("planAndConnect, .contactAddress, .contactViaAddress, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
connectContactViaAddress_(contact, dismiss: dismiss, incognito: incognito, cleanup: cleanup)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileConnectContactViaAddress(contact: contact))
|
||||
}
|
||||
}
|
||||
case let .groupLink(glp):
|
||||
switch glp {
|
||||
case .ok:
|
||||
if let incognito = incognito {
|
||||
showAlert(.groupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "Join group"))
|
||||
}
|
||||
case let .ownLink(groupInfo):
|
||||
logger.debug("planAndConnect, .groupLink, .ownLink, incognito=\(incognito?.description ?? "nil")")
|
||||
if let f = filterKnownGroup {
|
||||
f(groupInfo)
|
||||
}
|
||||
showActionSheet(.ownGroupLinkConfirmConnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito, groupInfo: groupInfo))
|
||||
case .connectingConfirmReconnect:
|
||||
logger.debug("planAndConnect, .groupLink, .connectingConfirmReconnect, incognito=\(incognito?.description ?? "nil")")
|
||||
if let incognito = incognito {
|
||||
showAlert(.groupLinkConnectingConfirmReconnect(connectionLink: connectionLink, connectionPlan: connectionPlan, incognito: incognito))
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfileDestructive(connectionLink: connectionLink, connectionPlan: connectionPlan, title: "You are already joining the group!\nRepeat join request?"))
|
||||
}
|
||||
case let .connectingProhibit(groupInfo_):
|
||||
logger.debug("planAndConnect, .groupLink, .connectingProhibit, incognito=\(incognito?.description ?? "nil")")
|
||||
showAlert(.groupLinkConnecting(connectionLink: connectionLink, groupInfo: groupInfo_))
|
||||
case let .known(groupInfo):
|
||||
logger.debug("planAndConnect, .groupLink, .known, incognito=\(incognito?.description ?? "nil")")
|
||||
if let f = filterKnownGroup {
|
||||
f(groupInfo)
|
||||
} else {
|
||||
openKnownGroup(groupInfo, dismiss: dismiss) { AlertManager.shared.showAlert(groupAlreadyExistsAlert(groupInfo)) }
|
||||
}
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
logger.debug("planAndConnect, plan error")
|
||||
if let incognito = incognito {
|
||||
connectViaLink(connectionLink, connectionPlan: nil, dismiss: dismiss, incognito: incognito, cleanup: cleanup)
|
||||
} else {
|
||||
showActionSheet(.askCurrentOrIncognitoProfile(connectionLink: connectionLink, connectionPlan: nil, title: "Connect via link"))
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func connectContactViaAddress_(_ contact: Contact, dismiss: Bool, incognito: Bool, cleanup: (() -> Void)? = nil) {
|
||||
Task {
|
||||
if dismiss {
|
||||
DispatchQueue.main.async {
|
||||
dismissAllSheets(animated: true)
|
||||
}
|
||||
}
|
||||
_ = await connectContactViaAddress(contact.contactId, incognito)
|
||||
cleanup?()
|
||||
}
|
||||
}
|
||||
|
||||
private func connectViaLink(
|
||||
_ connectionLink: String,
|
||||
connectionPlan: ConnectionPlan?,
|
||||
dismiss: Bool,
|
||||
incognito: Bool,
|
||||
cleanup: (() -> Void)?
|
||||
) {
|
||||
Task {
|
||||
if let (connReqType, pcc) = await apiConnect(incognito: incognito, connReq: connectionLink) {
|
||||
await MainActor.run {
|
||||
ChatModel.shared.updateContactConnection(pcc)
|
||||
}
|
||||
let crt: ConnReqType
|
||||
if let plan = connectionPlan {
|
||||
crt = planToConnReqType(plan)
|
||||
} else {
|
||||
crt = connReqType
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
if dismiss {
|
||||
dismissAllSheets(animated: true) {
|
||||
AlertManager.shared.showAlert(connReqSentAlert(crt))
|
||||
}
|
||||
} else {
|
||||
AlertManager.shared.showAlert(connReqSentAlert(crt))
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if dismiss {
|
||||
DispatchQueue.main.async {
|
||||
dismissAllSheets(animated: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
cleanup?()
|
||||
}
|
||||
}
|
||||
|
||||
func openKnownContact(_ contact: Contact, dismiss: Bool, showAlreadyExistsAlert: (() -> Void)?) {
|
||||
Task {
|
||||
let m = ChatModel.shared
|
||||
if let c = m.getContactChat(contact.contactId) {
|
||||
DispatchQueue.main.async {
|
||||
if dismiss {
|
||||
dismissAllSheets(animated: true) {
|
||||
m.chatId = c.id
|
||||
showAlreadyExistsAlert?()
|
||||
}
|
||||
} else {
|
||||
m.chatId = c.id
|
||||
showAlreadyExistsAlert?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func openKnownGroup(_ groupInfo: GroupInfo, dismiss: Bool, showAlreadyExistsAlert: (() -> Void)?) {
|
||||
Task {
|
||||
let m = ChatModel.shared
|
||||
if let g = m.getGroupChat(groupInfo.groupId) {
|
||||
DispatchQueue.main.async {
|
||||
if dismiss {
|
||||
dismissAllSheets(animated: true) {
|
||||
m.chatId = g.id
|
||||
showAlreadyExistsAlert?()
|
||||
}
|
||||
} else {
|
||||
m.chatId = g.id
|
||||
showAlreadyExistsAlert?()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func contactAlreadyConnectingAlert(_ contact: Contact) -> Alert {
|
||||
mkAlert(
|
||||
title: "Contact already exists",
|
||||
message: "You are already connecting to \(contact.displayName)."
|
||||
)
|
||||
}
|
||||
|
||||
func groupAlreadyExistsAlert(_ groupInfo: GroupInfo) -> Alert {
|
||||
mkAlert(
|
||||
title: "Group already exists",
|
||||
message: "You are already in group \(groupInfo.displayName)."
|
||||
)
|
||||
}
|
||||
|
||||
enum ConnReqType: Equatable {
|
||||
case invitation
|
||||
case contact
|
||||
case groupLink
|
||||
|
||||
var connReqSentText: LocalizedStringKey {
|
||||
switch self {
|
||||
case .invitation: return "You will be connected when your contact's device is online, please wait or check later!"
|
||||
case .contact: return "You will be connected when your connection request is accepted, please wait or check later!"
|
||||
case .groupLink: return "You will be connected when group link host's device is online, please wait or check later!"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func planToConnReqType(_ connectionPlan: ConnectionPlan) -> ConnReqType {
|
||||
switch connectionPlan {
|
||||
case .invitationLink: return .invitation
|
||||
case .contactAddress: return .contact
|
||||
case .groupLink: return .groupLink
|
||||
}
|
||||
}
|
||||
|
||||
func connReqSentAlert(_ type: ConnReqType) -> Alert {
|
||||
return mkAlert(
|
||||
title: "Connection request sent!",
|
||||
message: type.connReqSentText
|
||||
)
|
||||
}
|
||||
|
||||
#Preview {
|
||||
NewChatView(
|
||||
selection: .invite
|
||||
)
|
||||
}
|
||||
@@ -1,106 +0,0 @@
|
||||
//
|
||||
// PasteToConnectView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Ian Davies on 22/04/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct PasteToConnectView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@State private var connectionLink: String = ""
|
||||
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
|
||||
@FocusState private var linkEditorFocused: Bool
|
||||
@State private var alert: PlanAndConnectAlert?
|
||||
@State private var sheet: PlanAndConnectActionSheet?
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
Text("Connect via link")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
.onTapGesture { linkEditorFocused = false }
|
||||
|
||||
Section {
|
||||
linkEditor()
|
||||
|
||||
Button {
|
||||
if connectionLink == "" {
|
||||
connectionLink = UIPasteboard.general.string ?? ""
|
||||
} else {
|
||||
connectionLink = ""
|
||||
}
|
||||
} label: {
|
||||
if connectionLink == "" {
|
||||
settingsRow("doc.plaintext") { Text("Paste") }
|
||||
} else {
|
||||
settingsRow("multiply") { Text("Clear") }
|
||||
}
|
||||
}
|
||||
|
||||
Button {
|
||||
connect()
|
||||
} label: {
|
||||
settingsRow("link") { Text("Connect") }
|
||||
}
|
||||
.disabled(connectionLink == "" || connectionLink.trimmingCharacters(in: .whitespaces).firstIndex(of: " ") != nil)
|
||||
|
||||
IncognitoToggle(incognitoEnabled: $incognitoDefault)
|
||||
} footer: {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
sharedProfileInfo(incognitoDefault)
|
||||
Text("You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.")
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
.alert(item: $alert) { a in planAndConnectAlert(a, dismiss: true) }
|
||||
.actionSheet(item: $sheet) { s in planAndConnectActionSheet(s, dismiss: true) }
|
||||
}
|
||||
|
||||
private func linkEditor() -> some View {
|
||||
ZStack {
|
||||
Group {
|
||||
if connectionLink.isEmpty {
|
||||
TextEditor(text: Binding.constant(NSLocalizedString("Paste the link you received to connect with your contact.", comment: "placeholder")))
|
||||
.foregroundColor(.secondary)
|
||||
.disabled(true)
|
||||
}
|
||||
TextEditor(text: $connectionLink)
|
||||
.onSubmit(connect)
|
||||
.textInputAutocapitalization(.never)
|
||||
.disableAutocorrection(true)
|
||||
.focused($linkEditorFocused)
|
||||
}
|
||||
.allowsTightening(false)
|
||||
.padding(.horizontal, -5)
|
||||
.padding(.top, -8)
|
||||
.frame(height: 180, alignment: .topLeading)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
|
||||
private func connect() {
|
||||
let link = connectionLink.trimmingCharacters(in: .whitespaces)
|
||||
planAndConnect(
|
||||
link,
|
||||
showAlert: { alert = $0 },
|
||||
showActionSheet: { sheet = $0 },
|
||||
dismiss: true,
|
||||
incognito: incognitoDefault
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
struct PasteToConnectView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
PasteToConnectView()
|
||||
}
|
||||
}
|
||||
@@ -11,20 +11,12 @@ import CoreImage.CIFilterBuiltins
|
||||
|
||||
struct MutableQRCode: View {
|
||||
@Binding var uri: String
|
||||
@State private var image: UIImage?
|
||||
var withLogo: Bool = true
|
||||
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
if let image = image {
|
||||
qrCodeImage(image)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
image = generateImage(uri)
|
||||
}
|
||||
.onChange(of: uri) { _ in
|
||||
image = generateImage(uri)
|
||||
}
|
||||
QRCode(uri: uri, withLogo: withLogo, tintColor: tintColor)
|
||||
.id("simplex-qrcode-view-for-\(uri)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -32,9 +24,10 @@ struct SimpleXLinkQRCode: View {
|
||||
let uri: String
|
||||
var withLogo: Bool = true
|
||||
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
|
||||
var onShare: (() -> Void)? = nil
|
||||
|
||||
var body: some View {
|
||||
QRCode(uri: simplexChatLink(uri), withLogo: withLogo, tintColor: tintColor)
|
||||
QRCode(uri: simplexChatLink(uri), withLogo: withLogo, tintColor: tintColor, onShare: onShare)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -48,8 +41,9 @@ struct QRCode: View {
|
||||
let uri: String
|
||||
var withLogo: Bool = true
|
||||
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
|
||||
var onShare: (() -> Void)? = nil
|
||||
@State private var image: UIImage? = nil
|
||||
@State private var makeScreenshotBinding: () -> Void = {}
|
||||
@State private var makeScreenshotFunc: () -> Void = {}
|
||||
|
||||
var body: some View {
|
||||
ZStack {
|
||||
@@ -70,18 +64,20 @@ struct QRCode: View {
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
makeScreenshotBinding = {
|
||||
makeScreenshotFunc = {
|
||||
let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale)
|
||||
showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)])}
|
||||
showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)])
|
||||
onShare?()
|
||||
}
|
||||
}
|
||||
.frame(width: geo.size.width, height: geo.size.height)
|
||||
}
|
||||
}
|
||||
.onTapGesture(perform: makeScreenshotBinding)
|
||||
.onTapGesture(perform: makeScreenshotFunc)
|
||||
.onAppear {
|
||||
image = image ?? generateImage(uri)?.replaceColor(UIColor.black, tintColor)
|
||||
image = image ?? generateImage(uri, tintColor: tintColor)
|
||||
}
|
||||
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -93,13 +89,13 @@ private func qrCodeImage(_ image: UIImage) -> some View {
|
||||
.textSelection(.enabled)
|
||||
}
|
||||
|
||||
private func generateImage(_ uri: String) -> UIImage? {
|
||||
private func generateImage(_ uri: String, tintColor: UIColor) -> UIImage? {
|
||||
let context = CIContext()
|
||||
let filter = CIFilter.qrCodeGenerator()
|
||||
filter.message = Data(uri.utf8)
|
||||
if let outputImage = filter.outputImage,
|
||||
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
|
||||
return UIImage(cgImage: cgImage)
|
||||
return UIImage(cgImage: cgImage).replaceColor(UIColor.black, tintColor)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
@@ -1,79 +0,0 @@
|
||||
//
|
||||
// ConnectContactView.swift
|
||||
// SimpleX
|
||||
//
|
||||
// Created by Evgeny Poberezkin on 29/01/2022.
|
||||
// Copyright © 2022 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
import CodeScanner
|
||||
|
||||
struct ScanToConnectView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
|
||||
@State private var alert: PlanAndConnectAlert?
|
||||
@State private var sheet: PlanAndConnectActionSheet?
|
||||
|
||||
var body: some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
Text("Scan QR code")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.vertical)
|
||||
|
||||
CodeScannerView(codeTypes: [.qr], completion: processQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
|
||||
IncognitoToggle(incognitoEnabled: $incognitoDefault)
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 6)
|
||||
.background(
|
||||
RoundedRectangle(cornerRadius: 12, style: .continuous)
|
||||
.fill(Color(uiColor: .systemBackground))
|
||||
)
|
||||
.padding(.top)
|
||||
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
sharedProfileInfo(incognitoDefault)
|
||||
Text("If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.")
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.font(.footnote)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.padding()
|
||||
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
|
||||
}
|
||||
.background(Color(.systemGroupedBackground))
|
||||
.alert(item: $alert) { a in planAndConnectAlert(a, dismiss: true) }
|
||||
.actionSheet(item: $sheet) { s in planAndConnectActionSheet(s, dismiss: true) }
|
||||
}
|
||||
|
||||
func processQRCode(_ resp: Result<ScanResult, ScanError>) {
|
||||
switch resp {
|
||||
case let .success(r):
|
||||
planAndConnect(
|
||||
r.string,
|
||||
showAlert: { alert = $0 },
|
||||
showActionSheet: { sheet = $0 },
|
||||
dismiss: true,
|
||||
incognito: incognitoDefault
|
||||
)
|
||||
case let .failure(e):
|
||||
logger.error("ConnectContactView.processQRCode QR code error: \(e.localizedDescription)")
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
struct ConnectContactView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ScanToConnectView()
|
||||
}
|
||||
}
|
||||
@@ -11,12 +11,14 @@ import SimpleXChat
|
||||
|
||||
enum UserProfileAlert: Identifiable {
|
||||
case duplicateUserError
|
||||
case invalidDisplayNameError
|
||||
case createUserError(error: LocalizedStringKey)
|
||||
case invalidNameError(validName: String)
|
||||
|
||||
var id: String {
|
||||
switch self {
|
||||
case .duplicateUserError: return "duplicateUserError"
|
||||
case .invalidDisplayNameError: return "invalidDisplayNameError"
|
||||
case .createUserError: return "createUserError"
|
||||
case let .invalidNameError(validName): return "invalidNameError \(validName)"
|
||||
}
|
||||
@@ -187,6 +189,12 @@ private func createProfile(_ displayName: String, showAlert: (UserProfileAlert)
|
||||
} else {
|
||||
showAlert(.duplicateUserError)
|
||||
}
|
||||
case .chatCmdError(_, .error(.invalidDisplayName)):
|
||||
if m.currentUser == nil {
|
||||
AlertManager.shared.showAlert(invalidDisplayNameAlert)
|
||||
} else {
|
||||
showAlert(.invalidDisplayNameError)
|
||||
}
|
||||
default:
|
||||
let err: LocalizedStringKey = "Error: \(responseError(error))"
|
||||
if m.currentUser == nil {
|
||||
@@ -207,6 +215,7 @@ private func canCreateProfile(_ displayName: String) -> Bool {
|
||||
func userProfileAlert(_ alert: UserProfileAlert, _ displayName: Binding<String>) -> Alert {
|
||||
switch alert {
|
||||
case .duplicateUserError: return duplicateUserAlert
|
||||
case .invalidDisplayNameError: return invalidDisplayNameAlert
|
||||
case let .createUserError(err): return creatUserErrorAlert(err)
|
||||
case let .invalidNameError(name): return createInvalidNameAlert(name, displayName)
|
||||
}
|
||||
@@ -219,6 +228,13 @@ private var duplicateUserAlert: Alert {
|
||||
)
|
||||
}
|
||||
|
||||
private var invalidDisplayNameAlert: Alert {
|
||||
Alert(
|
||||
title: Text("Invalid display name!"),
|
||||
message: Text("This display name is invalid. Please choose another name.")
|
||||
)
|
||||
}
|
||||
|
||||
private func creatUserErrorAlert(_ err: LocalizedStringKey) -> Alert {
|
||||
Alert(
|
||||
title: Text("Error creating profile!"),
|
||||
|
||||
@@ -81,11 +81,6 @@ struct CreateSimpleXAddress: View {
|
||||
DispatchQueue.main.async {
|
||||
m.userAddress = UserContactLink(connReqContact: connReqContact)
|
||||
}
|
||||
if let u = try await apiSetProfileAddress(on: true) {
|
||||
DispatchQueue.main.async {
|
||||
m.updateUser(u)
|
||||
}
|
||||
}
|
||||
await MainActor.run { progressIndicator = false }
|
||||
} catch let error {
|
||||
logger.error("CreateSimpleXAddress create address: \(responseError(error))")
|
||||
@@ -100,7 +95,7 @@ struct CreateSimpleXAddress: View {
|
||||
} label: {
|
||||
Text("Create SimpleX address").font(.title)
|
||||
}
|
||||
Text("Your contacts in SimpleX will see it.\nYou can change it in Settings.")
|
||||
Text("You can make it visible to your SimpleX contacts via Settings.")
|
||||
.multilineTextAlignment(.center)
|
||||
.font(.footnote)
|
||||
.padding(.horizontal, 32)
|
||||
|
||||
@@ -332,7 +332,7 @@ struct ConnectDesktopView: View {
|
||||
|
||||
private func scanDesctopAddressView() -> some View {
|
||||
Section("Scan QR code from desktop") {
|
||||
CodeScannerView(codeTypes: [.qr], completion: processDesktopQRCode)
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processDesktopQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
.listRowBackground(Color.clear)
|
||||
|
||||
@@ -51,9 +51,9 @@ struct AdvancedNetworkSettings: View {
|
||||
}
|
||||
.disabled(currentNetCfg == NetCfg.proxyDefaults)
|
||||
|
||||
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [3_000000, 5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [15_000, 30_000, 60_000, 90_000, 120_000], label: secondsLabel)
|
||||
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [15_000, 30_000, 45_000, 60_000, 90_000, 120_000], label: secondsLabel)
|
||||
timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel)
|
||||
intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "")
|
||||
Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive)
|
||||
|
||||
@@ -10,24 +10,23 @@ import SwiftUI
|
||||
|
||||
struct IncognitoHelp: View {
|
||||
var body: some View {
|
||||
VStack(alignment: .leading) {
|
||||
List {
|
||||
Text("Incognito mode")
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
.padding(.vertical)
|
||||
ScrollView {
|
||||
VStack(alignment: .leading) {
|
||||
Group {
|
||||
Text("Incognito mode protects your privacy by using a new random profile for each contact.")
|
||||
Text("It allows having many anonymous connections without any shared data between them in a single chat profile.")
|
||||
Text("When you share an incognito profile with somebody, this profile will be used for the groups they invite you to.")
|
||||
}
|
||||
.padding(.bottom)
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
.listRowSeparator(.hidden)
|
||||
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||
VStack(alignment: .leading, spacing: 18) {
|
||||
Text("Incognito mode protects your privacy by using a new random profile for each contact.")
|
||||
Text("It allows having many anonymous connections without any shared data between them in a single chat profile.")
|
||||
Text("When you share an incognito profile with somebody, this profile will be used for the groups they invite you to.")
|
||||
Text("Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).")
|
||||
}
|
||||
.listRowBackground(Color.clear)
|
||||
}
|
||||
.frame(maxWidth: .infinity)
|
||||
.padding()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -14,9 +14,6 @@ struct NotificationsView: View {
|
||||
@State private var notificationMode: NotificationsMode = ChatModel.shared.notificationMode
|
||||
@State private var showAlert: NotificationAlert?
|
||||
@State private var legacyDatabase = dbContainerGroupDefault.get() == .documents
|
||||
// @AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
// @AppStorage(GROUP_DEFAULT_NTF_ENABLE_LOCAL, store: groupDefaults) private var ntfEnableLocal = false
|
||||
// @AppStorage(GROUP_DEFAULT_NTF_ENABLE_PERIODIC, store: groupDefaults) private var ntfEnablePeriodic = false
|
||||
|
||||
var body: some View {
|
||||
List {
|
||||
@@ -88,13 +85,6 @@ struct NotificationsView: View {
|
||||
.padding(.top, 1)
|
||||
}
|
||||
}
|
||||
|
||||
// if developerTools {
|
||||
// Section(String("Experimental")) {
|
||||
// Toggle(String("Always enable local"), isOn: $ntfEnableLocal)
|
||||
// Toggle(String("Always enable periodic"), isOn: $ntfEnablePeriodic)
|
||||
// }
|
||||
// }
|
||||
}
|
||||
.disabled(legacyDatabase)
|
||||
}
|
||||
@@ -119,7 +109,7 @@ struct NotificationsView: View {
|
||||
|
||||
private func ntfModeAlertTitle(_ mode: NotificationsMode) -> LocalizedStringKey {
|
||||
switch mode {
|
||||
case .off: return "Turn off notifications?"
|
||||
case .off: return "Use only local notifications?"
|
||||
case .periodic: return "Enable periodic notifications?"
|
||||
case .instant: return "Enable instant notifications?"
|
||||
}
|
||||
|
||||
@@ -63,7 +63,6 @@ struct PreferencesView: View {
|
||||
|
||||
private func featureFooter(_ feature: ChatFeature, _ allowFeature: Binding<FeatureAllowed>) -> some View {
|
||||
Text(feature.allowDescription(allowFeature.wrappedValue))
|
||||
.frame(height: 36, alignment: .topLeading)
|
||||
}
|
||||
|
||||
private func savePreferences() {
|
||||
|
||||
@@ -467,6 +467,7 @@ struct SimplexLockView: View {
|
||||
switch a {
|
||||
case .enableAuth:
|
||||
SetAppPasscodeView {
|
||||
m.contentViewAccessAuthenticated = true
|
||||
laLockDelay = 30
|
||||
prefPerformLA = true
|
||||
showChangePassword = true
|
||||
@@ -490,14 +491,23 @@ struct SimplexLockView: View {
|
||||
showLAAlert(.laPasscodeNotChangedAlert)
|
||||
}
|
||||
case .enableSelfDestruct:
|
||||
SetAppPasscodeView(passcodeKeychain: kcSelfDestructPassword, title: "Set passcode", reason: NSLocalizedString("Enable self-destruct passcode", comment: "set passcode view")) {
|
||||
SetAppPasscodeView(
|
||||
passcodeKeychain: kcSelfDestructPassword,
|
||||
prohibitedPasscodeKeychain: kcAppPassword,
|
||||
title: "Set passcode",
|
||||
reason: NSLocalizedString("Enable self-destruct passcode", comment: "set passcode view")
|
||||
) {
|
||||
updateSelfDestruct()
|
||||
showLAAlert(.laSelfDestructPasscodeSetAlert)
|
||||
} cancel: {
|
||||
revertSelfDestruct()
|
||||
}
|
||||
case .changeSelfDestructPasscode:
|
||||
SetAppPasscodeView(passcodeKeychain: kcSelfDestructPassword, reason: NSLocalizedString("Change self-destruct passcode", comment: "set passcode view")) {
|
||||
SetAppPasscodeView(
|
||||
passcodeKeychain: kcSelfDestructPassword,
|
||||
prohibitedPasscodeKeychain: kcAppPassword,
|
||||
reason: NSLocalizedString("Change self-destruct passcode", comment: "set passcode view")
|
||||
) {
|
||||
showLAAlert(.laSelfDestructPasscodeChangedAlert)
|
||||
} cancel: {
|
||||
showLAAlert(.laPasscodeNotChangedAlert)
|
||||
@@ -619,6 +629,7 @@ struct SimplexLockView: View {
|
||||
authenticate(reason: NSLocalizedString("Enable SimpleX Lock", comment: "authentication reason")) { laResult in
|
||||
switch laResult {
|
||||
case .success:
|
||||
m.contentViewAccessAuthenticated = true
|
||||
prefPerformLA = true
|
||||
laAlert = .laTurnedOnAlert
|
||||
case .failed:
|
||||
|
||||
@@ -21,7 +21,7 @@ struct ScanProtocolServer: View {
|
||||
.font(.largeTitle)
|
||||
.bold()
|
||||
.padding(.vertical)
|
||||
CodeScannerView(codeTypes: [.qr], completion: processQRCode)
|
||||
CodeScannerView(codeTypes: [.qr], scanMode: .oncePerCode, completion: processQRCode)
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.cornerRadius(12)
|
||||
.padding(.top)
|
||||
|
||||
@@ -95,6 +95,12 @@ let appDefaults: [String: Any] = [
|
||||
DEFAULT_CONNECT_REMOTE_VIA_MULTICAST_AUTO: true,
|
||||
]
|
||||
|
||||
// not used anymore
|
||||
enum ConnectViaLinkTab: String {
|
||||
case scan
|
||||
case paste
|
||||
}
|
||||
|
||||
enum SimpleXLinkMode: String, Identifiable {
|
||||
case description
|
||||
case full
|
||||
|
||||
@@ -190,7 +190,8 @@ struct UserAddressView: View {
|
||||
|
||||
@ViewBuilder private func existingAddressView(_ userAddress: UserContactLink) -> some View {
|
||||
Section {
|
||||
MutableQRCode(uri: Binding.constant(simplexChatLink(userAddress.connReqContact)))
|
||||
SimpleXLinkQRCode(uri: userAddress.connReqContact)
|
||||
.id("simplex-contact-address-qrcode-\(userAddress.connReqContact)")
|
||||
shareQRCodeButton(userAddress)
|
||||
if MFMailComposeViewController.canSendMail() {
|
||||
shareViaEmailButton(userAddress)
|
||||
|
||||
@@ -120,8 +120,10 @@ struct UserProfile: View {
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
LibraryImagePicker(image: $chosenImage) {
|
||||
didSelectItem in showImagePicker = false
|
||||
LibraryImagePicker(image: $chosenImage) { _ in
|
||||
await MainActor.run {
|
||||
showImagePicker = false
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: chosenImage) { image in
|
||||
|
||||
@@ -303,14 +303,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Добави нов контакт**: за да създадете своя еднократен QR код или линк за вашия контакт.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Създай линк / QR код**, който вашият контакт да използва.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -323,11 +326,6 @@
|
||||
<target>**Най-поверително**: не използвайте сървъра за известия SimpleX Chat, периодично проверявайте съобщенията във фонов режим (зависи от това колко често използвате приложението).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Поставете получения линк** или го отворете в браузъра и докоснете **Отваряне в мобилно приложение**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Моля, обърнете внимание**: НЯМА да можете да възстановите или промените паролата, ако я загубите.</target>
|
||||
@@ -338,11 +336,6 @@
|
||||
<target>**Препоръчително**: токенът на устройството и известията се изпращат до сървъра за уведомяване на SimpleX Chat, но не и съдържанието, размерът на съобщението или от кого е.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Сканирай QR код**: за да се свържете с вашия контакт лично или чрез видеообаждане.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Внимание**: Незабавните push известия изискват парола, запазена в Keychain.</target>
|
||||
@@ -440,11 +433,6 @@
|
||||
<target>1 седмица</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>Еднократен линк</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 минути</target>
|
||||
@@ -560,6 +548,10 @@
|
||||
<target>Добавете адрес към вашия профил, така че вашите контакти да могат да го споделят с други хора. Актуализацията на профила ще бъде изпратена до вашите контакти.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Добави предварително зададени сървъри</target>
|
||||
@@ -956,6 +948,10 @@
|
||||
<target>Обаждания</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Потребителският профил не може да се изтрие!</target>
|
||||
@@ -1072,6 +1068,10 @@
|
||||
<target>Чатът е спрян</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Чат настройки</target>
|
||||
@@ -1208,11 +1208,6 @@ This is your own one-time link!</source>
|
||||
<target>Свърване чрез линк</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Свърване чрез линк/QR код</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Свързване чрез еднократен линк за връзка</target>
|
||||
@@ -1380,11 +1375,6 @@ This is your own one-time link!</source>
|
||||
<target>Създайте нов профил в [настолното приложение](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Създай линк за еднократна покана</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1409,6 +1399,10 @@ This is your own one-time link!</source>
|
||||
<target>Създаден на %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Текущ kод за достъп</target>
|
||||
@@ -1950,6 +1944,10 @@ This cannot be undone!</source>
|
||||
<target>Активиране на автоматично изтриване на съобщения?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Активиране за всички</target>
|
||||
@@ -2015,6 +2013,10 @@ This cannot be undone!</source>
|
||||
<target>Криптирано съобщение или друго събитие</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Криптирано съобщение: грешка в базата данни</target>
|
||||
@@ -2240,6 +2242,10 @@ This cannot be undone!</source>
|
||||
<target>Грешка при зареждане на %@ сървъри</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Грешка при получаване на файл</target>
|
||||
@@ -2280,6 +2286,10 @@ This cannot be undone!</source>
|
||||
<target>Грешка при запазване на потребителска парола</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Грешка при изпращане на имейл</target>
|
||||
@@ -2749,11 +2759,6 @@ This cannot be undone!</source>
|
||||
<target>Ако не можете да се срещнете лично, покажете QR код във видеоразговора или споделете линка.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Ако не можете да се срещнете на живо, можете да **сканирате QR код във видеообаждането** или вашият контакт може да сподели линк за покана.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Ако въведете този kод за достъп, когато отваряте приложението, всички данни от приложението ще бъдат необратимо изтрити!</target>
|
||||
@@ -2909,15 +2914,27 @@ This cannot be undone!</source>
|
||||
<target>Интерфейс</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Невалиден линк за връзка</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Невалиден адрес на сървъра!</target>
|
||||
@@ -3032,10 +3049,18 @@ This is your link for group %@!</source>
|
||||
<target>Присъединяване към групата</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Запазете връзките си</target>
|
||||
@@ -3118,6 +3143,11 @@ This is your link for group %@!</source>
|
||||
<target>Съобщения на живо</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Локално</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Локално име</target>
|
||||
@@ -3357,6 +3387,10 @@ This is your link for group %@!</source>
|
||||
<target>Нов kод за достъп</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Нова заявка за контакт</target>
|
||||
@@ -3480,16 +3514,15 @@ This is your link for group %@!</source>
|
||||
- да деактивират членове (роля "наблюдател")</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Изключено</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Изключено (Локално)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ок</target>
|
||||
@@ -3629,9 +3662,16 @@ This is your link for group %@!</source>
|
||||
<target>Протокол и код с отворен код – всеки може да оперира собствени сървъри.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Отваряне на база данни…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3674,11 +3714,6 @@ This is your link for group %@!</source>
|
||||
<target>Парола за показване</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Постави</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3688,16 +3723,10 @@ This is your link for group %@!</source>
|
||||
<target>Постави изображение</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Постави получения линк</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Поставете линка, който сте получили, за да се свържете с вашия контакт.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>Хората могат да се свържат с вас само чрез ликовете, които споделяте.</target>
|
||||
@@ -3733,6 +3762,11 @@ This is your link for group %@!</source>
|
||||
<target>Моля, проверете вашите настройки и тези вашия за контакт.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Моля, свържете се с груповия администартор.</target>
|
||||
@@ -3936,6 +3970,10 @@ This is your link for group %@!</source>
|
||||
<target>Прочетете повече в [Ръководство за потребителя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Прочетете повече в [Ръководство на потребителя](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4144,6 +4182,10 @@ This is your link for group %@!</source>
|
||||
<target>Грешка при възстановяване на базата данни</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Покажи</target>
|
||||
@@ -4298,6 +4340,10 @@ This is your link for group %@!</source>
|
||||
<target>Търсене</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Сигурна опашка</target>
|
||||
@@ -4572,9 +4618,8 @@ This is your link for group %@!</source>
|
||||
<target>Сподели линк</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Сподели линк за еднократна покана</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4697,16 +4742,15 @@ This is your link for group %@!</source>
|
||||
<target>Някой</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Започни нов чат</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Започни чат</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Започни миграция</target>
|
||||
@@ -4831,6 +4875,14 @@ This is your link for group %@!</source>
|
||||
<target>Докосни за инкогнито вход</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Докосни за започване на нов чат</target>
|
||||
@@ -4893,6 +4945,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>Опитът за промяна на паролата на базата данни не беше завършен.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>Връзката, която приехте, ще бъде отказана!</target>
|
||||
@@ -4958,6 +5014,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>Сървърите за нови връзки на текущия ви чат профил **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Тема</target>
|
||||
@@ -5106,11 +5166,6 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
<target>Изключи</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Изключи известията?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Включи</target>
|
||||
@@ -5307,6 +5362,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Използвай нов инкогнито профил</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Използвай сървър</target>
|
||||
@@ -5556,11 +5615,6 @@ Repeat join request?</source>
|
||||
<target>Можете да приемате обаждания от заключен екран, без идентификация на устройство и приложението.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Можете също да се свържете, като натиснете върху линка. Ако се отвори в браузъра, натиснете върху бутона **Отваряне в мобилно приложение**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Можете да го създадете по-късно</target>
|
||||
@@ -5581,6 +5635,10 @@ Repeat join request?</source>
|
||||
<target>Можете да скриете или заглушите известията за потребителски профил - плъзнете надясно.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Вече можете да изпращате съобщения до %@</target>
|
||||
@@ -5621,6 +5679,10 @@ Repeat join request?</source>
|
||||
<target>Можете да използвате markdown за форматиране на съобщенията:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Не може да изпращате съобщения!</target>
|
||||
@@ -5805,13 +5867,6 @@ You can cancel this connection and remove the contact (and try later with a new
|
||||
<target>Вашите контакти могат да позволят пълното изтриване на съобщението.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Вашите контакти в SimpleX ще го видят.
|
||||
Можете да го промените в Настройки.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Вашите контакти ще останат свързани.</target>
|
||||
|
||||
@@ -303,14 +303,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Přidat nový kontakt**: pro vytvoření jednorázového QR kódu nebo odkazu pro váš kontakt.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Vytvořte odkaz / QR kód** pro váš kontakt.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -323,11 +326,6 @@
|
||||
<target>**Nejsoukromější**: nepoužívejte server oznámení SimpleX Chat, pravidelně kontrolujte zprávy na pozadí (závisí na tom, jak často aplikaci používáte).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Vložte přijatý odkaz** nebo jej otevřete v prohlížeči a klepněte na **Otevřít v mobilní aplikaci**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Upozornění**: Pokud heslo ztratíte, NEBUDETE jej moci obnovit ani změnit.</target>
|
||||
@@ -338,11 +336,6 @@
|
||||
<target>**Doporučeno**: Token zařízení a oznámení se odesílají na oznamovací server SimpleX Chat, ale nikoli obsah, velikost nebo od koho jsou zprávy.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>** Naskenujte QR kód**: pro připojení ke kontaktu osobně nebo prostřednictvím videohovoru.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Upozornění**: Okamžitě doručovaná oznámení vyžadují přístupové heslo uložené v Klíčence.</target>
|
||||
@@ -440,11 +433,6 @@
|
||||
<target>1 týden</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>Jednorázový odkaz</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 minut</target>
|
||||
@@ -560,6 +548,10 @@
|
||||
<target>Přidejte adresu do svého profilu, aby ji vaše kontakty mohly sdílet s dalšími lidmi. Aktualizace profilu bude zaslána vašim kontaktům.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Přidejte přednastavené servery</target>
|
||||
@@ -956,6 +948,10 @@
|
||||
<target>Hovory</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Nemohu smazat uživatelský profil!</target>
|
||||
@@ -1072,6 +1068,10 @@
|
||||
<target>Chat je zastaven</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Předvolby chatu</target>
|
||||
@@ -1208,11 +1208,6 @@ This is your own one-time link!</source>
|
||||
<target>Připojte se prostřednictvím odkazu</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Připojit se prostřednictvím odkazu / QR kódu</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Připojit se jednorázovým odkazem</target>
|
||||
@@ -1380,11 +1375,6 @@ This is your own one-time link!</source>
|
||||
<target>Vytvořit nový profil v [desktop app](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Vytvořit jednorázovou pozvánku</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1409,6 +1399,10 @@ This is your own one-time link!</source>
|
||||
<target>Vytvořeno na %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Aktuální heslo</target>
|
||||
@@ -1950,6 +1944,10 @@ This cannot be undone!</source>
|
||||
<target>Povolit automatické mazání zpráv?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Povolit pro všechny</target>
|
||||
@@ -2015,6 +2013,10 @@ This cannot be undone!</source>
|
||||
<target>Šifrovaná zpráva nebo jiná událost</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Šifrovaná zpráva: chyba databáze</target>
|
||||
@@ -2240,6 +2242,10 @@ This cannot be undone!</source>
|
||||
<target>Chyba načítání %@ serverů</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Chyba při příjmu souboru</target>
|
||||
@@ -2280,6 +2286,10 @@ This cannot be undone!</source>
|
||||
<target>Chyba ukládání hesla uživatele</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Chyba odesílání e-mailu</target>
|
||||
@@ -2749,11 +2759,6 @@ This cannot be undone!</source>
|
||||
<target>Pokud se nemůžete setkat osobně, zobrazte QR kód ve videohovoru nebo sdílejte odkaz.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Pokud se nemůžete setkat osobně, můžete **naskenovat QR kód během videohovoru**, nebo váš kontakt může sdílet odkaz na pozvánku.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Pokud tento přístupový kód zadáte při otevření aplikace, všechna data budou nenávratně smazána!</target>
|
||||
@@ -2909,15 +2914,27 @@ This cannot be undone!</source>
|
||||
<target>Rozhranní</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Neplatný odkaz na spojení</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Neplatná adresa serveru!</target>
|
||||
@@ -3032,10 +3049,18 @@ This is your link for group %@!</source>
|
||||
<target>Připojování ke skupině</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Zachovat vaše připojení</target>
|
||||
@@ -3118,6 +3143,11 @@ This is your link for group %@!</source>
|
||||
<target>Živé zprávy</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Místní</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Místní název</target>
|
||||
@@ -3357,6 +3387,10 @@ This is your link for group %@!</source>
|
||||
<target>Nové heslo</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Žádost o nový kontakt</target>
|
||||
@@ -3480,16 +3514,15 @@ This is your link for group %@!</source>
|
||||
- zakázat členy (role "pozorovatel")</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Vypnout</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Vypnuto (místní)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ok</target>
|
||||
@@ -3629,9 +3662,16 @@ This is your link for group %@!</source>
|
||||
<target>Protokol a kód s otevřeným zdrojovým kódem - servery může provozovat kdokoli.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Otvírání databáze…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3674,11 +3714,6 @@ This is your link for group %@!</source>
|
||||
<target>Heslo k zobrazení</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Vložit</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3688,16 +3723,10 @@ This is your link for group %@!</source>
|
||||
<target>Vložit obrázek</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Vložení přijatého odkazu</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Vložte odkaz, který jste obdrželi, do pole níže a spojte se se svým kontaktem.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>Lidé se s vámi mohou spojit pouze prostřednictvím odkazů, které sdílíte.</target>
|
||||
@@ -3733,6 +3762,11 @@ This is your link for group %@!</source>
|
||||
<target>Zkontrolujte prosím nastavení své i svého kontaktu.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Kontaktujte prosím správce skupiny.</target>
|
||||
@@ -3936,6 +3970,10 @@ This is your link for group %@!</source>
|
||||
<target>Další informace naleznete v [Uživatelské příručce](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Přečtěte si více v [Uživatelské příručce](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4144,6 +4182,10 @@ This is your link for group %@!</source>
|
||||
<target>Chyba obnovení databáze</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Odhalit</target>
|
||||
@@ -4298,6 +4340,10 @@ This is your link for group %@!</source>
|
||||
<target>Hledat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Zabezpečit frontu</target>
|
||||
@@ -4572,9 +4618,8 @@ This is your link for group %@!</source>
|
||||
<target>Sdílet odkaz</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Jednorázový zvací odkaz</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4697,16 +4742,15 @@ This is your link for group %@!</source>
|
||||
<target>Někdo</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Začít nový chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Začít chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Zahájit přenesení</target>
|
||||
@@ -4831,6 +4875,14 @@ This is your link for group %@!</source>
|
||||
<target>Klepnutím se připojíte inkognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Klepnutím na zahájíte nový chat</target>
|
||||
@@ -4893,6 +4945,10 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován
|
||||
<target>Pokus o změnu přístupové fráze databáze nebyl dokončen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>Připojení, které jste přijali, bude zrušeno!</target>
|
||||
@@ -4958,6 +5014,10 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován
|
||||
<target>Servery pro nová připojení vašeho aktuálního chat profilu **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Téma</target>
|
||||
@@ -5106,11 +5166,6 @@ Před zapnutím této funkce budete vyzváni k dokončení ověření.</target>
|
||||
<target>Vypnout</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Vypnout upozornění?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Zapnout</target>
|
||||
@@ -5307,6 +5362,10 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
|
||||
<target>Použít nový inkognito profil</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Použít server</target>
|
||||
@@ -5556,11 +5615,6 @@ Repeat join request?</source>
|
||||
<target>Můžete přijímat hovory z obrazovky zámku, bez ověření zařízení a aplikace.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Můžete se také připojit kliknutím na odkaz. Pokud se otevře v prohlížeči, klikněte na tlačítko **Otevřít v mobilní aplikaci**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Můžete vytvořit později</target>
|
||||
@@ -5581,6 +5635,10 @@ Repeat join request?</source>
|
||||
<target>Profil uživatele můžete skrýt nebo ztlumit - přejeďte prstem doprava.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Nyní můžete posílat zprávy %@</target>
|
||||
@@ -5621,6 +5679,10 @@ Repeat join request?</source>
|
||||
<target>K formátování zpráv můžete použít markdown:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Nemůžete posílat zprávy!</target>
|
||||
@@ -5805,13 +5867,6 @@ Toto připojení můžete zrušit a kontakt odebrat (a zkusit to později s nov
|
||||
<target>Vaše kontakty mohou povolit úplné mazání zpráv.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Vaše kontakty v SimpleX ji uvidí.
|
||||
Můžete ji změnit v Nastavení.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Vaše kontakty zůstanou připojeny.</target>
|
||||
|
||||
@@ -312,14 +312,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Fügen Sie einen neuen Kontakt hinzu**: Erzeugen Sie einen Einmal-QR-Code oder -Link für Ihren Kontakt.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Generieren Sie einen Einladungs-Link / QR code** für Ihren Kontakt.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -332,11 +335,6 @@
|
||||
<target>**Beste Privatsphäre**: Es wird kein SimpleX-Chat-Benachrichtigungs-Server genutzt, Nachrichten werden in periodischen Abständen im Hintergrund geprüft (dies hängt davon ab, wie häufig Sie die App nutzen).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Fügen Sie den von Ihrem Kontakt erhaltenen Link ein** oder öffnen Sie ihn im Browser und tippen Sie auf **In mobiler App öffnen**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Bitte beachten Sie**: Das Passwort kann NICHT wiederhergestellt oder geändert werden, wenn Sie es vergessen haben oder verlieren.</target>
|
||||
@@ -347,11 +345,6 @@
|
||||
<target>**Empfohlen**: Nur Ihr Geräte-Token und ihre Benachrichtigungen werden an den SimpleX-Chat-Benachrichtigungs-Server gesendet, aber weder der Nachrichteninhalt noch deren Größe oder von wem sie gesendet wurde.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Scannen Sie den QR-Code**, um sich während einem persönlichen Treffen oder per Videoanruf mit Ihrem Kontakt zu verbinden.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Warnung**: Sofortige Push-Benachrichtigungen erfordern die Eingabe eines Passworts, welches in Ihrem Schlüsselbund gespeichert ist.</target>
|
||||
@@ -453,11 +446,6 @@
|
||||
<target>wöchentlich</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>Einmal-Link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 Minuten</target>
|
||||
@@ -573,6 +561,10 @@
|
||||
<target>Fügen Sie die Adresse zu Ihrem Profil hinzu, damit Ihre Kontakte sie mit anderen Personen teilen können. Es wird eine Profilaktualisierung an Ihre Kontakte gesendet.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Füge voreingestellte Server hinzu</target>
|
||||
@@ -978,6 +970,10 @@
|
||||
<target>Anrufe</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Das Benutzerprofil kann nicht gelöscht werden!</target>
|
||||
@@ -1094,6 +1090,10 @@
|
||||
<target>Der Chat ist beendet</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Chat-Präferenzen</target>
|
||||
@@ -1238,11 +1238,6 @@ Das ist Ihr eigener Einmal-Link!</target>
|
||||
<target>Über einen Link verbinden</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Über einen Link / QR-Code verbinden</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Über einen Einmal-Link verbinden</target>
|
||||
@@ -1418,11 +1413,6 @@ Das ist Ihr eigener Einmal-Link!</target>
|
||||
<target>Neues Profil in der [Desktop-App] erstellen (https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Einmal-Einladungslink erstellen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Profil erstellen</target>
|
||||
@@ -1448,6 +1438,10 @@ Das ist Ihr eigener Einmal-Link!</target>
|
||||
<target>Erstellt am %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Aktueller Zugangscode</target>
|
||||
@@ -1998,6 +1992,10 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Automatisches Löschen von Nachrichten aktivieren?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Für Alle aktivieren</target>
|
||||
@@ -2063,6 +2061,10 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Verschlüsselte Nachricht oder ein anderes Ereignis</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Verschlüsselte Nachricht: Datenbankfehler</target>
|
||||
@@ -2293,6 +2295,10 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Fehler beim Laden von %@ Servern</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Fehler beim Empfangen der Datei</target>
|
||||
@@ -2333,6 +2339,10 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Fehler beim Speichern des Benutzer-Passworts</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Fehler beim Senden der eMail</target>
|
||||
@@ -2808,11 +2818,6 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Falls Sie sich nicht persönlich treffen können, zeigen Sie den QR-Code in einem Videoanruf oder teilen Sie den Link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Wenn Sie sich nicht persönlich treffen können, kann der **QR-Code während eines Videoanrufs gescannt werden**, oder Ihr Kontakt kann den Einladungslink über einen anderen Kanal mit Ihnen teilen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Wenn Sie diesen Zugangscode während des Öffnens der App eingeben, werden alle App-Daten unwiederbringlich gelöscht!</target>
|
||||
@@ -2970,16 +2975,28 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Schnittstelle</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Ungültiger Verbindungslink</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<target>Ungültiger Name!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Ungültige Serveradresse!</target>
|
||||
@@ -3098,11 +3115,19 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Der Gruppe beitreten</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<target>Die App muss geöffnet bleiben, um sie vom Desktop aus nutzen zu können</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Ihre Verbindungen beibehalten</target>
|
||||
@@ -3188,6 +3213,11 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Live Nachrichten</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Lokal</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Lokaler Name</target>
|
||||
@@ -3428,6 +3458,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Neuer Zugangscode</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Neue Kontaktanfrage</target>
|
||||
@@ -3552,16 +3586,15 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
- Gruppenmitglieder deaktivieren ("Beobachter"-Rolle)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Aus</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Aus (Lokal)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ok</target>
|
||||
@@ -3702,9 +3735,16 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Open-Source-Protokoll und -Code – Jede Person kann ihre eigenen Server aufsetzen und nutzen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Öffne Datenbank …</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3747,11 +3787,6 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Passwort anzeigen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Einfügen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Desktop-Adresse einfügen</target>
|
||||
@@ -3762,16 +3797,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Bild einfügen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Fügen Sie den erhaltenen Link ein</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Um sich mit Ihrem Kontakt zu verbinden, fügen Sie den erhaltenen Link in das Feld unten ein.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>Verbindungen mit Kontakten sind nur über Links möglich, die Sie oder Ihre Kontakte untereinander teilen.</target>
|
||||
@@ -3807,6 +3836,11 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Bitte überprüfen sie sowohl Ihre, als auch die Präferenzen Ihres Kontakts.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Bitte kontaktieren Sie den Gruppen-Administrator.</target>
|
||||
@@ -4012,6 +4046,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Mehr dazu in der [Benutzeranleitung](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address) lesen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Mehr dazu in der [Benutzeranleitung](https://simplex.chat/docs/guide/readme.html#connect-to-friends) lesen.</target>
|
||||
@@ -4222,6 +4260,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Fehler bei der Wiederherstellung der Datenbank</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Aufdecken</target>
|
||||
@@ -4377,6 +4419,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Suche</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Sichere Warteschlange</target>
|
||||
@@ -4652,9 +4698,8 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Link teilen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Einmal-Einladungslink teilen</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4777,16 +4822,15 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Jemand</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Starten Sie einen neuen Chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Starten Sie den Chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Starten Sie die Migration</target>
|
||||
@@ -4912,6 +4956,14 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Tippen, um Inkognito beizutreten</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Tippen, um einen neuen Chat zu starten</target>
|
||||
@@ -4974,6 +5026,10 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro
|
||||
<target>Die Änderung des Datenbank-Passworts konnte nicht abgeschlossen werden.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>Die von Ihnen akzeptierte Verbindung wird abgebrochen!</target>
|
||||
@@ -5039,6 +5095,10 @@ Dies kann passieren, wenn es einen Fehler gegeben hat oder die Verbindung kompro
|
||||
<target>Server der neuen Verbindungen von Ihrem aktuellen Chat-Profil **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Design</target>
|
||||
@@ -5191,11 +5251,6 @@ Sie werden aufgefordert, die Authentifizierung abzuschließen, bevor diese Funkt
|
||||
<target>Abschalten</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Benachrichtigungen abschalten?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Einschalten</target>
|
||||
@@ -5398,6 +5453,10 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
|
||||
<target>Nutzen Sie das neue Inkognito-Profil</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Server nutzen</target>
|
||||
@@ -5660,11 +5719,6 @@ Verbindungsanfrage wiederholen?</target>
|
||||
<target>Sie können Anrufe ohne Geräte- und App-Authentifizierung vom Sperrbildschirm aus annehmen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Sie können sich auch verbinden, indem Sie auf den Link klicken. Wenn er im Browser geöffnet wird, klicken Sie auf die Schaltfläche **In mobiler App öffnen**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Sie können dies später erstellen</target>
|
||||
@@ -5685,6 +5739,10 @@ Verbindungsanfrage wiederholen?</target>
|
||||
<target>Sie können ein Benutzerprofil verbergen oder stummschalten - wischen Sie es nach rechts.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Sie können nun Nachrichten an %@ versenden</target>
|
||||
@@ -5725,6 +5783,10 @@ Verbindungsanfrage wiederholen?</target>
|
||||
<target>Um Nachrichteninhalte zu formatieren, können Sie Markdowns verwenden:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Sie können keine Nachrichten versenden!</target>
|
||||
@@ -5914,13 +5976,6 @@ Sie können diese Verbindung abbrechen und den Kontakt entfernen (und es später
|
||||
<target>Ihre Kontakte können die unwiederbringliche Löschung von Nachrichten erlauben.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Ihre Kontakte in SimpleX werden es sehen.
|
||||
Sie können es in den Einstellungen ändern.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Ihre Kontakte bleiben verbunden.</target>
|
||||
|
||||
@@ -31,32 +31,39 @@ Available in v5.1</source>
|
||||
<source> (</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" (can be copied)" xml:space="preserve">
|
||||
<trans-unit id=" (can be copied)" xml:space="preserve" approved="no">
|
||||
<source> (can be copied)</source>
|
||||
<target state="translated"> (μπορεί να αντιγραφή)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="!1 colored!" xml:space="preserve">
|
||||
<trans-unit id="!1 colored!" xml:space="preserve" approved="no">
|
||||
<source>!1 colored!</source>
|
||||
<target state="translated">!1 έγχρωμο!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="#secret#" xml:space="preserve">
|
||||
<trans-unit id="#secret#" xml:space="preserve" approved="no">
|
||||
<source>#secret#</source>
|
||||
<target state="translated">#μυστικό#</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@" xml:space="preserve">
|
||||
<trans-unit id="%@" xml:space="preserve" approved="no">
|
||||
<source>%@</source>
|
||||
<target state="translated">%@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ %@" xml:space="preserve">
|
||||
<trans-unit id="%@ %@" xml:space="preserve" approved="no">
|
||||
<source>%@ %@</source>
|
||||
<target state="translated">%@ %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ / %@" xml:space="preserve">
|
||||
<trans-unit id="%@ / %@" xml:space="preserve" approved="no">
|
||||
<source>%@ / %@</source>
|
||||
<target state="translated">%@ / %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve">
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve" approved="no">
|
||||
<source>%@ is connected!</source>
|
||||
<target state="translated">%@ είναι συνδεδεμένο!</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is not verified" xml:space="preserve">
|
||||
@@ -4162,6 +4169,51 @@ SimpleX servers cannot see your profile.</source>
|
||||
<source>\~strike~</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ connected" xml:space="preserve" approved="no">
|
||||
<source>%@ connected</source>
|
||||
<target state="translated">%@ συνδεδεμένο</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="# %@" xml:space="preserve" approved="no">
|
||||
<source># %@</source>
|
||||
<target state="translated"># %@</target>
|
||||
<note>copied message info title, # <title></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@" xml:space="preserve" approved="no">
|
||||
<source>%@ and %@</source>
|
||||
<target state="translated">%@ και %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ at %@:" xml:space="preserve" approved="no">
|
||||
<source>%1$@ at %2$@:</source>
|
||||
<target state="translated">%1$@ στις %2$@:</target>
|
||||
<note>copied message info, <sender> at <time></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## History" xml:space="preserve" approved="no">
|
||||
<source>## History</source>
|
||||
<target state="translated">## Ιστορικό</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## In reply to" xml:space="preserve" approved="no">
|
||||
<source>## In reply to</source>
|
||||
<target state="translated">## Ως απαντηση σε</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ (current)" xml:space="preserve" approved="no">
|
||||
<source>%@ (current)</source>
|
||||
<target state="translated">%@ (τωρινό)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ (current):" xml:space="preserve" approved="no">
|
||||
<source>%@ (current):</source>
|
||||
<target state="translated">%@ (τωρινό):</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve" approved="no">
|
||||
<source>%@ and %@ connected</source>
|
||||
<target state="translated">%@ και %@ συνδεδεμένο</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="el" datatype="plaintext">
|
||||
|
||||
@@ -312,14 +312,19 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<target>**Add contact**: to create a new invitation link, or connect via a link you received.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Add new contact**: to create your one-time QR Code or link for your contact.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Create link / QR code** for your contact to use.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<target>**Create group**: to create a new group.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -332,11 +337,6 @@
|
||||
<target>**Most private**: do not use SimpleX Chat notifications server, check messages periodically in the background (depends on how often you use the app).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Paste received link** or open it in the browser and tap **Open in mobile app**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</target>
|
||||
@@ -347,11 +347,6 @@
|
||||
<target>**Recommended**: device token and notifications are sent to SimpleX Chat notification server, but not the message content, size or who it is from.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Scan QR code**: to connect to your contact in person or via video call.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Warning**: Instant push notifications require passphrase saved in Keychain.</target>
|
||||
@@ -453,11 +448,6 @@
|
||||
<target>1 week</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>1-time link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 minutes</target>
|
||||
@@ -573,6 +563,11 @@
|
||||
<target>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<target>Add contact</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Add preset servers</target>
|
||||
@@ -978,6 +973,11 @@
|
||||
<target>Calls</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<target>Camera not available</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Can't delete user profile!</target>
|
||||
@@ -1094,6 +1094,11 @@
|
||||
<target>Chat is stopped</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<target>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Chat preferences</target>
|
||||
@@ -1238,11 +1243,6 @@ This is your own one-time link!</target>
|
||||
<target>Connect via link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Connect via link / QR code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Connect via one-time link</target>
|
||||
@@ -1418,11 +1418,6 @@ This is your own one-time link!</target>
|
||||
<target>Create new profile in [desktop app](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Create one-time invitation link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Create profile</target>
|
||||
@@ -1448,6 +1443,11 @@ This is your own one-time link!</target>
|
||||
<target>Created on %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<target>Creating link…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Current Passcode</target>
|
||||
@@ -1998,6 +1998,11 @@ This cannot be undone!</target>
|
||||
<target>Enable automatic message deletion?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<target>Enable camera access</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Enable for all</target>
|
||||
@@ -2063,6 +2068,11 @@ This cannot be undone!</target>
|
||||
<target>Encrypted message or another event</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<target>Encrypted message: app is stopped</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Encrypted message: database error</target>
|
||||
@@ -2293,6 +2303,11 @@ This cannot be undone!</target>
|
||||
<target>Error loading %@ servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<target>Error opening chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Error receiving file</target>
|
||||
@@ -2333,6 +2348,11 @@ This cannot be undone!</target>
|
||||
<target>Error saving user password</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<target>Error scanning code: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Error sending email</target>
|
||||
@@ -2808,11 +2828,6 @@ This cannot be undone!</target>
|
||||
<target>If you can't meet in person, show QR code in a video call, or share the link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>If you enter this passcode when opening the app, all app data will be irreversibly removed!</target>
|
||||
@@ -2970,16 +2985,31 @@ This cannot be undone!</target>
|
||||
<target>Interface</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<target>Invalid QR code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Invalid connection link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<target>Invalid link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<target>Invalid name!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<target>Invalid response</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Invalid server address!</target>
|
||||
@@ -3098,11 +3128,21 @@ This is your link for group %@!</target>
|
||||
<target>Joining group</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<target>Keep</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<target>Keep the app open to use it from desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<target>Keep unused invitation?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Keep your connections</target>
|
||||
@@ -3188,6 +3228,11 @@ This is your link for group %@!</target>
|
||||
<target>Live messages</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Local</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Local name</target>
|
||||
@@ -3428,6 +3473,11 @@ This is your link for group %@!</target>
|
||||
<target>New Passcode</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<target>New chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>New contact request</target>
|
||||
@@ -3552,16 +3602,16 @@ This is your link for group %@!</target>
|
||||
- disable members ("observer" role)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<target>OK</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Off</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Off (Local)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ok</target>
|
||||
@@ -3702,9 +3752,19 @@ This is your link for group %@!</target>
|
||||
<target>Open-source protocol and code – anybody can run the servers.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Opening database…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<target>Opening app…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<target>Or scan QR code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<target>Or show this code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3747,11 +3807,6 @@ This is your link for group %@!</target>
|
||||
<target>Password to show</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Paste</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Paste desktop address</target>
|
||||
@@ -3762,16 +3817,11 @@ This is your link for group %@!</target>
|
||||
<target>Paste image</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Paste received link</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<target>Paste the link you received</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Paste the link you received to connect with your contact.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>People can connect to you only via the links you share.</target>
|
||||
@@ -3807,6 +3857,13 @@ This is your link for group %@!</target>
|
||||
<target>Please check yours and your contact preferences.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<target>Please contact developers.
|
||||
Error: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Please contact group admin.</target>
|
||||
@@ -4012,6 +4069,11 @@ This is your link for group %@!</target>
|
||||
<target>Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<target>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4222,6 +4284,11 @@ This is your link for group %@!</target>
|
||||
<target>Restore database error</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<target>Retry</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Reveal</target>
|
||||
@@ -4377,6 +4444,11 @@ This is your link for group %@!</target>
|
||||
<target>Search</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<target>Search or paste SimpleX link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Secure queue</target>
|
||||
@@ -4652,9 +4724,9 @@ This is your link for group %@!</target>
|
||||
<target>Share link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Share one-time invitation link</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<target>Share this 1-time invite link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4777,16 +4849,16 @@ This is your link for group %@!</target>
|
||||
<target>Somebody</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Start a new chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Start chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<target>Start chat?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Start migration</target>
|
||||
@@ -4912,6 +4984,16 @@ This is your link for group %@!</target>
|
||||
<target>Tap to join incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<target>Tap to paste link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<target>Tap to scan</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Tap to start a new chat</target>
|
||||
@@ -4974,6 +5056,11 @@ It can happen because of some bug or when the connection is compromised.</target
|
||||
<target>The attempt to change database passphrase was not completed.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<target>The code you scanned is not a SimpleX link QR code.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>The connection you accepted will be cancelled!</target>
|
||||
@@ -5039,6 +5126,11 @@ It can happen because of some bug or when the connection is compromised.</target
|
||||
<target>The servers for new connections of your current chat profile **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<target>The text you pasted is not a SimpleX link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Theme</target>
|
||||
@@ -5191,11 +5283,6 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
<target>Turn off</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Turn off notifications?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Turn on</target>
|
||||
@@ -5398,6 +5485,11 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Use new incognito profile</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<target>Use only local notifications?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Use server</target>
|
||||
@@ -5660,11 +5752,6 @@ Repeat join request?</target>
|
||||
<target>You can accept calls from lock screen, without device and app authentication.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>You can create it later</target>
|
||||
@@ -5685,6 +5772,11 @@ Repeat join request?</target>
|
||||
<target>You can hide or mute a user profile - swipe it to the right.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<source>You can make it visible to your SimpleX contacts via Settings.</source>
|
||||
<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>
|
||||
@@ -5725,6 +5817,11 @@ Repeat join request?</target>
|
||||
<target>You can use markdown to format messages:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<target>You can view invitation link again in connection details.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>You can't send messages!</target>
|
||||
@@ -5914,13 +6011,6 @@ You can cancel this connection and remove the contact (and try later with a new
|
||||
<target>Your contacts can allow full message deletion.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Your contacts will remain connected.</target>
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -303,14 +303,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Lisää uusi kontakti**: luo kertakäyttöinen QR-koodi tai linkki kontaktille.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Luo linkki / QR-koodi* kontaktille.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -323,11 +326,6 @@
|
||||
<target>**Yksityisin**: älä käytä SimpleX Chat -ilmoituspalvelinta, tarkista viestit ajoittain taustalla (riippuu siitä, kuinka usein käytät sovellusta).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Liitä vastaanotettu linkki** tai avaa se selaimessa ja napauta **Avaa mobiilisovelluksessa**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Huomaa**: et voi palauttaa tai muuttaa tunnuslausetta, jos kadotat sen.</target>
|
||||
@@ -338,11 +336,6 @@
|
||||
<target>**Suositus**: laitetunnus ja ilmoitukset lähetetään SimpleX Chat -ilmoituspalvelimelle, mutta ei viestin sisältöä, kokoa tai sitä, keneltä se on peräisin.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Skannaa QR-koodi**: muodosta yhteys kontaktiisi henkilökohtaisesti tai videopuhelun kautta.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Varoitus**: Välittömät push-ilmoitukset vaativat tunnuslauseen, joka on tallennettu Keychainiin.</target>
|
||||
@@ -437,11 +430,6 @@
|
||||
<target>1 viikko</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>Kertakäyttölinkki</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 minuuttia</target>
|
||||
@@ -557,6 +545,10 @@
|
||||
<target>Lisää osoite profiiliisi, jotta kontaktisi voivat jakaa sen muiden kanssa. Profiilipäivitys lähetetään kontakteillesi.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Lisää esiasetettuja palvelimia</target>
|
||||
@@ -951,6 +943,10 @@
|
||||
<target>Puhelut</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Käyttäjäprofiilia ei voi poistaa!</target>
|
||||
@@ -1067,6 +1063,10 @@
|
||||
<target>Chat on pysäytetty</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Chat-asetukset</target>
|
||||
@@ -1203,11 +1203,6 @@ This is your own one-time link!</source>
|
||||
<target>Yhdistä linkin kautta</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Yhdistä linkillä / QR-koodilla</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Yhdistä kertalinkillä</target>
|
||||
@@ -1375,11 +1370,6 @@ This is your own one-time link!</source>
|
||||
<target>Luo uusi profiili [työpöytäsovelluksessa](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Luo kertakutsulinkki</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1404,6 +1394,10 @@ This is your own one-time link!</source>
|
||||
<target>Luotu %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Nykyinen pääsykoodi</target>
|
||||
@@ -1945,6 +1939,10 @@ This cannot be undone!</source>
|
||||
<target>Ota automaattinen viestien poisto käyttöön?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Salli kaikille</target>
|
||||
@@ -2009,6 +2007,10 @@ This cannot be undone!</source>
|
||||
<target>Salattu viesti tai muu tapahtuma</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Salattu viesti: tietokantavirhe</target>
|
||||
@@ -2233,6 +2235,10 @@ This cannot be undone!</source>
|
||||
<target>Virhe %@-palvelimien lataamisessa</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Virhe tiedoston vastaanottamisessa</target>
|
||||
@@ -2273,6 +2279,10 @@ This cannot be undone!</source>
|
||||
<target>Virhe käyttäjän salasanan tallentamisessa</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Virhe sähköpostin lähettämisessä</target>
|
||||
@@ -2741,11 +2751,6 @@ This cannot be undone!</source>
|
||||
<target>Jos et voi tavata henkilökohtaisesti, näytä QR-koodi videopuhelussa tai jaa linkki.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Jos et voi tavata henkilökohtaisesti, voit **skannata QR-koodin videopuhelussa** tai kontaktisi voi jakaa kutsulinkin.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Jos syötät tämän pääsykoodin sovellusta avatessasi, kaikki sovelluksen tiedot poistetaan peruuttamattomasti!</target>
|
||||
@@ -2901,15 +2906,27 @@ This cannot be undone!</source>
|
||||
<target>Käyttöliittymä</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Virheellinen yhteyslinkki</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Virheellinen palvelinosoite!</target>
|
||||
@@ -3024,10 +3041,18 @@ This is your link for group %@!</source>
|
||||
<target>Liittyy ryhmään</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Pidä kontaktisi</target>
|
||||
@@ -3110,6 +3135,11 @@ This is your link for group %@!</source>
|
||||
<target>Live-viestit</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Paikallinen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Paikallinen nimi</target>
|
||||
@@ -3349,6 +3379,10 @@ This is your link for group %@!</source>
|
||||
<target>Uusi pääsykoodi</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Uusi kontaktipyyntö</target>
|
||||
@@ -3471,16 +3505,15 @@ This is your link for group %@!</source>
|
||||
- poista jäsenet käytöstä ("tarkkailija" rooli)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Pois</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Pois (Paikallinen)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ok</target>
|
||||
@@ -3619,9 +3652,16 @@ This is your link for group %@!</source>
|
||||
<target>Avoimen lähdekoodin protokolla ja koodi - kuka tahansa voi käyttää palvelimia.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Avataan tietokantaa…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3664,11 +3704,6 @@ This is your link for group %@!</source>
|
||||
<target>Salasana näytettäväksi</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Liitä</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3678,16 +3713,10 @@ This is your link for group %@!</source>
|
||||
<target>Liitä kuva</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Liitä vastaanotettu linkki</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Liitä saamasi linkki, jonka avulla voit muodostaa yhteyden kontaktiisi.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>Ihmiset voivat ottaa sinuun yhteyttä vain jakamiesi linkkien kautta.</target>
|
||||
@@ -3723,6 +3752,11 @@ This is your link for group %@!</source>
|
||||
<target>Tarkista omasi ja kontaktin asetukset.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Ota yhteyttä ryhmän ylläpitäjään.</target>
|
||||
@@ -3926,6 +3960,10 @@ This is your link for group %@!</source>
|
||||
<target>Lue lisää [Käyttöoppaasta](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Lue lisää [Käyttöoppaasta](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4134,6 +4172,10 @@ This is your link for group %@!</source>
|
||||
<target>Virhe tietokannan palauttamisessa</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Paljasta</target>
|
||||
@@ -4288,6 +4330,10 @@ This is your link for group %@!</source>
|
||||
<target>Haku</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Turvallinen jono</target>
|
||||
@@ -4561,9 +4607,8 @@ This is your link for group %@!</source>
|
||||
<target>Jaa linkki</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Jaa kertakutsulinkki</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4685,16 +4730,15 @@ This is your link for group %@!</source>
|
||||
<target>Joku</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Aloita uusi keskustelu</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Aloita keskustelu</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Aloita siirto</target>
|
||||
@@ -4819,6 +4863,14 @@ This is your link for group %@!</source>
|
||||
<target>Napauta liittyäksesi incognito-tilassa</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Aloita uusi keskustelu napauttamalla</target>
|
||||
@@ -4881,6 +4933,10 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.</t
|
||||
<target>Tietokannan tunnuslauseen muuttamista ei suoritettu loppuun.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>Hyväksymäsi yhteys peruuntuu!</target>
|
||||
@@ -4946,6 +5002,10 @@ Tämä voi johtua jostain virheestä tai siitä, että yhteys on vaarantunut.</t
|
||||
<target>Palvelimet nykyisen keskusteluprofiilisi uusille yhteyksille **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Teema</target>
|
||||
@@ -5093,11 +5153,6 @@ Sinua kehotetaan suorittamaan todennus loppuun, ennen kuin tämä ominaisuus ote
|
||||
<target>Sammuta</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Kytke ilmoitukset pois päältä?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Kytke päälle</target>
|
||||
@@ -5294,6 +5349,10 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
|
||||
<target>Käytä uutta incognito-profiilia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Käytä palvelinta</target>
|
||||
@@ -5543,11 +5602,6 @@ Repeat join request?</source>
|
||||
<target>Voit vastaanottaa puheluita lukitusnäytöltä ilman laitteen ja sovelluksen todennusta.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Voit myös muodostaa yhteyden klikkaamalla linkkiä. Jos se avautuu selaimessa, napsauta **Avaa mobiilisovelluksessa**-painiketta.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Voit luoda sen myöhemmin</target>
|
||||
@@ -5568,6 +5622,10 @@ Repeat join request?</source>
|
||||
<target>Voit piilottaa tai mykistää käyttäjäprofiilin pyyhkäisemällä sitä oikealle.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Voit nyt lähettää viestejä %@:lle</target>
|
||||
@@ -5608,6 +5666,10 @@ Repeat join request?</source>
|
||||
<target>Voit käyttää markdownia viestien muotoiluun:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Et voi lähettää viestejä!</target>
|
||||
@@ -5792,13 +5854,6 @@ Voit peruuttaa tämän yhteyden ja poistaa kontaktin (ja yrittää myöhemmin uu
|
||||
<target>Kontaktisi voivat sallia viestien täydellisen poistamisen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Kontaktisi SimpleX:ssä näkevät sen.
|
||||
Voit muuttaa sitä Asetuksista.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Kontaktisi pysyvät yhdistettyinä.</target>
|
||||
|
||||
@@ -312,14 +312,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Ajouter un nouveau contact** : pour créer un lien ou code QR unique pour votre contact.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Créer un lien / code QR** que votre contact pourra utiliser.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -332,11 +335,6 @@
|
||||
<target>**Confidentiel** : ne pas utiliser le serveur de notifications SimpleX, vérification de nouveaux messages periodiquement en arrière plan (dépend de l'utilisation de l'app).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Collez le lien reçu** ou ouvrez-le dans votre navigateur et appuyez sur **Open in mobile app**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Veuillez noter** : vous NE pourrez PAS récupérer ou modifier votre phrase secrète si vous la perdez.</target>
|
||||
@@ -347,11 +345,6 @@
|
||||
<target>**Recommandé** : le token de l'appareil et les notifications sont envoyés au serveur de notifications SimpleX, mais pas le contenu du message, sa taille ou son auteur.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Scanner le code QR** : pour vous connecter à votre contact en personne ou par appel vidéo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Avertissement** : les notifications push instantanées nécessitent une phrase secrète enregistrée dans la keychain.</target>
|
||||
@@ -453,11 +446,6 @@
|
||||
<target>1 semaine</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>Lien à usage unique</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 minutes</target>
|
||||
@@ -573,6 +561,10 @@
|
||||
<target>Ajoutez une adresse à votre profil, afin que vos contacts puissent la partager avec d'autres personnes. La mise à jour du profil sera envoyée à vos contacts.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Ajouter des serveurs prédéfinis</target>
|
||||
@@ -978,6 +970,10 @@
|
||||
<target>Appels</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Impossible de supprimer le profil d'utilisateur !</target>
|
||||
@@ -1094,6 +1090,10 @@
|
||||
<target>Le chat est arrêté</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Préférences de chat</target>
|
||||
@@ -1238,11 +1238,6 @@ Il s'agit de votre propre lien unique !</target>
|
||||
<target>Se connecter via un lien</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Se connecter via un lien / code QR</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Se connecter via un lien unique</target>
|
||||
@@ -1418,11 +1413,6 @@ Il s'agit de votre propre lien unique !</target>
|
||||
<target>Créer un nouveau profil sur [l'application de bureau](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Créer un lien d'invitation unique</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Créer le profil</target>
|
||||
@@ -1448,6 +1438,10 @@ Il s'agit de votre propre lien unique !</target>
|
||||
<target>Créé le %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Code d'accès actuel</target>
|
||||
@@ -1998,6 +1992,10 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Activer la suppression automatique des messages ?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Activer pour tous</target>
|
||||
@@ -2063,6 +2061,10 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Message chiffrée ou autre événement</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Message chiffrée : erreur de base de données</target>
|
||||
@@ -2293,6 +2295,10 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Erreur lors du chargement des serveurs %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Erreur lors de la réception du fichier</target>
|
||||
@@ -2333,6 +2339,10 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Erreur d'enregistrement du mot de passe de l'utilisateur</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Erreur lors de l'envoi de l'e-mail</target>
|
||||
@@ -2808,11 +2818,6 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Si vous ne pouvez pas vous rencontrer en personne, montrez le code QR lors d'un appel vidéo ou partagez le lien.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Si vous ne pouvez pas voir la personne, vous pouvez **scanner le code QR dans un appel vidéo**, ou votre contact peut vous partager un lien d'invitation.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Si vous saisissez ce code à l'ouverture de l'application, toutes les données de l'application seront irréversiblement supprimées !</target>
|
||||
@@ -2970,16 +2975,28 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Interface</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Lien de connection invalide</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<target>Nom invalide !</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Adresse de serveur invalide !</target>
|
||||
@@ -3098,11 +3115,19 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Entrain de rejoindre le groupe</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<target>Garder l'application ouverte pour l'utiliser depuis le bureau</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Conserver vos connexions</target>
|
||||
@@ -3188,6 +3213,11 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Messages dynamiques</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Local</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Nom local</target>
|
||||
@@ -3428,6 +3458,10 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Nouveau code d'accès</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Nouvelle demande de contact</target>
|
||||
@@ -3552,16 +3586,15 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
- désactiver des membres (rôle "observateur")</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Off</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Off (Local)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ok</target>
|
||||
@@ -3702,9 +3735,16 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Protocole et code open-source – n'importe qui peut heberger un serveur.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Ouverture de la base de données…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3747,11 +3787,6 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Mot de passe à entrer</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Coller</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Coller l'adresse du bureau</target>
|
||||
@@ -3762,16 +3797,10 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Coller l'image</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Coller le lien reçu</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Collez le lien que vous avez reçu dans le cadre ci-dessous pour vous connecter avec votre contact.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>On ne peut se connecter à vous qu’avec les liens que vous partagez.</target>
|
||||
@@ -3807,6 +3836,11 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Veuillez vérifier vos préférences ainsi que celles de votre contact.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Veuillez contacter l'administrateur du groupe.</target>
|
||||
@@ -4012,6 +4046,10 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Pour en savoir plus, consultez le [Guide de l'utilisateur](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Pour en savoir plus, consultez le [Guide de l'utilisateur](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4222,6 +4260,10 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Erreur de restauration de la base de données</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Révéler</target>
|
||||
@@ -4377,6 +4419,10 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Recherche</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>File d'attente sécurisée</target>
|
||||
@@ -4652,9 +4698,8 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Partager le lien</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Partager un lien d'invitation unique</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4777,16 +4822,15 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Quelqu'un</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Commencer une nouvelle conversation</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Démarrer le chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Démarrer la migration</target>
|
||||
@@ -4912,6 +4956,14 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Appuyez pour rejoindre incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Appuyez ici pour démarrer une nouvelle discussion</target>
|
||||
@@ -4974,6 +5026,10 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise.
|
||||
<target>La tentative de modification de la phrase secrète de la base de données n'a pas abouti.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>La connexion que vous avez acceptée sera annulée !</target>
|
||||
@@ -5039,6 +5095,10 @@ Cela peut se produire en raison d'un bug ou lorsque la connexion est compromise.
|
||||
<target>Les serveurs pour les nouvelles connexions de votre profil de chat actuel **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Thème</target>
|
||||
@@ -5191,11 +5251,6 @@ Vous serez invité à confirmer l'authentification avant que cette fonction ne s
|
||||
<target>Désactiver</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Désactiver les notifications ?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Activer</target>
|
||||
@@ -5398,6 +5453,10 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
|
||||
<target>Utiliser un nouveau profil incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Utiliser ce serveur</target>
|
||||
@@ -5660,11 +5719,6 @@ Répéter la demande d'adhésion ?</target>
|
||||
<target>Vous pouvez accepter des appels à partir de l'écran de verrouillage, sans authentification de l'appareil ou de l'application.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Vous pouvez également vous connecter en cliquant sur le lien. S'il s'ouvre dans le navigateur, cliquez sur le bouton **Open in mobile app**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Vous pouvez la créer plus tard</target>
|
||||
@@ -5685,6 +5739,10 @@ Répéter la demande d'adhésion ?</target>
|
||||
<target>Vous pouvez masquer ou mettre en sourdine un profil d'utilisateur - faites-le glisser vers la droite.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Vous pouvez maintenant envoyer des messages à %@</target>
|
||||
@@ -5725,6 +5783,10 @@ Répéter la demande d'adhésion ?</target>
|
||||
<target>Vous pouvez utiliser le format markdown pour mettre en forme les messages :</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Vous ne pouvez pas envoyer de messages !</target>
|
||||
@@ -5914,13 +5976,6 @@ Vous pouvez annuler la connexion et supprimer le contact (et réessayer plus tar
|
||||
<target>Vos contacts peuvent autoriser la suppression complète des messages.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Vos contacts dans SimpleX la verront.
|
||||
Vous pouvez modifier ce choix dans les Paramètres.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Vos contacts resteront connectés.</target>
|
||||
@@ -6537,12 +6592,12 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="offered %@" xml:space="preserve">
|
||||
<source>offered %@</source>
|
||||
<target>offert %@</target>
|
||||
<target>propose %@</target>
|
||||
<note>feature offered item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="offered %@: %@" xml:space="preserve">
|
||||
<source>offered %1$@: %2$@</source>
|
||||
<target>offert %1$@ : %2$@</target>
|
||||
<target>propose %1$@ : %2$@</target>
|
||||
<note>feature offered item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="on" xml:space="preserve">
|
||||
|
||||
@@ -94,7 +94,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve">
|
||||
<source>%@ and %@ connected</source>
|
||||
<target>%@ e %@ sono connessi/e</target>
|
||||
<target>%@ e %@ si sono connessi/e</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ at %@:" xml:space="preserve">
|
||||
@@ -139,7 +139,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
|
||||
<source>%@, %@ and %lld other members connected</source>
|
||||
<target>%@, %@ e altri %lld membri sono connessi</target>
|
||||
<target>%@, %@ e altri %lld membri si sono connessi</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@:" xml:space="preserve">
|
||||
@@ -312,14 +312,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Aggiungi un contatto**: per creare il tuo codice QR o link una tantum per il tuo contatto.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Crea link / codice QR** da usare per il tuo contatto.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -332,11 +335,6 @@
|
||||
<target>**Il più privato**: non usare il server di notifica di SimpleX Chat, controlla i messaggi periodicamente in secondo piano (dipende da quanto spesso usi l'app).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Incolla il link ricevuto** o aprilo nel browser e tocca **Apri in app mobile**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Nota bene**: NON potrai recuperare o cambiare la password se la perdi.</target>
|
||||
@@ -347,11 +345,6 @@
|
||||
<target>**Consigliato**: vengono inviati il token del dispositivo e le notifiche al server di notifica di SimpleX Chat, ma non il contenuto del messaggio,la sua dimensione o il suo mittente.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Scansiona codice QR**: per connetterti al contatto di persona o via videochiamata.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Attenzione**: le notifiche push istantanee richiedono una password salvata nel portachiavi.</target>
|
||||
@@ -453,11 +446,6 @@
|
||||
<target>1 settimana</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>Link una tantum</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 minuti</target>
|
||||
@@ -573,6 +561,10 @@
|
||||
<target>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.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Aggiungi server preimpostati</target>
|
||||
@@ -978,6 +970,10 @@
|
||||
<target>Chiamate</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Impossibile eliminare il profilo utente!</target>
|
||||
@@ -1094,6 +1090,10 @@
|
||||
<target>Chat fermata</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Preferenze della chat</target>
|
||||
@@ -1181,7 +1181,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm new passphrase…" xml:space="preserve">
|
||||
<source>Confirm new passphrase…</source>
|
||||
<target>Conferma password nuova…</target>
|
||||
<target>Conferma nuova password…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm password" xml:space="preserve">
|
||||
@@ -1196,6 +1196,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect automatically" xml:space="preserve">
|
||||
<source>Connect automatically</source>
|
||||
<target>Connetti automaticamente</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect incognito" xml:space="preserve">
|
||||
@@ -1237,11 +1238,6 @@ Questo è il tuo link una tantum!</target>
|
||||
<target>Connetti via link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Connetti via link / codice QR</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Connetti via link una tantum</target>
|
||||
@@ -1417,11 +1413,6 @@ Questo è il tuo link una tantum!</target>
|
||||
<target>Crea un nuovo profilo nell'[app desktop](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Crea link di invito una tantum</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Crea profilo</target>
|
||||
@@ -1447,6 +1438,10 @@ Questo è il tuo link una tantum!</target>
|
||||
<target>Creato il %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Codice di accesso attuale</target>
|
||||
@@ -1914,6 +1909,7 @@ Non è reversibile!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Discover via local network" xml:space="preserve">
|
||||
<source>Discover via local network</source>
|
||||
<target>Individua via rete locale</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Do NOT use SimpleX for emergency calls." xml:space="preserve">
|
||||
@@ -1996,6 +1992,10 @@ Non è reversibile!</target>
|
||||
<target>Attivare l'eliminazione automatica dei messaggi?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Attiva per tutti</target>
|
||||
@@ -2061,6 +2061,10 @@ Non è reversibile!</target>
|
||||
<target>Messaggio crittografato o altro evento</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Messaggio crittografato: errore del database</target>
|
||||
@@ -2291,6 +2295,10 @@ Non è reversibile!</target>
|
||||
<target>Errore nel caricamento dei server %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Errore nella ricezione del file</target>
|
||||
@@ -2331,6 +2339,10 @@ Non è reversibile!</target>
|
||||
<target>Errore nel salvataggio della password utente</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Errore nell'invio dell'email</target>
|
||||
@@ -2558,6 +2570,7 @@ Non è reversibile!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Desktop trovato</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="French interface" xml:space="preserve">
|
||||
@@ -2805,11 +2818,6 @@ Non è reversibile!</target>
|
||||
<target>Se non potete incontrarvi di persona, mostra il codice QR in una videochiamata o condividi il link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Se non potete incontrarvi di persona, puoi **scansionare il codice QR durante la videochiamata** oppure il tuo contatto può condividere un link di invito.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Se inserisci questo codice all'apertura dell'app, tutti i dati di essa verranno rimossi in modo irreversibile!</target>
|
||||
@@ -2967,16 +2975,28 @@ Non è reversibile!</target>
|
||||
<target>Interfaccia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Link di connessione non valido</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<target>Nome non valido!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Indirizzo del server non valido!</target>
|
||||
@@ -3095,11 +3115,19 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Ingresso nel gruppo</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<target>Tieni aperta l'app per usarla dal desktop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Mantieni le tue connessioni</target>
|
||||
@@ -3185,6 +3213,11 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Messaggi in diretta</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Locale</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Nome locale</target>
|
||||
@@ -3425,6 +3458,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Nuovo codice di accesso</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Nuova richiesta di contatto</target>
|
||||
@@ -3527,6 +3564,7 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Not compatible!" xml:space="preserve">
|
||||
<source>Not compatible!</source>
|
||||
<target>Non compatibile!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notifications" xml:space="preserve">
|
||||
@@ -3548,16 +3586,15 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
- disattivare i membri (ruolo "osservatore")</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Off</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Off (Locale)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ok</target>
|
||||
@@ -3698,9 +3735,16 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Protocollo e codice open source: chiunque può gestire i server.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Apertura del database…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3743,11 +3787,6 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Password per mostrare</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Incolla</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Incolla l'indirizzo desktop</target>
|
||||
@@ -3758,16 +3797,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Incolla immagine</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Incolla il link ricevuto</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Incolla il link che hai ricevuto nella casella sottostante per connetterti con il tuo contatto.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>Le persone possono connettersi a te solo tramite i link che condividi.</target>
|
||||
@@ -3803,6 +3836,11 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Controlla le preferenze tue e del tuo contatto.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Contatta l'amministratore del gruppo.</target>
|
||||
@@ -4008,6 +4046,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Maggiori informazioni nella [Guida per l'utente](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Maggiori informazioni nella [Guida per l'utente](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4218,6 +4260,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Errore di ripristino del database</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Rivela</target>
|
||||
@@ -4373,6 +4419,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Cerca</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Coda sicura</target>
|
||||
@@ -4648,9 +4698,8 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Condividi link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Condividi link di invito una tantum</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4720,7 +4769,7 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX contact address" xml:space="preserve">
|
||||
<source>SimpleX contact address</source>
|
||||
<target>Indirizzo del contatto SimpleX</target>
|
||||
<target>Indirizzo di contatto SimpleX</target>
|
||||
<note>simplex link type</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX encrypted message or connection event" xml:space="preserve">
|
||||
@@ -4773,16 +4822,15 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Qualcuno</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Inizia una nuova chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Avvia chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Avvia la migrazione</target>
|
||||
@@ -4908,6 +4956,14 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Toccare per entrare in incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Tocca per iniziare una chat</target>
|
||||
@@ -4970,6 +5026,10 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.</ta
|
||||
<target>Il tentativo di cambiare la password del database non è stato completato.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>La connessione che hai accettato verrà annullata!</target>
|
||||
@@ -5035,6 +5095,10 @@ Può accadere a causa di qualche bug o quando la connessione è compromessa.</ta
|
||||
<target>I server per le nuove connessioni del profilo di chat attuale **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Tema</target>
|
||||
@@ -5187,11 +5251,6 @@ Ti verrà chiesto di completare l'autenticazione prima di attivare questa funzio
|
||||
<target>Spegni</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Spegnere le notifiche?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Attiva</target>
|
||||
@@ -5394,6 +5453,10 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
|
||||
<target>Usa nuovo profilo in incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Usa il server</target>
|
||||
@@ -5501,6 +5564,7 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
|
||||
</trans-unit>
|
||||
<trans-unit id="Waiting for desktop..." xml:space="preserve">
|
||||
<source>Waiting for desktop...</source>
|
||||
<target>In attesa del desktop...</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Waiting for file" xml:space="preserve">
|
||||
@@ -5655,11 +5719,6 @@ Ripetere la richiesta di ingresso?</target>
|
||||
<target>Puoi accettare chiamate dalla schermata di blocco, senza l'autenticazione del dispositivo e dell'app.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Puoi anche connetterti cliccando il link. Se si apre nel browser, clicca il pulsante **Apri nell'app mobile**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Puoi crearlo più tardi</target>
|
||||
@@ -5680,6 +5739,10 @@ Ripetere la richiesta di ingresso?</target>
|
||||
<target>Puoi nascondere o silenziare un profilo utente - scorrilo verso destra.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Ora puoi inviare messaggi a %@</target>
|
||||
@@ -5720,6 +5783,10 @@ Ripetere la richiesta di ingresso?</target>
|
||||
<target>Puoi usare il markdown per formattare i messaggi:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Non puoi inviare messaggi!</target>
|
||||
@@ -5909,13 +5976,6 @@ Puoi annullare questa connessione e rimuovere il contatto (e riprovare più tard
|
||||
<target>I tuoi contatti possono consentire l'eliminazione completa dei messaggi.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>I tuoi contatti in SimpleX lo vedranno.
|
||||
Puoi modificarlo nelle impostazioni.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>I tuoi contatti resteranno connessi.</target>
|
||||
@@ -5995,7 +6055,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" xml:space="preserve">
|
||||
<source>[Star on GitHub](https://github.com/simplex-chat/simplex-chat)</source>
|
||||
<target>[Stella su GitHub](https://github.com/simplex-chat/simplex-chat)</target>
|
||||
<target>[Dai una stella su GitHub](https://github.com/simplex-chat/simplex-chat)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="_italic_" xml:space="preserve">
|
||||
@@ -6050,6 +6110,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="author" xml:space="preserve">
|
||||
<source>author</source>
|
||||
<target>autore</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="bad message ID" xml:space="preserve">
|
||||
@@ -6631,7 +6692,7 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="updated group profile" xml:space="preserve">
|
||||
<source>updated group profile</source>
|
||||
<target>profilo del gruppo aggiornato</target>
|
||||
<target>ha aggiornato il profilo del gruppo</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@" xml:space="preserve">
|
||||
|
||||
@@ -103,6 +103,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ connected" xml:space="preserve">
|
||||
<source>%@ connected</source>
|
||||
<target>%@ 接続中</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve">
|
||||
@@ -214,10 +215,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld messages marked deleted" xml:space="preserve">
|
||||
<source>%lld messages marked deleted</source>
|
||||
<target>%lld 件のメッセージが削除されました</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld messages moderated by %@" xml:space="preserve">
|
||||
<source>%lld messages moderated by %@</source>
|
||||
<target>%@ により%lld 件のメッセージが検閲されました</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld minutes" xml:space="preserve">
|
||||
@@ -303,14 +306,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**新しい連絡先を追加**: 連絡先のワンタイム QR コードまたはリンクを作成します。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>連絡先が使用する **リンク/QR コードを作成します**。</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -323,11 +329,6 @@
|
||||
<target>**最もプライベート**: SimpleX Chat 通知サーバーを使用せず、バックグラウンドで定期的にメッセージをチェックします (アプリの使用頻度によって異なります)。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**受信したリンク**を貼り付けるか、ブラウザーで開いて [**モバイル アプリで開く**] をタップします。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**注意**: パスフレーズを紛失すると、パスフレーズを復元または変更できなくなります。</target>
|
||||
@@ -338,11 +339,6 @@
|
||||
<target>**推奨**: デバイス トークンと通知は SimpleX Chat 通知サーバーに送信されますが、メッセージの内容、サイズ、送信者は送信されません。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**QR コードをスキャン**: 直接またはビデオ通話で連絡先に接続します。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**警告**: 即時の プッシュ通知には、キーチェーンに保存されたパスフレーズが必要です。</target>
|
||||
@@ -437,11 +433,6 @@
|
||||
<target>1週間</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>使い捨てのリンク</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5分</target>
|
||||
@@ -557,6 +548,10 @@
|
||||
<target>プロフィールにアドレスを追加し、連絡先があなたのアドレスを他の人と共有できるようにします。プロフィールの更新は連絡先に送信されます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>既存サーバを追加</target>
|
||||
@@ -953,6 +948,10 @@
|
||||
<target>通話</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>ユーザープロフィールが削除できません!</target>
|
||||
@@ -1069,6 +1068,10 @@
|
||||
<target>チャットが停止してます</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>チャット設定</target>
|
||||
@@ -1205,11 +1208,6 @@ This is your own one-time link!</source>
|
||||
<target>リンク経由で接続</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>リンク・QRコード経由で接続</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>使い捨てリンク経由で接続しますか?</target>
|
||||
@@ -1377,11 +1375,6 @@ This is your own one-time link!</source>
|
||||
<target>[デスクトップアプリ](https://simplex.chat/downloads/)で新しいプロファイルを作成します。 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>使い捨ての招待リンクを生成する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1406,6 +1399,10 @@ This is your own one-time link!</source>
|
||||
<target>%@ によって作成されました</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>現在のパスコード</target>
|
||||
@@ -1947,6 +1944,10 @@ This cannot be undone!</source>
|
||||
<target>自動メッセージ削除を有効にしますか?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>すべて有効</target>
|
||||
@@ -2012,6 +2013,10 @@ This cannot be undone!</source>
|
||||
<target>暗号化されたメッセージまたは別のイベント</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>暗号化されたメッセージ : データベースエラー</target>
|
||||
@@ -2236,6 +2241,10 @@ This cannot be undone!</source>
|
||||
<target>%@ サーバーのロード中にエラーが発生</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>ファイル受信にエラー発生</target>
|
||||
@@ -2276,6 +2285,10 @@ This cannot be undone!</source>
|
||||
<target>ユーザーパスワード保存エラー</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>メールの送信にエラー発生</target>
|
||||
@@ -2744,11 +2757,6 @@ This cannot be undone!</source>
|
||||
<target>直接会えない場合は、ビデオ通話で QR コードを表示するか、リンクを共有してください。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>直接会えない場合は、**ビデオ通話で QR コードを表示する**か、リンクを共有してください。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>アプリを開くときにこのパスコードを入力すると、アプリのすべてのデータが元に戻せないように削除されます!</target>
|
||||
@@ -2904,15 +2912,27 @@ This cannot be undone!</source>
|
||||
<target>インターフェース</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>無効な接続リンク</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>無効なサーバアドレス!</target>
|
||||
@@ -3027,10 +3047,18 @@ This is your link for group %@!</source>
|
||||
<target>グループに参加</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>接続を維持</target>
|
||||
@@ -3113,6 +3141,11 @@ This is your link for group %@!</source>
|
||||
<target>ライブメッセージ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>自分のみ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>ローカルネーム</target>
|
||||
@@ -3351,6 +3384,10 @@ This is your link for group %@!</source>
|
||||
<target>新しいパスコード</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>新しい繋がりのリクエスト</target>
|
||||
@@ -3474,16 +3511,15 @@ This is your link for group %@!</source>
|
||||
- メンバーを無効にする (メッセージの送信不可)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>オフ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>オフ(自分のみ)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>OK</target>
|
||||
@@ -3623,9 +3659,16 @@ This is your link for group %@!</source>
|
||||
<target>プロトコル技術とコードはオープンソースで、どなたでもご自分のサーバを運用できます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>データベースを開いています…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3668,11 +3711,6 @@ This is your link for group %@!</source>
|
||||
<target>パスワードを表示する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>貼り付け</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3682,16 +3720,10 @@ This is your link for group %@!</source>
|
||||
<target>画像の貼り付け</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>頂いたリンクを貼り付ける</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>連絡相手から頂いたリンクを以下の入力欄に貼り付けて繋がります。</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>あなたと繋がることができるのは、あなたからリンクを頂いた方のみです。</target>
|
||||
@@ -3727,6 +3759,11 @@ This is your link for group %@!</source>
|
||||
<target>あなたと連絡先の設定を確認してください。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>グループの管理者に連絡してください。</target>
|
||||
@@ -3930,6 +3967,10 @@ This is your link for group %@!</source>
|
||||
<target>詳しくは[ユーザーガイド](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)をご覧ください。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>詳しくは[ユーザーガイド](https://simplex.chat/docs/guide/readme.html#connect-to-friends)をご覧ください。</target>
|
||||
@@ -4137,6 +4178,10 @@ This is your link for group %@!</source>
|
||||
<target>データベース復元エラー</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>開示する</target>
|
||||
@@ -4291,6 +4336,10 @@ This is your link for group %@!</source>
|
||||
<target>検索</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>待ち行列セキュリティ確認</target>
|
||||
@@ -4557,9 +4606,8 @@ This is your link for group %@!</source>
|
||||
<target>リンクを送る</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>使い捨ての招待リンクを共有</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4682,16 +4730,15 @@ This is your link for group %@!</source>
|
||||
<target>誰か</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>新しいチャットを開始する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>チャットを開始する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>移行の開始</target>
|
||||
@@ -4816,6 +4863,14 @@ This is your link for group %@!</source>
|
||||
<target>タップしてシークレットモードで参加</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>タップして新しいチャットを始める</target>
|
||||
@@ -4878,6 +4933,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>データベースのパスフレーズ変更が完了してません。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>承認済の接続がキャンセルされます!</target>
|
||||
@@ -4943,6 +5002,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>現在のチャットプロフィールの新しい接続のサーバ **%@**。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>テーマ</target>
|
||||
@@ -5089,11 +5152,6 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
<target>オフにする</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>通知をオフにしますか?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>オンにする</target>
|
||||
@@ -5290,6 +5348,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>新しいシークレットプロファイルを使用する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>サーバを使う</target>
|
||||
@@ -5539,11 +5601,6 @@ Repeat join request?</source>
|
||||
<target>デバイスやアプリの認証を行わずに、ロック画面から通話を受けることができます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>リンクをクリックすることでも接続できます。ブラウザで開いた場合は、**モバイルアプリで開く**ボタンをクリックしてください。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>後からでも作成できます</target>
|
||||
@@ -5564,6 +5621,10 @@ Repeat join request?</source>
|
||||
<target>ユーザープロファイルを右にスワイプすると、非表示またはミュートにすることができます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>%@ にメッセージを送信できるようになりました</target>
|
||||
@@ -5604,6 +5665,10 @@ Repeat join request?</source>
|
||||
<target>メッセージの書式にmarkdownを使用することができます:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>メッセージを送信できませんでした!</target>
|
||||
@@ -5788,13 +5853,6 @@ You can cancel this connection and remove the contact (and try later with a new
|
||||
<target>連絡先がメッセージの完全削除を許可できます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>SimpleX の連絡先に表示されます。
|
||||
設定で変更できます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>連絡先は接続されたままになります。</target>
|
||||
|
||||
@@ -312,14 +312,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Nieuw contact toevoegen**: om uw eenmalige QR-code of link voor uw contact te maken.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Maak een link / QR-code aan** die uw contact kan gebruiken.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -332,11 +335,6 @@
|
||||
<target>**Meest privé**: gebruik geen SimpleX Chat-notificatie server, controleer berichten regelmatig op de achtergrond (afhankelijk van hoe vaak u de app gebruikt).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Plak de ontvangen link** of open deze in de browser en tik op **Openen in mobiele app**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Let op**: u kunt het wachtwoord NIET herstellen of wijzigen als u het kwijtraakt.</target>
|
||||
@@ -347,11 +345,6 @@
|
||||
<target>**Aanbevolen**: apparaattoken en meldingen worden naar de SimpleX Chat-meldingsserver gestuurd, maar niet de berichtinhoud, -grootte of van wie het afkomstig is.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Scan QR-code**: om persoonlijk of via een video gesprek verbinding te maken met uw contact.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Waarschuwing**: voor directe push meldingen is een wachtwoord vereist dat is opgeslagen in de Keychain.</target>
|
||||
@@ -453,11 +446,6 @@
|
||||
<target>1 week</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>Eenmalige link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 minuten</target>
|
||||
@@ -573,6 +561,10 @@
|
||||
<target>Voeg een adres toe aan uw profiel, zodat uw contacten het met andere mensen kunnen delen. Profiel update wordt naar uw contacten verzonden.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Vooraf ingestelde servers toevoegen</target>
|
||||
@@ -770,7 +762,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Always use relay" xml:space="preserve">
|
||||
<source>Always use relay</source>
|
||||
<target>Verbinden via relais</target>
|
||||
<target>Altijd relay gebruiken</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="An empty chat profile with the provided name is created, and the app opens as usual." xml:space="preserve">
|
||||
@@ -978,6 +970,10 @@
|
||||
<target>Oproepen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Kan gebruikers profiel niet verwijderen!</target>
|
||||
@@ -1094,6 +1090,10 @@
|
||||
<target>Chat is gestopt</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Gesprek voorkeuren</target>
|
||||
@@ -1238,11 +1238,6 @@ Dit is uw eigen eenmalige link!</target>
|
||||
<target>Maak verbinding via link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Maak verbinding via link / QR-code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Verbinden via een eenmalige link?</target>
|
||||
@@ -1418,11 +1413,6 @@ Dit is uw eigen eenmalige link!</target>
|
||||
<target>Maak een nieuw profiel aan in [desktop-app](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Maak een eenmalige uitnodiging link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Maak een profiel aan</target>
|
||||
@@ -1448,6 +1438,10 @@ Dit is uw eigen eenmalige link!</target>
|
||||
<target>Gemaakt op %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Huidige toegangscode</target>
|
||||
@@ -1618,7 +1612,7 @@ Dit is uw eigen eenmalige link!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete and notify contact" xml:space="preserve">
|
||||
<source>Delete and notify contact</source>
|
||||
<target>Contact verwijderen en op de hoogte stellen</target>
|
||||
<target>Verwijderen en contact op de hoogte stellen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete archive" xml:space="preserve">
|
||||
@@ -1998,6 +1992,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Automatisch verwijderen van berichten aanzetten?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Inschakelen voor iedereen</target>
|
||||
@@ -2063,6 +2061,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Versleuteld bericht of een andere gebeurtenis</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Versleuteld bericht: database fout</target>
|
||||
@@ -2293,6 +2295,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Fout bij het laden van %@ servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Fout bij ontvangen van bestand</target>
|
||||
@@ -2333,6 +2339,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Fout bij opslaan gebruikers wachtwoord</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Fout bij het verzenden van e-mail</target>
|
||||
@@ -2425,7 +2435,7 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Expand" xml:space="preserve">
|
||||
<source>Expand</source>
|
||||
<target>Uitbreiden</target>
|
||||
<target>Uitklappen</target>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Export database" xml:space="preserve">
|
||||
@@ -2808,11 +2818,6 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Als je elkaar niet persoonlijk kunt ontmoeten, laat dan de QR-code zien in een videogesprek of deel de link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Als u elkaar niet persoonlijk kunt ontmoeten, kunt u **de QR-code scannen in het video gesprek**, of uw contact kan een uitnodiging link delen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Als u deze toegangscode invoert bij het openen van de app, worden alle app-gegevens onomkeerbaar verwijderd!</target>
|
||||
@@ -2962,7 +2967,7 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Instantly" xml:space="preserve">
|
||||
<source>Instantly</source>
|
||||
<target>Meteen</target>
|
||||
<target>Direct</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Interface" xml:space="preserve">
|
||||
@@ -2970,16 +2975,28 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Interface</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Ongeldige verbinding link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<target>Ongeldige naam!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Ongeldig server adres!</target>
|
||||
@@ -3098,11 +3115,19 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Deel nemen aan groep</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<target>Houd de app geopend om deze vanaf de desktop te gebruiken</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Behoud uw verbindingen</target>
|
||||
@@ -3188,6 +3213,11 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Live berichten</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Lokaal</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Lokale naam</target>
|
||||
@@ -3428,6 +3458,10 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Nieuwe toegangscode</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Nieuw contactverzoek</target>
|
||||
@@ -3552,16 +3586,15 @@ Dit is jouw link voor groep %@!</target>
|
||||
- schakel leden uit ("waarnemer" rol)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Uit</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Uit (lokaal)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>OK</target>
|
||||
@@ -3702,9 +3735,16 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Open-source protocol en code. Iedereen kan de servers draaien.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Database openen…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3747,11 +3787,6 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Wachtwoord om weer te geven</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Plakken</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Desktopadres plakken</target>
|
||||
@@ -3762,16 +3797,10 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Afbeelding plakken</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Plak de ontvangen link</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Plak de link die je hebt ontvangen in het vak hieronder om verbinding te maken met je contact.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>Mensen kunnen alleen verbinding met u maken via de links die u deelt.</target>
|
||||
@@ -3807,6 +3836,11 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Controleer de uwe en uw contact voorkeuren.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Neem contact op met de groep beheerder.</target>
|
||||
@@ -4012,6 +4046,10 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/app-settings.html#uw-simplex-contactadres).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Lees meer in de [Gebruikershandleiding](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4222,6 +4260,10 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Database fout herstellen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Onthullen</target>
|
||||
@@ -4377,6 +4419,10 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Zoeken</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Veilige wachtrij</target>
|
||||
@@ -4652,9 +4698,8 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Deel link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Eenmalige uitnodiging link delen</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4777,16 +4822,15 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Iemand</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Begin een nieuw gesprek</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Begin gesprek</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Start migratie</target>
|
||||
@@ -4912,6 +4956,14 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Tik om incognito lid te worden</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Tik om een nieuw gesprek te starten</target>
|
||||
@@ -4974,6 +5026,10 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast.</target>
|
||||
<target>De poging om het wachtwoord van de database te wijzigen is niet voltooid.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>De door u geaccepteerde verbinding wordt geannuleerd!</target>
|
||||
@@ -5039,6 +5095,10 @@ Het kan gebeuren vanwege een bug of wanneer de verbinding is aangetast.</target>
|
||||
<target>De servers voor nieuwe verbindingen van uw huidige chat profiel **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Thema</target>
|
||||
@@ -5191,11 +5251,6 @@ U wordt gevraagd de authenticatie te voltooien voordat deze functie wordt ingesc
|
||||
<target>Uitschakelen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Schakel meldingen uit?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Zet aan</target>
|
||||
@@ -5398,6 +5453,10 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
|
||||
<target>Gebruik een nieuw incognitoprofiel</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Gebruik server</target>
|
||||
@@ -5660,11 +5719,6 @@ Deelnameverzoek herhalen?</target>
|
||||
<target>U kunt oproepen van het vergrendelingsscherm accepteren, zonder apparaat- en app-verificatie.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>U kunt ook verbinding maken door op de link te klikken. Als het in de browser wordt geopend, klikt u op de knop **Openen in mobiele app**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>U kan het later maken</target>
|
||||
@@ -5685,6 +5739,10 @@ Deelnameverzoek herhalen?</target>
|
||||
<target>U kunt een gebruikers profiel verbergen of dempen - veeg het naar rechts.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Je kunt nu berichten sturen naar %@</target>
|
||||
@@ -5725,6 +5783,10 @@ Deelnameverzoek herhalen?</target>
|
||||
<target>U kunt markdown gebruiken voor opmaak in berichten:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Je kunt geen berichten versturen!</target>
|
||||
@@ -5914,13 +5976,6 @@ U kunt deze verbinding verbreken en het contact verwijderen en later proberen me
|
||||
<target>Uw contacten kunnen volledige verwijdering van berichten toestaan.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Uw contacten in SimpleX kunnen het zien.
|
||||
U kunt dit wijzigen in Instellingen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Uw contacten blijven verbonden.</target>
|
||||
@@ -6667,7 +6722,7 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="via relay" xml:space="preserve">
|
||||
<source>via relay</source>
|
||||
<target>via relais</target>
|
||||
<target>via relay</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
|
||||
|
||||
@@ -312,14 +312,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Dodaj nowy kontakt**: aby stworzyć swój jednorazowy kod QR lub link dla kontaktu.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Utwórz link / kod QR**, aby Twój kontakt mógł z niego skorzystać.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -332,11 +335,6 @@
|
||||
<target>**Najbardziej prywatny**: nie korzystaj z serwera powiadomień SimpleX Chat, sprawdzaj wiadomości okresowo w tle (zależy jak często korzystasz z aplikacji).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Wklej otrzymany link** lub otwórz go w przeglądarce i dotknij **Otwórz w aplikacji mobilnej**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Uwaga**: NIE będziesz w stanie odzyskać lub zmienić hasła, jeśli je stracisz.</target>
|
||||
@@ -347,11 +345,6 @@
|
||||
<target>**Zalecane**: token urządzenia i powiadomienia są wysyłane do serwera powiadomień SimpleX Chat, ale nie treść wiadomości, rozmiar lub od kogo jest.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Skanuj kod QR**: aby połączyć się z kontaktem osobiście lub za pomocą połączenia wideo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Uwaga**: Natychmiastowe powiadomienia push wymagają hasła zapisanego w Keychain.</target>
|
||||
@@ -453,11 +446,6 @@
|
||||
<target>1 tydzień</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>1-razowy link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 minut</target>
|
||||
@@ -573,6 +561,10 @@
|
||||
<target>Dodaj adres do swojego profilu, aby Twoje kontakty mogły go udostępnić innym osobom. Aktualizacja profilu zostanie wysłana do Twoich kontaktów.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Dodaj gotowe serwery</target>
|
||||
@@ -978,6 +970,10 @@
|
||||
<target>Połączenia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Nie można usunąć profilu użytkownika!</target>
|
||||
@@ -1094,6 +1090,10 @@
|
||||
<target>Czat jest zatrzymany</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Preferencje czatu</target>
|
||||
@@ -1238,11 +1238,6 @@ To jest twój jednorazowy link!</target>
|
||||
<target>Połącz się przez link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Połącz się przez link / kod QR</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Połącz przez jednorazowy link</target>
|
||||
@@ -1418,11 +1413,6 @@ To jest twój jednorazowy link!</target>
|
||||
<target>Utwórz nowy profil w [aplikacji desktopowej](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Utwórz jednorazowy link do zaproszenia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Utwórz profil</target>
|
||||
@@ -1448,6 +1438,10 @@ To jest twój jednorazowy link!</target>
|
||||
<target>Utworzony w dniu %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Aktualny Pin</target>
|
||||
@@ -1998,6 +1992,10 @@ To nie może być cofnięte!</target>
|
||||
<target>Czy włączyć automatyczne usuwanie wiadomości?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Włącz dla wszystkich</target>
|
||||
@@ -2063,6 +2061,10 @@ To nie może być cofnięte!</target>
|
||||
<target>Zaszyfrowana wiadomość lub inne zdarzenie</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Zaszyfrowana wiadomość: błąd bazy danych</target>
|
||||
@@ -2293,6 +2295,10 @@ To nie może być cofnięte!</target>
|
||||
<target>Błąd ładowania %@ serwerów</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Błąd odbioru pliku</target>
|
||||
@@ -2333,6 +2339,10 @@ To nie może być cofnięte!</target>
|
||||
<target>Błąd zapisu hasła użytkownika</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Błąd wysyłania e-mail</target>
|
||||
@@ -2808,11 +2818,6 @@ To nie może być cofnięte!</target>
|
||||
<target>Jeśli nie możesz spotkać się osobiście, pokaż kod QR w rozmowie wideo lub udostępnij link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Jeśli nie możesz spotkać się osobiście, możesz **zeskanować kod QR w rozmowie wideo** lub Twój kontakt może udostępnić link z zaproszeniem.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Jeśli wprowadzisz ten pin podczas otwierania aplikacji, wszystkie dane aplikacji zostaną nieodwracalnie usunięte!</target>
|
||||
@@ -2970,16 +2975,28 @@ To nie może być cofnięte!</target>
|
||||
<target>Interfejs</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Nieprawidłowy link połączenia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<target>Nieprawidłowa nazwa!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Nieprawidłowy adres serwera!</target>
|
||||
@@ -3098,11 +3115,19 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Dołączanie do grupy</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<target>Zostaw aplikację otwartą i używaj ją z komputera</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Zachowaj swoje połączenia</target>
|
||||
@@ -3188,6 +3213,11 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Wiadomości na żywo</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Lokalnie</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Nazwa lokalna</target>
|
||||
@@ -3428,6 +3458,10 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Nowy Pin</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Nowa prośba o kontakt</target>
|
||||
@@ -3552,16 +3586,15 @@ To jest twój link do grupy %@!</target>
|
||||
- wyłączyć członków (rola "obserwatora")</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Wyłączony</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Wyłączony (Lokalnie)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ok</target>
|
||||
@@ -3702,9 +3735,16 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Otwarto źródłowy protokół i kod - każdy może uruchomić serwery.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Otwieranie bazy danych…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3747,11 +3787,6 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Hasło do wyświetlenia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Wklej</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Wklej adres komputera</target>
|
||||
@@ -3762,16 +3797,10 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Wklej obraz</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Wklej otrzymany link</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Wklej otrzymany link w pole poniżej, aby połączyć się z kontaktem.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>Ludzie mogą się z Tobą połączyć tylko poprzez linki, które udostępniasz.</target>
|
||||
@@ -3807,6 +3836,11 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Proszę sprawdzić preferencje Twoje i Twojego kontaktu.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Skontaktuj się z administratorem grupy.</target>
|
||||
@@ -4012,6 +4046,10 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Przeczytaj więcej w [Podręczniku Użytkownika](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Przeczytaj więcej w [Podręczniku Użytkownika](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4222,6 +4260,10 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Błąd przywracania bazy danych</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Ujawnij</target>
|
||||
@@ -4377,6 +4419,10 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Szukaj</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Bezpieczna kolejka</target>
|
||||
@@ -4652,9 +4698,8 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Udostępnij link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Jednorazowy link zaproszenia</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4777,16 +4822,15 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Ktoś</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Rozpocznij nowy czat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Rozpocznij czat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Rozpocznij migrację</target>
|
||||
@@ -4912,6 +4956,14 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Dotnij, aby dołączyć w trybie incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Dotknij, aby rozpocząć nowy czat</target>
|
||||
@@ -4974,6 +5026,10 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom
|
||||
<target>Próba zmiany hasła bazy danych nie została zakończona.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>Zaakceptowane przez Ciebie połączenie zostanie anulowane!</target>
|
||||
@@ -5039,6 +5095,10 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom
|
||||
<target>Serwery dla nowych połączeń bieżącego profilu czatu **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Motyw</target>
|
||||
@@ -5191,11 +5251,6 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania.</ta
|
||||
<target>Wyłącz</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Wyłączyć powiadomienia?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Włącz</target>
|
||||
@@ -5398,6 +5453,10 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
|
||||
<target>Użyj nowego profilu incognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Użyj serwera</target>
|
||||
@@ -5660,11 +5719,6 @@ Powtórzyć prośbę dołączenia?</target>
|
||||
<target>Możesz przyjmować połączenia z ekranu blokady, bez uwierzytelniania urządzenia i aplikacji.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Możesz też połączyć się klikając w link. Jeśli otworzy się on w przeglądarce, kliknij przycisk **Otwórz w aplikacji mobilnej**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Możesz go utworzyć później</target>
|
||||
@@ -5685,6 +5739,10 @@ Powtórzyć prośbę dołączenia?</target>
|
||||
<target>Możesz ukryć lub wyciszyć profil użytkownika - przesuń palcem w prawo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Możesz teraz wysyłać wiadomości do %@</target>
|
||||
@@ -5725,6 +5783,10 @@ Powtórzyć prośbę dołączenia?</target>
|
||||
<target>Możesz używać markdown do formatowania wiadomości:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Nie możesz wysyłać wiadomości!</target>
|
||||
@@ -5914,13 +5976,6 @@ Możesz anulować to połączenie i usunąć kontakt (i spróbować później z
|
||||
<target>Twoje kontakty mogą zezwolić na pełne usunięcie wiadomości.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Twoje kontakty w SimpleX będą to widzieć.
|
||||
Możesz to zmienić w Ustawieniach.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Twoje kontakty pozostaną połączone.</target>
|
||||
|
||||
@@ -312,14 +312,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Добавить новый контакт**: чтобы создать одноразовый QR код или ссылку для Вашего контакта.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Создать ссылку / QR код** для Вашего контакта.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -332,11 +335,6 @@
|
||||
<target>**Самый конфиденциальный**: не использовать сервер уведомлений SimpleX Chat, проверять сообщения периодически в фоновом режиме (зависит от того насколько часто Вы используете приложение).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Вставить полученную ссылку**, или откройте её в браузере и нажмите **Open in mobile app**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Внимание**: Вы не сможете восстановить или поменять пароль, если Вы его потеряете.</target>
|
||||
@@ -347,11 +345,6 @@
|
||||
<target>**Рекомендовано**: токен устройства и уведомления отправляются на сервер SimpleX Chat, но сервер не получает сами сообщения, их размер или от кого они.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Сканировать QR код**: соединиться с Вашим контактом при встрече или во время видеозвонка.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Внимание**: для работы мгновенных уведомлений пароль должен быть сохранен в Keychain.</target>
|
||||
@@ -453,11 +446,6 @@
|
||||
<target>1 неделю</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>Одноразовая ссылка</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 минут</target>
|
||||
@@ -573,6 +561,10 @@
|
||||
<target>Добавьте адрес в свой профиль, чтобы Ваши контакты могли поделиться им. Профиль будет отправлен Вашим контактам.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Добавить серверы по умолчанию</target>
|
||||
@@ -978,6 +970,10 @@
|
||||
<target>Звонки</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Нельзя удалить профиль пользователя!</target>
|
||||
@@ -1094,6 +1090,10 @@
|
||||
<target>Чат остановлен</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Предпочтения</target>
|
||||
@@ -1238,11 +1238,6 @@ This is your own one-time link!</source>
|
||||
<target>Соединиться через ссылку</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Соединиться через ссылку / QR код</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Соединиться через одноразовую ссылку</target>
|
||||
@@ -1418,11 +1413,6 @@ This is your own one-time link!</source>
|
||||
<target>Создайте новый профиль в [приложении для компьютера](https://simplex.chat/downloads/). 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Создать ссылку-приглашение</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Создать профиль</target>
|
||||
@@ -1448,6 +1438,10 @@ This is your own one-time link!</source>
|
||||
<target>Дата создания %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Текущий Код</target>
|
||||
@@ -1998,6 +1992,10 @@ This cannot be undone!</source>
|
||||
<target>Включить автоматическое удаление сообщений?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Включить для всех</target>
|
||||
@@ -2063,6 +2061,10 @@ This cannot be undone!</source>
|
||||
<target>Зашифрованное сообщение или событие чата</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Зашифрованное сообщение: ошибка базы данных</target>
|
||||
@@ -2293,6 +2295,10 @@ This cannot be undone!</source>
|
||||
<target>Ошибка загрузки %@ серверов</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Ошибка при получении файла</target>
|
||||
@@ -2333,6 +2339,10 @@ This cannot be undone!</source>
|
||||
<target>Ошибка при сохранении пароля пользователя</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Ошибка отправки email</target>
|
||||
@@ -2808,11 +2818,6 @@ This cannot be undone!</source>
|
||||
<target>Если Вы не можете встретиться лично, покажите QR-код во время видеозвонка или поделитесь ссылкой.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Если Вы не можете встретиться лично, Вы можете **сосканировать QR код во время видеозвонка**, или Ваш контакт может отправить Вам ссылку.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Если Вы введете этот код при открытии приложения, все данные приложения будут безвозвратно удалены!</target>
|
||||
@@ -2970,16 +2975,28 @@ This cannot be undone!</source>
|
||||
<target>Интерфейс</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Ошибка в ссылке контакта</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<target>Неверное имя!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Ошибка в адресе сервера!</target>
|
||||
@@ -3098,11 +3115,19 @@ This is your link for group %@!</source>
|
||||
<target>Вступление в группу</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<target>Оставьте приложение открытым, чтобы использовать его с компьютера</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Сохраните Ваши соединения</target>
|
||||
@@ -3188,6 +3213,11 @@ This is your link for group %@!</source>
|
||||
<target>"Живые" сообщения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Локальные</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Локальное имя</target>
|
||||
@@ -3428,6 +3458,10 @@ This is your link for group %@!</source>
|
||||
<target>Новый Код</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Новый запрос на соединение</target>
|
||||
@@ -3552,16 +3586,15 @@ This is your link for group %@!</source>
|
||||
- приостанавливать членов (роль "наблюдатель")</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Выключено</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Выключить (Локальные)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Ок</target>
|
||||
@@ -3702,9 +3735,16 @@ This is your link for group %@!</source>
|
||||
<target>Открытый протокол и код - кто угодно может запустить сервер.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Открытие базы данных…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3747,11 +3787,6 @@ This is your link for group %@!</source>
|
||||
<target>Пароль чтобы раскрыть</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Вставить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<target>Вставить адрес компьютера</target>
|
||||
@@ -3762,16 +3797,10 @@ This is your link for group %@!</source>
|
||||
<target>Вставить изображение</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Вставить полученную ссылку</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Чтобы соединиться, вставьте ссылку, полученную от Вашего контакта.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>С Вами можно соединиться только через созданные Вами ссылки.</target>
|
||||
@@ -3807,6 +3836,11 @@ This is your link for group %@!</source>
|
||||
<target>Проверьте предпочтения Вашего контакта.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Пожалуйста, свяжитесь с админом группы.</target>
|
||||
@@ -4012,6 +4046,10 @@ This is your link for group %@!</source>
|
||||
<target>Узнать больше в [Руководстве пользователя](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Узнать больше в [Руководстве пользователя](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4222,6 +4260,10 @@ This is your link for group %@!</source>
|
||||
<target>Ошибка при восстановлении базы данных</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Показать</target>
|
||||
@@ -4377,6 +4419,10 @@ This is your link for group %@!</source>
|
||||
<target>Поиск</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Защита очереди</target>
|
||||
@@ -4652,9 +4698,8 @@ This is your link for group %@!</source>
|
||||
<target>Поделиться ссылкой</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Поделиться ссылкой-приглашением</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4777,16 +4822,15 @@ This is your link for group %@!</source>
|
||||
<target>Контакт</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Начать новый разговор</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Запустить чат</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Запустить перемещение данных</target>
|
||||
@@ -4912,6 +4956,14 @@ This is your link for group %@!</source>
|
||||
<target>Нажмите, чтобы вступить инкогнито</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Нажмите, чтобы начать чат</target>
|
||||
@@ -4974,6 +5026,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>Попытка поменять пароль базы данных не была завершена.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>Подтвержденное соединение будет отменено!</target>
|
||||
@@ -5039,6 +5095,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>Серверы для новых соединений Вашего текущего профиля чата **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Тема</target>
|
||||
@@ -5191,11 +5251,6 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
<target>Выключить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Выключить уведомления?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Включить</target>
|
||||
@@ -5398,6 +5453,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Использовать новый Инкогнито профиль</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Использовать сервер</target>
|
||||
@@ -5660,11 +5719,6 @@ Repeat join request?</source>
|
||||
<target>Вы можете принимать звонки на экране блокировки, без аутентификации.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Вы также можете соединиться, открыв ссылку. Если ссылка откроется в браузере, нажмите кнопку **Open in mobile app**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Вы можете создать его позже</target>
|
||||
@@ -5685,6 +5739,10 @@ Repeat join request?</source>
|
||||
<target>Вы можете скрыть профиль или выключить уведомления - потяните его вправо.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Вы теперь можете отправлять сообщения %@</target>
|
||||
@@ -5725,6 +5783,10 @@ Repeat join request?</source>
|
||||
<target>Вы можете форматировать сообщения:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Вы не можете отправлять сообщения!</target>
|
||||
@@ -5914,13 +5976,6 @@ You can cancel this connection and remove the contact (and try later with a new
|
||||
<target>Ваши контакты могут разрешить окончательное удаление сообщений.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Ваши контакты в SimpleX получат этот адрес.
|
||||
Вы можете изменить это в Настройках.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Ваши контакты сохранятся.</target>
|
||||
|
||||
@@ -297,14 +297,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**เพิ่มผู้ติดต่อใหม่**: เพื่อสร้างคิวอาร์โค้ดแบบใช้ครั้งเดียวหรือลิงก์สำหรับผู้ติดต่อของคุณ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**สร้างลิงค์ / คิวอาร์โค้ด** เพื่อให้ผู้ติดต่อของคุณใช้</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -317,11 +320,6 @@
|
||||
<target>**ส่วนตัวที่สุด**: ไม่ใช้เซิร์ฟเวอร์การแจ้งเตือนของ SimpleX Chat ตรวจสอบข้อความเป็นระยะในพื้นหลัง (ขึ้นอยู่กับความถี่ที่คุณใช้แอป)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**แปะลิงก์ที่ได้รับ** หรือเปิดในเบราว์เซอร์แล้วแตะ **เปิดในแอปมือถือ**</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**โปรดทราบ**: คุณจะไม่สามารถกู้คืนหรือเปลี่ยนรหัสผ่านได้หากคุณทำรหัสผ่านหาย</target>
|
||||
@@ -332,11 +330,6 @@
|
||||
<target>**แนะนำ**: โทเค็นอุปกรณ์และการแจ้งเตือนจะถูกส่งไปยังเซิร์ฟเวอร์การแจ้งเตือนของ SimpleX Chat แต่ไม่ใช่เนื้อหาข้อความ ขนาด หรือผู้ที่ส่ง</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**สแกนคิวอาร์โค้ด**: เพื่อเชื่อมต่อกับผู้ติดต่อของคุณด้วยตนเองหรือผ่านการสนทนาทางวิดีโอ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**คำเตือน**: การแจ้งเตือนแบบพุชทันทีจำเป็นต้องบันทึกรหัสผ่านไว้ใน Keychain</target>
|
||||
@@ -431,11 +424,6 @@
|
||||
<target>1 สัปดาห์</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>ลิงก์สำหรับใช้ 1 ครั้ง</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 นาที</target>
|
||||
@@ -549,6 +537,10 @@
|
||||
<target>เพิ่มที่อยู่ลงในโปรไฟล์ของคุณ เพื่อให้ผู้ติดต่อของคุณสามารถแชร์กับผู้อื่นได้ การอัปเดตโปรไฟล์จะถูกส่งไปยังผู้ติดต่อของคุณ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>เพิ่มเซิร์ฟเวอร์ที่ตั้งไว้ล่วงหน้า</target>
|
||||
@@ -943,6 +935,10 @@
|
||||
<target>โทร</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>ไม่สามารถลบโปรไฟล์ผู้ใช้ได้!</target>
|
||||
@@ -1059,6 +1055,10 @@
|
||||
<target>การแชทหยุดทํางานแล้ว</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>ค่ากําหนดในการแชท</target>
|
||||
@@ -1194,11 +1194,6 @@ This is your own one-time link!</source>
|
||||
<target>เชื่อมต่อผ่านลิงก์</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>เชื่อมต่อผ่านลิงค์ / คิวอาร์โค้ด</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1364,11 +1359,6 @@ This is your own one-time link!</source>
|
||||
<source>Create new profile in [desktop app](https://simplex.chat/downloads/). 💻</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>สร้างลิงก์เชิญแบบใช้ครั้งเดียว</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1393,6 +1383,10 @@ This is your own one-time link!</source>
|
||||
<target>สร้างเมื่อ %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>รหัสผ่านปัจจุบัน</target>
|
||||
@@ -1932,6 +1926,10 @@ This cannot be undone!</source>
|
||||
<target>เปิดใช้งานการลบข้อความอัตโนมัติ?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>เปิดใช้งานสําหรับทุกคน</target>
|
||||
@@ -1995,6 +1993,10 @@ This cannot be undone!</source>
|
||||
<target>ข้อความที่ encrypt หรือเหตุการณ์อื่น</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>ข้อความที่ encrypt: ความผิดพลาดในฐานข้อมูล</target>
|
||||
@@ -2218,6 +2220,10 @@ This cannot be undone!</source>
|
||||
<target>โหลดเซิร์ฟเวอร์ %@ ผิดพลาด</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>เกิดข้อผิดพลาดในการรับไฟล์</target>
|
||||
@@ -2258,6 +2264,10 @@ This cannot be undone!</source>
|
||||
<target>เกิดข้อผิดพลาดในการบันทึกรหัสผ่านผู้ใช้</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>เกิดข้อผิดพลาดในการส่งอีเมล</target>
|
||||
@@ -2726,11 +2736,6 @@ This cannot be undone!</source>
|
||||
<target>หากคุณไม่สามารถพบกันในชีวิตจริงได้ ให้แสดงคิวอาร์โค้ดในวิดีโอคอล หรือแชร์ลิงก์</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>หากคุณไม่สามารถพบปะด้วยตนเอง คุณสามารถ **สแกนคิวอาร์โค้ดผ่านการสนทนาทางวิดีโอ** หรือผู้ติดต่อของคุณสามารถแชร์ลิงก์เชิญได้</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>หากคุณใส่รหัสผ่านนี้เมื่อเปิดแอป ข้อมูลแอปทั้งหมดจะถูกลบอย่างถาวร!</target>
|
||||
@@ -2885,15 +2890,27 @@ This cannot be undone!</source>
|
||||
<target>อินเตอร์เฟซ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>ลิงค์เชื่อมต่อไม่ถูกต้อง</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>ที่อยู่เซิร์ฟเวอร์ไม่ถูกต้อง!</target>
|
||||
@@ -3007,10 +3024,18 @@ This is your link for group %@!</source>
|
||||
<target>กำลังจะเข้าร่วมกลุ่ม</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>รักษาการเชื่อมต่อของคุณ</target>
|
||||
@@ -3093,6 +3118,11 @@ This is your link for group %@!</source>
|
||||
<target>ข้อความสด</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>ในเครื่อง</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>ชื่อภายในเครื่องเท่านั้น</target>
|
||||
@@ -3331,6 +3361,10 @@ This is your link for group %@!</source>
|
||||
<target>รหัสผ่านใหม่</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>คำขอติดต่อใหม่</target>
|
||||
@@ -3452,16 +3486,15 @@ This is your link for group %@!</source>
|
||||
- ปิดการใช้งานสมาชิก (บทบาท "ผู้สังเกตการณ์")</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>ปิด</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>ปิด (ในเครื่อง)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>ตกลง</target>
|
||||
@@ -3600,9 +3633,16 @@ This is your link for group %@!</source>
|
||||
<target>โปรโตคอลและโค้ดโอเพ่นซอร์ส – ใคร ๆ ก็สามารถเปิดใช้เซิร์ฟเวอร์ได้</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>กำลังเปิดฐานข้อมูล…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3645,11 +3685,6 @@ This is your link for group %@!</source>
|
||||
<target>รหัสผ่านที่จะแสดง</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>แปะ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3659,15 +3694,10 @@ This is your link for group %@!</source>
|
||||
<target>แปะภาพ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>แปะลิงก์ที่ได้รับ</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>ผู้คนสามารถเชื่อมต่อกับคุณผ่านลิงก์ที่คุณแบ่งปันเท่านั้น</target>
|
||||
@@ -3703,6 +3733,11 @@ This is your link for group %@!</source>
|
||||
<target>โปรดตรวจสอบความต้องการของคุณและการตั้งค่าผู้ติดต่อ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>โปรดติดต่อผู้ดูแลกลุ่ม</target>
|
||||
@@ -3906,6 +3941,10 @@ This is your link for group %@!</source>
|
||||
<target>อ่านเพิ่มเติมใน[คู่มือผู้ใช้](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>อ่านเพิ่มเติมใน[คู่มือผู้ใช้](https://simplex.chat/docs/guide/readme.html#connect-to-friends)</target>
|
||||
@@ -4112,6 +4151,10 @@ This is your link for group %@!</source>
|
||||
<target>กู้คืนข้อผิดพลาดของฐานข้อมูล</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>เปิดเผย</target>
|
||||
@@ -4266,6 +4309,10 @@ This is your link for group %@!</source>
|
||||
<target>ค้นหา</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>คิวที่ปลอดภัย</target>
|
||||
@@ -4537,9 +4584,8 @@ This is your link for group %@!</source>
|
||||
<target>แชร์ลิงก์</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>แชร์ลิงก์เชิญแบบใช้ครั้งเดียว</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4659,16 +4705,15 @@ This is your link for group %@!</source>
|
||||
<target>ใครบางคน</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>เริ่มแชทใหม่</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>เริ่มแชท</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>เริ่มการย้ายข้อมูล</target>
|
||||
@@ -4793,6 +4838,14 @@ This is your link for group %@!</source>
|
||||
<target>แตะเพื่อเข้าร่วมโหมดไม่ระบุตัวตน</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>แตะเพื่อเริ่มแชทใหม่</target>
|
||||
@@ -4856,6 +4909,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>ความพยายามในการเปลี่ยนรหัสผ่านของฐานข้อมูลไม่เสร็จสมบูรณ์</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>การเชื่อมต่อที่คุณยอมรับจะถูกยกเลิก!</target>
|
||||
@@ -4921,6 +4978,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>เซิร์ฟเวอร์สำหรับการเชื่อมต่อใหม่ของโปรไฟล์การแชทปัจจุบันของคุณ **%@**</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>ธีม</target>
|
||||
@@ -5066,11 +5127,6 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
<target>ปิด</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>ปิดการแจ้งเตือนไหม?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>เปิด</target>
|
||||
@@ -5265,6 +5321,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<source>Use new incognito profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>ใช้เซิร์ฟเวอร์</target>
|
||||
@@ -5514,11 +5574,6 @@ Repeat join request?</source>
|
||||
<target>คุณสามารถรับสายจากหน้าจอล็อกโดยไม่ต้องมีการตรวจสอบสิทธิ์อุปกรณ์และแอป</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>คุณสามารถเชื่อมต่อได้โดยคลิกที่ลิงค์ หากเปิดในเบราว์เซอร์ ให้คลิกปุ่ม **เปิดในแอปมือถือ**</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>คุณสามารถสร้างได้ในภายหลัง</target>
|
||||
@@ -5539,6 +5594,10 @@ Repeat join request?</source>
|
||||
<target>คุณสามารถซ่อนหรือปิดเสียงโปรไฟล์ผู้ใช้ - ปัดไปทางขวา</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>ตอนนี้คุณสามารถส่งข้อความถึง %@</target>
|
||||
@@ -5579,6 +5638,10 @@ Repeat join request?</source>
|
||||
<target>คุณสามารถใช้มาร์กดาวน์เพื่อจัดรูปแบบข้อความ:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>คุณไม่สามารถส่งข้อความได้!</target>
|
||||
@@ -5762,13 +5825,6 @@ You can cancel this connection and remove the contact (and try later with a new
|
||||
<target>ผู้ติดต่อของคุณสามารถอนุญาตให้ลบข้อความทั้งหมดได้</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>ผู้ติดต่อของคุณใน SimpleX จะเห็น
|
||||
คุณสามารถเปลี่ยนได้ในการตั้งค่า</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>ผู้ติดต่อของคุณจะยังคงเชื่อมต่ออยู่</target>
|
||||
|
||||
@@ -5,25 +5,31 @@
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.3.1" build-num="14E300c"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<trans-unit id=" " xml:space="preserve" approved="no">
|
||||
<source>
|
||||
</source>
|
||||
<target state="needs-translation">
|
||||
</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<trans-unit id=" " xml:space="preserve" approved="no">
|
||||
<source> </source>
|
||||
<target state="needs-translation"> </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<trans-unit id=" " xml:space="preserve" approved="no">
|
||||
<source> </source>
|
||||
<target state="needs-translation"> </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<trans-unit id=" " xml:space="preserve" approved="no">
|
||||
<source> </source>
|
||||
<target state="translated"> </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" (" xml:space="preserve">
|
||||
<trans-unit id=" (" xml:space="preserve" approved="no">
|
||||
<source> (</source>
|
||||
<target state="needs-translation"> (</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" (can be copied)" xml:space="preserve" approved="no">
|
||||
@@ -343,8 +349,9 @@
|
||||
<target state="translated">30 saniye</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=": " xml:space="preserve">
|
||||
<trans-unit id=": " xml:space="preserve" approved="no">
|
||||
<source>: </source>
|
||||
<target state="needs-translation">: </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="<p>Hi!</p> <p><a href="%@">Connect to me via SimpleX Chat</a></p>" xml:space="preserve" approved="no">
|
||||
@@ -390,8 +397,9 @@
|
||||
<source>Abort changing address?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="About SimpleX" xml:space="preserve">
|
||||
<trans-unit id="About SimpleX" xml:space="preserve" approved="no">
|
||||
<source>About SimpleX</source>
|
||||
<target state="translated">SimpleX Hakkında</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="About SimpleX Chat" xml:space="preserve">
|
||||
@@ -406,8 +414,9 @@
|
||||
<source>Accent color</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept" xml:space="preserve">
|
||||
<trans-unit id="Accept" xml:space="preserve" approved="no">
|
||||
<source>Accept</source>
|
||||
<target state="translated">Kabul et</target>
|
||||
<note>accept contact request via notification
|
||||
accept incoming call via notification</note>
|
||||
</trans-unit>
|
||||
@@ -435,48 +444,57 @@
|
||||
<source>Add profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
|
||||
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve" approved="no">
|
||||
<source>Add servers by scanning QR codes.</source>
|
||||
<target state="translated">Karekod taratarak sunucuları ekleyin.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add server…" xml:space="preserve">
|
||||
<trans-unit id="Add server…" xml:space="preserve" approved="no">
|
||||
<source>Add server…</source>
|
||||
<target state="translated">Sunucu ekle…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add to another device" xml:space="preserve">
|
||||
<source>Add to another device</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add welcome message" xml:space="preserve">
|
||||
<trans-unit id="Add welcome message" xml:space="preserve" approved="no">
|
||||
<source>Add welcome message</source>
|
||||
<target state="translated">Karşılama mesajı ekleyin</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Address" xml:space="preserve">
|
||||
<trans-unit id="Address" xml:space="preserve" approved="no">
|
||||
<source>Address</source>
|
||||
<target state="translated">Adres</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Address change will be aborted. Old receiving address will be used." xml:space="preserve">
|
||||
<trans-unit id="Address change will be aborted. Old receiving address will be used." xml:space="preserve" approved="no">
|
||||
<source>Address change will be aborted. Old receiving address will be used.</source>
|
||||
<target state="translated">Adres değişikliği iptal edilecek. Eski alıcı adresi kullanılacaktır.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Admins can create the links to join groups." xml:space="preserve">
|
||||
<source>Admins can create the links to join groups.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Advanced network settings" xml:space="preserve">
|
||||
<trans-unit id="Advanced network settings" xml:space="preserve" approved="no">
|
||||
<source>Advanced network settings</source>
|
||||
<target state="translated">GGelişmiş ağ ayarları</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All app data is deleted." xml:space="preserve">
|
||||
<trans-unit id="All app data is deleted." xml:space="preserve" approved="no">
|
||||
<source>All app data is deleted.</source>
|
||||
<target state="translated">Tüm uygulama verileri silinir.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All chats and messages will be deleted - this cannot be undone!" xml:space="preserve">
|
||||
<trans-unit id="All chats and messages will be deleted - this cannot be undone!" xml:space="preserve" approved="no">
|
||||
<source>All chats and messages will be deleted - this cannot be undone!</source>
|
||||
<target state="translated">Tüm konuşmalar ve mesajlar silinecektir. Bu, geri alınamaz!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All data is erased when it is entered." xml:space="preserve">
|
||||
<trans-unit id="All data is erased when it is entered." xml:space="preserve" approved="no">
|
||||
<source>All data is erased when it is entered.</source>
|
||||
<target state="translated">Kullanıldığında bütün veriler silinir.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All group members will remain connected." xml:space="preserve" approved="no">
|
||||
@@ -484,24 +502,29 @@
|
||||
<target state="translated">Tüm grup üyeleri bağlı kalacaktır.</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">
|
||||
<trans-unit id="All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." xml:space="preserve" approved="no">
|
||||
<source>All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you.</source>
|
||||
<target state="translated">Tüm mesajlar silinecektir. Bu, geri alınamaz! Mesajlar, YALNIZCA senin için silinecektir.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
|
||||
<trans-unit id="All your contacts will remain connected." xml:space="preserve" approved="no">
|
||||
<source>All your contacts will remain connected.</source>
|
||||
<target state="translated">Konuştuğun kişilerin tümü bağlı kalacaktır.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All your contacts will remain connected. Profile update will be sent to your contacts." xml:space="preserve">
|
||||
<trans-unit id="All your contacts will remain connected. Profile update will be sent to your contacts." xml:space="preserve" approved="no">
|
||||
<source>All your contacts will remain connected. Profile update will be sent to your contacts.</source>
|
||||
<target state="translated">Tüm kişileriniz bağlı kalacaktır. Profil güncellemesi kişilerinize gönderilecektir.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow" xml:space="preserve">
|
||||
<trans-unit id="Allow" xml:space="preserve" approved="no">
|
||||
<source>Allow</source>
|
||||
<target state="translated">İzin ver</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow calls only if your contact allows them." xml:space="preserve">
|
||||
<trans-unit id="Allow calls only if your contact allows them." xml:space="preserve" approved="no">
|
||||
<source>Allow calls only if your contact allows them.</source>
|
||||
<target state="translated">Yalnızca irtibat kişiniz izin veriyorsa aramalara izin verin.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
|
||||
@@ -512,8 +535,9 @@
|
||||
<source>Allow irreversible message deletion only if your contact allows it to you.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow message reactions only if your contact allows them." xml:space="preserve">
|
||||
<trans-unit id="Allow message reactions only if your contact allows them." xml:space="preserve" approved="no">
|
||||
<source>Allow message reactions only if your contact allows them.</source>
|
||||
<target state="translated">Yalnızca kişin mesaj tepkilerine izin veriyorsa sen de ver.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow message reactions." xml:space="preserve">
|
||||
@@ -525,32 +549,39 @@
|
||||
<target state="translated">Üyelere direkt mesaj göndermeye izin ver.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow sending disappearing messages." xml:space="preserve">
|
||||
<trans-unit id="Allow sending disappearing messages." xml:space="preserve" approved="no">
|
||||
<source>Allow sending disappearing messages.</source>
|
||||
<target state="translated">Kendiliğinden yok olan mesajlar göndermeye izin ver.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to irreversibly delete sent messages." xml:space="preserve">
|
||||
<trans-unit id="Allow to irreversibly delete sent messages." xml:space="preserve" approved="no">
|
||||
<source>Allow to irreversibly delete sent messages.</source>
|
||||
<target state="translated">Gönderilen mesajların kalıcı olarak silinmesine izin ver.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve" approved="no">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target state="translated">Dosya ve medya göndermeye izin ver.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send voice messages." xml:space="preserve">
|
||||
<trans-unit id="Allow to send voice messages." xml:space="preserve" approved="no">
|
||||
<source>Allow to send voice messages.</source>
|
||||
<target state="translated">Sesli mesaj göndermeye izin ver.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow voice messages only if your contact allows them." xml:space="preserve">
|
||||
<trans-unit id="Allow voice messages only if your contact allows them." xml:space="preserve" approved="no">
|
||||
<source>Allow voice messages only if your contact allows them.</source>
|
||||
<target state="translated">Yalnızca kişiniz sesli mesaj göndermeye izin veriyorsa sen de ver.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow voice messages?" xml:space="preserve">
|
||||
<trans-unit id="Allow voice messages?" xml:space="preserve" approved="no">
|
||||
<source>Allow voice messages?</source>
|
||||
<target state="translated">Sesli mesajlara izin ver?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow your contacts adding message reactions." xml:space="preserve">
|
||||
<trans-unit id="Allow your contacts adding message reactions." xml:space="preserve" approved="no">
|
||||
<source>Allow your contacts adding message reactions.</source>
|
||||
<target state="translated">Konuştuğun kişilerin mesajlarına tepki eklemesine izin ver.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow your contacts to call you." xml:space="preserve">
|
||||
@@ -577,24 +608,28 @@
|
||||
<source>Always use relay</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="An empty chat profile with the provided name is created, and the app opens as usual." xml:space="preserve">
|
||||
<trans-unit id="An empty chat profile with the provided name is created, and the app opens as usual." xml:space="preserve" approved="no">
|
||||
<source>An empty chat profile with the provided name is created, and the app opens as usual.</source>
|
||||
<target state="translated">Verilen adla boş bir sohbet profili oluşturulur ve uygulama her zamanki gibi açılır.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Answer call" xml:space="preserve">
|
||||
<trans-unit id="Answer call" xml:space="preserve" approved="no">
|
||||
<source>Answer call</source>
|
||||
<target state="translated">Aramayı cevapla</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="App build: %@" xml:space="preserve">
|
||||
<source>App build: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="App icon" xml:space="preserve">
|
||||
<trans-unit id="App icon" xml:space="preserve" approved="no">
|
||||
<source>App icon</source>
|
||||
<target state="translated">Uygulama simgesi</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="App passcode" xml:space="preserve">
|
||||
<trans-unit id="App passcode" xml:space="preserve" approved="no">
|
||||
<source>App passcode</source>
|
||||
<target state="translated">Uygulama erişim kodu</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="App passcode is replaced with self-destruct passcode." xml:space="preserve">
|
||||
@@ -4893,6 +4928,41 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target state="needs-translation">\~strike~</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept connection request?" xml:space="preserve" approved="no">
|
||||
<source>Accept connection request?</source>
|
||||
<target state="translated">Bağlantı isteğini kabul et?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="# %@" xml:space="preserve" approved="no">
|
||||
<source># %@</source>
|
||||
<target state="needs-translation"># %@</target>
|
||||
<note>copied message info title, # <title></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Already connecting!" xml:space="preserve" approved="no">
|
||||
<source>Already connecting!</source>
|
||||
<target state="translated">Zaten bağlanılıyor!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="A few more things" xml:space="preserve" approved="no">
|
||||
<source>A few more things</source>
|
||||
<target state="translated">Birkaç şey daha</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## History" xml:space="preserve" approved="no">
|
||||
<source>## History</source>
|
||||
<target state="translated">## Geçmiş</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="A new random profile will be shared." xml:space="preserve" approved="no">
|
||||
<source>A new random profile will be shared.</source>
|
||||
<target state="translated">Yeni bir rastgele profil paylaşılacak.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Already joining the group!" xml:space="preserve" approved="no">
|
||||
<source>Already joining the group!</source>
|
||||
<target state="translated">Zaten gruba bağlanılıyor!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="tr" datatype="plaintext">
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@" xml:space="preserve">
|
||||
<source>%@ and %@</source>
|
||||
<target>%@ та %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve">
|
||||
@@ -103,6 +104,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ connected" xml:space="preserve">
|
||||
<source>%@ connected</source>
|
||||
<target>%@ підключено</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve">
|
||||
@@ -132,6 +134,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||
<source>%@, %@ and %lld members</source>
|
||||
<target>%@, %@ та %lld учасників</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
|
||||
@@ -201,6 +204,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld group events" xml:space="preserve">
|
||||
<source>%lld group events</source>
|
||||
<target>%lld групові заходи</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld members" xml:space="preserve">
|
||||
@@ -210,14 +214,17 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld messages blocked" xml:space="preserve">
|
||||
<source>%lld messages blocked</source>
|
||||
<target>%lld повідомлень заблоковано</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld messages marked deleted" xml:space="preserve">
|
||||
<source>%lld messages marked deleted</source>
|
||||
<target>%lld повідомлень позначено як видалені</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld messages moderated by %@" xml:space="preserve">
|
||||
<source>%lld messages moderated by %@</source>
|
||||
<target>%lld повідомлень модерує %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld minutes" xml:space="preserve">
|
||||
@@ -227,6 +234,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld new interface languages" xml:space="preserve">
|
||||
<source>%lld new interface languages</source>
|
||||
<target>%lld нові мови інтерфейсу</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld second(s)" xml:space="preserve">
|
||||
@@ -291,10 +299,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="(new)" xml:space="preserve">
|
||||
<source>(new)</source>
|
||||
<target>(новий)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(this device v%@)" xml:space="preserve">
|
||||
<source>(this device v%@)</source>
|
||||
<target>(цей пристрій v%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
@@ -302,14 +312,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**Додати новий контакт**: щоб створити одноразовий QR-код або посилання для свого контакту.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**Створіть посилання / QR-код** для використання вашим контактом.</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -322,11 +335,6 @@
|
||||
<target>**Найбільш приватний**: не використовуйте сервер сповіщень SimpleX Chat, періодично перевіряйте повідомлення у фоновому режимі (залежить від того, як часто ви користуєтесь додатком).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**Вставте отримане посилання** або відкрийте його в браузері і натисніть **Відкрити в мобільному додатку**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**Зверніть увагу: ви НЕ зможете відновити або змінити пароль, якщо втратите його.</target>
|
||||
@@ -337,11 +345,6 @@
|
||||
<target>**Рекомендується**: токен пристрою та сповіщення надсилаються на сервер сповіщень SimpleX Chat, але не вміст повідомлення, його розмір або від кого воно надійшло.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**Відскануйте QR-код**: щоб з'єднатися з вашим контактом особисто або за допомогою відеодзвінка.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**Попередження**: Для отримання миттєвих пуш-сповіщень потрібна парольна фраза, збережена у брелоку.</target>
|
||||
@@ -371,6 +374,9 @@
|
||||
<source>- connect to [directory service](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) (BETA)!
|
||||
- delivery receipts (up to 20 members).
|
||||
- faster and more stable.</source>
|
||||
<target>- підключитися до [сервера каталогів](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex. im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id. цибуля) (БЕТА)!
|
||||
- підтвердження доставлення (до 20 учасників).
|
||||
- швидше і стабільніше.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- more stable message delivery. - a bit better groups. - and more!" xml:space="preserve">
|
||||
@@ -386,6 +392,9 @@
|
||||
<source>- optionally notify deleted contacts.
|
||||
- profile names with spaces.
|
||||
- and more!</source>
|
||||
<target>- опція сповіщати про видалені контакти.
|
||||
- імена профілів з пробілами.
|
||||
- та багато іншого!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="- voice messages up to 5 minutes. - custom time to disappear. - editing history." xml:space="preserve">
|
||||
@@ -404,6 +413,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="0 sec" xml:space="preserve">
|
||||
<source>0 sec</source>
|
||||
<target>0 сек</target>
|
||||
<note>time to disappear</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="0s" xml:space="preserve">
|
||||
@@ -436,11 +446,6 @@
|
||||
<target>1 тиждень</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>1-разове посилання</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5 хвилин</target>
|
||||
@@ -556,6 +561,10 @@
|
||||
<target>Додайте адресу до свого профілю, щоб ваші контакти могли поділитися нею з іншими людьми. Повідомлення про оновлення профілю буде надіслано вашим контактам.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>Додавання попередньо встановлених серверів</target>
|
||||
@@ -633,6 +642,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="All new messages from %@ will be hidden!" xml:space="preserve">
|
||||
<source>All new messages from %@ will be hidden!</source>
|
||||
<target>Всі нові повідомлення від %@ будуть приховані!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
|
||||
@@ -742,10 +752,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Already connecting!" xml:space="preserve">
|
||||
<source>Already connecting!</source>
|
||||
<target>Вже підключаємось!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Already joining the group!" xml:space="preserve">
|
||||
<source>Already joining the group!</source>
|
||||
<target>Вже приєднуємося до групи!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Always use relay" xml:space="preserve">
|
||||
@@ -770,6 +782,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="App encrypts new local files (except videos)." xml:space="preserve">
|
||||
<source>App encrypts new local files (except videos).</source>
|
||||
<target>Додаток шифрує нові локальні файли (крім відео).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="App icon" xml:space="preserve">
|
||||
@@ -869,6 +882,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad desktop address" xml:space="preserve">
|
||||
<source>Bad desktop address</source>
|
||||
<target>Неправильна адреса робочого столу</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Bad message ID" xml:space="preserve">
|
||||
@@ -883,6 +897,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Better groups" xml:space="preserve">
|
||||
<source>Better groups</source>
|
||||
<target>Кращі групи</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Better messages" xml:space="preserve">
|
||||
@@ -892,18 +907,22 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Block" xml:space="preserve">
|
||||
<source>Block</source>
|
||||
<target>Блокувати</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block group members" xml:space="preserve">
|
||||
<source>Block group members</source>
|
||||
<target>Учасники групи блокування</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member" xml:space="preserve">
|
||||
<source>Block member</source>
|
||||
<target>Заблокувати користувача</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Block member?" xml:space="preserve">
|
||||
<source>Block member?</source>
|
||||
<target>Заблокувати користувача?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Both you and your contact can add message reactions." xml:space="preserve">
|
||||
@@ -933,6 +952,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" xml:space="preserve">
|
||||
<source>Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!</source>
|
||||
<target>Болгарською, фінською, тайською та українською мовами - завдяки користувачам та [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="By chat profile (default) or [by connection](https://simplex.chat/blog/20230204-simplex-chat-v4-5-user-chat-profiles.html#transport-isolation) (BETA)." xml:space="preserve">
|
||||
@@ -950,6 +970,10 @@
|
||||
<target>Дзвінки</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>Не можу видалити профіль користувача!</target>
|
||||
@@ -1066,6 +1090,10 @@
|
||||
<target>Чат зупинено</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>Налаштування чату</target>
|
||||
@@ -1168,6 +1196,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect automatically" xml:space="preserve">
|
||||
<source>Connect automatically</source>
|
||||
<target>Підключення автоматично</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect incognito" xml:space="preserve">
|
||||
@@ -1177,24 +1206,31 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to desktop" xml:space="preserve">
|
||||
<source>Connect to desktop</source>
|
||||
<target>Підключення до комп'ютера</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself?" xml:space="preserve">
|
||||
<source>Connect to yourself?</source>
|
||||
<target>З'єднатися з самим собою?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself? This is your own SimpleX address!" xml:space="preserve">
|
||||
<source>Connect to yourself?
|
||||
This is your own SimpleX address!</source>
|
||||
<target>З'єднатися з самим собою?
|
||||
Це ваша власна SimpleX-адреса!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect to yourself? This is your own one-time link!" xml:space="preserve">
|
||||
<source>Connect to yourself?
|
||||
This is your own one-time link!</source>
|
||||
<target>Підключитися до себе?
|
||||
Це ваше власне одноразове посилання!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via contact address" xml:space="preserve">
|
||||
<source>Connect via contact address</source>
|
||||
<target>Підключіться за контактною адресою</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link" xml:space="preserve">
|
||||
@@ -1202,11 +1238,6 @@ This is your own one-time link!</source>
|
||||
<target>Підключіться за посиланням</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>Підключитися за посиланням / QR-кодом</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>Під'єднатися за одноразовим посиланням</target>
|
||||
@@ -1214,10 +1245,12 @@ This is your own one-time link!</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect with %@" xml:space="preserve">
|
||||
<source>Connect with %@</source>
|
||||
<target>Підключитися до %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected desktop" xml:space="preserve">
|
||||
<source>Connected desktop</source>
|
||||
<target>Підключений робочий стіл</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connected to desktop" xml:space="preserve">
|
||||
@@ -1373,11 +1406,6 @@ This is your own one-time link!</source>
|
||||
<source>Create new profile in [desktop app](https://simplex.chat/downloads/). 💻</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>Створіть одноразове посилання-запрошення</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1402,6 +1430,10 @@ This is your own one-time link!</source>
|
||||
<target>Створено %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>Поточний пароль</target>
|
||||
@@ -1942,6 +1974,10 @@ This cannot be undone!</source>
|
||||
<target>Увімкнути автоматичне видалення повідомлень?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>Увімкнути для всіх</target>
|
||||
@@ -2005,6 +2041,10 @@ This cannot be undone!</source>
|
||||
<target>Зашифроване повідомлення або інша подія</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>Зашифроване повідомлення: помилка бази даних</target>
|
||||
@@ -2228,6 +2268,10 @@ This cannot be undone!</source>
|
||||
<target>Помилка завантаження %@ серверів</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>Помилка отримання файлу</target>
|
||||
@@ -2268,6 +2312,10 @@ This cannot be undone!</source>
|
||||
<target>Помилка збереження пароля користувача</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>Помилка надсилання електронного листа</target>
|
||||
@@ -2736,11 +2784,6 @@ This cannot be undone!</source>
|
||||
<target>Якщо ви не можете зустрітися особисто, покажіть QR-код у відеодзвінку або поділіться посиланням.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Якщо ви не можете зустрітися особисто, ви можете **сканувати QR-код у відеодзвінку**, або ваш контакт може поділитися посиланням на запрошення.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>Якщо ви введете цей пароль при відкритті програми, всі дані програми будуть безповоротно видалені!</target>
|
||||
@@ -2896,15 +2939,27 @@ This cannot be undone!</source>
|
||||
<target>Інтерфейс</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Неправильне посилання для підключення</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>Неправильна адреса сервера!</target>
|
||||
@@ -3019,10 +3074,18 @@ This is your link for group %@!</source>
|
||||
<target>Приєднання до групи</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>Зберігайте свої зв'язки</target>
|
||||
@@ -3105,6 +3168,11 @@ This is your link for group %@!</source>
|
||||
<target>Живі повідомлення</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>Локально</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>Місцева назва</target>
|
||||
@@ -3344,6 +3412,10 @@ This is your link for group %@!</source>
|
||||
<target>Новий пароль</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Новий запит на контакт</target>
|
||||
@@ -3466,16 +3538,15 @@ This is your link for group %@!</source>
|
||||
- відключати користувачів (роль "спостерігач")</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>Вимкнено</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>Вимкнено (локально)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>Гаразд</target>
|
||||
@@ -3614,9 +3685,16 @@ This is your link for group %@!</source>
|
||||
<target>Протокол і код з відкритим вихідним кодом - будь-хто може запускати сервери.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>Відкриття бази даних…</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3659,11 +3737,6 @@ This is your link for group %@!</source>
|
||||
<target>Показати пароль</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>Вставити</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3673,16 +3746,10 @@ This is your link for group %@!</source>
|
||||
<target>Вставити зображення</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>Вставте отримане посилання</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>Вставте отримане посилання для зв'язку з вашим контактом.</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>Люди можуть зв'язатися з вами лише за посиланнями, якими ви ділитеся.</target>
|
||||
@@ -3718,6 +3785,11 @@ This is your link for group %@!</source>
|
||||
<target>Будь ласка, перевірте свої та контактні налаштування.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>Зверніться до адміністратора групи.</target>
|
||||
@@ -3921,6 +3993,10 @@ This is your link for group %@!</source>
|
||||
<target>Читайте більше в [Посібнику користувача](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>Читайте більше в [Посібнику користувача](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
@@ -4129,6 +4205,10 @@ This is your link for group %@!</source>
|
||||
<target>Відновлення помилки бази даних</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>Показувати</target>
|
||||
@@ -4283,6 +4363,10 @@ This is your link for group %@!</source>
|
||||
<target>Пошук</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>Безпечна черга</target>
|
||||
@@ -4556,9 +4640,8 @@ This is your link for group %@!</source>
|
||||
<target>Поділіться посиланням</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>Поділіться посиланням на одноразове запрошення</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4680,16 +4763,15 @@ This is your link for group %@!</source>
|
||||
<target>Хтось</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>Почніть новий чат</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>Почати чат</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>Почати міграцію</target>
|
||||
@@ -4814,6 +4896,14 @@ This is your link for group %@!</source>
|
||||
<target>Натисніть, щоб приєднатися інкогніто</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>Натисніть, щоб почати новий чат</target>
|
||||
@@ -4876,6 +4966,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>Спроба змінити пароль до бази даних не була завершена.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>Прийняте вами з'єднання буде скасовано!</target>
|
||||
@@ -4941,6 +5035,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>Сервери для нових підключень вашого поточного профілю чату **%@**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>Тема</target>
|
||||
@@ -5088,11 +5186,6 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
<target>Вимкнути</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>Вимкнути сповіщення?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>Ввімкнути</target>
|
||||
@@ -5289,6 +5382,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Використовуйте новий профіль інкогніто</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>Використовувати сервер</target>
|
||||
@@ -5538,11 +5635,6 @@ Repeat join request?</source>
|
||||
<target>Ви можете приймати дзвінки з екрана блокування без автентифікації пристрою та програми.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>Ви також можете підключитися за посиланням. Якщо воно відкриється в браузері, натисніть кнопку **Відкрити в мобільному додатку**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Ви можете створити його пізніше</target>
|
||||
@@ -5563,6 +5655,10 @@ Repeat join request?</source>
|
||||
<target>Ви можете приховати або вимкнути звук профілю користувача - проведіть по ньому вправо.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>Тепер ви можете надсилати повідомлення на адресу %@</target>
|
||||
@@ -5603,6 +5699,10 @@ Repeat join request?</source>
|
||||
<target>Ви можете використовувати розмітку для форматування повідомлень:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>Ви не можете надсилати повідомлення!</target>
|
||||
@@ -5787,13 +5887,6 @@ You can cancel this connection and remove the contact (and try later with a new
|
||||
<target>Ваші контакти можуть дозволити повне видалення повідомлень.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>Ваші контакти в SimpleX побачать це.
|
||||
Ви можете змінити його в Налаштуваннях.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>Ваші контакти залишаться на зв'язку.</target>
|
||||
|
||||
@@ -303,14 +303,17 @@
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve">
|
||||
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code or link for your contact.</source>
|
||||
<target>**添加新联系人**:为您的联系人创建一次性二维码或者链接。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Create link / QR code** for your contact to use." xml:space="preserve">
|
||||
<source>**Create link / QR code** for your contact to use.</source>
|
||||
<target>**创建链接 / 二维码** 给您的联系人使用。</target>
|
||||
<trans-unit id="**Create group**: to create a new group." xml:space="preserve">
|
||||
<source>**Create group**: to create a new group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**More private**: check new messages every 20 minutes. Device token is shared with SimpleX Chat server, but not how many contacts or messages you have." xml:space="preserve">
|
||||
@@ -323,11 +326,6 @@
|
||||
<target>**最私密**:不使用 SimpleX Chat 通知服务器,在后台定期检查消息(取决于您多经常使用应用程序)。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Paste received link** or open it in the browser and tap **Open in mobile app**." xml:space="preserve">
|
||||
<source>**Paste received link** or open it in the browser and tap **Open in mobile app**.</source>
|
||||
<target>**粘贴收到的链接**或者在浏览器里打开并且点击**在移动应用程序里打开**。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Please note**: you will NOT be able to recover or change passphrase if you lose it." xml:space="preserve">
|
||||
<source>**Please note**: you will NOT be able to recover or change passphrase if you lose it.</source>
|
||||
<target>**请注意**:如果您丢失密码,您将无法恢复或者更改密码。</target>
|
||||
@@ -338,11 +336,6 @@
|
||||
<target>**推荐**:设备令牌和通知会发送至 SimpleX Chat 通知服务器,但是消息内容、大小或者发送人不会。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact in person or via video call." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact in person or via video call.</source>
|
||||
<target>**扫描二维码**:见面或者通过视频通话来连接您的联系人。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Warning**: Instant push notifications require passphrase saved in Keychain." xml:space="preserve">
|
||||
<source>**Warning**: Instant push notifications require passphrase saved in Keychain.</source>
|
||||
<target>**警告**:及时推送通知需要保存在钥匙串的密码。</target>
|
||||
@@ -440,11 +433,6 @@
|
||||
<target>1周</target>
|
||||
<note>time interval</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1-time link" xml:space="preserve">
|
||||
<source>1-time link</source>
|
||||
<target>一次性链接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="5 minutes" xml:space="preserve">
|
||||
<source>5 minutes</source>
|
||||
<target>5分钟</target>
|
||||
@@ -560,6 +548,10 @@
|
||||
<target>将地址添加到您的个人资料,以便您的联系人可以与其他人共享。个人资料更新将发送给您的联系人。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add preset servers" xml:space="preserve">
|
||||
<source>Add preset servers</source>
|
||||
<target>添加预设服务器</target>
|
||||
@@ -956,6 +948,10 @@
|
||||
<target>通话</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Camera not available" xml:space="preserve">
|
||||
<source>Camera not available</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Can't delete user profile!" xml:space="preserve">
|
||||
<source>Can't delete user profile!</source>
|
||||
<target>无法删除用户个人资料!</target>
|
||||
@@ -1072,6 +1068,10 @@
|
||||
<target>聊天已停止</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat." xml:space="preserve">
|
||||
<source>Chat is stopped. If you already used this database on another device, you should transfer it back before starting chat.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat preferences" xml:space="preserve">
|
||||
<source>Chat preferences</source>
|
||||
<target>聊天偏好设置</target>
|
||||
@@ -1208,11 +1208,6 @@ This is your own one-time link!</source>
|
||||
<target>通过链接连接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via link / QR code" xml:space="preserve">
|
||||
<source>Connect via link / QR code</source>
|
||||
<target>通过群组链接/二维码连接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve">
|
||||
<source>Connect via one-time link</source>
|
||||
<target>通过一次性链接连接</target>
|
||||
@@ -1380,11 +1375,6 @@ This is your own one-time link!</source>
|
||||
<target>在[桌面应用程序](https://simplex.chat/downloads/)中创建新的个人资料。 💻</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create one-time invitation link" xml:space="preserve">
|
||||
<source>Create one-time invitation link</source>
|
||||
<target>创建一次性邀请链接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -1409,6 +1399,10 @@ This is your own one-time link!</source>
|
||||
<target>创建于 %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Creating link…" xml:space="preserve">
|
||||
<source>Creating link…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Current Passcode" xml:space="preserve">
|
||||
<source>Current Passcode</source>
|
||||
<target>当前密码</target>
|
||||
@@ -1950,6 +1944,10 @@ This cannot be undone!</source>
|
||||
<target>启用自动删除消息?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable camera access" xml:space="preserve">
|
||||
<source>Enable camera access</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>全部启用</target>
|
||||
@@ -2015,6 +2013,10 @@ This cannot be undone!</source>
|
||||
<target>加密消息或其他事件</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: app is stopped" xml:space="preserve">
|
||||
<source>Encrypted message: app is stopped</source>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted message: database error" xml:space="preserve">
|
||||
<source>Encrypted message: database error</source>
|
||||
<target>加密消息:数据库错误</target>
|
||||
@@ -2240,6 +2242,10 @@ This cannot be undone!</source>
|
||||
<target>加载 %@ 服务器错误</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error opening chat" xml:space="preserve">
|
||||
<source>Error opening chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error receiving file" xml:space="preserve">
|
||||
<source>Error receiving file</source>
|
||||
<target>接收文件错误</target>
|
||||
@@ -2280,6 +2286,10 @@ This cannot be undone!</source>
|
||||
<target>保存用户密码时出错</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error scanning code: %@" xml:space="preserve">
|
||||
<source>Error scanning code: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error sending email" xml:space="preserve">
|
||||
<source>Error sending email</source>
|
||||
<target>发送电邮错误</target>
|
||||
@@ -2749,11 +2759,6 @@ This cannot be undone!</source>
|
||||
<target>如果您不能亲自见面,可以在视频通话中展示二维码,或分享链接。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>如果您不能亲自见面,您可以**扫描视频通话中的二维码**,或者您的联系人可以分享邀请链接。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
|
||||
<source>If you enter this passcode when opening the app, all app data will be irreversibly removed!</source>
|
||||
<target>如果您在打开应用时输入该密码,所有应用程序数据将被不可撤回地删除!</target>
|
||||
@@ -2909,15 +2914,27 @@ This cannot be undone!</source>
|
||||
<target>界面</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid QR code" xml:space="preserve">
|
||||
<source>Invalid QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>无效的连接链接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid link" xml:space="preserve">
|
||||
<source>Invalid link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid name!" xml:space="preserve">
|
||||
<source>Invalid name!</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid response" xml:space="preserve">
|
||||
<source>Invalid response</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid server address!" xml:space="preserve">
|
||||
<source>Invalid server address!</source>
|
||||
<target>无效的服务器地址!</target>
|
||||
@@ -3032,10 +3049,18 @@ This is your link for group %@!</source>
|
||||
<target>加入群组中</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep" xml:space="preserve">
|
||||
<source>Keep</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||
<source>Keep the app open to use it from desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep unused invitation?" xml:space="preserve">
|
||||
<source>Keep unused invitation?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>保持连接</target>
|
||||
@@ -3118,6 +3143,11 @@ This is your link for group %@!</source>
|
||||
<target>实时消息</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local" xml:space="preserve">
|
||||
<source>Local</source>
|
||||
<target>本地</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Local name" xml:space="preserve">
|
||||
<source>Local name</source>
|
||||
<target>本地名称</target>
|
||||
@@ -3357,6 +3387,10 @@ This is your link for group %@!</source>
|
||||
<target>新密码</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New chat" xml:space="preserve">
|
||||
<source>New chat</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>新联系人请求</target>
|
||||
@@ -3480,16 +3514,15 @@ This is your link for group %@!</source>
|
||||
- 禁用成员(“观察员”角色)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="OK" xml:space="preserve">
|
||||
<source>OK</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<source>Off</source>
|
||||
<target>关闭</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<source>Off (Local)</source>
|
||||
<target>关闭(本地)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<source>Ok</source>
|
||||
<target>好的</target>
|
||||
@@ -3629,9 +3662,16 @@ This is your link for group %@!</source>
|
||||
<target>开源协议和代码——任何人都可以运行服务器。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Opening database…" xml:space="preserve">
|
||||
<source>Opening database…</source>
|
||||
<target>打开数据库中……</target>
|
||||
<trans-unit id="Opening app…" xml:space="preserve">
|
||||
<source>Opening app…</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or scan QR code" xml:space="preserve">
|
||||
<source>Or scan QR code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Or show this code" xml:space="preserve">
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
@@ -3674,11 +3714,6 @@ This is your link for group %@!</source>
|
||||
<target>显示密码</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste" xml:space="preserve">
|
||||
<source>Paste</source>
|
||||
<target>粘贴</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste desktop address" xml:space="preserve">
|
||||
<source>Paste desktop address</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -3688,16 +3723,10 @@ This is your link for group %@!</source>
|
||||
<target>粘贴图片</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste received link" xml:space="preserve">
|
||||
<source>Paste received link</source>
|
||||
<target>粘贴收到的链接</target>
|
||||
<trans-unit id="Paste the link you received" xml:space="preserve">
|
||||
<source>Paste the link you received</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Paste the link you received to connect with your contact." xml:space="preserve">
|
||||
<source>Paste the link you received to connect with your contact.</source>
|
||||
<target>将您收到的链接粘贴到下面的框中以与您的联系人联系。</target>
|
||||
<note>placeholder</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="People can connect to you only via the links you share." xml:space="preserve">
|
||||
<source>People can connect to you only via the links you share.</source>
|
||||
<target>人们只能通过您共享的链接与您建立联系。</target>
|
||||
@@ -3733,6 +3762,11 @@ This is your link for group %@!</source>
|
||||
<target>请检查您和您的联系人偏好设置。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact developers. Error: %@" xml:space="preserve">
|
||||
<source>Please contact developers.
|
||||
Error: %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please contact group admin." xml:space="preserve">
|
||||
<source>Please contact group admin.</source>
|
||||
<target>请联系群组管理员。</target>
|
||||
@@ -3936,6 +3970,10 @@ This is your link for group %@!</source>
|
||||
<target>在 [用户指南](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address) 中阅读更多内容。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target>在 [用户指南](https://simplex.chat/docs/guide/readme.html#connect-to-friends) 中阅读更多内容。</target>
|
||||
@@ -4144,6 +4182,10 @@ This is your link for group %@!</source>
|
||||
<target>恢复数据库错误</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Retry" xml:space="preserve">
|
||||
<source>Retry</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reveal" xml:space="preserve">
|
||||
<source>Reveal</source>
|
||||
<target>揭示</target>
|
||||
@@ -4298,6 +4340,10 @@ This is your link for group %@!</source>
|
||||
<target>搜索</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Search or paste SimpleX link" xml:space="preserve">
|
||||
<source>Search or paste SimpleX link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Secure queue" xml:space="preserve">
|
||||
<source>Secure queue</source>
|
||||
<target>保护队列</target>
|
||||
@@ -4572,9 +4618,8 @@ This is your link for group %@!</source>
|
||||
<target>分享链接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share one-time invitation link" xml:space="preserve">
|
||||
<source>Share one-time invitation link</source>
|
||||
<target>分享一次性邀请链接</target>
|
||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||
<source>Share this 1-time invite link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||
@@ -4697,16 +4742,15 @@ This is your link for group %@!</source>
|
||||
<target>某人</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start a new chat" xml:space="preserve">
|
||||
<source>Start a new chat</source>
|
||||
<target>开始新聊天</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat" xml:space="preserve">
|
||||
<source>Start chat</source>
|
||||
<target>开始聊天</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start chat?" xml:space="preserve">
|
||||
<source>Start chat?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start migration" xml:space="preserve">
|
||||
<source>Start migration</source>
|
||||
<target>开始迁移</target>
|
||||
@@ -4831,6 +4875,14 @@ This is your link for group %@!</source>
|
||||
<target>点击以加入隐身聊天</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to paste link" xml:space="preserve">
|
||||
<source>Tap to paste link</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to scan" xml:space="preserve">
|
||||
<source>Tap to scan</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap to start a new chat" xml:space="preserve">
|
||||
<source>Tap to start a new chat</source>
|
||||
<target>点击开始一个新聊天</target>
|
||||
@@ -4893,6 +4945,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>更改数据库密码的尝试未完成。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The code you scanned is not a SimpleX link QR code." xml:space="preserve">
|
||||
<source>The code you scanned is not a SimpleX link QR code.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The connection you accepted will be cancelled!" xml:space="preserve">
|
||||
<source>The connection you accepted will be cancelled!</source>
|
||||
<target>您接受的连接将被取消!</target>
|
||||
@@ -4958,6 +5014,10 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target>您当前聊天资料 **%@** 的新连接服务器。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The text you pasted is not a SimpleX link." xml:space="preserve">
|
||||
<source>The text you pasted is not a SimpleX link.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Theme" xml:space="preserve">
|
||||
<source>Theme</source>
|
||||
<target>主题</target>
|
||||
@@ -5106,11 +5166,6 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
<target>关闭</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn off notifications?" xml:space="preserve">
|
||||
<source>Turn off notifications?</source>
|
||||
<target>关闭通知?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Turn on" xml:space="preserve">
|
||||
<source>Turn on</source>
|
||||
<target>打开</target>
|
||||
@@ -5307,6 +5362,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>使用新的隐身配置文件</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use only local notifications?" xml:space="preserve">
|
||||
<source>Use only local notifications?</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
<source>Use server</source>
|
||||
<target>使用服务器</target>
|
||||
@@ -5556,11 +5615,6 @@ Repeat join request?</source>
|
||||
<target>您可以从锁屏上接听电话,无需设备和应用程序的认证。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
|
||||
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
|
||||
<target>您也可以通过点击链接进行连接。如果在浏览器中打开,请点击“在移动应用程序中打开”按钮。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>您可以以后创建它</target>
|
||||
@@ -5581,6 +5635,10 @@ Repeat join request?</source>
|
||||
<target>您可以隐藏或静音用户个人资料——只需向右滑动。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can make it visible to your SimpleX contacts via Settings." xml:space="preserve">
|
||||
<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>
|
||||
<target>您现在可以给 %@ 发送消息</target>
|
||||
@@ -5621,6 +5679,10 @@ Repeat join request?</source>
|
||||
<target>您可以使用 markdown 来编排消息格式:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can view invitation link again in connection details." xml:space="preserve">
|
||||
<source>You can view invitation link again in connection details.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can't send messages!" xml:space="preserve">
|
||||
<source>You can't send messages!</source>
|
||||
<target>您无法发送消息!</target>
|
||||
@@ -5805,13 +5867,6 @@ You can cancel this connection and remove the contact (and try later with a new
|
||||
<target>您的联系人可以允许完全删除消息。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts in SimpleX will see it. You can change it in Settings." xml:space="preserve">
|
||||
<source>Your contacts in SimpleX will see it.
|
||||
You can change it in Settings.</source>
|
||||
<target>您的 SimpleX 的联系人会看到它。
|
||||
您可以在设置中更改它。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your contacts will remain connected." xml:space="preserve">
|
||||
<source>Your contacts will remain connected.</source>
|
||||
<target>与您的联系人保持连接。</target>
|
||||
|
||||
@@ -5868,6 +5868,36 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
<target state="translated">你和你的聯絡人可以新增訊息互動。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ connected" xml:space="preserve" approved="no">
|
||||
<source>%@ connected</source>
|
||||
<target state="translated">%@ 已連接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="# %@" xml:space="preserve" approved="no">
|
||||
<source># %@</source>
|
||||
<target state="translated"># %@</target>
|
||||
<note>copied message info title, # <title></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@" xml:space="preserve" approved="no">
|
||||
<source>%@ and %@</source>
|
||||
<target state="translated">%@ 和 %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## History" xml:space="preserve" approved="no">
|
||||
<source>## History</source>
|
||||
<target state="translated">紀錄</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## In reply to" xml:space="preserve" approved="no">
|
||||
<source>## In reply to</source>
|
||||
<target state="translated">回覆</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve" approved="no">
|
||||
<source>%@ and %@ connected</source>
|
||||
<target state="translated">%@ 和 %@ 已連接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="zh-Hant" datatype="plaintext">
|
||||
|
||||
64
apps/ios/SimpleX NSE/ConcurrentQueue.swift
Normal file
64
apps/ios/SimpleX NSE/ConcurrentQueue.swift
Normal file
@@ -0,0 +1,64 @@
|
||||
//
|
||||
// ConcurrentQueue.swift
|
||||
// SimpleX NSE
|
||||
//
|
||||
// Created by Evgeny on 08/12/2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
struct DequeueElement<T> {
|
||||
var elementId: UUID?
|
||||
var task: Task<T?, Never>
|
||||
}
|
||||
|
||||
class ConcurrentQueue<T> {
|
||||
private var queue: [T] = []
|
||||
private var queueLock = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.concurrent-queue.lock.\(UUID())")
|
||||
private var continuations = [(elementId: UUID, continuation: CheckedContinuation<T?, Never>)]()
|
||||
|
||||
func enqueue(_ el: T) {
|
||||
resumeContinuation(el) { self.queue.append(el) }
|
||||
}
|
||||
|
||||
func frontEnqueue(_ el: T) {
|
||||
resumeContinuation(el) { self.queue.insert(el, at: 0) }
|
||||
}
|
||||
|
||||
private func resumeContinuation(_ el: T, add: @escaping () -> Void) {
|
||||
queueLock.sync {
|
||||
if let (_, cont) = continuations.first {
|
||||
continuations.remove(at: 0)
|
||||
cont.resume(returning: el)
|
||||
} else {
|
||||
add()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func dequeue() -> DequeueElement<T> {
|
||||
queueLock.sync {
|
||||
if queue.isEmpty {
|
||||
let elementId = UUID()
|
||||
let task = Task {
|
||||
await withCheckedContinuation { cont in
|
||||
continuations.append((elementId, cont))
|
||||
}
|
||||
}
|
||||
return DequeueElement(elementId: elementId, task: task)
|
||||
} else {
|
||||
let el = queue.remove(at: 0)
|
||||
return DequeueElement(task: Task { el })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func cancelDequeue(_ elementId: UUID) {
|
||||
queueLock.sync {
|
||||
let cancelled = continuations.filter { $0.elementId == elementId }
|
||||
continuations.removeAll { $0.elementId == elementId }
|
||||
cancelled.forEach { $0.continuation.resume(returning: nil) }
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -14,91 +14,233 @@ import SimpleXChat
|
||||
|
||||
let logger = Logger()
|
||||
|
||||
let suspendingDelay: UInt64 = 2_000_000_000
|
||||
let appSuspendingDelay: UInt64 = 2_500_000_000
|
||||
|
||||
typealias NtfStream = AsyncStream<NSENotification>
|
||||
typealias SuspendSchedule = (delay: TimeInterval, timeout: Int)
|
||||
|
||||
let nseSuspendSchedule: SuspendSchedule = (2, 4)
|
||||
|
||||
let fastNSESuspendSchedule: SuspendSchedule = (1, 1)
|
||||
|
||||
typealias NtfStream = ConcurrentQueue<NSENotification>
|
||||
|
||||
// Notifications are delivered via concurrent queues, as they are all received from chat controller in a single loop that
|
||||
// writes to ConcurrentQueue and when notification is processed, the instance of Notification service extension reads from the queue.
|
||||
// One queue per connection (entity) is used.
|
||||
// The concurrent queues allow read cancellation, to ensure that notifications are not lost in case the current thread completes
|
||||
// before expected notification is read (multiple notifications can be expected, because one notification can be delivered for several messages).
|
||||
actor PendingNtfs {
|
||||
static let shared = PendingNtfs()
|
||||
private var ntfStreams: [String: NtfStream] = [:]
|
||||
private var ntfConts: [String: NtfStream.Continuation] = [:]
|
||||
|
||||
func createStream(_ id: String) {
|
||||
logger.debug("PendingNtfs.createStream: \(id, privacy: .public)")
|
||||
if ntfStreams.index(forKey: id) == nil {
|
||||
ntfStreams[id] = AsyncStream { cont in
|
||||
ntfConts[id] = cont
|
||||
logger.debug("PendingNtfs.createStream: store continuation")
|
||||
}
|
||||
func createStream(_ id: String) async {
|
||||
logger.debug("NotificationService PendingNtfs.createStream: \(id)")
|
||||
if ntfStreams[id] == nil {
|
||||
ntfStreams[id] = ConcurrentQueue()
|
||||
logger.debug("NotificationService PendingNtfs.createStream: created ConcurrentQueue")
|
||||
}
|
||||
}
|
||||
|
||||
func readStream(_ id: String, for nse: NotificationService, msgCount: Int = 1, showNotifications: Bool) async {
|
||||
logger.debug("PendingNtfs.readStream: \(id, privacy: .public) \(msgCount, privacy: .public)")
|
||||
func readStream(_ id: String, for nse: NotificationService, ntfInfo: NtfMessages) async {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: \(id) \(ntfInfo.ntfMessages.count)")
|
||||
if !ntfInfo.user.showNotifications {
|
||||
nse.setBestAttemptNtf(.empty)
|
||||
}
|
||||
if let s = ntfStreams[id] {
|
||||
logger.debug("PendingNtfs.readStream: has stream")
|
||||
var rcvCount = max(1, msgCount)
|
||||
for await ntf in s {
|
||||
nse.setBestAttemptNtf(showNotifications ? ntf : .empty)
|
||||
rcvCount -= 1
|
||||
if rcvCount == 0 || ntf.categoryIdentifier == ntfCategoryCallInvitation { break }
|
||||
logger.debug("NotificationService PendingNtfs.readStream: has stream")
|
||||
var expected = Set(ntfInfo.ntfMessages.map { $0.msgId })
|
||||
logger.debug("NotificationService PendingNtfs.readStream: expecting: \(expected)")
|
||||
var readCancelled = false
|
||||
var dequeued: DequeueElement<NSENotification>?
|
||||
nse.cancelRead = {
|
||||
readCancelled = true
|
||||
if let elementId = dequeued?.elementId {
|
||||
s.cancelDequeue(elementId)
|
||||
}
|
||||
}
|
||||
logger.debug("PendingNtfs.readStream: exiting")
|
||||
while !readCancelled {
|
||||
dequeued = s.dequeue()
|
||||
if let ntf = await dequeued?.task.value {
|
||||
if readCancelled {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: read cancelled, put ntf to queue front")
|
||||
s.frontEnqueue(ntf)
|
||||
break
|
||||
} else if case let .msgInfo(info) = ntf {
|
||||
let found = expected.remove(info.msgId)
|
||||
if found != nil {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: msgInfo, last: \(expected.isEmpty)")
|
||||
if expected.isEmpty { break }
|
||||
} else if let msgTs = ntfInfo.msgTs, info.msgTs > msgTs {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: unexpected msgInfo")
|
||||
s.frontEnqueue(ntf)
|
||||
break
|
||||
}
|
||||
} else if ntfInfo.user.showNotifications {
|
||||
logger.debug("NotificationService PendingNtfs.readStream: setting best attempt")
|
||||
nse.setBestAttemptNtf(ntf)
|
||||
if ntf.isCallInvitation { break }
|
||||
}
|
||||
} else {
|
||||
break
|
||||
}
|
||||
}
|
||||
nse.cancelRead = nil
|
||||
logger.debug("NotificationService PendingNtfs.readStream: exiting")
|
||||
}
|
||||
}
|
||||
|
||||
func writeStream(_ id: String, _ ntf: NSENotification) {
|
||||
logger.debug("PendingNtfs.writeStream: \(id, privacy: .public)")
|
||||
if let cont = ntfConts[id] {
|
||||
logger.debug("PendingNtfs.writeStream: writing ntf")
|
||||
cont.yield(ntf)
|
||||
func writeStream(_ id: String, _ ntf: NSENotification) async {
|
||||
logger.debug("NotificationService PendingNtfs.writeStream: \(id)")
|
||||
if let s = ntfStreams[id] {
|
||||
logger.debug("NotificationService PendingNtfs.writeStream: writing ntf")
|
||||
s.enqueue(ntf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// The current implementation assumes concurrent notification delivery and uses semaphores
|
||||
// to process only one notification per connection (entity) at a time.
|
||||
class NtfStreamSemaphores {
|
||||
static let shared = NtfStreamSemaphores()
|
||||
private static let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-semaphores.lock")
|
||||
private var semaphores: [String: DispatchSemaphore] = [:]
|
||||
|
||||
func waitForStream(_ id: String) {
|
||||
streamSemaphore(id, value: 0)?.wait()
|
||||
}
|
||||
|
||||
func signalStreamReady(_ id: String) {
|
||||
streamSemaphore(id, value: 1)?.signal()
|
||||
}
|
||||
|
||||
// this function returns nil if semaphore is just created, so passed value shoud be coordinated with the desired end value of the semaphore
|
||||
private func streamSemaphore(_ id: String, value: Int) -> DispatchSemaphore? {
|
||||
NtfStreamSemaphores.queue.sync {
|
||||
if let s = semaphores[id] {
|
||||
return s
|
||||
} else {
|
||||
semaphores[id] = DispatchSemaphore(value: value)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
enum NSENotification {
|
||||
case nse(notification: UNMutableNotificationContent)
|
||||
case callkit(invitation: RcvCallInvitation)
|
||||
case nse(UNMutableNotificationContent)
|
||||
case callkit(RcvCallInvitation)
|
||||
case empty
|
||||
case msgInfo(NtfMsgInfo)
|
||||
|
||||
var categoryIdentifier: String? {
|
||||
var isCallInvitation: Bool {
|
||||
switch self {
|
||||
case let .nse(ntf): return ntf.categoryIdentifier
|
||||
case .callkit: return ntfCategoryCallInvitation
|
||||
case .empty: return nil
|
||||
case let .nse(ntf): ntf.categoryIdentifier == ntfCategoryCallInvitation
|
||||
case .callkit: true
|
||||
case .empty: false
|
||||
case .msgInfo: false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Once the last thread in the process completes processing chat controller is suspended, and the database is closed, to avoid
|
||||
// background crashes and contention for database with the application (both UI and background fetch triggered either on schedule
|
||||
// or when background notification is received.
|
||||
class NSEThreads {
|
||||
static let shared = NSEThreads()
|
||||
private static let queue = DispatchQueue(label: "chat.simplex.app.SimpleX-NSE.notification-threads.lock")
|
||||
private var allThreads: Set<UUID> = []
|
||||
private var activeThreads: Set<UUID> = []
|
||||
|
||||
func newThread() -> UUID {
|
||||
NSEThreads.queue.sync {
|
||||
let (_, t) = allThreads.insert(UUID())
|
||||
return t
|
||||
}
|
||||
}
|
||||
|
||||
func startThread(_ t: UUID) {
|
||||
NSEThreads.queue.sync {
|
||||
if allThreads.contains(t) {
|
||||
_ = activeThreads.insert(t)
|
||||
} else {
|
||||
logger.warning("NotificationService startThread: thread \(t) was removed before it started")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func endThread(_ t: UUID) -> Bool {
|
||||
NSEThreads.queue.sync {
|
||||
let tActive = activeThreads.remove(t)
|
||||
let t = allThreads.remove(t)
|
||||
if tActive != nil && activeThreads.isEmpty {
|
||||
return true
|
||||
}
|
||||
if t != nil && allThreads.isEmpty {
|
||||
NSEChatState.shared.set(.suspended)
|
||||
}
|
||||
return false
|
||||
}
|
||||
}
|
||||
|
||||
var noThreads: Bool {
|
||||
allThreads.isEmpty
|
||||
}
|
||||
}
|
||||
|
||||
// Notification service extension creates a new instance of the class and calls didReceive for each notification.
|
||||
// Each didReceive is called in its own thread, but multiple calls can be made in one process, and, empirically, there is never
|
||||
// more than one process of notification service extension exists at a time.
|
||||
// Soon after notification service delivers the last notification it is either suspended or terminated.
|
||||
class NotificationService: UNNotificationServiceExtension {
|
||||
var contentHandler: ((UNNotificationContent) -> Void)?
|
||||
var bestAttemptNtf: NSENotification?
|
||||
var badgeCount: Int = 0
|
||||
// thread is added to allThreads here - if thread did not start chat,
|
||||
// chat does not need to be suspended but NSE state still needs to be set to "suspended".
|
||||
var threadId: UUID? = NSEThreads.shared.newThread()
|
||||
var receiveEntityId: String?
|
||||
var cancelRead: (() -> Void)?
|
||||
var appSubscriber: AppSubscriber?
|
||||
var returnedSuspension = false
|
||||
|
||||
override func didReceive(_ request: UNNotificationRequest, withContentHandler contentHandler: @escaping (UNNotificationContent) -> Void) {
|
||||
logger.debug("DEBUGGING: NotificationService.didReceive")
|
||||
if let ntf = request.content.mutableCopy() as? UNMutableNotificationContent {
|
||||
setBestAttemptNtf(ntf)
|
||||
}
|
||||
let ntf = if let ntf_ = request.content.mutableCopy() as? UNMutableNotificationContent { ntf_ } else { UNMutableNotificationContent() }
|
||||
setBestAttemptNtf(ntf)
|
||||
self.contentHandler = contentHandler
|
||||
registerGroupDefaults()
|
||||
let appState = appStateGroupDefault.get()
|
||||
logger.debug("NotificationService: app is \(appState.rawValue)")
|
||||
switch appState {
|
||||
case .stopped:
|
||||
setBadgeCount()
|
||||
setBestAttemptNtf(createAppStoppedNtf())
|
||||
deliverBestAttemptNtf()
|
||||
case .suspended:
|
||||
logger.debug("NotificationService: app is suspended")
|
||||
setBadgeCount()
|
||||
receiveNtfMessages(request, contentHandler)
|
||||
case .suspending:
|
||||
logger.debug("NotificationService: app is suspending")
|
||||
setBadgeCount()
|
||||
Task {
|
||||
var state = appState
|
||||
for _ in 1...5 {
|
||||
_ = try await Task.sleep(nanoseconds: suspendingDelay)
|
||||
state = appStateGroupDefault.get()
|
||||
if state == .suspended || state != .suspending { break }
|
||||
let state: AppState = await withCheckedContinuation { cont in
|
||||
appSubscriber = appStateSubscriber { s in
|
||||
if s == .suspended { appSuspension(s) }
|
||||
}
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + Double(appSuspendTimeout) + 1) {
|
||||
logger.debug("NotificationService: appSuspension timeout")
|
||||
appSuspension(appStateGroupDefault.get())
|
||||
}
|
||||
|
||||
@Sendable
|
||||
func appSuspension(_ s: AppState) {
|
||||
if !self.returnedSuspension {
|
||||
self.returnedSuspension = true
|
||||
self.appSubscriber = nil // this disposes of appStateSubscriber
|
||||
cont.resume(returning: s)
|
||||
}
|
||||
}
|
||||
}
|
||||
logger.debug("NotificationService: app state is \(state.rawValue, privacy: .public)")
|
||||
logger.debug("NotificationService: app state is now \(state.rawValue)")
|
||||
if state.inactive {
|
||||
receiveNtfMessages(request, contentHandler)
|
||||
} else {
|
||||
@@ -106,7 +248,6 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
}
|
||||
default:
|
||||
logger.debug("NotificationService: app state is \(appState.rawValue, privacy: .public)")
|
||||
deliverBestAttemptNtf()
|
||||
}
|
||||
}
|
||||
@@ -121,27 +262,35 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
if let ntfData = userInfo["notificationData"] as? [AnyHashable : Any],
|
||||
let nonce = ntfData["nonce"] as? String,
|
||||
let encNtfInfo = ntfData["message"] as? String,
|
||||
let dbStatus = startChat() {
|
||||
// check it here again
|
||||
appStateGroupDefault.get().inactive {
|
||||
// thread is added to activeThreads tracking set here - if thread started chat it needs to be suspended
|
||||
if let t = threadId { NSEThreads.shared.startThread(t) }
|
||||
let dbStatus = startChat()
|
||||
if case .ok = dbStatus,
|
||||
let ntfMsgInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
|
||||
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfMsgInfo), privacy: .public)")
|
||||
if let connEntity = ntfMsgInfo.connEntity {
|
||||
let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
|
||||
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessages.count))")
|
||||
if let connEntity = ntfInfo.connEntity_ {
|
||||
setBestAttemptNtf(
|
||||
ntfMsgInfo.ntfsEnabled
|
||||
? .nse(notification: createConnectionEventNtf(ntfMsgInfo.user, connEntity))
|
||||
ntfInfo.ntfsEnabled
|
||||
? .nse(createConnectionEventNtf(ntfInfo.user, connEntity))
|
||||
: .empty
|
||||
)
|
||||
if let id = connEntity.id {
|
||||
Task {
|
||||
logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id, privacy: .public)")
|
||||
await PendingNtfs.shared.createStream(id)
|
||||
await PendingNtfs.shared.readStream(id, for: self, msgCount: ntfMsgInfo.ntfMessages.count, showNotifications: ntfMsgInfo.user.showNotifications)
|
||||
deliverBestAttemptNtf()
|
||||
receiveEntityId = id
|
||||
NtfStreamSemaphores.shared.waitForStream(id)
|
||||
if receiveEntityId != nil {
|
||||
Task {
|
||||
logger.debug("NotificationService: receiveNtfMessages: in Task, connEntity id \(id)")
|
||||
await PendingNtfs.shared.createStream(id)
|
||||
await PendingNtfs.shared.readStream(id, for: self, ntfInfo: ntfInfo)
|
||||
deliverBestAttemptNtf()
|
||||
}
|
||||
}
|
||||
return
|
||||
}
|
||||
}
|
||||
return
|
||||
} else {
|
||||
} else if let dbStatus = dbStatus {
|
||||
setBestAttemptNtf(createErrorNtf(dbStatus))
|
||||
}
|
||||
}
|
||||
@@ -150,7 +299,7 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
|
||||
override func serviceExtensionTimeWillExpire() {
|
||||
logger.debug("DEBUGGING: NotificationService.serviceExtensionTimeWillExpire")
|
||||
deliverBestAttemptNtf()
|
||||
deliverBestAttemptNtf(urgent: true)
|
||||
}
|
||||
|
||||
func setBadgeCount() {
|
||||
@@ -159,91 +308,301 @@ class NotificationService: UNNotificationServiceExtension {
|
||||
}
|
||||
|
||||
func setBestAttemptNtf(_ ntf: UNMutableNotificationContent) {
|
||||
setBestAttemptNtf(.nse(notification: ntf))
|
||||
setBestAttemptNtf(.nse(ntf))
|
||||
}
|
||||
|
||||
func setBestAttemptNtf(_ ntf: NSENotification) {
|
||||
logger.debug("NotificationService.setBestAttemptNtf")
|
||||
if case let .nse(notification) = ntf {
|
||||
notification.badge = badgeCount as NSNumber
|
||||
bestAttemptNtf = .nse(notification: notification)
|
||||
bestAttemptNtf = .nse(notification)
|
||||
} else {
|
||||
bestAttemptNtf = ntf
|
||||
}
|
||||
}
|
||||
|
||||
private func deliverBestAttemptNtf() {
|
||||
private func deliverBestAttemptNtf(urgent: Bool = false) {
|
||||
logger.debug("NotificationService.deliverBestAttemptNtf")
|
||||
if let cancel = cancelRead {
|
||||
cancelRead = nil
|
||||
cancel()
|
||||
}
|
||||
if let id = receiveEntityId {
|
||||
receiveEntityId = nil
|
||||
NtfStreamSemaphores.shared.signalStreamReady(id)
|
||||
}
|
||||
let suspend: Bool
|
||||
if let t = threadId {
|
||||
threadId = nil
|
||||
suspend = NSEThreads.shared.endThread(t) && NSEThreads.shared.noThreads
|
||||
} else {
|
||||
suspend = false
|
||||
}
|
||||
deliverCallkitOrNotification(urgent: urgent, suspend: suspend)
|
||||
}
|
||||
|
||||
private func deliverCallkitOrNotification(urgent: Bool, suspend: Bool = false) {
|
||||
if case .callkit = bestAttemptNtf {
|
||||
logger.debug("NotificationService.deliverCallkitOrNotification: will suspend, callkit")
|
||||
if urgent {
|
||||
// suspending NSE even though there may be other notifications
|
||||
// to allow the app to process callkit call
|
||||
suspendChat(0)
|
||||
deliverNotification()
|
||||
} else {
|
||||
// suspending NSE with delay and delivering after the suspension
|
||||
// because pushkit notification must be processed without delay
|
||||
// to avoid app termination
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + fastNSESuspendSchedule.delay) {
|
||||
suspendChat(fastNSESuspendSchedule.timeout)
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + Double(fastNSESuspendSchedule.timeout)) {
|
||||
self.deliverNotification()
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if suspend {
|
||||
logger.debug("NotificationService.deliverCallkitOrNotification: will suspend")
|
||||
if urgent {
|
||||
suspendChat(0)
|
||||
} else {
|
||||
// suspension is delayed to allow chat core finalise any processing
|
||||
// (e.g., send delivery receipts)
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + nseSuspendSchedule.delay) {
|
||||
if NSEThreads.shared.noThreads {
|
||||
suspendChat(nseSuspendSchedule.timeout)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
deliverNotification()
|
||||
}
|
||||
}
|
||||
|
||||
private func deliverNotification() {
|
||||
if let handler = contentHandler, let ntf = bestAttemptNtf {
|
||||
contentHandler = nil
|
||||
bestAttemptNtf = nil
|
||||
let deliver: (UNMutableNotificationContent?) -> Void = { ntf in
|
||||
let useNtf = if let ntf = ntf {
|
||||
appStateGroupDefault.get().running ? UNMutableNotificationContent() : ntf
|
||||
} else {
|
||||
UNMutableNotificationContent()
|
||||
}
|
||||
handler(useNtf)
|
||||
}
|
||||
switch ntf {
|
||||
case let .nse(content): handler(content)
|
||||
case let .nse(content): deliver(content)
|
||||
case let .callkit(invitation):
|
||||
logger.debug("NotificationService reportNewIncomingVoIPPushPayload for \(invitation.contact.id)")
|
||||
CXProvider.reportNewIncomingVoIPPushPayload([
|
||||
"displayName": invitation.contact.displayName,
|
||||
"contactId": invitation.contact.id,
|
||||
"media": invitation.callType.media.rawValue
|
||||
]) { error in
|
||||
if error == nil {
|
||||
handler(UNMutableNotificationContent())
|
||||
} else {
|
||||
logger.debug("reportNewIncomingVoIPPushPayload success to CallController for \(invitation.contact.id)")
|
||||
handler(createCallInvitationNtf(invitation))
|
||||
}
|
||||
logger.debug("reportNewIncomingVoIPPushPayload result: \(error)")
|
||||
deliver(error == nil ? nil : createCallInvitationNtf(invitation))
|
||||
}
|
||||
case .empty: handler(UNMutableNotificationContent())
|
||||
case .empty: deliver(nil) // used to mute notifications that did not unsubscribe yet
|
||||
case .msgInfo: deliver(nil) // unreachable, the best attempt is never set to msgInfo
|
||||
}
|
||||
bestAttemptNtf = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var chatStarted = false
|
||||
var networkConfig: NetCfg = getNetCfg()
|
||||
var xftpConfig: XFTPFileConfig? = getXFTPCfg()
|
||||
// nseStateGroupDefault must not be used in NSE directly, only via this singleton
|
||||
class NSEChatState {
|
||||
static let shared = NSEChatState()
|
||||
private var value_ = NSEState.created
|
||||
|
||||
var value: NSEState {
|
||||
value_
|
||||
}
|
||||
|
||||
func set(_ state: NSEState) {
|
||||
nseStateGroupDefault.set(state)
|
||||
sendNSEState(state)
|
||||
value_ = state
|
||||
}
|
||||
|
||||
init() {
|
||||
// This is always set to .created state, as in case previous start of NSE crashed in .active state, it is stored correctly.
|
||||
// Otherwise the app will be activating slower
|
||||
set(.created)
|
||||
}
|
||||
}
|
||||
|
||||
var appSubscriber: AppSubscriber = appStateSubscriber { state in
|
||||
logger.debug("NotificationService: appSubscriber")
|
||||
if state.running && NSEChatState.shared.value.canSuspend {
|
||||
logger.debug("NotificationService: appSubscriber app state \(state.rawValue), suspending")
|
||||
suspendChat(fastNSESuspendSchedule.timeout)
|
||||
}
|
||||
}
|
||||
|
||||
func appStateSubscriber(onState: @escaping (AppState) -> Void) -> AppSubscriber {
|
||||
appMessageSubscriber { msg in
|
||||
if case let .state(state) = msg {
|
||||
logger.debug("NotificationService: appStateSubscriber \(state.rawValue)")
|
||||
onState(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
var receiverStarted = false
|
||||
let startLock = DispatchSemaphore(value: 1)
|
||||
let suspendLock = DispatchSemaphore(value: 1)
|
||||
var networkConfig: NetCfg = getNetCfg()
|
||||
let xftpConfig: XFTPFileConfig? = getXFTPCfg()
|
||||
|
||||
// startChat uses semaphore startLock to ensure that only one didReceive thread can start chat controller
|
||||
// Subsequent calls to didReceive will be waiting on semaphore and won't start chat again, as it will be .active
|
||||
func startChat() -> DBMigrationResult? {
|
||||
hs_init(0, nil)
|
||||
if chatStarted { return .ok }
|
||||
let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation())
|
||||
logger.debug("NotificationService: startChat")
|
||||
// only skip creating if there is chat controller
|
||||
if case .active = NSEChatState.shared.value, hasChatCtrl() { return .ok }
|
||||
|
||||
startLock.wait()
|
||||
defer { startLock.signal() }
|
||||
|
||||
if hasChatCtrl() {
|
||||
return switch NSEChatState.shared.value {
|
||||
case .created: doStartChat()
|
||||
case .starting: .ok // it should never get to this branch, as it would be waiting for start on startLock
|
||||
case .active: .ok
|
||||
case .suspending: activateChat()
|
||||
case .suspended: activateChat()
|
||||
}
|
||||
} else {
|
||||
// Ignore state in preference if there is no chat controller.
|
||||
// State in preference may have failed to update e.g. because of a crash.
|
||||
NSEChatState.shared.set(.created)
|
||||
return doStartChat()
|
||||
}
|
||||
}
|
||||
|
||||
func doStartChat() -> DBMigrationResult? {
|
||||
logger.debug("NotificationService: doStartChat")
|
||||
haskell_init_nse()
|
||||
let (_, dbStatus) = chatMigrateInit(confirmMigrations: defaultMigrationConfirmation(), backgroundMode: true)
|
||||
logger.debug("NotificationService: doStartChat \(String(describing: dbStatus))")
|
||||
if dbStatus != .ok {
|
||||
resetChatCtrl()
|
||||
NSEChatState.shared.set(.created)
|
||||
return dbStatus
|
||||
}
|
||||
let state = NSEChatState.shared.value
|
||||
NSEChatState.shared.set(.starting)
|
||||
if let user = apiGetActiveUser() {
|
||||
logger.debug("active user \(String(describing: user))")
|
||||
logger.debug("NotificationService active user \(String(describing: user))")
|
||||
do {
|
||||
try setNetworkConfig(networkConfig)
|
||||
try apiSetTempFolder(tempFolder: getTempFilesDirectory().path)
|
||||
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
|
||||
try setXFTPConfig(xftpConfig)
|
||||
try apiSetEncryptLocalFiles(privacyEncryptLocalFilesGroupDefault.get())
|
||||
let justStarted = try apiStartChat()
|
||||
chatStarted = true
|
||||
if justStarted {
|
||||
chatLastStartGroupDefault.set(Date.now)
|
||||
Task { await receiveMessages() }
|
||||
// prevent suspension while starting chat
|
||||
suspendLock.wait()
|
||||
defer { suspendLock.signal() }
|
||||
if NSEChatState.shared.value == .starting {
|
||||
updateNetCfg()
|
||||
let justStarted = try apiStartChat()
|
||||
NSEChatState.shared.set(.active)
|
||||
if justStarted {
|
||||
chatLastStartGroupDefault.set(Date.now)
|
||||
Task {
|
||||
if !receiverStarted {
|
||||
receiverStarted = true
|
||||
await receiveMessages()
|
||||
}
|
||||
}
|
||||
}
|
||||
return .ok
|
||||
}
|
||||
return .ok
|
||||
} catch {
|
||||
logger.error("NotificationService startChat error: \(responseError(error), privacy: .public)")
|
||||
logger.error("NotificationService startChat error: \(responseError(error))")
|
||||
}
|
||||
} else {
|
||||
logger.debug("no active user")
|
||||
logger.debug("NotificationService: no active user")
|
||||
}
|
||||
if NSEChatState.shared.value == .starting { NSEChatState.shared.set(state) }
|
||||
return nil
|
||||
}
|
||||
|
||||
func activateChat() -> DBMigrationResult? {
|
||||
logger.debug("NotificationService: activateChat")
|
||||
let state = NSEChatState.shared.value
|
||||
NSEChatState.shared.set(.active)
|
||||
if apiActivateChat() {
|
||||
logger.debug("NotificationService: activateChat: after apiActivateChat")
|
||||
return .ok
|
||||
} else {
|
||||
NSEChatState.shared.set(state)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
// suspendChat uses semaphore suspendLock to ensure that only one suspension can happen.
|
||||
func suspendChat(_ timeout: Int) {
|
||||
logger.debug("NotificationService: suspendChat")
|
||||
let state = NSEChatState.shared.value
|
||||
if !state.canSuspend {
|
||||
logger.error("NotificationService suspendChat called, current state: \(state.rawValue)")
|
||||
} else if hasChatCtrl() {
|
||||
// only suspend if we have chat controller to avoid crashes when suspension is
|
||||
// attempted when chat controller was not created
|
||||
suspendLock.wait()
|
||||
defer { suspendLock.signal() }
|
||||
|
||||
NSEChatState.shared.set(.suspending)
|
||||
if apiSuspendChat(timeoutMicroseconds: timeout * 1000000) {
|
||||
logger.debug("NotificationService: suspendChat: after apiSuspendChat")
|
||||
DispatchQueue.global().asyncAfter(deadline: .now() + Double(timeout) + 1, execute: chatSuspended)
|
||||
} else {
|
||||
NSEChatState.shared.set(state)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func chatSuspended() {
|
||||
logger.debug("NotificationService chatSuspended")
|
||||
if case .suspending = NSEChatState.shared.value {
|
||||
NSEChatState.shared.set(.suspended)
|
||||
chatCloseStore()
|
||||
logger.debug("NotificationService chatSuspended: suspended")
|
||||
}
|
||||
}
|
||||
|
||||
// A single loop is used per Notification service extension process to receive and process all messages depending on the NSE state
|
||||
// If the extension is not active yet, or suspended/suspending, or the app is running, the notifications will no be received.
|
||||
func receiveMessages() async {
|
||||
logger.debug("NotificationService receiveMessages")
|
||||
while true {
|
||||
updateNetCfg()
|
||||
switch NSEChatState.shared.value {
|
||||
// it should never get to "created" and "starting" branches, as NSE state is set to .active before the loop start
|
||||
case .created: await delayWhenInactive()
|
||||
case .starting: await delayWhenInactive()
|
||||
case .active: await receiveMsg()
|
||||
case .suspending: await receiveMsg()
|
||||
case .suspended: await delayWhenInactive()
|
||||
}
|
||||
}
|
||||
|
||||
func receiveMsg() async {
|
||||
if let msg = await chatRecvMsg() {
|
||||
logger.debug("NotificationService receiveMsg: message")
|
||||
if let (id, ntf) = await receivedMsgNtf(msg) {
|
||||
logger.debug("NotificationService receiveMsg: notification")
|
||||
await PendingNtfs.shared.createStream(id)
|
||||
await PendingNtfs.shared.writeStream(id, ntf)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func delayWhenInactive() async {
|
||||
logger.debug("NotificationService delayWhenInactive")
|
||||
_ = try? await Task.sleep(nanoseconds: 1000_000000)
|
||||
}
|
||||
}
|
||||
|
||||
func chatRecvMsg() async -> ChatResponse? {
|
||||
@@ -257,14 +616,14 @@ private let isInChina = SKStorefront().countryCode == "CHN"
|
||||
private func useCallKit() -> Bool { !isInChina && callKitEnabledGroupDefault.get() }
|
||||
|
||||
func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
logger.debug("NotificationService processReceivedMsg: \(res.responseType)")
|
||||
logger.debug("NotificationService receivedMsgNtf: \(res.responseType)")
|
||||
switch res {
|
||||
case let .contactConnected(user, contact, _):
|
||||
return (contact.id, .nse(notification: createContactConnectedNtf(user, contact)))
|
||||
return (contact.id, .nse(createContactConnectedNtf(user, contact)))
|
||||
// case let .contactConnecting(contact):
|
||||
// TODO profile update
|
||||
case let .receivedContactRequest(user, contactRequest):
|
||||
return (UserContact(contactRequest: contactRequest).id, .nse(notification: createContactRequestNtf(user, contactRequest)))
|
||||
return (UserContact(contactRequest: contactRequest).id, .nse(createContactRequestNtf(user, contactRequest)))
|
||||
case let .newChatItem(user, aChatItem):
|
||||
let cInfo = aChatItem.chatInfo
|
||||
var cItem = aChatItem.chatItem
|
||||
@@ -274,7 +633,7 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
if let file = cItem.autoReceiveFile() {
|
||||
cItem = autoReceiveFile(file, encrypted: cItem.encryptLocalFile) ?? cItem
|
||||
}
|
||||
let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(notification: createMessageReceivedNtf(user, cInfo, cItem)) : .empty
|
||||
let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(createMessageReceivedNtf(user, cInfo, cItem)) : .empty
|
||||
return cItem.showNotification ? (aChatItem.chatId, ntf) : nil
|
||||
case let .rcvFileSndCancelled(_, aChatItem, _):
|
||||
cleanupFile(aChatItem)
|
||||
@@ -292,10 +651,18 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
// Do not post it without CallKit support, iOS will stop launching the app without showing CallKit
|
||||
return (
|
||||
invitation.contact.id,
|
||||
useCallKit() ? .callkit(invitation: invitation) : .nse(notification: createCallInvitationNtf(invitation))
|
||||
useCallKit() ? .callkit(invitation) : .nse(createCallInvitationNtf(invitation))
|
||||
)
|
||||
case let .ntfMessage(_, connEntity, ntfMessage):
|
||||
return if let id = connEntity.id { (id, .msgInfo(ntfMessage)) } else { nil }
|
||||
case .chatSuspended:
|
||||
chatSuspended()
|
||||
return nil
|
||||
case let .chatError(_, err):
|
||||
logger.error("NotificationService receivedMsgNtf error: \(String(describing: err))")
|
||||
return nil
|
||||
default:
|
||||
logger.debug("NotificationService processReceivedMsg ignored event: \(res.responseType)")
|
||||
logger.debug("NotificationService receivedMsgNtf ignored event: \(res.responseType)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
@@ -308,17 +675,22 @@ func updateNetCfg() {
|
||||
try setNetworkConfig(networkConfig)
|
||||
networkConfig = newNetConfig
|
||||
} catch {
|
||||
logger.error("NotificationService apply changed network config error: \(responseError(error), privacy: .public)")
|
||||
logger.error("NotificationService apply changed network config error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func apiGetActiveUser() -> User? {
|
||||
let r = sendSimpleXCmd(.showActiveUser)
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(String(describing: r))")
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd response: \(r.responseType)")
|
||||
switch r {
|
||||
case let .activeUser(user): return user
|
||||
case .chatCmdError(_, .error(.noActiveUser)): return nil
|
||||
case .chatCmdError(_, .error(.noActiveUser)):
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd no active user")
|
||||
return nil
|
||||
case let .chatCmdError(_, err):
|
||||
logger.debug("apiGetActiveUser sendSimpleXCmd error: \(String(describing: err))")
|
||||
return nil
|
||||
default:
|
||||
logger.error("NotificationService apiGetActiveUser unexpected response: \(String(describing: r))")
|
||||
return nil
|
||||
@@ -326,7 +698,7 @@ func apiGetActiveUser() -> User? {
|
||||
}
|
||||
|
||||
func apiStartChat() throws -> Bool {
|
||||
let r = sendSimpleXCmd(.startChat(subscribe: false, expire: false, xftp: false))
|
||||
let r = sendSimpleXCmd(.startChat(mainApp: false))
|
||||
switch r {
|
||||
case .chatStarted: return true
|
||||
case .chatRunning: return false
|
||||
@@ -334,6 +706,21 @@ func apiStartChat() throws -> Bool {
|
||||
}
|
||||
}
|
||||
|
||||
func apiActivateChat() -> Bool {
|
||||
chatReopenStore()
|
||||
let r = sendSimpleXCmd(.apiActivateChat(restoreChat: false))
|
||||
if case .cmdOk = r { return true }
|
||||
logger.error("NotificationService apiActivateChat error: \(String(describing: r))")
|
||||
return false
|
||||
}
|
||||
|
||||
func apiSuspendChat(timeoutMicroseconds: Int) -> Bool {
|
||||
let r = sendSimpleXCmd(.apiSuspendChat(timeoutMicroseconds: timeoutMicroseconds))
|
||||
if case .cmdOk = r { return true }
|
||||
logger.error("NotificationService apiSuspendChat error: \(String(describing: r))")
|
||||
return false
|
||||
}
|
||||
|
||||
func apiSetTempFolder(tempFolder: String) throws {
|
||||
let r = sendSimpleXCmd(.setTempFolder(tempFolder: tempFolder))
|
||||
if case .cmdOk = r { return }
|
||||
@@ -364,12 +751,13 @@ 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 {
|
||||
return NtfMessages(user: user, connEntity: connEntity, msgTs: msgTs, ntfMessages: ntfMessages)
|
||||
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)
|
||||
} else if case let .chatCmdError(_, error) = r {
|
||||
logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))")
|
||||
} else {
|
||||
logger.debug("apiGetNtfMessage ignored response: \(r.responseType, privacy: .public) \(String.init(describing: r), privacy: .private)")
|
||||
logger.debug("apiGetNtfMessage ignored response: \(r.responseType) \(String.init(describing: r))")
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -405,11 +793,11 @@ func setNetworkConfig(_ cfg: NetCfg) throws {
|
||||
|
||||
struct NtfMessages {
|
||||
var user: User
|
||||
var connEntity: ConnectionEntity?
|
||||
var connEntity_: ConnectionEntity?
|
||||
var msgTs: Date?
|
||||
var ntfMessages: [NtfMsgInfo]
|
||||
|
||||
var ntfsEnabled: Bool {
|
||||
user.showNotifications && (connEntity?.ntfsEnabled ?? false)
|
||||
user.showNotifications && (connEntity_?.ntfsEnabled ?? false)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,7 +16,6 @@
|
||||
18415C6C56DBCEC2CBBD2F11 /* WebRTCClient.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18415323A4082FC92887F906 /* WebRTCClient.swift */; };
|
||||
18415F9A2D551F9757DA4654 /* CIVideoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 18415FD2E36F13F596A45BB4 /* CIVideoView.swift */; };
|
||||
18415FEFE153C5920BFB7828 /* GroupWelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 1841516F0CE5992B0EDFB377 /* GroupWelcomeView.swift */; };
|
||||
3C8C548928133C84000A3EC7 /* PasteToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */; };
|
||||
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */; };
|
||||
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* CILinkView.swift */; };
|
||||
5C00164428A26FBC0094D739 /* ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C00164328A26FBC0094D739 /* ContextMenu.swift */; };
|
||||
@@ -30,6 +29,11 @@
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C245F192B4DB982001CC39F /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F142B4DB982001CC39F /* libgmpxx.a */; };
|
||||
5C245F1A2B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F152B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl-ghc9.6.3.a */; };
|
||||
5C245F1B2B4DB982001CC39F /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F162B4DB982001CC39F /* libgmp.a */; };
|
||||
5C245F1C2B4DB982001CC39F /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F172B4DB982001CC39F /* libffi.a */; };
|
||||
5C245F1D2B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C245F182B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl.a */; };
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
@@ -56,7 +60,6 @@
|
||||
5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */; };
|
||||
5C65DAF929D0CC20003CEE45 /* DeveloperView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C65DAF829D0CC20003CEE45 /* DeveloperView.swift */; };
|
||||
5C65F343297D45E100B67AF3 /* VersionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C65F341297D3F3600B67AF3 /* VersionView.swift */; };
|
||||
5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; };
|
||||
5C6BA667289BD954009B8ECC /* DismissSheets.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6BA666289BD954009B8ECC /* DismissSheets.swift */; };
|
||||
5C7031162953C97F00150A12 /* CIFeaturePreferenceView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7031152953C97F00150A12 /* CIFeaturePreferenceView.swift */; };
|
||||
5C7505A227B65FDB00BE3227 /* CIMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A127B65FDB00BE3227 /* CIMetaView.swift */; };
|
||||
@@ -93,8 +96,6 @@
|
||||
5CB0BA92282713FD00B3292C /* CreateProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB0BA91282713FD00B3292C /* CreateProfile.swift */; };
|
||||
5CB0BA9A2827FD8800B3292C /* HowItWorks.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB0BA992827FD8800B3292C /* HowItWorks.swift */; };
|
||||
5CB2084F28DA4B4800D024EC /* RTCServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB2084E28DA4B4800D024EC /* RTCServers.swift */; };
|
||||
5CB2085128DB64CA00D024EC /* CreateLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB2085028DB64CA00D024EC /* CreateLinkView.swift */; };
|
||||
5CB2085328DB7CAF00D024EC /* ConnectViaLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB2085228DB7CAF00D024EC /* ConnectViaLinkView.swift */; };
|
||||
5CB346E52868AA7F001FD2EF /* SuspendChat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E42868AA7F001FD2EF /* SuspendChat.swift */; };
|
||||
5CB346E72868D76D001FD2EF /* NotificationsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E62868D76D001FD2EF /* NotificationsView.swift */; };
|
||||
5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */; };
|
||||
@@ -116,15 +117,8 @@
|
||||
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; };
|
||||
5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */; };
|
||||
5CCB939C297EFCB100399E78 /* NavStackCompat.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCB939B297EFCB100399E78 /* NavStackCompat.swift */; };
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; };
|
||||
5CCD403727A5F9A200368C90 /* ScanToConnectView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */; };
|
||||
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */ = {isa = PBXBuildFile; fileRef = 5CD67B8D2B0E858A00C510B1 /* hs_init.h */; settings = {ATTRIBUTES = (Public, ); }; };
|
||||
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */ = {isa = PBXBuildFile; fileRef = 5CD67B8E2B0E858A00C510B1 /* hs_init.c */; };
|
||||
5CD67BA02B120ADF00C510B1 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9B2B120ADF00C510B1 /* libgmp.a */; };
|
||||
5CD67BA12B120ADF00C510B1 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9C2B120ADF00C510B1 /* libgmpxx.a */; };
|
||||
5CD67BA22B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9D2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a */; };
|
||||
5CD67BA32B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9E2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a */; };
|
||||
5CD67BA42B120ADF00C510B1 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CD67B9F2B120ADF00C510B1 /* libffi.a */; };
|
||||
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CDCAD472818589900503DA2 /* NotificationService.swift */; };
|
||||
5CE2BA702845308900EC33A6 /* SimpleXChat.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; };
|
||||
5CE2BA712845308900EC33A6 /* SimpleXChat.framework in Embed Frameworks */ = {isa = PBXBuildFile; fileRef = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */; settings = {ATTRIBUTES = (CodeSignOnCopy, RemoveHeadersOnCopy, ); }; };
|
||||
@@ -150,10 +144,15 @@
|
||||
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
|
||||
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; };
|
||||
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; };
|
||||
5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */; };
|
||||
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */; };
|
||||
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF937212B25034A00E1D781 /* NSESubscriber.swift */; };
|
||||
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; };
|
||||
5CFA59D12864782E00863A68 /* ChatArchiveView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59CF286477B400863A68 /* ChatArchiveView.swift */; };
|
||||
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
|
||||
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */; };
|
||||
640417CD2B29B8C200CCB412 /* NewChatMenuButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640417CB2B29B8C200CCB412 /* NewChatMenuButton.swift */; };
|
||||
640417CE2B29B8C200CCB412 /* NewChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640417CC2B29B8C200CCB412 /* NewChatView.swift */; };
|
||||
6407BA83295DA85D0082BA18 /* CIInvalidJSONView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */; };
|
||||
6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */; };
|
||||
6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */; };
|
||||
@@ -185,6 +184,7 @@
|
||||
64D0C2C629FAC1EC00B38D5F /* AddContactLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C529FAC1EC00B38D5F /* AddContactLearnMore.swift */; };
|
||||
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; };
|
||||
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
|
||||
8C05382E2B39887E006436DC /* VideoUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C05382D2B39887E006436DC /* VideoUtils.swift */; };
|
||||
D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; };
|
||||
D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; };
|
||||
D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; };
|
||||
@@ -259,7 +259,6 @@
|
||||
18415B08031E8FB0F7FC27F9 /* CallViewRenderers.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CallViewRenderers.swift; sourceTree = "<group>"; };
|
||||
18415DAAAD1ADBEDB0EDA852 /* VideoPlayerView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = VideoPlayerView.swift; sourceTree = "<group>"; };
|
||||
18415FD2E36F13F596A45BB4 /* CIVideoView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = CIVideoView.swift; sourceTree = "<group>"; };
|
||||
3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = PasteToConnectView.swift; sourceTree = "<group>"; };
|
||||
3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeLinkView.swift; sourceTree = "<group>"; };
|
||||
3CDBCF4727FF621E00354CDD /* CILinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CILinkView.swift; sourceTree = "<group>"; };
|
||||
5C00164328A26FBC0094D739 /* ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenu.swift; sourceTree = "<group>"; };
|
||||
@@ -276,6 +275,11 @@
|
||||
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
|
||||
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||
5C245F142B4DB982001CC39F /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C245F152B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C245F162B4DB982001CC39F /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C245F172B4DB982001CC39F /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C245F182B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl.a"; sourceTree = "<group>"; };
|
||||
5C2E260627A2941F00F70299 /* SimpleXAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXAPI.swift; sourceTree = "<group>"; };
|
||||
5C2E260A27A30CFA00F70299 /* ChatListView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListView.swift; sourceTree = "<group>"; };
|
||||
5C2E260E27A30FDC00F70299 /* ChatView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatView.swift; sourceTree = "<group>"; };
|
||||
@@ -315,7 +319,6 @@
|
||||
5C65DAED29CB8908003CEE45 /* es */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = es; path = es.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
5C65DAF829D0CC20003CEE45 /* DeveloperView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeveloperView.swift; sourceTree = "<group>"; };
|
||||
5C65F341297D3F3600B67AF3 /* VersionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = VersionView.swift; sourceTree = "<group>"; };
|
||||
5C6AD81227A834E300348BD7 /* NewChatButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NewChatButton.swift; sourceTree = "<group>"; };
|
||||
5C6BA666289BD954009B8ECC /* DismissSheets.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DismissSheets.swift; sourceTree = "<group>"; };
|
||||
5C6D183229E93FBA00D430B3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = "pl.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5C6D183329E93FBA00D430B3 /* pl */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = pl; path = pl.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
@@ -372,8 +375,6 @@
|
||||
5CB0BA91282713FD00B3292C /* CreateProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateProfile.swift; sourceTree = "<group>"; };
|
||||
5CB0BA992827FD8800B3292C /* HowItWorks.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HowItWorks.swift; sourceTree = "<group>"; };
|
||||
5CB2084E28DA4B4800D024EC /* RTCServers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = RTCServers.swift; sourceTree = "<group>"; };
|
||||
5CB2085028DB64CA00D024EC /* CreateLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateLinkView.swift; sourceTree = "<group>"; };
|
||||
5CB2085228DB7CAF00D024EC /* ConnectViaLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectViaLinkView.swift; sourceTree = "<group>"; };
|
||||
5CB2085428DE647400D024EC /* de */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = de; path = de.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SuspendChat.swift; sourceTree = "<group>"; };
|
||||
5CB346E62868D76D001FD2EF /* NotificationsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationsView.swift; sourceTree = "<group>"; };
|
||||
@@ -399,15 +400,8 @@
|
||||
5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5CC868F229EB540C0017BBFD /* CIRcvDecryptionError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIRcvDecryptionError.swift; sourceTree = "<group>"; };
|
||||
5CCB939B297EFCB100399E78 /* NavStackCompat.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NavStackCompat.swift; sourceTree = "<group>"; };
|
||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; };
|
||||
5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ScanToConnectView.swift; sourceTree = "<group>"; };
|
||||
5CD67B8D2B0E858A00C510B1 /* hs_init.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = hs_init.h; sourceTree = "<group>"; };
|
||||
5CD67B8E2B0E858A00C510B1 /* hs_init.c */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.c; path = hs_init.c; sourceTree = "<group>"; };
|
||||
5CD67B9B2B120ADF00C510B1 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CD67B9C2B120ADF00C510B1 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CD67B9D2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a"; sourceTree = "<group>"; };
|
||||
5CD67B9E2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5CD67B9F2B120ADF00C510B1 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CDCAD452818589900503DA2 /* SimpleX NSE.appex */ = {isa = PBXFileReference; explicitFileType = "wrapper.app-extension"; includeInIndex = 0; path = "SimpleX NSE.appex"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5CDCAD472818589900503DA2 /* NotificationService.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NotificationService.swift; sourceTree = "<group>"; };
|
||||
5CDCAD492818589900503DA2 /* Info.plist */ = {isa = PBXFileReference; lastKnownFileType = text.plist.xml; path = Info.plist; sourceTree = "<group>"; };
|
||||
@@ -434,9 +428,14 @@
|
||||
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
|
||||
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; };
|
||||
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; };
|
||||
5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConcurrentQueue.swift; sourceTree = "<group>"; };
|
||||
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFileSubscriber.swift; sourceTree = "<group>"; };
|
||||
5CF937212B25034A00E1D781 /* NSESubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESubscriber.swift; sourceTree = "<group>"; };
|
||||
5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; };
|
||||
5CFA59CF286477B400863A68 /* ChatArchiveView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatArchiveView.swift; sourceTree = "<group>"; };
|
||||
5CFE0920282EEAF60002594B /* ZoomableScrollView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; name = ZoomableScrollView.swift; path = Shared/Views/ZoomableScrollView.swift; sourceTree = SOURCE_ROOT; };
|
||||
640417CB2B29B8C200CCB412 /* NewChatMenuButton.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewChatMenuButton.swift; sourceTree = "<group>"; };
|
||||
640417CC2B29B8C200CCB412 /* NewChatView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = NewChatView.swift; sourceTree = "<group>"; };
|
||||
6407BA82295DA85D0082BA18 /* CIInvalidJSONView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIInvalidJSONView.swift; sourceTree = "<group>"; };
|
||||
6419EC552AB8BC8B004A607A /* ContextInvitingContactMemberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextInvitingContactMemberView.swift; sourceTree = "<group>"; };
|
||||
6419EC572AB97507004A607A /* CIMemberCreatedContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIMemberCreatedContactView.swift; sourceTree = "<group>"; };
|
||||
@@ -470,6 +469,7 @@
|
||||
64DAE1502809D9F5000DA960 /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
|
||||
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.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>"; };
|
||||
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; };
|
||||
@@ -511,12 +511,12 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CD67BA12B120ADF00C510B1 /* libgmpxx.a in Frameworks */,
|
||||
5CD67BA22B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a in Frameworks */,
|
||||
5C245F192B4DB982001CC39F /* libgmpxx.a in Frameworks */,
|
||||
5C245F1C2B4DB982001CC39F /* libffi.a in Frameworks */,
|
||||
5C245F1D2B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl.a in Frameworks */,
|
||||
5C245F1B2B4DB982001CC39F /* libgmp.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5CD67BA02B120ADF00C510B1 /* libgmp.a in Frameworks */,
|
||||
5CD67BA42B120ADF00C510B1 /* libffi.a in Frameworks */,
|
||||
5CD67BA32B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a in Frameworks */,
|
||||
5C245F1A2B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl-ghc9.6.3.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
@@ -579,11 +579,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CD67B9F2B120ADF00C510B1 /* libffi.a */,
|
||||
5CD67B9B2B120ADF00C510B1 /* libgmp.a */,
|
||||
5CD67B9C2B120ADF00C510B1 /* libgmpxx.a */,
|
||||
5CD67B9E2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u-ghc8.10.7.a */,
|
||||
5CD67B9D2B120ADF00C510B1 /* libHSsimplex-chat-5.4.0.6-9DfazyElTA72omjHp0C93u.a */,
|
||||
5C245F172B4DB982001CC39F /* libffi.a */,
|
||||
5C245F162B4DB982001CC39F /* libgmp.a */,
|
||||
5C245F142B4DB982001CC39F /* libgmpxx.a */,
|
||||
5C245F152B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl-ghc9.6.3.a */,
|
||||
5C245F182B4DB982001CC39F /* libHSsimplex-chat-5.5.0.0-K5xQiJJwtSUKGqIyB7d1Tl.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -608,6 +608,7 @@
|
||||
5C35CFC727B2782E00FB6C6D /* BGManager.swift */,
|
||||
5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */,
|
||||
5CB346E42868AA7F001FD2EF /* SuspendChat.swift */,
|
||||
5CF937212B25034A00E1D781 /* NSESubscriber.swift */,
|
||||
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */,
|
||||
5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */,
|
||||
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
|
||||
@@ -635,6 +636,7 @@
|
||||
64466DCB29FFE3E800E3D48D /* MailView.swift */,
|
||||
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */,
|
||||
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */,
|
||||
8C05382D2B39887E006436DC /* VideoUtils.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
@@ -727,14 +729,10 @@
|
||||
5CB924DD27A8622200ACCCDD /* NewChat */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5C6AD81227A834E300348BD7 /* NewChatButton.swift */,
|
||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */,
|
||||
5CCD403627A5F9A200368C90 /* ScanToConnectView.swift */,
|
||||
3C8C548828133C84000A3EC7 /* PasteToConnectView.swift */,
|
||||
640417CB2B29B8C200CCB412 /* NewChatMenuButton.swift */,
|
||||
640417CC2B29B8C200CCB412 /* NewChatView.swift */,
|
||||
5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */,
|
||||
6442E0B9287F169300CEC0F9 /* AddGroupView.swift */,
|
||||
5CB2085028DB64CA00D024EC /* CreateLinkView.swift */,
|
||||
5CB2085228DB7CAF00D024EC /* ConnectViaLinkView.swift */,
|
||||
64D0C2C529FAC1EC00B38D5F /* AddContactLearnMore.swift */,
|
||||
);
|
||||
path = NewChat;
|
||||
@@ -788,6 +786,7 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CDCAD5128186DE400503DA2 /* SimpleX NSE.entitlements */,
|
||||
5CF9371D2B23429500E1D781 /* ConcurrentQueue.swift */,
|
||||
5CDCAD472818589900503DA2 /* NotificationService.swift */,
|
||||
5CDCAD492818589900503DA2 /* Info.plist */,
|
||||
5CB0BA862826CB3A00B3292C /* InfoPlist.strings */,
|
||||
@@ -808,6 +807,7 @@
|
||||
64DAE1502809D9F5000DA960 /* FileUtils.swift */,
|
||||
5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */,
|
||||
5C00168028C4FE760094D739 /* KeyChain.swift */,
|
||||
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */,
|
||||
5CE2BA76284530BF00EC33A6 /* SimpleXChat.h */,
|
||||
5CE2BA8A2845332200EC33A6 /* SimpleX.h */,
|
||||
5CE2BA78284530CC00EC33A6 /* SimpleXChat.docc */,
|
||||
@@ -1101,8 +1101,8 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */,
|
||||
640417CE2B29B8C200CCB412 /* NewChatView.swift in Sources */,
|
||||
6440CA03288AECA70062C672 /* AddGroupMembersView.swift in Sources */,
|
||||
5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */,
|
||||
5C3F1D58284363C400EC8A82 /* PrivacySettings.swift in Sources */,
|
||||
5C55A923283CEDE600C4E99E /* SoundPlayer.swift in Sources */,
|
||||
5C93292F29239A170090FFF9 /* ProtocolServersView.swift in Sources */,
|
||||
@@ -1149,13 +1149,11 @@
|
||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
||||
5CC868F329EB540C0017BBFD /* CIRcvDecryptionError.swift in Sources */,
|
||||
5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */,
|
||||
3C8C548928133C84000A3EC7 /* PasteToConnectView.swift in Sources */,
|
||||
5C9D13A3282187BB00AB8B43 /* WebRTC.swift in Sources */,
|
||||
5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */,
|
||||
5CB0BA8E2827126500B3292C /* OnboardingView.swift in Sources */,
|
||||
6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */,
|
||||
5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */,
|
||||
5CB2085128DB64CA00D024EC /* CreateLinkView.swift in Sources */,
|
||||
5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */,
|
||||
5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */,
|
||||
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */,
|
||||
@@ -1164,8 +1162,8 @@
|
||||
5CB9250D27A9432000ACCCDD /* ChatListNavLink.swift in Sources */,
|
||||
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */,
|
||||
5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */,
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */,
|
||||
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */,
|
||||
640417CD2B29B8C200CCB412 /* NewChatMenuButton.swift in Sources */,
|
||||
5CFE0921282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
|
||||
5C3A88CE27DF50170060F1C2 /* DetermineWidth.swift in Sources */,
|
||||
5C7505A527B679EE00BE3227 /* NavLinkPlain.swift in Sources */,
|
||||
@@ -1180,6 +1178,7 @@
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */,
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */,
|
||||
6442E0BA287F169300CEC0F9 /* AddGroupView.swift in Sources */,
|
||||
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */,
|
||||
6419EC582AB97507004A607A /* CIMemberCreatedContactView.swift in Sources */,
|
||||
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */,
|
||||
64466DCC29FFE3E800E3D48D /* MailView.swift in Sources */,
|
||||
@@ -1199,9 +1198,9 @@
|
||||
6448BBB628FA9D56000D2AB9 /* GroupLinkView.swift in Sources */,
|
||||
5CB346E92869E8BA001FD2EF /* PushEnvironment.swift in Sources */,
|
||||
5C55A91F283AD0E400C4E99E /* CallManager.swift in Sources */,
|
||||
5CCD403727A5F9A200368C90 /* ScanToConnectView.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 */,
|
||||
@@ -1221,7 +1220,6 @@
|
||||
5C93293F2928E0FD0090FFF9 /* AudioRecPlay.swift in Sources */,
|
||||
5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */,
|
||||
5CE4407227ADB1D0007B033A /* Emoji.swift in Sources */,
|
||||
5CB2085328DB7CAF00D024EC /* ConnectViaLinkView.swift in Sources */,
|
||||
5C9CC7A928C532AB00BEF955 /* DatabaseErrorView.swift in Sources */,
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */,
|
||||
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */,
|
||||
@@ -1259,6 +1257,7 @@
|
||||
files = (
|
||||
5CDCAD482818589900503DA2 /* NotificationService.swift in Sources */,
|
||||
5CFE0922282EEAF60002594B /* ZoomableScrollView.swift in Sources */,
|
||||
5CF9371E2B23429500E1D781 /* ConcurrentQueue.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -1266,6 +1265,7 @@
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */,
|
||||
5C00168128C4FE760094D739 /* KeyChain.swift in Sources */,
|
||||
5CE2BA97284537A800EC33A6 /* dummy.m in Sources */,
|
||||
5CE2BA922845340900EC33A6 /* FileUtils.swift in Sources */,
|
||||
@@ -1502,7 +1502,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 184;
|
||||
CURRENT_PROJECT_VERSION = 187;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1524,7 +1524,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4;
|
||||
MARKETING_VERSION = 5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1545,7 +1545,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 184;
|
||||
CURRENT_PROJECT_VERSION = 187;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1567,7 +1567,7 @@
|
||||
"$(inherited)",
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4;
|
||||
MARKETING_VERSION = 5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1626,7 +1626,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 184;
|
||||
CURRENT_PROJECT_VERSION = 187;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1639,7 +1639,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4;
|
||||
MARKETING_VERSION = 5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1658,7 +1658,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 184;
|
||||
CURRENT_PROJECT_VERSION = 187;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1671,7 +1671,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
MARKETING_VERSION = 5.4;
|
||||
MARKETING_VERSION = 5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1690,7 +1690,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 184;
|
||||
CURRENT_PROJECT_VERSION = 187;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1714,7 +1714,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MARKETING_VERSION = 5.4;
|
||||
MARKETING_VERSION = 5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1736,7 +1736,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 184;
|
||||
CURRENT_PROJECT_VERSION = 187;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1760,7 +1760,7 @@
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MARKETING_VERSION = 5.4;
|
||||
MARKETING_VERSION = 5.5;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
|
||||
@@ -41,7 +41,7 @@
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Release"
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1400"
|
||||
wasCreatedForAppExtension = "YES"
|
||||
version = "2.0">
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
@@ -47,16 +47,14 @@
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = ""
|
||||
selectedLauncherIdentifier = "Xcode.IDEFoundation.Launcher.PosixSpawn"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
askForAppToLaunch = "Yes"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
launchAutomaticallySubstyle = "2">
|
||||
allowLocationSimulation = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
|
||||
@@ -12,12 +12,16 @@ private var chatController: chat_ctrl?
|
||||
|
||||
private var migrationResult: (Bool, DBMigrationResult)?
|
||||
|
||||
public func getChatCtrl(_ useKey: String? = nil) -> chat_ctrl {
|
||||
public func hasChatCtrl() -> Bool {
|
||||
chatController != nil
|
||||
}
|
||||
|
||||
public func getChatCtrl() -> chat_ctrl {
|
||||
if let controller = chatController { return controller }
|
||||
fatalError("chat controller not initialized")
|
||||
}
|
||||
|
||||
public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: MigrationConfirmation? = nil) -> (Bool, DBMigrationResult) {
|
||||
public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: MigrationConfirmation? = nil, backgroundMode: Bool = false) -> (Bool, DBMigrationResult) {
|
||||
if let res = migrationResult { return res }
|
||||
let dbPath = getAppDatabasePath().path
|
||||
var dbKey = ""
|
||||
@@ -41,7 +45,7 @@ public func chatMigrateInit(_ useKey: String? = nil, confirmMigrations: Migratio
|
||||
var cKey = dbKey.cString(using: .utf8)!
|
||||
var cConfirm = confirm.rawValue.cString(using: .utf8)!
|
||||
// the last parameter of chat_migrate_init is used to return the pointer to chat controller
|
||||
let cjson = chat_migrate_init(&cPath, &cKey, &cConfirm, &chatController)!
|
||||
let cjson = chat_migrate_init_key(&cPath, &cKey, 1, &cConfirm, backgroundMode ? 1 : 0, &chatController)!
|
||||
let dbRes = dbMigrationResult(fromCString(cjson))
|
||||
let encrypted = dbKey != ""
|
||||
let keychainErr = dbRes == .ok && useKeychain && encrypted && !kcDatabasePassword.set(dbKey)
|
||||
@@ -57,6 +61,13 @@ public func chatCloseStore() {
|
||||
}
|
||||
}
|
||||
|
||||
public func chatReopenStore() {
|
||||
let err = fromCString(chat_reopen_store(getChatCtrl()))
|
||||
if err != "" {
|
||||
logger.error("chatReopenStore error: \(err)")
|
||||
}
|
||||
}
|
||||
|
||||
public func resetChatCtrl() {
|
||||
chatController = nil
|
||||
migrationResult = nil
|
||||
|
||||
@@ -25,9 +25,9 @@ public enum ChatCommand {
|
||||
case apiMuteUser(userId: Int64)
|
||||
case apiUnmuteUser(userId: Int64)
|
||||
case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?)
|
||||
case startChat(subscribe: Bool, expire: Bool, xftp: Bool)
|
||||
case startChat(mainApp: Bool)
|
||||
case apiStopChat
|
||||
case apiActivateChat
|
||||
case apiActivateChat(restoreChat: Bool)
|
||||
case apiSuspendChat(timeoutMicroseconds: Int)
|
||||
case setTempFolder(tempFolder: String)
|
||||
case setFilesFolder(filesFolder: String)
|
||||
@@ -154,9 +154,9 @@ 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(subscribe, expire, xftp): return "/_start subscribe=\(onOff(subscribe)) expire=\(onOff(expire)) xftp=\(onOff(xftp))"
|
||||
case let .startChat(mainApp): return "/_start main=\(onOff(mainApp))"
|
||||
case .apiStopChat: return "/_stop"
|
||||
case .apiActivateChat: return "/_app activate"
|
||||
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
|
||||
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
|
||||
case let .setTempFolder(tempFolder): return "/_temp_folder \(tempFolder)"
|
||||
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
|
||||
@@ -505,8 +505,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection)
|
||||
case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection)
|
||||
case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan)
|
||||
case sentConfirmation(user: UserRef)
|
||||
case sentInvitation(user: UserRef)
|
||||
case sentConfirmation(user: UserRef, connection: PendingContactConnection)
|
||||
case sentInvitation(user: UserRef, connection: PendingContactConnection)
|
||||
case sentInvitationToContact(user: UserRef, contact: Contact, customUserProfile: Profile?)
|
||||
case contactAlreadyExists(user: UserRef, contact: Contact)
|
||||
case contactRequestAlreadyAccepted(user: UserRef, contact: Contact)
|
||||
@@ -604,8 +604,8 @@ public enum ChatResponse: Decodable, Error {
|
||||
case callInvitations(callInvitations: [RcvCallInvitation])
|
||||
case ntfTokenStatus(status: NtfTknStatus)
|
||||
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode)
|
||||
case ntfMessages(user_: User?, connEntity: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
||||
case newContactConnection(user: UserRef, connection: PendingContactConnection)
|
||||
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
|
||||
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo)
|
||||
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
|
||||
// remote desktop responses/events
|
||||
case remoteCtrlList(remoteCtrls: [RemoteCtrlInfo])
|
||||
@@ -613,7 +613,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case remoteCtrlConnecting(remoteCtrl_: RemoteCtrlInfo?, ctrlAppInfo: CtrlAppInfo, appVersion: String)
|
||||
case remoteCtrlSessionCode(remoteCtrl_: RemoteCtrlInfo?, sessionCode: String)
|
||||
case remoteCtrlConnected(remoteCtrl: RemoteCtrlInfo)
|
||||
case remoteCtrlStopped
|
||||
case remoteCtrlStopped(rcsState: RemoteCtrlSessionState, rcStopReason: RemoteCtrlStopReason)
|
||||
// misc
|
||||
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
|
||||
case cmdOk(user: UserRef?)
|
||||
@@ -752,7 +752,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case .ntfTokenStatus: return "ntfTokenStatus"
|
||||
case .ntfToken: return "ntfToken"
|
||||
case .ntfMessages: return "ntfMessages"
|
||||
case .newContactConnection: return "newContactConnection"
|
||||
case .ntfMessage: return "ntfMessage"
|
||||
case .contactConnectionDeleted: return "contactConnectionDeleted"
|
||||
case .remoteCtrlList: return "remoteCtrlList"
|
||||
case .remoteCtrlFound: return "remoteCtrlFound"
|
||||
@@ -803,11 +803,11 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .contactCode(u, contact, connectionCode): return withUser(u, "contact: \(String(describing: contact))\nconnectionCode: \(connectionCode)")
|
||||
case let .groupMemberCode(u, groupInfo, member, connectionCode): return withUser(u, "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionCode: \(connectionCode)")
|
||||
case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)")
|
||||
case let .invitation(u, connReqInvitation, _): return withUser(u, connReqInvitation)
|
||||
case let .invitation(u, connReqInvitation, connection): return withUser(u, "connReqInvitation: \(connReqInvitation)\nconnection: \(connection)")
|
||||
case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection))
|
||||
case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan))
|
||||
case .sentConfirmation: return noDetails
|
||||
case .sentInvitation: return noDetails
|
||||
case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection))
|
||||
case let .sentInvitation(u, connection): return withUser(u, String(describing: connection))
|
||||
case let .sentInvitationToContact(u, contact, _): return withUser(u, String(describing: contact))
|
||||
case let .contactAlreadyExists(u, contact): return withUser(u, String(describing: contact))
|
||||
case let .contactRequestAlreadyAccepted(u, contact): return withUser(u, String(describing: contact))
|
||||
@@ -900,7 +900,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case let .ntfTokenStatus(status): return String(describing: status)
|
||||
case let .ntfToken(token, status, ntfMode): return "token: \(token)\nstatus: \(status.rawValue)\nntfMode: \(ntfMode.rawValue)"
|
||||
case let .ntfMessages(u, connEntity, msgTs, ntfMessages): return withUser(u, "connEntity: \(String(describing: connEntity))\nmsgTs: \(String(describing: msgTs))\nntfMessages: \(String(describing: ntfMessages))")
|
||||
case let .newContactConnection(u, connection): return withUser(u, String(describing: connection))
|
||||
case let .ntfMessage(u, connEntity, ntfMessage): return withUser(u, "connEntity: \(String(describing: connEntity))\nntfMessage: \(String(describing: ntfMessage))")
|
||||
case let .contactConnectionDeleted(u, connection): return withUser(u, String(describing: connection))
|
||||
case let .remoteCtrlList(remoteCtrls): return String(describing: remoteCtrls)
|
||||
case let .remoteCtrlFound(remoteCtrl, ctrlAppInfo_, appVersion, compatible): return "remoteCtrl:\n\(String(describing: remoteCtrl))\nctrlAppInfo_:\n\(String(describing: ctrlAppInfo_))\nappVersion: \(appVersion)\ncompatible: \(compatible)"
|
||||
@@ -1207,9 +1207,9 @@ public struct NetCfg: Codable, Equatable {
|
||||
public static let defaults: NetCfg = NetCfg(
|
||||
socksProxy: nil,
|
||||
sessionMode: TransportSessionMode.user,
|
||||
tcpConnectTimeout: 15_000_000,
|
||||
tcpTimeout: 10_000_000,
|
||||
tcpTimeoutPerKb: 30_000,
|
||||
tcpConnectTimeout: 20_000_000,
|
||||
tcpTimeout: 15_000_000,
|
||||
tcpTimeoutPerKb: 45_000,
|
||||
tcpKeepAlive: KeepAliveOpts.defaults,
|
||||
smpPingInterval: 1200_000_000,
|
||||
smpPingCount: 3,
|
||||
@@ -1498,6 +1498,8 @@ public enum PushProvider: String, Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
// This notification mode is for app core, UI uses AppNotificationsMode.off to mean completely disable,
|
||||
// and .local for periodic background checks
|
||||
public enum NotificationsMode: String, Decodable, SelectableItem {
|
||||
case off = "OFF"
|
||||
case periodic = "PERIODIC"
|
||||
@@ -1505,9 +1507,9 @@ public enum NotificationsMode: String, Decodable, SelectableItem {
|
||||
|
||||
public var label: LocalizedStringKey {
|
||||
switch self {
|
||||
case .off: return "Off (Local)"
|
||||
case .periodic: return "Periodically"
|
||||
case .instant: return "Instantly"
|
||||
case .off: "Local"
|
||||
case .periodic: "Periodically"
|
||||
case .instant: "Instantly"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1552,6 +1554,13 @@ public enum RemoteCtrlSessionState: Decodable {
|
||||
case connected(sessionCode: String)
|
||||
}
|
||||
|
||||
public enum RemoteCtrlStopReason: Decodable {
|
||||
case discoveryFailed(chatError: ChatError)
|
||||
case connectionFailed(chatError: ChatError)
|
||||
case setupFailed(chatError: ChatError)
|
||||
case disconnected
|
||||
}
|
||||
|
||||
public struct CtrlAppInfo: Decodable {
|
||||
public var appVersionRange: AppVersionRange
|
||||
public var deviceName: String
|
||||
@@ -1601,6 +1610,7 @@ public enum ChatErrorType: Decodable {
|
||||
case userUnknown
|
||||
case activeUserExists
|
||||
case userExists
|
||||
case invalidDisplayName
|
||||
case differentActiveUser(commandUserId: Int64, activeUserId: Int64)
|
||||
case cantDeleteActiveUser(userId: Int64)
|
||||
case cantDeleteLastUser(userId: Int64)
|
||||
|
||||
@@ -9,12 +9,16 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
public let appSuspendTimeout: Int = 15 // seconds
|
||||
|
||||
let GROUP_DEFAULT_APP_STATE = "appState"
|
||||
let GROUP_DEFAULT_NSE_STATE = "nseState"
|
||||
let GROUP_DEFAULT_DB_CONTAINER = "dbContainer"
|
||||
public let GROUP_DEFAULT_CHAT_LAST_START = "chatLastStart"
|
||||
public let GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN = "chatLastBackgroundRun"
|
||||
let GROUP_DEFAULT_NTF_PREVIEW_MODE = "ntfPreviewMode"
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal"
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic"
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal" // no longer used
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic" // no longer used
|
||||
let GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES = "privacyAcceptImages"
|
||||
public let GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE = "privacyTransferImagesInline" // no longer used
|
||||
public let GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES = "privacyEncryptLocalFiles"
|
||||
@@ -66,13 +70,23 @@ public func registerGroupDefaults() {
|
||||
])
|
||||
}
|
||||
|
||||
public enum AppState: String {
|
||||
public enum AppState: String, Codable {
|
||||
case active
|
||||
case activating
|
||||
case bgRefresh
|
||||
case suspending
|
||||
case suspended
|
||||
case stopped
|
||||
|
||||
public var running: Bool {
|
||||
switch self {
|
||||
case .active: return true
|
||||
case .activating: return true
|
||||
case .bgRefresh: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
public var inactive: Bool {
|
||||
switch self {
|
||||
case .suspending: return true
|
||||
@@ -84,23 +98,57 @@ public enum AppState: String {
|
||||
public var canSuspend: Bool {
|
||||
switch self {
|
||||
case .active: return true
|
||||
case .activating: return true
|
||||
case .bgRefresh: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum NSEState: String, Codable {
|
||||
case created
|
||||
case starting
|
||||
case active
|
||||
case suspending
|
||||
case suspended
|
||||
|
||||
public var inactive: Bool {
|
||||
switch self {
|
||||
case .created: true
|
||||
case .suspended: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
public var canSuspend: Bool {
|
||||
if case .active = self { true } else { false }
|
||||
}
|
||||
}
|
||||
|
||||
public enum DBContainer: String {
|
||||
case documents
|
||||
case group
|
||||
}
|
||||
|
||||
// appStateGroupDefault must not be used in the app directly, only via AppChatState singleton
|
||||
public let appStateGroupDefault = EnumDefault<AppState>(
|
||||
defaults: groupDefaults,
|
||||
forKey: GROUP_DEFAULT_APP_STATE,
|
||||
withDefault: .active
|
||||
)
|
||||
|
||||
// nseStateGroupDefault must not be used in NSE directly, only via NSEChatState singleton
|
||||
public let nseStateGroupDefault = EnumDefault<NSEState>(
|
||||
defaults: groupDefaults,
|
||||
forKey: GROUP_DEFAULT_NSE_STATE,
|
||||
withDefault: .suspended // so that NSE that was never launched does not delay the app from resuming
|
||||
)
|
||||
|
||||
// inactive app states do not include "stopped" state
|
||||
public func allowBackgroundRefresh() -> Bool {
|
||||
appStateGroupDefault.get().inactive && nseStateGroupDefault.get().inactive
|
||||
}
|
||||
|
||||
public let dbContainerGroupDefault = EnumDefault<DBContainer>(
|
||||
defaults: groupDefaults,
|
||||
forKey: GROUP_DEFAULT_DB_CONTAINER,
|
||||
@@ -109,6 +157,8 @@ public let dbContainerGroupDefault = EnumDefault<DBContainer>(
|
||||
|
||||
public let chatLastStartGroupDefault = DateDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CHAT_LAST_START)
|
||||
|
||||
public let chatLastBackgroundRunGroupDefault = DateDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_CHAT_LAST_BACKGROUND_RUN)
|
||||
|
||||
public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>(
|
||||
defaults: groupDefaults,
|
||||
forKey: GROUP_DEFAULT_NTF_PREVIEW_MODE,
|
||||
@@ -117,10 +167,6 @@ public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>(
|
||||
|
||||
public let incognitoGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_INCOGNITO)
|
||||
|
||||
public let ntfEnableLocalGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_ENABLE_LOCAL)
|
||||
|
||||
public let ntfEnablePeriodicGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_ENABLE_PERIODIC)
|
||||
|
||||
public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES)
|
||||
|
||||
public let privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES)
|
||||
|
||||
@@ -172,7 +172,6 @@ public func fromLocalProfile (_ profile: LocalProfile) -> Profile {
|
||||
}
|
||||
|
||||
public struct UserProfileUpdateSummary: Decodable {
|
||||
public var notChanged: Int
|
||||
public var updateSuccesses: Int
|
||||
public var updateFailures: Int
|
||||
public var changedContacts: [Contact]
|
||||
@@ -616,8 +615,8 @@ public enum ChatFeature: String, Decodable, Feature {
|
||||
}
|
||||
case .fullDelete:
|
||||
switch allowed {
|
||||
case .always: return "Allow your contacts to irreversibly delete sent messages."
|
||||
case .yes: return "Allow irreversible message deletion only if your contact allows it to you."
|
||||
case .always: return "Allow your contacts to irreversibly delete sent messages. (24 hours)"
|
||||
case .yes: return "Allow irreversible message deletion only if your contact allows it to you. (24 hours)"
|
||||
case .no: return "Contacts can mark messages for deletion; you will be able to view them."
|
||||
}
|
||||
case .reactions:
|
||||
@@ -653,11 +652,11 @@ public enum ChatFeature: String, Decodable, Feature {
|
||||
: "Disappearing messages are prohibited in this chat."
|
||||
case .fullDelete:
|
||||
return enabled.forUser && enabled.forContact
|
||||
? "Both you and your contact can irreversibly delete sent messages."
|
||||
? "Both you and your contact can irreversibly delete sent messages. (24 hours)"
|
||||
: enabled.forUser
|
||||
? "Only you can irreversibly delete messages (your contact can mark them for deletion)."
|
||||
? "Only you can irreversibly delete messages (your contact can mark them for deletion). (24 hours)"
|
||||
: enabled.forContact
|
||||
? "Only your contact can irreversibly delete messages (you can mark them for deletion)."
|
||||
? "Only your contact can irreversibly delete messages (you can mark them for deletion). (24 hours)"
|
||||
: "Irreversible message deletion is prohibited in this chat."
|
||||
case .reactions:
|
||||
return enabled.forUser && enabled.forContact
|
||||
@@ -694,6 +693,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case reactions
|
||||
case voice
|
||||
case files
|
||||
case history
|
||||
|
||||
public var id: Self { self }
|
||||
|
||||
@@ -712,6 +712,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .reactions: return NSLocalizedString("Message reactions", comment: "chat feature")
|
||||
case .voice: return NSLocalizedString("Voice messages", comment: "chat feature")
|
||||
case .files: return NSLocalizedString("Files and media", comment: "chat feature")
|
||||
case .history: return NSLocalizedString("Visible history", comment: "chat feature")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -723,6 +724,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .reactions: return "face.smiling"
|
||||
case .voice: return "mic"
|
||||
case .files: return "doc"
|
||||
case .history: return "clock"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -734,6 +736,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .reactions: return "face.smiling.fill"
|
||||
case .voice: return "mic.fill"
|
||||
case .files: return "doc.fill"
|
||||
case .history: return "clock.fill"
|
||||
}
|
||||
}
|
||||
|
||||
@@ -759,7 +762,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
}
|
||||
case .fullDelete:
|
||||
switch enabled {
|
||||
case .on: return "Allow to irreversibly delete sent messages."
|
||||
case .on: return "Allow to irreversibly delete sent messages. (24 hours)"
|
||||
case .off: return "Prohibit irreversible message deletion."
|
||||
}
|
||||
case .reactions:
|
||||
@@ -777,6 +780,11 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .on: return "Allow to send files and media."
|
||||
case .off: return "Prohibit sending files and media."
|
||||
}
|
||||
case .history:
|
||||
switch enabled {
|
||||
case .on: return "Send up to 100 last messages to new members."
|
||||
case .off: return "Do not send history to new members."
|
||||
}
|
||||
}
|
||||
} else {
|
||||
switch self {
|
||||
@@ -792,7 +800,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
}
|
||||
case .fullDelete:
|
||||
switch enabled {
|
||||
case .on: return "Group members can irreversibly delete sent messages."
|
||||
case .on: return "Group members can irreversibly delete sent messages. (24 hours)"
|
||||
case .off: return "Irreversible message deletion is prohibited in this group."
|
||||
}
|
||||
case .reactions:
|
||||
@@ -810,6 +818,11 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .on: return "Group members can send files and media."
|
||||
case .off: return "Files and media are prohibited in this group."
|
||||
}
|
||||
case .history:
|
||||
switch enabled {
|
||||
case .on: return "Up to 100 last messages are sent to new members."
|
||||
case .off: return "History is not sent to new members."
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -949,6 +962,7 @@ public struct FullGroupPreferences: Decodable, Equatable {
|
||||
public var reactions: GroupPreference
|
||||
public var voice: GroupPreference
|
||||
public var files: GroupPreference
|
||||
public var history: GroupPreference
|
||||
|
||||
public init(
|
||||
timedMessages: TimedMessagesGroupPreference,
|
||||
@@ -956,7 +970,8 @@ public struct FullGroupPreferences: Decodable, Equatable {
|
||||
fullDelete: GroupPreference,
|
||||
reactions: GroupPreference,
|
||||
voice: GroupPreference,
|
||||
files: GroupPreference
|
||||
files: GroupPreference,
|
||||
history: GroupPreference
|
||||
) {
|
||||
self.timedMessages = timedMessages
|
||||
self.directMessages = directMessages
|
||||
@@ -964,6 +979,7 @@ public struct FullGroupPreferences: Decodable, Equatable {
|
||||
self.reactions = reactions
|
||||
self.voice = voice
|
||||
self.files = files
|
||||
self.history = history
|
||||
}
|
||||
|
||||
public static let sampleData = FullGroupPreferences(
|
||||
@@ -972,7 +988,8 @@ public struct FullGroupPreferences: Decodable, Equatable {
|
||||
fullDelete: GroupPreference(enable: .off),
|
||||
reactions: GroupPreference(enable: .on),
|
||||
voice: GroupPreference(enable: .on),
|
||||
files: GroupPreference(enable: .on)
|
||||
files: GroupPreference(enable: .on),
|
||||
history: GroupPreference(enable: .on)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -983,14 +1000,16 @@ public struct GroupPreferences: Codable {
|
||||
public var reactions: GroupPreference?
|
||||
public var voice: GroupPreference?
|
||||
public var files: GroupPreference?
|
||||
public var history: GroupPreference?
|
||||
|
||||
public init(
|
||||
timedMessages: TimedMessagesGroupPreference?,
|
||||
directMessages: GroupPreference?,
|
||||
fullDelete: GroupPreference?,
|
||||
reactions: GroupPreference?,
|
||||
voice: GroupPreference?,
|
||||
files: GroupPreference?
|
||||
timedMessages: TimedMessagesGroupPreference? = nil,
|
||||
directMessages: GroupPreference? = nil,
|
||||
fullDelete: GroupPreference? = nil,
|
||||
reactions: GroupPreference? = nil,
|
||||
voice: GroupPreference? = nil,
|
||||
files: GroupPreference? = nil,
|
||||
history: GroupPreference? = nil
|
||||
) {
|
||||
self.timedMessages = timedMessages
|
||||
self.directMessages = directMessages
|
||||
@@ -998,6 +1017,7 @@ public struct GroupPreferences: Codable {
|
||||
self.reactions = reactions
|
||||
self.voice = voice
|
||||
self.files = files
|
||||
self.history = history
|
||||
}
|
||||
|
||||
public static let sampleData = GroupPreferences(
|
||||
@@ -1006,7 +1026,8 @@ public struct GroupPreferences: Codable {
|
||||
fullDelete: GroupPreference(enable: .off),
|
||||
reactions: GroupPreference(enable: .on),
|
||||
voice: GroupPreference(enable: .on),
|
||||
files: GroupPreference(enable: .on)
|
||||
files: GroupPreference(enable: .on),
|
||||
history: GroupPreference(enable: .on)
|
||||
)
|
||||
}
|
||||
|
||||
@@ -1017,7 +1038,8 @@ public func toGroupPreferences(_ fullPreferences: FullGroupPreferences) -> Group
|
||||
fullDelete: fullPreferences.fullDelete,
|
||||
reactions: fullPreferences.reactions,
|
||||
voice: fullPreferences.voice,
|
||||
files: fullPreferences.files
|
||||
files: fullPreferences.files,
|
||||
history: fullPreferences.history
|
||||
)
|
||||
}
|
||||
|
||||
@@ -2016,7 +2038,8 @@ public enum ConnectionEntity: Decodable {
|
||||
}
|
||||
|
||||
public struct NtfMsgInfo: Decodable {
|
||||
|
||||
public var msgId: String
|
||||
public var msgTs: Date
|
||||
}
|
||||
|
||||
public struct AChatItem: Decodable {
|
||||
@@ -3107,6 +3130,10 @@ extension MsgContent: Encodable {
|
||||
public struct FormattedText: Decodable {
|
||||
public var text: String
|
||||
public var format: Format?
|
||||
|
||||
public var isSecret: Bool {
|
||||
if case .secret = format { true } else { false }
|
||||
}
|
||||
}
|
||||
|
||||
public enum Format: Decodable, Equatable {
|
||||
@@ -3120,6 +3147,15 @@ public enum Format: Decodable, Equatable {
|
||||
case simplexLink(linkType: SimplexLinkType, simplexUri: String, smpHosts: [String])
|
||||
case email
|
||||
case phone
|
||||
|
||||
public var isSimplexLink: Bool {
|
||||
get {
|
||||
switch (self) {
|
||||
case .simplexLink: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum SimplexLinkType: String, Decodable {
|
||||
|
||||
@@ -17,7 +17,7 @@ public func writeCryptoFile(path: String, data: Data) throws -> CryptoFileArgs {
|
||||
let ptr: UnsafeMutableRawPointer = malloc(data.count)
|
||||
memcpy(ptr, (data as NSData).bytes, data.count)
|
||||
var cPath = path.cString(using: .utf8)!
|
||||
let cjson = chat_write_file(&cPath, ptr, Int32(data.count))!
|
||||
let cjson = chat_write_file(getChatCtrl(), &cPath, ptr, Int32(data.count))!
|
||||
let d = fromCString(cjson).data(using: .utf8)!
|
||||
switch try jsonDecoder.decode(WriteFileResult.self, from: d) {
|
||||
case let .result(cfArgs): return cfArgs
|
||||
@@ -50,7 +50,7 @@ public func readCryptoFile(path: String, cryptoArgs: CryptoFileArgs) throws -> D
|
||||
public func encryptCryptoFile(fromPath: String, toPath: String) throws -> CryptoFileArgs {
|
||||
var cFromPath = fromPath.cString(using: .utf8)!
|
||||
var cToPath = toPath.cString(using: .utf8)!
|
||||
let cjson = chat_encrypt_file(&cFromPath, &cToPath)!
|
||||
let cjson = chat_encrypt_file(getChatCtrl(), &cFromPath, &cToPath)!
|
||||
let d = fromCString(cjson).data(using: .utf8)!
|
||||
switch try jsonDecoder.decode(WriteFileResult.self, from: d) {
|
||||
case let .result(cfArgs): return cfArgs
|
||||
|
||||
@@ -69,13 +69,29 @@ func fileModificationDate(_ path: String) -> Date? {
|
||||
}
|
||||
}
|
||||
|
||||
public func deleteAppDatabaseAndFiles() {
|
||||
let fm = FileManager.default
|
||||
let dbPath = getAppDatabasePath().path
|
||||
do {
|
||||
try fm.removeItem(atPath: dbPath + CHAT_DB)
|
||||
try fm.removeItem(atPath: dbPath + AGENT_DB)
|
||||
} catch let error {
|
||||
logger.error("Failed to delete all databases: \(error)")
|
||||
}
|
||||
try? fm.removeItem(atPath: dbPath + CHAT_DB_BAK)
|
||||
try? fm.removeItem(atPath: dbPath + AGENT_DB_BAK)
|
||||
try? fm.removeItem(at: getTempFilesDirectory())
|
||||
try? fm.createDirectory(at: getTempFilesDirectory(), withIntermediateDirectories: true)
|
||||
deleteAppFiles()
|
||||
_ = kcDatabasePassword.remove()
|
||||
storeDBPassphraseGroupDefault.set(true)
|
||||
}
|
||||
|
||||
public func deleteAppFiles() {
|
||||
let fm = FileManager.default
|
||||
do {
|
||||
let fileNames = try fm.contentsOfDirectory(atPath: getAppFilesDirectory().path)
|
||||
for fileName in fileNames {
|
||||
removeFile(fileName)
|
||||
}
|
||||
try fm.removeItem(at: getAppFilesDirectory())
|
||||
try fm.createDirectory(at: getAppFilesDirectory(), withIntermediateDirectories: true)
|
||||
} catch {
|
||||
logger.error("FileUtils deleteAppFiles error: \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
@@ -146,6 +146,13 @@ public func createErrorNtf(_ dbStatus: DBMigrationResult) -> UNMutableNotificati
|
||||
)
|
||||
}
|
||||
|
||||
public func createAppStoppedNtf() -> UNMutableNotificationContent {
|
||||
return createNotification(
|
||||
categoryIdentifier: ntfCategoryConnectionEvent,
|
||||
title: NSLocalizedString("Encrypted message: app is stopped", comment: "notification")
|
||||
)
|
||||
}
|
||||
|
||||
private func groupMsgNtfTitle(_ groupInfo: GroupInfo, _ groupMember: GroupMember, hideContent: Bool) -> String {
|
||||
hideContent
|
||||
? NSLocalizedString("Group message:", comment: "notification")
|
||||
|
||||
99
apps/ios/SimpleXChat/SharedFileSubscriber.swift
Normal file
99
apps/ios/SimpleXChat/SharedFileSubscriber.swift
Normal file
@@ -0,0 +1,99 @@
|
||||
//
|
||||
// SharedFileSubscriber.swift
|
||||
// SimpleXChat
|
||||
//
|
||||
// Created by Evgeny on 09/12/2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
public typealias AppSubscriber = SharedFileSubscriber<ProcessMessage<AppProcessMessage>>
|
||||
|
||||
public typealias NSESubscriber = SharedFileSubscriber<ProcessMessage<NSEProcessMessage>>
|
||||
|
||||
public class SharedFileSubscriber<Message: Codable>: NSObject, NSFilePresenter {
|
||||
var fileURL: URL
|
||||
public var presentedItemURL: URL?
|
||||
public var presentedItemOperationQueue: OperationQueue = .main
|
||||
var subscriber: (Message) -> Void
|
||||
|
||||
init(fileURL: URL, onMessage: @escaping (Message) -> Void) {
|
||||
self.fileURL = fileURL
|
||||
presentedItemURL = fileURL
|
||||
subscriber = onMessage
|
||||
super.init()
|
||||
NSFileCoordinator.addFilePresenter(self)
|
||||
}
|
||||
|
||||
public func presentedItemDidChange() {
|
||||
do {
|
||||
let data = try Data(contentsOf: fileURL)
|
||||
let msg = try jsonDecoder.decode(Message.self, from: data)
|
||||
subscriber(msg)
|
||||
} catch let error {
|
||||
logger.error("presentedItemDidChange error: \(error)")
|
||||
}
|
||||
}
|
||||
|
||||
static func notify(url: URL, message: Message) {
|
||||
let fc = NSFileCoordinator(filePresenter: nil)
|
||||
fc.coordinate(writingItemAt: url, options: [], error: nil) { newURL in
|
||||
do {
|
||||
let data = try jsonEncoder.encode(message)
|
||||
try data.write(to: newURL, options: [.atomic])
|
||||
} catch {
|
||||
logger.error("notifyViaSharedFile error: \(error)")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
deinit {
|
||||
NSFileCoordinator.removeFilePresenter(self)
|
||||
}
|
||||
}
|
||||
|
||||
let appMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.messages", isDirectory: false)
|
||||
|
||||
let nseMessagesSharedFile = getGroupContainerDirectory().appendingPathComponent("chat.simplex.app.SimpleX-NSE.messages", isDirectory: false)
|
||||
|
||||
public struct ProcessMessage<Message: Codable>: Codable {
|
||||
var createdAt: Date = Date.now
|
||||
var message: Message
|
||||
}
|
||||
|
||||
public enum AppProcessMessage: Codable {
|
||||
case state(state: AppState)
|
||||
}
|
||||
|
||||
public enum NSEProcessMessage: Codable {
|
||||
case state(state: NSEState)
|
||||
}
|
||||
|
||||
public func sendAppProcessMessage(_ message: AppProcessMessage) {
|
||||
SharedFileSubscriber.notify(url: appMessagesSharedFile, message: ProcessMessage(message: message))
|
||||
}
|
||||
|
||||
public func sendNSEProcessMessage(_ message: NSEProcessMessage) {
|
||||
SharedFileSubscriber.notify(url: nseMessagesSharedFile, message: ProcessMessage(message: message))
|
||||
}
|
||||
|
||||
public func appMessageSubscriber(onMessage: @escaping (AppProcessMessage) -> Void) -> AppSubscriber {
|
||||
SharedFileSubscriber(fileURL: appMessagesSharedFile) { (msg: ProcessMessage<AppProcessMessage>) in
|
||||
onMessage(msg.message)
|
||||
}
|
||||
}
|
||||
|
||||
public func nseMessageSubscriber(onMessage: @escaping (NSEProcessMessage) -> Void) -> NSESubscriber {
|
||||
SharedFileSubscriber(fileURL: nseMessagesSharedFile) { (msg: ProcessMessage<NSEProcessMessage>) in
|
||||
onMessage(msg.message)
|
||||
}
|
||||
}
|
||||
|
||||
public func sendAppState(_ state: AppState) {
|
||||
sendAppProcessMessage(.state(state: state))
|
||||
}
|
||||
|
||||
public func sendNSEState(_ state: NSEState) {
|
||||
sendNSEProcessMessage(.state(state: state))
|
||||
}
|
||||
@@ -16,20 +16,20 @@ extern void hs_init(int argc, char **argv[]);
|
||||
typedef void* chat_ctrl;
|
||||
|
||||
// the last parameter is used to return the pointer to chat controller
|
||||
extern char *chat_migrate_init(char *path, char *key, char *confirm, chat_ctrl *ctrl);
|
||||
extern char *chat_migrate_init_key(char *path, char *key, int keepKey, char *confirm, int backgroundMode, chat_ctrl *ctrl);
|
||||
extern char *chat_close_store(chat_ctrl ctl);
|
||||
extern char *chat_reopen_store(chat_ctrl ctl);
|
||||
extern char *chat_send_cmd(chat_ctrl ctl, char *cmd);
|
||||
extern char *chat_recv_msg(chat_ctrl ctl);
|
||||
extern char *chat_recv_msg_wait(chat_ctrl ctl, int wait);
|
||||
extern char *chat_parse_markdown(char *str);
|
||||
extern char *chat_parse_server(char *str);
|
||||
extern char *chat_password_hash(char *pwd, char *salt);
|
||||
extern char *chat_valid_name(char *name);
|
||||
extern char *chat_encrypt_media(char *key, char *frame, int len);
|
||||
extern char *chat_encrypt_media(chat_ctrl ctl, char *key, char *frame, int len);
|
||||
extern char *chat_decrypt_media(char *key, char *frame, int len);
|
||||
|
||||
// chat_write_file returns null-terminated string with JSON of WriteFileResult
|
||||
extern char *chat_write_file(char *path, char *data, int len);
|
||||
extern char *chat_write_file(chat_ctrl ctl, char *path, char *data, int len);
|
||||
|
||||
// chat_read_file returns a buffer with:
|
||||
// result status (1 byte), then if
|
||||
@@ -38,7 +38,7 @@ extern char *chat_write_file(char *path, char *data, int len);
|
||||
extern char *chat_read_file(char *path, char *key, char *nonce);
|
||||
|
||||
// chat_encrypt_file returns null-terminated string with JSON of WriteFileResult
|
||||
extern char *chat_encrypt_file(char *fromPath, char *toPath);
|
||||
extern char *chat_encrypt_file(chat_ctrl ctl, char *fromPath, char *toPath);
|
||||
|
||||
// chat_decrypt_file returns null-terminated string with the error message
|
||||
extern char *chat_decrypt_file(char *fromPath, char *key, char *nonce, char *toPath);
|
||||
|
||||
@@ -23,3 +23,19 @@ void haskell_init(void) {
|
||||
char **pargv = argv;
|
||||
hs_init_with_rtsopts(&argc, &pargv);
|
||||
}
|
||||
|
||||
void haskell_init_nse(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);
|
||||
}
|
||||
|
||||
@@ -11,4 +11,6 @@
|
||||
|
||||
void haskell_init(void);
|
||||
|
||||
void haskell_init_nse(void);
|
||||
|
||||
#endif /* hs_init_h */
|
||||
|
||||
@@ -1959,6 +1959,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Live messages" = "Съобщения на живо";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Local" = "Локално";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Local name" = "Локално име";
|
||||
|
||||
@@ -2222,9 +2225,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Off" = "Изключено";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Off (Local)" = "Изключено (Локално)";
|
||||
|
||||
/* feature offered item */
|
||||
"offered %@" = "предлага %@";
|
||||
|
||||
@@ -2315,9 +2315,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Open-source protocol and code – anybody can run the servers." = "Протокол и код с отворен код – всеки може да оперира собствени сървъри.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Opening database…" = "Отваряне на база данни…";
|
||||
|
||||
/* member role */
|
||||
"owner" = "собственик";
|
||||
|
||||
@@ -3218,9 +3215,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Turn off" = "Изключи";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Turn off notifications?" = "Изключи известията?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Turn on" = "Включи";
|
||||
|
||||
@@ -3641,9 +3635,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts can allow full message deletion." = "Вашите контакти могат да позволят пълното изтриване на съобщението.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts in SimpleX will see it.\nYou can change it in Settings." = "Вашите контакти в SimpleX ще го видят.\nМожете да го промените в Настройки.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts will remain connected." = "Вашите контакти ще останат свързани.";
|
||||
|
||||
|
||||
@@ -1956,6 +1956,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Live messages" = "Živé zprávy";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Local" = "Místní";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Local name" = "Místní název";
|
||||
|
||||
@@ -2219,9 +2222,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Off" = "Vypnout";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Off (Local)" = "Vypnuto (místní)";
|
||||
|
||||
/* feature offered item */
|
||||
"offered %@" = "nabídl %@";
|
||||
|
||||
@@ -2312,9 +2312,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Open-source protocol and code – anybody can run the servers." = "Protokol a kód s otevřeným zdrojovým kódem - servery může provozovat kdokoli.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Opening database…" = "Otvírání databáze…";
|
||||
|
||||
/* member role */
|
||||
"owner" = "vlastník";
|
||||
|
||||
@@ -3215,9 +3212,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Turn off" = "Vypnout";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Turn off notifications?" = "Vypnout upozornění?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Turn on" = "Zapnout";
|
||||
|
||||
@@ -3638,9 +3632,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts can allow full message deletion." = "Vaše kontakty mohou povolit úplné mazání zpráv.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts in SimpleX will see it.\nYou can change it in Settings." = "Vaše kontakty v SimpleX ji uvidí.\nMůžete ji změnit v Nastavení.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts will remain connected." = "Vaše kontakty zůstanou připojeny.";
|
||||
|
||||
|
||||
@@ -2163,6 +2163,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Live messages" = "Live Nachrichten";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Local" = "Lokal";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Local name" = "Lokaler Name";
|
||||
|
||||
@@ -2432,9 +2435,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Off" = "Aus";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Off (Local)" = "Aus (Lokal)";
|
||||
|
||||
/* feature offered item */
|
||||
"offered %@" = "angeboten %@";
|
||||
|
||||
@@ -2528,9 +2528,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Open-source protocol and code – anybody can run the servers." = "Open-Source-Protokoll und -Code – Jede Person kann ihre eigenen Server aufsetzen und nutzen.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Opening database…" = "Öffne Datenbank …";
|
||||
|
||||
/* member role */
|
||||
"owner" = "Eigentümer";
|
||||
|
||||
@@ -3467,9 +3464,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Turn off" = "Abschalten";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Turn off notifications?" = "Benachrichtigungen abschalten?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Turn on" = "Einschalten";
|
||||
|
||||
@@ -3959,9 +3953,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts can allow full message deletion." = "Ihre Kontakte können die unwiederbringliche Löschung von Nachrichten erlauben.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts in SimpleX will see it.\nYou can change it in Settings." = "Ihre Kontakte in SimpleX werden es sehen.\nSie können es in den Einstellungen ändern.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts will remain connected." = "Ihre Kontakte bleiben verbunden.";
|
||||
|
||||
|
||||
@@ -25,6 +25,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- entrega de mensajes más estable.\n- grupos un poco mejores.\n- ¡y más!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"- optionally notify deleted contacts.\n- profile names with spaces.\n- and more!" = "- notificar opcionalmente a los contactos eliminados.\n- nombres de perfil con espacios.\n- ¡...y más!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- mensajes de voz de hasta 5 minutos.\n- tiempo personalizado para mensajes temporales.\n- historial de edición.";
|
||||
|
||||
@@ -43,6 +46,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"(" = "(";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(new)" = "(nuevo)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(this device v%@)" = "(este dispositivo v%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
")" = ")";
|
||||
|
||||
@@ -118,12 +127,18 @@
|
||||
/* No comment provided by engineer. */
|
||||
"%@ %@" = "%@ %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ and %@" = "%@ y %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ and %@ connected" = "%@ y %@ conectados";
|
||||
|
||||
/* copied message info, <sender> at <time> */
|
||||
"%@ at %@:" = "%1$@ a las %2$@:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ connected" = "%@ conectado";
|
||||
|
||||
/* notification title */
|
||||
"%@ is connected!" = "%@ ¡está conectado!";
|
||||
|
||||
@@ -139,6 +154,9 @@
|
||||
/* notification title */
|
||||
"%@ wants to connect!" = "¡ %@ quiere contactar!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@, %@ and %lld members" = "%@, %@ y %lld miembro(s) más";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@, %@ and %lld other members connected" = "%@, %@ y %lld miembros más conectados";
|
||||
|
||||
@@ -178,9 +196,21 @@
|
||||
/* No comment provided by engineer. */
|
||||
"%lld file(s) with total size of %@" = "%lld archivo(s) con un tamaño total de %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld group events" = "%lld evento(s) de grupo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld members" = "%lld miembros";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld messages blocked" = "%lld mensaje(s) bloqueado(s)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld messages marked deleted" = "%lld mensaje(s) marcado(s) eliminado(s)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld messages moderated by %@" = "%lld mensaje(s) moderado(s) por %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld minutes" = "%lld minutos";
|
||||
|
||||
@@ -229,6 +259,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"~strike~" = "\\~strike~";
|
||||
|
||||
/* time to disappear */
|
||||
"0 sec" = "0 seg";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"0s" = "0s";
|
||||
|
||||
@@ -371,6 +404,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"All messages will be deleted - this cannot be undone! The messages will be deleted ONLY for you." = "Se eliminarán todos los mensajes SOLO para tí. ¡No podrá deshacerse!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"All new messages from %@ will be hidden!" = "¡Los mensajes nuevos de %@ estarán ocultos!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"All your contacts will remain connected." = "Todos tus contactos permanecerán conectados.";
|
||||
|
||||
@@ -434,6 +470,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Already connected?" = "¿Ya está conectado?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Already connecting!" = "¡Ya en proceso de conexión!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Already joining the group!" = "¡Ya en proceso de unirse al grupo!";
|
||||
|
||||
/* pref value */
|
||||
"always" = "siempre";
|
||||
|
||||
@@ -443,6 +485,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"An empty chat profile with the provided name is created, and the app opens as usual." = "Se creará un perfil vacío con el nombre proporcionado, y la aplicación se abrirá como de costumbre.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"and %lld other events" = "y %lld evento(s) más";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Answer call" = "Responder llamada";
|
||||
|
||||
@@ -500,6 +545,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Authentication unavailable" = "Autenticación no disponible";
|
||||
|
||||
/* member role */
|
||||
"author" = "autor";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Auto-accept" = "Auto aceptar";
|
||||
|
||||
@@ -512,6 +560,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Back" = "Volver";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Bad desktop address" = "Dirección ordenador incorrecta";
|
||||
|
||||
/* integrity error chat item */
|
||||
"bad message hash" = "hash de mensaje erróneo";
|
||||
|
||||
@@ -524,9 +575,27 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Bad message ID" = "ID de mensaje incorrecto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better groups" = "Grupos mejorados";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better messages" = "Mensajes mejorados";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block" = "Bloquear";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block group members" = "Bloquear miembros del grupo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block member" = "Bloquear miembro";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Block member?" = "¿Bloquear miembro?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"blocked" = "bloqueado";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"bold" = "negrita";
|
||||
|
||||
@@ -720,12 +789,30 @@
|
||||
/* server test step */
|
||||
"Connect" = "Conectar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect automatically" = "Conectar automáticamente";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "Conectar incognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to desktop" = "Conectar con ordenador";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "contacta con los desarrolladores de SimpleX Chat.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to yourself?" = "¿Conectarte a tí mismo?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to yourself?\nThis is your own one-time link!" = "¿Conectarte a tí mismo?\n¡Este es tu propio enlace de un solo uso!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect to yourself?\nThis is your own SimpleX address!" = "¿Conectarte a tí mismo?\n¡Esta es tu propia dirección SimpleX!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via contact address" = "Conectar mediante dirección de contacto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via link" = "Conectar mediante enlace";
|
||||
|
||||
@@ -735,12 +822,21 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via one-time link" = "Conectar mediante enlace de un sólo uso";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect with %@" = "Conectar con %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connected" = "conectado";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected desktop" = "Ordenador conectado";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"connected directly" = "conectado directamente";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connected to desktop" = "Conectado con ordenador";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connecting" = "conectando";
|
||||
|
||||
@@ -765,6 +861,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Conectando con el servidor... (error: %@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting to desktop" = "Conectando con ordenador";
|
||||
|
||||
/* chat list item title */
|
||||
"connecting…" = "conectando…";
|
||||
|
||||
@@ -783,6 +882,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Connection request sent!" = "¡Solicitud de conexión enviada!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection terminated" = "Conexión finalizada";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection timeout" = "Tiempo de conexión expirado";
|
||||
|
||||
@@ -831,15 +933,24 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Core version: v%@" = "Versión Core: v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Correct name to %@?" = "¿Corregir el nombre a %@?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create" = "Crear";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create a group using a random profile." = "Crear grupo usando perfil aleatorio.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create an address to let people connect with you." = "Crea una dirección para que otras personas puedan conectar contigo.";
|
||||
|
||||
/* server test step */
|
||||
"Create file" = "Crear archivo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create group" = "Crear grupo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create group link" = "Crear enlace de grupo";
|
||||
|
||||
@@ -852,6 +963,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Create one-time invitation link" = "Crea enlace de invitación de un uso";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create profile" = "Crear perfil";
|
||||
|
||||
/* server test step */
|
||||
"Create queue" = "Crear cola";
|
||||
|
||||
@@ -859,7 +973,7 @@
|
||||
"Create secret group" = "Crea grupo secreto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create SimpleX address" = "Crear tu dirección SimpleX";
|
||||
"Create SimpleX address" = "Crear dirección SimpleX";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create your profile" = "Crea tu perfil";
|
||||
@@ -966,6 +1080,9 @@
|
||||
/* chat item action */
|
||||
"Delete" = "Eliminar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete %lld messages?" = "¿Elimina %lld mensajes?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete address" = "Eliminar dirección";
|
||||
|
||||
@@ -978,6 +1095,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Delete all files" = "Eliminar todos los archivos";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete and notify contact" = "Eliminar y notificar contacto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete archive" = "Eliminar archivo";
|
||||
|
||||
@@ -999,6 +1119,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Delete Contact" = "Eliminar contacto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete contact?\nThis cannot be undone!" = "¿Eliminar contacto?\n¡No podrá deshacerse!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete database" = "Eliminar base de datos";
|
||||
|
||||
@@ -1074,6 +1197,9 @@
|
||||
/* copied message info */
|
||||
"Deleted at: %@" = "Eliminado: %@";
|
||||
|
||||
/* rcv direct event chat item */
|
||||
"deleted contact" = "contacto eliminado";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"deleted group" = "grupo eliminado";
|
||||
|
||||
@@ -1089,6 +1215,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Description" = "Descripción";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Desktop address" = "Dirección ordenador";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Desktop app version %@ is not compatible with this app." = "La versión de aplicación del ordenador %\" no es compatible con esta aplicación.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Desktop devices" = "Ordenadores";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Develop" = "Desarrollo";
|
||||
|
||||
@@ -1152,9 +1287,15 @@
|
||||
/* server test step */
|
||||
"Disconnect" = "Desconectar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disconnect desktop?" = "¿Desconectar ordenador?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Discover and join groups" = "Descubre y únete a grupos";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Discover via local network" = "Descubrir en red local";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Do it later" = "Hacer más tarde";
|
||||
|
||||
@@ -1290,6 +1431,12 @@
|
||||
/* chat item text */
|
||||
"encryption re-negotiation allowed for %@" = "renegociar el cifrado permitido para %@";
|
||||
|
||||
/* message decrypt error item */
|
||||
"Encryption re-negotiation error" = "Error de renegociación de cifrado";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Encryption re-negotiation failed." = "Renegociación de cifrado fallida.";
|
||||
|
||||
/* chat item text */
|
||||
"encryption re-negotiation required" = "se requiere renegociar el cifrado";
|
||||
|
||||
@@ -1305,6 +1452,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enter correct passphrase." = "Introduce la contraseña correcta.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter group name…" = "Nombre del grupo…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter Passcode" = "Introduce Código";
|
||||
|
||||
@@ -1317,12 +1467,18 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enter server manually" = "Introduce el servidor manualmente";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter this device name…" = "Nombre de este dispositivo…";
|
||||
|
||||
/* placeholder */
|
||||
"Enter welcome message…" = "Introduce mensaje de bienvenida…";
|
||||
|
||||
/* placeholder */
|
||||
"Enter welcome message… (optional)" = "Introduce mensaje de bienvenida… (opcional)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter your name…" = "Introduce tu nombre…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"error" = "error";
|
||||
|
||||
@@ -1494,6 +1650,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Exit without saving" = "Salir sin guardar";
|
||||
|
||||
/* chat item action */
|
||||
"Expand" = "Expandir";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Export database" = "Exportar base de datos";
|
||||
|
||||
@@ -1512,6 +1671,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Fast and no wait until the sender is online!" = "¡Rápido y sin necesidad de esperar a que el remitente esté en línea!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Faster joining and more reliable messages." = "Mensajería más segura y conexión más rápida.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Favorite" = "Favoritos";
|
||||
|
||||
@@ -1569,6 +1731,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"For console" = "Para consola";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Found desktop" = "Ordenador encontrado";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"French interface" = "Interfaz en francés";
|
||||
|
||||
@@ -1581,6 +1746,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Full name:" = "Nombre completo:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fully decentralized – visible only to members." = "Completamente descentralizado: sólo visible a los miembros.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fully re-implemented - work in background!" = "Completamente reimplementado: ¡funciona en segundo plano!";
|
||||
|
||||
@@ -1593,6 +1761,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Group" = "Grupo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Group already exists" = "El grupo ya existe";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Group already exists!" = "¡El grupo ya existe!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"group deleted" = "grupo eliminado";
|
||||
|
||||
@@ -1764,6 +1938,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito" = "Incógnito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito groups" = "Grupos incógnito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "Modo incógnito";
|
||||
|
||||
@@ -1791,6 +1968,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible database version" = "Versión de base de datos incompatible";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incompatible version" = "Versión incompatible";
|
||||
|
||||
/* PIN entry */
|
||||
"Incorrect passcode" = "Código de acceso incorrecto";
|
||||
|
||||
@@ -1830,6 +2010,9 @@
|
||||
/* invalid chat item */
|
||||
"invalid data" = "datos no válidos";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid name!" = "¡Nombre no válido!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid server address!" = "¡Dirección de servidor no válida!";
|
||||
|
||||
@@ -1858,7 +2041,7 @@
|
||||
"invited %@" = "ha invitado a %@";
|
||||
|
||||
/* chat list item title */
|
||||
"invited to connect" = "invitado a conectarse";
|
||||
"invited to connect" = "invitación a conectarse";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"invited via your group link" = "se ha unido mediante tu enlace de grupo";
|
||||
@@ -1908,12 +2091,24 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Join group" = "Únete al grupo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Join group?" = "¿Unirse al grupo?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Join incognito" = "Únete en modo incógnito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Join with current profile" = "Unirte con el perfil actual";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Join your group?\nThis is your link for group %@!" = "¿Unirse a tu grupo?\n¡Este es tu enlace para el grupo %@!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Joining group" = "Entrando al grupo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Keep the app open to use it from desktop" = "Mantén la aplicación abierta para usarla desde el ordenador";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Keep your connections" = "Conserva tus conexiones";
|
||||
|
||||
@@ -1950,6 +2145,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Limitations" = "Limitaciones";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Link mobile and desktop apps! 🔗" = "¡Enlazar aplicación móvil con ordenador! 🔗";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Linked desktop options" = "Opciones ordenador enlazado";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Linked desktops" = "Ordenadores enlazados";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"LIVE" = "EN VIVO";
|
||||
|
||||
@@ -1959,6 +2163,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Live messages" = "Mensajes en vivo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Local" = "Local";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Local name" = "Nombre local";
|
||||
|
||||
@@ -2055,6 +2262,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Messages & files" = "Mensajes";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Messages from %@ will be shown!" = "¡Los mensajes de %@ serán mostrados!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Migrating database archive…" = "Migrando base de datos…";
|
||||
|
||||
@@ -2202,6 +2412,9 @@
|
||||
/* copied message info in history */
|
||||
"no text" = "sin texto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Not compatible!" = "¡No compatible!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Notifications" = "Notificaciones";
|
||||
|
||||
@@ -2222,9 +2435,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Off" = "Desactivado";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Off (Local)" = "Desactivado (Local)";
|
||||
|
||||
/* feature offered item */
|
||||
"offered %@" = "ofrecido %@";
|
||||
|
||||
@@ -2306,6 +2516,9 @@
|
||||
/* authentication reason */
|
||||
"Open chat console" = "Abrir consola de Chat";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open group" = "Grupo abierto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open Settings" = "Abrir Configuración";
|
||||
|
||||
@@ -2315,9 +2528,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Open-source protocol and code – anybody can run the servers." = "Protocolo y código abiertos: cualquiera puede usar los servidores.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Opening database…" = "Abriendo base de datos…";
|
||||
|
||||
/* member role */
|
||||
"owner" = "propietario";
|
||||
|
||||
@@ -2342,6 +2552,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Paste" = "Pegar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste desktop address" = "Pegar dirección de ordenador";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Paste image" = "Pegar imagen";
|
||||
|
||||
@@ -2438,6 +2651,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Profile image" = "Imagen del perfil";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Profile name" = "Nombre del perfil";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Profile name:" = "Nombre del perfil:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Profile password" = "Contraseña del perfil";
|
||||
|
||||
@@ -2603,6 +2822,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Renegotiate encryption?" = "¿Renegociar cifrado?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Repeat connection request?" = "¿Repetir solicitud de conexión?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Repeat join request?" = "¿Repetir solicitud de admisión?";
|
||||
|
||||
/* chat item action */
|
||||
"Reply" = "Responder";
|
||||
|
||||
@@ -2714,6 +2939,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Scan QR code" = "Escanear código QR";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Scan QR code from desktop" = "Escanear código QR desde ordenador";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Scan security code from your contact's app." = "Escanea el código de seguridad desde la aplicación de tu contacto.";
|
||||
|
||||
@@ -2858,6 +3086,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Servers" = "Servidores";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Session code" = "Código de sesión";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Set 1 day" = "Establecer 1 día";
|
||||
|
||||
@@ -3044,6 +3275,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Tap to activate profile." = "Pulsa sobre un perfil para activarlo.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Tap to Connect" = "Pulsa para conectar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Tap to join" = "Pulsa para unirse";
|
||||
|
||||
@@ -3164,12 +3398,21 @@
|
||||
/* notification title */
|
||||
"this contact" = "este contacto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This device name" = "Nombre del dispositivo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This group has over %lld members, delivery receipts are not sent." = "Este grupo tiene más de %lld miembros, no se enviarán confirmaciones de entrega.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This group no longer exists." = "Este grupo ya no existe.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This is your own one-time link!" = "¡Este es tu propio enlace de un solo uso!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This is your own SimpleX address!" = "¡Esta es tu propia dirección SimpleX!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This setting applies to messages in your current chat profile **%@**." = "Esta configuración se aplica a los mensajes del perfil actual **%@**.";
|
||||
|
||||
@@ -3179,17 +3422,20 @@
|
||||
/* No comment provided by engineer. */
|
||||
"To connect, your contact can scan QR code or use the link in the app." = "Para conectarse, tu contacto puede escanear el código QR o usar el enlace en la aplicación.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To hide unwanted messages." = "Para ocultar mensajes no deseados.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To make a new connection" = "Para hacer una conexión nueva";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Para proteger la privacidad, en lugar de los identificadores de usuario que usan el resto de plataformas, SimpleX dispone de identificadores para las colas de mensajes, independientes para cada uno de tus contactos.";
|
||||
"To protect privacy, instead of user IDs used by all other platforms, SimpleX has identifiers for message queues, separate for each of your contacts." = "Para proteger tu privacidad, en lugar de los identificadores de usuario que usan el resto de plataformas, SimpleX dispone de identificadores para las colas de mensajes, independientes para cada uno de tus contactos.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To protect timezone, image/voice files use UTC." = "Para proteger la zona horaria, los archivos de imagen/voz usan la hora UTC.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Para proteger tu información, activa Bloqueo SimpleX.\nSe te pedirá que completes la autenticación antes de activar esta función.";
|
||||
"To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Para proteger tu información, activa el Bloqueo SimpleX.\nSe te pedirá que completes la autenticación antes de activar esta función.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To record voice message please grant permission to use Microphone." = "Para grabar el mensaje de voz concede permiso para usar el micrófono.";
|
||||
@@ -3218,15 +3464,21 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Turn off" = "Desactivar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Turn off notifications?" = "¿Desactivar notificaciones?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Turn on" = "Activar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unable to record voice message" = "No se puede grabar mensaje de voz";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unblock" = "Desbloquear";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unblock member" = "Desbloquear miembro";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unblock member?" = "¿Desbloquear miembro?";
|
||||
|
||||
/* item status description */
|
||||
"Unexpected error: %@" = "Error inesperado: %@";
|
||||
|
||||
@@ -3266,6 +3518,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"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." = "A menos que tu contacto haya eliminado la conexión o\nque este enlace ya se haya usado, podría ser un error. Por favor, notifícalo.\nPara conectarte, pide a tu contacto que cree otro enlace de conexión y comprueba que tienes buena conexión de red.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink" = "Desenlazar";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlink desktop?" = "¿Desenlazar ordenador?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unlock" = "Desbloquear";
|
||||
|
||||
@@ -3320,6 +3578,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Use for new connections" = "Usar para conexiones nuevas";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use from desktop" = "Usar desde ordenador";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use iOS call interface" = "Usar interfaz de llamada de iOS";
|
||||
|
||||
@@ -3341,12 +3602,24 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Using SimpleX Chat servers." = "Usar servidores SimpleX Chat.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@" = "v%@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@ (%@)" = "v%@ (%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify code with desktop" = "Verificar código con ordenador";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connection" = "Verificar conexión";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connection security" = "Comprobar la seguridad de la conexión";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify connections" = "Verificar conexiones";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Verify security code" = "Comprobar código de seguridad";
|
||||
|
||||
@@ -3365,6 +3638,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"via relay" = "mediante retransmisor";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Via secure quantum resistant protocol." = "Mediante protocolo seguro de resistencia cuántica.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Video call" = "Videollamada";
|
||||
|
||||
@@ -3404,6 +3680,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"waiting for confirmation…" = "esperando confirmación…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for desktop..." = "Esperando ordenador...";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Waiting for file" = "Esperando archivo";
|
||||
|
||||
@@ -3473,6 +3752,27 @@
|
||||
/* No comment provided by engineer. */
|
||||
"You are already connected to %@." = "Ya estás conectado a %@.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are already connecting to %@." = "Ya estás conectando con %@.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are already connecting via this one-time link!" = "¡Ya estás conectando mediante este enlace de un solo uso!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are already in group %@." = "Ya estás en el grupo %@.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are already joining the group %@." = "Ya estás uniéndote al grupo %@.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are already joining the group via this link!" = "¡Ya estás uniéndote al grupo mediante este enlace!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are already joining the group via this link." = "Ya estás uniéndote al grupo mediante este enlace.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are already joining the group!\nRepeat join request?" = "¡En proceso de unirte al grupo!\n¿Repetir solicitud de admisión?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are connected to the server used to receive messages from this contact." = "Estás conectado al servidor usado para recibir mensajes de este contacto.";
|
||||
|
||||
@@ -3548,6 +3848,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"You could not be verified; please try again." = "No has podido ser autenticado. Inténtalo de nuevo.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You have already requested connection via this address!" = "¡Ya has solicitado la conexión mediante esta dirección!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You have already requested connection!\nRepeat connection request?" = "Ya has solicitado la conexión\n¿Repetir solicitud?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You have no chats" = "No tienes chats";
|
||||
|
||||
@@ -3582,7 +3888,7 @@
|
||||
"You sent group invitation" = "Has enviado una invitación de grupo";
|
||||
|
||||
/* chat list item description */
|
||||
"you shared one-time link" = "has compartido un enlace de un uso";
|
||||
"you shared one-time link" = "enlace de un solo uso";
|
||||
|
||||
/* chat list item description */
|
||||
"you shared one-time link incognito" = "has compartido enlace de un solo uso en modo incógnito";
|
||||
@@ -3590,6 +3896,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"You will be connected to group when the group host's device is online, please wait or check later!" = "Te conectarás al grupo cuando el dispositivo del anfitrión esté en línea, por favor espera o compruébalo más tarde.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You will be connected when group link host's device is online, please wait or check later!" = "Te conectarás cuando el dispositivo propietario del grupo esté en línea, por favor espera o compruébalo más tarde.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You will be connected when your connection request is accepted, please wait or check later!" = "Te conectarás cuando tu solicitud se acepte, por favor espera o compruébalo más tarde.";
|
||||
|
||||
@@ -3599,6 +3908,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Se te pedirá identificarte cuándo inicies o continues usando la aplicación tras 30 segundos en segundo plano.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You will connect to all group members." = "Te conectarás con todos los miembros del grupo.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You will still receive calls and notifications from muted profiles when they are active." = "Seguirás recibiendo llamadas y notificaciones de los perfiles silenciados cuando estén activos.";
|
||||
|
||||
@@ -3641,9 +3953,6 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts can allow full message deletion." = "Tus contactos pueden permitir la eliminación completa de mensajes.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts in SimpleX will see it.\nYou can change it in Settings." = "Tus contactos en SimpleX lo verán.\nPuedes cambiarlo en Configuración.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your contacts will remain connected." = "Tus contactos permanecerán conectados.";
|
||||
|
||||
@@ -3662,6 +3971,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Your privacy" = "Privacidad";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile" = "Tu perfil";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile **%@** will be shared." = "Tu perfil **%@** será compartido.";
|
||||
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user