mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Compare commits
65 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| b93c7759e1 | |||
| 2c56bc606b | |||
| a3cba562c6 | |||
| 2c86b71329 | |||
| 94fb94f06a | |||
| 3cdba73109 | |||
| 8efa023327 | |||
| cf60183e74 | |||
| 5fddf64adb | |||
| 77698a9883 | |||
| 181323ce13 | |||
| b0c28e77c4 | |||
| ff657a444c | |||
| 2dff6c8859 | |||
| 7b582b2cf9 | |||
| 55954a004b | |||
| 54e1e10382 | |||
| a87aaa50c7 | |||
| fdef37e8f3 | |||
| 364d889056 | |||
| 923a5e2984 | |||
| 272b02b686 | |||
| 1682577ede | |||
| 281d9c7f79 | |||
| 45682aa7ce | |||
| ad65622407 | |||
| 113a57c7c7 | |||
| e76440ee66 | |||
| 82fd3b9004 | |||
| b5a0269aa2 | |||
| 7cd4a417e7 | |||
| 748572ace9 | |||
| a27f30ce12 | |||
| a90641c1d1 | |||
| 47b783e727 | |||
| edeaf36e8b | |||
| 5e8e4c295c | |||
| 37eef3c6c9 | |||
| b6c23b59ca | |||
| e6baca5610 | |||
| 0c4b843a3f | |||
| 68f359c904 | |||
| e60dbf6add | |||
| 67d5b6eace | |||
| 43e233f0eb | |||
| 83b939d215 | |||
| 4d6283630a | |||
| 6ff3024238 | |||
| aff71c58d7 | |||
| 8aed568199 | |||
| 0ec3e0c18d | |||
| c7f1af8742 | |||
| 4793173465 | |||
| aa67692465 | |||
| af02a92442 | |||
| 461142b875 | |||
| 0b214acf97 | |||
| 1c90eb0a2e | |||
| 7a5d4a5a3d | |||
| 4aac3c7922 | |||
| 134465fd9d | |||
| 0f076d9ac9 | |||
| 95d57bc4e1 | |||
| 10f8b8086e | |||
| 960482f527 |
@@ -78,10 +78,10 @@ jobs:
|
||||
uses: actions/checkout@v3
|
||||
|
||||
- name: Setup Haskell
|
||||
uses: haskell-actions/setup@v2
|
||||
uses: haskell/actions/setup@v2
|
||||
with:
|
||||
ghc-version: "9.6.2"
|
||||
cabal-version: "3.10.1.0"
|
||||
ghc-version: "8.10.7"
|
||||
cabal-version: "latest"
|
||||
|
||||
- name: Cache dependencies
|
||||
uses: actions/cache@v3
|
||||
|
||||
+1
-1
@@ -131,4 +131,4 @@ You accept our Terms of Service ("Terms") by installing or using any of our apps
|
||||
|
||||
**Ending these Terms**. You may end these Terms with SimpleX Chat at any time by deleting SimpleX Chat app(s) from your device and discontinuing use of our Services. The provisions related to Licenses, Disclaimers, Limitation of Liability, Resolving dispute, Availability, Changes to the terms, Enforcing the terms, and Ending these Terms will survive termination of your relationship with SimpleX Chat.
|
||||
|
||||
Updated August 17, 2022
|
||||
Updated August 17, 2023
|
||||
|
||||
@@ -2,7 +2,7 @@
|
||||
[](https://github.com/simplex-chat/simplex-chat/releases)
|
||||
[](https://github.com/simplex-chat/simplex-chat/releases)
|
||||
[](https://www.reddit.com/r/SimpleXChat)
|
||||
[](https://mastodon.social/@simplex)
|
||||
<a rel="me" href="https://mastodon.social/@simplex"></a>
|
||||
|
||||
| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md) |
|
||||
|
||||
@@ -15,7 +15,7 @@
|
||||
## Welcome to SimpleX Chat!
|
||||
|
||||
1. 📲 [Install the app](#install-the-app).
|
||||
2. ↔️ [Connect to the team](#connect-to-the-team-via-the-app) and [join user groups](#join-user-groups).
|
||||
2. ↔️ [Connect to the team](#connect-to-the-team), [join user groups](#join-user-groups) and [follow our updates](#follow-our-updates).
|
||||
3. 🤝 [Make a private connection](#make-a-private-connection) with a friend.
|
||||
4. 🔤 [Help translating SimpleX Chat](#help-translating-simplex-chat).
|
||||
5. ⚡️ [Contribute](#contribute) and [help us with donations](#help-us-with-donations).
|
||||
@@ -40,14 +40,22 @@
|
||||
- 🚀 [TestFlight preview for iOS](https://testflight.apple.com/join/DWuT2LQu) with the new features 1-2 weeks earlier - **limited to 10,000 users**!
|
||||
- 🖥 Available as a terminal (console) [app / CLI](#zap-quick-installation-of-a-terminal-app) on Linux, MacOS, Windows.
|
||||
|
||||
## Connect to the team via the app
|
||||
## Connect to the team
|
||||
|
||||
You can connect to the team via the app using "chat with the developers button" available when you have no conversations in the profile, "Send questions and ideas" in the app settings or via our [SimpleX address](https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23%2F%3Fv%3D1%26dh%3DMCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%253D%26srv%3Dbylepyau3ty4czmn77q4fglvperknl4bi2eb2fdy2bh4jxtf32kf73yd.onion). Please connect to:
|
||||
|
||||
- to ask any questions
|
||||
- to suggest any improvements
|
||||
- to share anything relevant
|
||||
|
||||
We are replying the questions manually, so it is not instant – it can take up to 24 hours.
|
||||
|
||||
If you are interested in helping us to integrate open-source language models, and in [joining our team](./docs/JOIN_TEAM.md), please get in touch.
|
||||
|
||||
## Join user groups
|
||||
|
||||
You can join the groups created by other users via the new [directory service](https://simplex.chat/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). We are not responsible for the content shared in these groups.
|
||||
|
||||
**Please note**: The groups below are created for the users to be able to ask questions, make suggestions and ask questions about SimpleX Chat only.
|
||||
|
||||
You also can:
|
||||
@@ -79,7 +87,14 @@ There are groups in other languages, that we have the apps interface translated
|
||||
|
||||
You can join either by opening these links in the app or by opening them in a desktop browser and scanning the QR code.
|
||||
|
||||
You can also join the group created by other users by searching for them via the [directory service](https://simplex.chat/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). We are not responsible for the content shared in these groups.
|
||||
## Follow our updates
|
||||
|
||||
We publish our updates and releases via:
|
||||
|
||||
- [Reddit](https://www.reddit.com/r/SimpleXChat/), [Twitter](https://twitter.com/SimpleXChat), [Lemmy](https://lemmy.ml/c/simplex), [Mastodon](https://mastodon.social/@simplex) and [Nostr](https://snort.social/p/npub1exv22uulqnmlluszc4yk92jhs2e5ajcs6mu3t00a6avzjcalj9csm7d828).
|
||||
- SimpleX Chat [team profile](#connect-to-the-team).
|
||||
- [blog](https://simplex.chat/blog/) and [RSS feed](https://simplex.chat/feed.rss).
|
||||
- [mailing list](https://simplex.chat/#join-simplex), very rarely.
|
||||
|
||||
## Make a private connection
|
||||
|
||||
@@ -171,7 +186,7 @@ SimpleX Chat founder
|
||||
- [News and updates](#news-and-updates)
|
||||
- [Quick installation of a terminal app](#zap-quick-installation-of-a-terminal-app)
|
||||
- [SimpleX Platform design](#simplex-platform-design)
|
||||
- [Privacy: technical details and limitations](#privacy-technical-details-and-limitations)
|
||||
- [Privacy and security: technical details and limitations](#privacy-and-security-technical-details-and-limitations)
|
||||
- [For developers](#for-developers)
|
||||
- [Roadmap](#roadmap)
|
||||
- [Disclaimers, Security contact, License](#disclaimers)
|
||||
@@ -262,7 +277,7 @@ See [SimpleX whitepaper](https://github.com/simplex-chat/simplexmq/blob/stable/p
|
||||
|
||||
See [SimpleX Chat Protocol](./docs/protocol/simplex-chat.md) for the format of messages sent between chat clients over [SimpleX Messaging Protocol](https://github.com/simplex-chat/simplexmq/blob/stable/protocol/simplex-messaging.md).
|
||||
|
||||
## Privacy: technical details and limitations
|
||||
## Privacy and security: technical details and limitations
|
||||
|
||||
SimpleX Chat is a work in progress – we are releasing improvements as they are ready. You have to decide if the current state is good enough for your usage scenario.
|
||||
|
||||
@@ -281,12 +296,15 @@ What is already implemented:
|
||||
9. To protect your IP address all SimpleX Chat clients support accessing messaging servers via Tor - see [v3.1 release announcement](./blog/20220808-simplex-chat-v3.1-chat-groups.md) for more details.
|
||||
10. Local database encryption with passphrase - your contacts, groups and all sent and received messages are stored encrypted. If you used SimpleX Chat before v4.0 you need to enable the encryption via the app settings.
|
||||
11. Transport isolation - different TCP connections and Tor circuits are used for traffic of different user profiles, optionally - for different contacts and group member connections.
|
||||
12. Manual messaging queue rotations to move conversation to another SMP relay.
|
||||
|
||||
We plan to add soon:
|
||||
We plan to add:
|
||||
|
||||
1. Automatic message queue rotation. Currently the queues created between two users are used until the queue is manually changed by the user or contact is deleted. We are planning to add automatic queue rotation to make these identifiers temporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days).
|
||||
2. Local files encryption. Currently the images and files you send and receive are stored in the app unencrypted, you can delete them via `Settings / Database passphrase & export`.
|
||||
3. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time.
|
||||
1. Local files encryption. Currently the images and files you send and receive are stored in the app unencrypted, you can delete them via `Settings / Database passphrase & export`. This is currently in progress.
|
||||
2. Senders' SMP relays and recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party.
|
||||
3. Automatic message queue rotation and redundancy. Currently the queues created between two users are used until the queue is manually changed by the user or contact is deleted. We are planning to add automatic queue rotation to make these identifiers temporary and rotate based on some schedule TBC (e.g., every X messages, or every X hours/days).
|
||||
4. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time.
|
||||
5. Reproducible builds – this is the limitation of the development stack, but we will be investing into solving this problem. Users can still build all applications and services from the source code.
|
||||
|
||||
## For developers
|
||||
|
||||
|
||||
@@ -103,9 +103,15 @@ class AudioPlayer: NSObject, AVAudioPlayerDelegate {
|
||||
self.onFinishPlayback = onFinishPlayback
|
||||
}
|
||||
|
||||
func start(fileName: String, at: TimeInterval?) {
|
||||
let url = getAppFilePath(fileName)
|
||||
audioPlayer = try? AVAudioPlayer(contentsOf: url)
|
||||
func start(fileSource: CryptoFile, at: TimeInterval?) {
|
||||
let url = getAppFilePath(fileSource.filePath)
|
||||
if let cfArgs = fileSource.cryptoArgs {
|
||||
if let data = try? readCryptoFile(path: url.path, cryptoArgs: cfArgs) {
|
||||
audioPlayer = try? AVAudioPlayer(data: data)
|
||||
}
|
||||
} else {
|
||||
audioPlayer = try? AVAudioPlayer(contentsOf: url)
|
||||
}
|
||||
audioPlayer?.delegate = self
|
||||
audioPlayer?.prepareToPlay()
|
||||
if let at = at {
|
||||
|
||||
@@ -563,6 +563,10 @@ final class ChatModel: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func getGroupMember(_ groupMemberId: Int64) -> GroupMember? {
|
||||
groupMembers.first(where: { $0.groupMemberId == groupMemberId })
|
||||
}
|
||||
|
||||
func upsertGroupMember(_ groupInfo: GroupInfo, _ member: GroupMember) -> Bool {
|
||||
// user member was updated
|
||||
if groupInfo.membership.groupMemberId == member.groupMemberId {
|
||||
|
||||
@@ -11,42 +11,43 @@ import SimpleXChat
|
||||
import SwiftUI
|
||||
import AVKit
|
||||
|
||||
func getLoadedFilePath(_ file: CIFile?) -> String? {
|
||||
if let fileName = getLoadedFileName(file) {
|
||||
return getAppFilePath(fileName).path
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLoadedFileName(_ file: CIFile?) -> String? {
|
||||
if let file = file,
|
||||
file.loaded,
|
||||
let fileName = file.filePath {
|
||||
return fileName
|
||||
func getLoadedFileSource(_ file: CIFile?) -> CryptoFile? {
|
||||
if let file = file, file.loaded {
|
||||
return file.fileSource
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getLoadedImage(_ file: CIFile?) -> UIImage? {
|
||||
let loadedFilePath = getLoadedFilePath(file)
|
||||
if let loadedFilePath = loadedFilePath, let fileName = file?.filePath {
|
||||
let filePath = getAppFilePath(fileName)
|
||||
if let fileSource = getLoadedFileSource(file) {
|
||||
let filePath = getAppFilePath(fileSource.filePath)
|
||||
do {
|
||||
let data = try Data(contentsOf: filePath)
|
||||
let data = try getFileData(filePath, fileSource.cryptoArgs)
|
||||
let img = UIImage(data: data)
|
||||
try img?.setGifFromData(data, levelOfIntegrity: 1.0)
|
||||
return img
|
||||
do {
|
||||
try img?.setGifFromData(data, levelOfIntegrity: 1.0)
|
||||
return img
|
||||
} catch {
|
||||
return UIImage(data: data)
|
||||
}
|
||||
} catch {
|
||||
return UIImage(contentsOfFile: loadedFilePath)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func getFileData(_ path: URL, _ cfArgs: CryptoFileArgs?) throws -> Data {
|
||||
if let cfArgs = cfArgs {
|
||||
return try readCryptoFile(path: path.path, cryptoArgs: cfArgs)
|
||||
} else {
|
||||
return try Data(contentsOf: path)
|
||||
}
|
||||
}
|
||||
|
||||
func getLoadedVideo(_ file: CIFile?) -> URL? {
|
||||
let loadedFilePath = getLoadedFilePath(file)
|
||||
if loadedFilePath != nil, let fileName = file?.filePath {
|
||||
let filePath = getAppFilePath(fileName)
|
||||
if let fileSource = getLoadedFileSource(file) {
|
||||
let filePath = getAppFilePath(fileSource.filePath)
|
||||
if FileManager.default.fileExists(atPath: filePath.path) {
|
||||
return filePath
|
||||
}
|
||||
@@ -54,18 +55,18 @@ func getLoadedVideo(_ file: CIFile?) -> URL? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func saveAnimImage(_ image: UIImage) -> String? {
|
||||
func saveAnimImage(_ image: UIImage) -> CryptoFile? {
|
||||
let fileName = generateNewFileName("IMG", "gif")
|
||||
guard let imageData = image.imageData else { return nil }
|
||||
return saveFile(imageData, fileName)
|
||||
return saveFile(imageData, fileName, encrypted: privacyEncryptLocalFilesGroupDefault.get())
|
||||
}
|
||||
|
||||
func saveImage(_ uiImage: UIImage) -> String? {
|
||||
func saveImage(_ uiImage: UIImage) -> CryptoFile? {
|
||||
let hasAlpha = imageHasAlpha(uiImage)
|
||||
let ext = hasAlpha ? "png" : "jpg"
|
||||
if let imageDataResized = resizeImageToDataSize(uiImage, maxDataSize: MAX_IMAGE_SIZE, hasAlpha: hasAlpha) {
|
||||
let fileName = generateNewFileName("IMG", ext)
|
||||
return saveFile(imageDataResized, fileName)
|
||||
return saveFile(imageDataResized, fileName, encrypted: privacyEncryptLocalFilesGroupDefault.get())
|
||||
}
|
||||
return nil
|
||||
}
|
||||
@@ -157,13 +158,19 @@ func imageHasAlpha(_ img: UIImage) -> Bool {
|
||||
return false
|
||||
}
|
||||
|
||||
func saveFileFromURL(_ url: URL) -> String? {
|
||||
let savedFile: String?
|
||||
func saveFileFromURL(_ url: URL, encrypted: Bool) -> CryptoFile? {
|
||||
let savedFile: CryptoFile?
|
||||
if url.startAccessingSecurityScopedResource() {
|
||||
do {
|
||||
let fileData = try Data(contentsOf: url)
|
||||
let fileName = uniqueCombine(url.lastPathComponent)
|
||||
savedFile = saveFile(fileData, fileName)
|
||||
let toPath = getAppFilePath(fileName).path
|
||||
if encrypted {
|
||||
let cfArgs = try encryptCryptoFile(fromPath: url.path, toPath: toPath)
|
||||
savedFile = CryptoFile(filePath: fileName, cryptoArgs: cfArgs)
|
||||
} else {
|
||||
try FileManager.default.copyItem(atPath: url.path, toPath: toPath)
|
||||
savedFile = CryptoFile.plain(fileName)
|
||||
}
|
||||
} catch {
|
||||
logger.error("FileUtils.saveFileFromURL error: \(error.localizedDescription)")
|
||||
savedFile = nil
|
||||
@@ -176,18 +183,16 @@ func saveFileFromURL(_ url: URL) -> String? {
|
||||
return savedFile
|
||||
}
|
||||
|
||||
func saveFileFromURLWithoutLoad(_ url: URL) -> String? {
|
||||
let savedFile: String?
|
||||
func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
|
||||
do {
|
||||
let fileName = uniqueCombine(url.lastPathComponent)
|
||||
try FileManager.default.moveItem(at: url, to: getAppFilePath(fileName))
|
||||
ChatModel.shared.filesToDelete.remove(url)
|
||||
savedFile = fileName
|
||||
return CryptoFile.plain(fileName)
|
||||
} catch {
|
||||
logger.error("FileUtils.saveFileFromURLWithoutLoad error: \(error.localizedDescription)")
|
||||
savedFile = nil
|
||||
logger.error("ImageUtils.moveTempFileFromURL error: \(error.localizedDescription)")
|
||||
return nil
|
||||
}
|
||||
return savedFile
|
||||
}
|
||||
|
||||
func generateNewFileName(_ prefix: String, _ ext: String) -> String {
|
||||
@@ -288,4 +293,4 @@ extension UIImage {
|
||||
}
|
||||
return self
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -315,34 +315,18 @@ func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws -
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? {
|
||||
func apiSendMessage(sendRef: SendRef, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? {
|
||||
let chatModel = ChatModel.shared
|
||||
let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl)
|
||||
let r: ChatResponse
|
||||
if type == .direct {
|
||||
var cItem: ChatItem? = nil
|
||||
let endTask = beginBGTask({
|
||||
if let cItem = cItem {
|
||||
DispatchQueue.main.async {
|
||||
chatModel.messageDelivery.removeValue(forKey: cItem.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
r = await chatSendCmd(cmd, bgTask: false)
|
||||
if case let .newChatItem(_, aChatItem) = r {
|
||||
cItem = aChatItem.chatItem
|
||||
chatModel.messageDelivery[aChatItem.chatItem.id] = endTask
|
||||
return cItem
|
||||
}
|
||||
if let networkErrorAlert = networkErrorAlert(r) {
|
||||
AlertManager.shared.showAlert(networkErrorAlert)
|
||||
} else {
|
||||
sendMessageErrorAlert(r)
|
||||
}
|
||||
endTask()
|
||||
return nil
|
||||
} else {
|
||||
r = await chatSendCmd(cmd, bgDelay: msgDelay)
|
||||
let cmd: ChatCommand = .apiSendMessage(sendRef: sendRef, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl)
|
||||
switch sendRef {
|
||||
case .direct:
|
||||
let cItem = await sendMessageDirect(cmd)
|
||||
return cItem
|
||||
case .group(_, .some(_)):
|
||||
let cItem = await sendMessageDirect(cmd)
|
||||
return cItem
|
||||
case .group(_, .none):
|
||||
let r = await chatSendCmd(cmd, bgDelay: msgDelay)
|
||||
if case let .newChatItem(_, aChatItem) = r {
|
||||
return aChatItem.chatItem
|
||||
}
|
||||
@@ -351,6 +335,31 @@ func apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int6
|
||||
}
|
||||
}
|
||||
|
||||
private func sendMessageDirect(_ cmd: ChatCommand) async -> ChatItem? {
|
||||
let chatModel = ChatModel.shared
|
||||
var cItem: ChatItem? = nil
|
||||
let endTask = beginBGTask({
|
||||
if let cItem = cItem {
|
||||
DispatchQueue.main.async {
|
||||
chatModel.messageDelivery.removeValue(forKey: cItem.id)
|
||||
}
|
||||
}
|
||||
})
|
||||
let r = await chatSendCmd(cmd, bgTask: false)
|
||||
if case let .newChatItem(_, aChatItem) = r {
|
||||
cItem = aChatItem.chatItem
|
||||
chatModel.messageDelivery[aChatItem.chatItem.id] = endTask
|
||||
return cItem
|
||||
}
|
||||
if let networkErrorAlert = networkErrorAlert(r) {
|
||||
AlertManager.shared.showAlert(networkErrorAlert)
|
||||
} else {
|
||||
sendMessageErrorAlert(r)
|
||||
}
|
||||
endTask()
|
||||
return nil
|
||||
}
|
||||
|
||||
private func sendMessageErrorAlert(_ r: ChatResponse) {
|
||||
logger.error("apiSendMessage error: \(String(describing: r))")
|
||||
AlertManager.shared.showAlertMsg(
|
||||
@@ -807,14 +816,14 @@ func apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) async throws {
|
||||
try await sendCommandOkResp(.apiChatUnread(type: type, id: id, unreadChat: unreadChat))
|
||||
}
|
||||
|
||||
func receiveFile(user: any UserLike, fileId: Int64, auto: Bool = false) async {
|
||||
if let chatItem = await apiReceiveFile(fileId: fileId, auto: auto) {
|
||||
func receiveFile(user: any UserLike, fileId: Int64, encrypted: Bool, auto: Bool = false) async {
|
||||
if let chatItem = await apiReceiveFile(fileId: fileId, encrypted: encrypted, auto: auto) {
|
||||
await chatItemSimpleUpdate(user, chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
func apiReceiveFile(fileId: Int64, inline: Bool? = nil, auto: Bool = false) async -> AChatItem? {
|
||||
let r = await chatSendCmd(.receiveFile(fileId: fileId, inline: inline))
|
||||
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil, auto: Bool = false) async -> AChatItem? {
|
||||
let r = await chatSendCmd(.receiveFile(fileId: fileId, encrypted: encrypted, inline: inline))
|
||||
let am = AlertManager.shared
|
||||
if case let .rcvFileAccepted(_, chatItem) = r { return chatItem }
|
||||
if case .rcvFileAcceptedSndCancelled = r {
|
||||
@@ -1357,7 +1366,7 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
}
|
||||
if let file = cItem.autoReceiveFile() {
|
||||
Task {
|
||||
await receiveFile(user: user, fileId: file.fileId, auto: true)
|
||||
await receiveFile(user: user, fileId: file.fileId, encrypted: cItem.encryptLocalFile, auto: true)
|
||||
}
|
||||
}
|
||||
if cItem.showNotification {
|
||||
@@ -1660,15 +1669,3 @@ private struct UserResponse: Decodable {
|
||||
var user: User?
|
||||
var error: String?
|
||||
}
|
||||
|
||||
struct RuntimeError: Error {
|
||||
let message: String
|
||||
|
||||
init(_ message: String) {
|
||||
self.message = message
|
||||
}
|
||||
|
||||
public var localizedDescription: String {
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,8 @@ struct CIFileView: View {
|
||||
|
||||
var body: some View {
|
||||
let metaReserve = edited
|
||||
? " "
|
||||
: " "
|
||||
? " "
|
||||
: " "
|
||||
Button(action: fileAction) {
|
||||
HStack(alignment: .bottom, spacing: 6) {
|
||||
fileIndicator()
|
||||
@@ -84,7 +84,8 @@ struct CIFileView: View {
|
||||
Task {
|
||||
logger.debug("CIFileView fileAction - in .rcvInvitation, in Task")
|
||||
if let user = ChatModel.shared.currentUser {
|
||||
await receiveFile(user: user, fileId: file.fileId)
|
||||
let encrypted = file.fileProtocol == .xftp && privacyEncryptLocalFilesGroupDefault.get()
|
||||
await receiveFile(user: user, fileId: file.fileId, encrypted: encrypted)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
@@ -109,9 +110,8 @@ struct CIFileView: View {
|
||||
}
|
||||
case .rcvComplete:
|
||||
logger.debug("CIFileView fileAction - in .rcvComplete")
|
||||
if let filePath = getLoadedFilePath(file) {
|
||||
let url = URL(fileURLWithPath: filePath)
|
||||
showShareSheet(items: [url])
|
||||
if let fileSource = getLoadedFileSource(file) {
|
||||
saveCryptoFile(fileSource)
|
||||
}
|
||||
default: break
|
||||
}
|
||||
@@ -193,6 +193,30 @@ struct CIFileView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func saveCryptoFile(_ fileSource: CryptoFile) {
|
||||
if let cfArgs = fileSource.cryptoArgs {
|
||||
let url = getAppFilePath(fileSource.filePath)
|
||||
let tempUrl = getTempFilesDirectory().appendingPathComponent(fileSource.filePath)
|
||||
Task {
|
||||
do {
|
||||
try decryptCryptoFile(fromPath: url.path, cryptoArgs: cfArgs, toPath: tempUrl.path)
|
||||
await MainActor.run {
|
||||
showShareSheet(items: [tempUrl]) {
|
||||
removeFile(tempUrl)
|
||||
}
|
||||
}
|
||||
} catch {
|
||||
await MainActor.run {
|
||||
AlertManager.shared.showAlertMsg(title: "Error decrypting file", message: "Error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
let url = getAppFilePath(fileSource.filePath)
|
||||
showShareSheet(items: [url])
|
||||
}
|
||||
}
|
||||
|
||||
struct CIFileView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let sentFile: ChatItem = ChatItem(
|
||||
|
||||
@@ -16,6 +16,7 @@ struct CIImageView: View {
|
||||
let maxWidth: CGFloat
|
||||
@Binding var imgWidth: CGFloat?
|
||||
@State var scrollProxy: ScrollViewProxy?
|
||||
@State var metaColor: Color
|
||||
@State private var showFullScreenImage = false
|
||||
|
||||
var body: some View {
|
||||
@@ -36,9 +37,8 @@ struct CIImageView: View {
|
||||
case .rcvInvitation:
|
||||
Task {
|
||||
if let user = ChatModel.shared.currentUser {
|
||||
await receiveFile(user: user, fileId: file.fileId)
|
||||
await receiveFile(user: user, fileId: file.fileId, encrypted: chatItem.encryptLocalFile)
|
||||
}
|
||||
// TODO image accepted alert?
|
||||
}
|
||||
case .rcvAccepted:
|
||||
switch file.fileProtocol {
|
||||
@@ -110,7 +110,7 @@ struct CIImageView: View {
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: size, height: size)
|
||||
.foregroundColor(.white)
|
||||
.foregroundColor(metaColor)
|
||||
.padding(padding)
|
||||
}
|
||||
|
||||
|
||||
@@ -21,27 +21,28 @@ struct CIMetaView: View {
|
||||
} else {
|
||||
let meta = chatItem.meta
|
||||
let ttl = chat.chatInfo.timedMessagesTTL
|
||||
let encrypted = chatItem.encryptedFile
|
||||
switch meta.itemStatus {
|
||||
case let .sndSent(sndProgress):
|
||||
switch sndProgress {
|
||||
case .complete: ciMetaText(meta, chatTTL: ttl, color: metaColor, sent: .sent)
|
||||
case .partial: ciMetaText(meta, chatTTL: ttl, color: paleMetaColor, sent: .sent)
|
||||
case .complete: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .sent)
|
||||
case .partial: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .sent)
|
||||
}
|
||||
case let .sndRcvd(_, sndProgress):
|
||||
switch sndProgress {
|
||||
case .complete:
|
||||
ZStack {
|
||||
ciMetaText(meta, chatTTL: ttl, color: metaColor, sent: .rcvd1)
|
||||
ciMetaText(meta, chatTTL: ttl, color: metaColor, sent: .rcvd2)
|
||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd1)
|
||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd2)
|
||||
}
|
||||
case .partial:
|
||||
ZStack {
|
||||
ciMetaText(meta, chatTTL: ttl, color: paleMetaColor, sent: .rcvd1)
|
||||
ciMetaText(meta, chatTTL: ttl, color: paleMetaColor, sent: .rcvd2)
|
||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd1)
|
||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd2)
|
||||
}
|
||||
}
|
||||
default:
|
||||
ciMetaText(meta, chatTTL: ttl, color: metaColor)
|
||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -53,7 +54,7 @@ enum SentCheckmark {
|
||||
case rcvd2
|
||||
}
|
||||
|
||||
func ciMetaText(_ meta: CIMeta, chatTTL: Int?, color: Color = .clear, transparent: Bool = false, sent: SentCheckmark? = nil) -> Text {
|
||||
func ciMetaText(_ meta: CIMeta, chatTTL: Int?, encrypted: Bool?, color: Color = .clear, transparent: Bool = false, sent: SentCheckmark? = nil) -> Text {
|
||||
var r = Text("")
|
||||
if meta.itemEdited {
|
||||
r = r + statusIconText("pencil", color)
|
||||
@@ -80,7 +81,11 @@ func ciMetaText(_ meta: CIMeta, chatTTL: Int?, color: Color = .clear, transparen
|
||||
} else if !meta.disappearing {
|
||||
r = r + statusIconText("circlebadge.fill", .clear) + Text(" ")
|
||||
}
|
||||
return (r + meta.timestampText.foregroundColor(color)).font(.caption)
|
||||
if let enc = encrypted {
|
||||
r = r + statusIconText(enc ? "lock" : "lock.open", color) + Text(" ")
|
||||
}
|
||||
r = r + meta.timestampText.foregroundColor(color)
|
||||
return r.font(.caption)
|
||||
}
|
||||
|
||||
private func statusIconText(_ icon: String, _ color: Color) -> Text {
|
||||
|
||||
@@ -41,7 +41,7 @@ struct CIRcvDecryptionError: View {
|
||||
.onAppear {
|
||||
// for direct chat ConnectionStats are populated on opening chat, see ChatView onAppear
|
||||
if case let .group(groupInfo) = chat.chatInfo,
|
||||
case let .groupRcv(groupMember) = chatItem.chatDir {
|
||||
case let .groupRcv(groupMember, _) = chatItem.chatDir {
|
||||
do {
|
||||
let (member, stats) = try apiGroupMemberInfo(groupInfo.apiId, groupMember.groupMemberId)
|
||||
if let s = stats {
|
||||
@@ -78,7 +78,7 @@ struct CIRcvDecryptionError: View {
|
||||
basicDecryptionErrorItem()
|
||||
}
|
||||
} else if case let .group(groupInfo) = chat.chatInfo,
|
||||
case let .groupRcv(groupMember) = chatItem.chatDir,
|
||||
case let .groupRcv(groupMember, _) = chatItem.chatDir,
|
||||
let modelMember = ChatModel.shared.groupMembers.first(where: { $0.id == groupMember.id }),
|
||||
let memberStats = modelMember.activeConn?.connectionStats {
|
||||
if memberStats.ratchetSyncAllowed {
|
||||
@@ -118,7 +118,7 @@ struct CIRcvDecryptionError: View {
|
||||
.foregroundColor(syncSupported ? .accentColor : .secondary)
|
||||
.font(.callout)
|
||||
+ Text(" ")
|
||||
+ ciMetaText(chatItem.meta, chatTTL: nil, transparent: true)
|
||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true)
|
||||
)
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
@@ -139,7 +139,7 @@ struct CIRcvDecryptionError: View {
|
||||
.foregroundColor(.red)
|
||||
.italic()
|
||||
+ Text(" ")
|
||||
+ ciMetaText(chatItem.meta, chatTTL: nil, transparent: true)
|
||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true)
|
||||
}
|
||||
.padding(.horizontal, 12)
|
||||
CIMetaView(chatItem: chatItem)
|
||||
|
||||
@@ -59,7 +59,7 @@ struct CIVideoView: View {
|
||||
if let file = file {
|
||||
switch file.fileStatus {
|
||||
case .rcvInvitation:
|
||||
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
|
||||
receiveFileIfValidSize(file: file, encrypted: false, receiveFile: receiveFile)
|
||||
case .rcvAccepted:
|
||||
switch file.fileProtocol {
|
||||
case .xftp:
|
||||
@@ -85,7 +85,7 @@ struct CIVideoView: View {
|
||||
}
|
||||
if let file = file, case .rcvInvitation = file.fileStatus {
|
||||
Button {
|
||||
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
|
||||
receiveFileIfValidSize(file: file, encrypted: false, receiveFile: receiveFile)
|
||||
} label: {
|
||||
playPauseIcon("play.fill")
|
||||
}
|
||||
@@ -253,10 +253,11 @@ struct CIVideoView: View {
|
||||
.padding([.trailing, .top], 11)
|
||||
}
|
||||
|
||||
private func receiveFileIfValidSize(file: CIFile, receiveFile: @escaping (User, Int64, Bool) async -> Void) {
|
||||
// TODO encrypt: where file size is checked?
|
||||
private func receiveFileIfValidSize(file: CIFile, encrypted: Bool, receiveFile: @escaping (User, Int64, Bool, Bool) async -> Void) {
|
||||
Task {
|
||||
if let user = ChatModel.shared.currentUser {
|
||||
await receiveFile(user, file.fileId, false)
|
||||
await receiveFile(user, file.fileId, encrypted, false)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -159,7 +159,8 @@ struct VoiceMessagePlayer: View {
|
||||
}
|
||||
}
|
||||
.onChange(of: chatModel.stopPreviousRecPlay) { it in
|
||||
if let recordingFileName = getLoadedFileName(recordingFile), chatModel.stopPreviousRecPlay != getAppFilePath(recordingFileName) {
|
||||
if let recordingFileName = getLoadedFileSource(recordingFile)?.filePath,
|
||||
chatModel.stopPreviousRecPlay != getAppFilePath(recordingFileName) {
|
||||
audioPlayer?.stop()
|
||||
playbackState = .noPlayback
|
||||
playbackTime = TimeInterval(0)
|
||||
@@ -174,8 +175,8 @@ struct VoiceMessagePlayer: View {
|
||||
switch playbackState {
|
||||
case .noPlayback:
|
||||
Button {
|
||||
if let recordingFileName = getLoadedFileName(recordingFile) {
|
||||
startPlayback(recordingFileName)
|
||||
if let recordingSource = getLoadedFileSource(recordingFile) {
|
||||
startPlayback(recordingSource)
|
||||
}
|
||||
} label: {
|
||||
playPauseIcon("play.fill")
|
||||
@@ -219,7 +220,7 @@ struct VoiceMessagePlayer: View {
|
||||
Button {
|
||||
Task {
|
||||
if let user = ChatModel.shared.currentUser {
|
||||
await receiveFile(user: user, fileId: recordingFile.fileId)
|
||||
await receiveFile(user: user, fileId: recordingFile.fileId, encrypted: privacyEncryptLocalFilesGroupDefault.get())
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
@@ -251,8 +252,8 @@ struct VoiceMessagePlayer: View {
|
||||
.clipShape(Circle())
|
||||
}
|
||||
|
||||
private func startPlayback(_ recordingFileName: String) {
|
||||
chatModel.stopPreviousRecPlay = getAppFilePath(recordingFileName)
|
||||
private func startPlayback(_ recordingSource: CryptoFile) {
|
||||
chatModel.stopPreviousRecPlay = getAppFilePath(recordingSource.filePath)
|
||||
audioPlayer = AudioPlayer(
|
||||
onTimer: { playbackTime = $0 },
|
||||
onFinishPlayback: {
|
||||
@@ -260,7 +261,7 @@ struct VoiceMessagePlayer: View {
|
||||
playbackTime = TimeInterval(0)
|
||||
}
|
||||
)
|
||||
audioPlayer?.start(fileName: recordingFileName, at: playbackTime)
|
||||
audioPlayer?.start(fileSource: recordingSource, at: playbackTime)
|
||||
playbackState = .playing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -33,7 +33,7 @@ struct DeletedItemView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
Group {
|
||||
DeletedItemView(chatItem: ChatItem.getDeletedContentSample())
|
||||
DeletedItemView(chatItem: ChatItem.getDeletedContentSample(dir: .groupRcv(groupMember: GroupMember.sampleData)))
|
||||
DeletedItemView(chatItem: ChatItem.getDeletedContentSample(dir: .groupRcv(groupMember: GroupMember.sampleData, messageScope: .msGroup)))
|
||||
}
|
||||
.previewLayout(.fixed(width: 360, height: 200))
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -80,7 +80,7 @@ struct MsgContentView: View {
|
||||
}
|
||||
|
||||
private func reserveSpaceForMeta(_ mt: CIMeta) -> Text {
|
||||
(rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, transparent: true)
|
||||
(rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, transparent: true)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -290,7 +290,7 @@ struct ChatItemInfoView: View {
|
||||
|
||||
private func membersStatuses(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> [(GroupMember, CIStatus)] {
|
||||
memberDeliveryStatuses.compactMap({ mds in
|
||||
if let mem = ChatModel.shared.groupMembers.first(where: { $0.groupMemberId == mds.groupMemberId }) {
|
||||
if let mem = ChatModel.shared.getGroupMember(mds.groupMemberId) {
|
||||
return (mem, mds.memberDeliveryStatus)
|
||||
} else {
|
||||
return nil
|
||||
|
||||
@@ -141,6 +141,9 @@ struct ChatView: View {
|
||||
.appSheet(isPresented: $showChatInfoSheet) {
|
||||
GroupChatInfoView(chat: chat, groupInfo: groupInfo)
|
||||
}
|
||||
.appSheet(item: $selectedMember) { member in
|
||||
GroupMemberInfoView(groupInfo: groupInfo, member: member, navigation: true)
|
||||
}
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
@@ -207,6 +210,13 @@ struct ChatView: View {
|
||||
logger.error("apiContactInfo error: \(responseError(error))")
|
||||
}
|
||||
}
|
||||
} else if case let .group(groupInfo) = cInfo {
|
||||
Task {
|
||||
let groupMembers = await apiListMembers(groupInfo.groupId)
|
||||
await MainActor.run {
|
||||
ChatModel.shared.groupMembers = groupMembers
|
||||
}
|
||||
}
|
||||
}
|
||||
if chatModel.draftChatId == cInfo.id, let draft = chatModel.draft {
|
||||
composeState = draft
|
||||
@@ -428,29 +438,36 @@ struct ChatView: View {
|
||||
}
|
||||
|
||||
@ViewBuilder private func chatItemView(_ ci: ChatItem, _ maxWidth: CGFloat) -> some View {
|
||||
if case let .groupRcv(member) = ci.chatDir,
|
||||
if case let .groupRcv(member, msgScope) = ci.chatDir,
|
||||
case let .group(groupInfo) = chat.chatInfo {
|
||||
let (prevItem, nextItem) = chatModel.getChatItemNeighbors(ci)
|
||||
if ci.memberConnected != nil && nextItem?.memberConnected != nil {
|
||||
// memberConnected events are aggregated at the last chat item in a row of such events, see ChatItemView
|
||||
ZStack {} // scroll doesn't work if it's EmptyView()
|
||||
} else {
|
||||
if prevItem == nil || showMemberImage(member, prevItem) {
|
||||
if prevItem == nil || showMemberImage(member, msgScope, prevItem) {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
if ci.content.showMemberName {
|
||||
Text(member.displayName)
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.leading, memberImageSize + 14)
|
||||
.padding(.top, 7)
|
||||
Group {
|
||||
if msgScope == .msDirect {
|
||||
Text("\(member.displayName) **only to you**")
|
||||
} else {
|
||||
Text(member.displayName)
|
||||
}
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.leading, memberImageSize + 14)
|
||||
.padding(.top, 7)
|
||||
}
|
||||
HStack(alignment: .top, spacing: 8) {
|
||||
ProfileImage(imageStr: member.memberProfile.image)
|
||||
.frame(width: memberImageSize, height: memberImageSize)
|
||||
.onTapGesture { selectedMember = member }
|
||||
.appSheet(item: $selectedMember) { member in
|
||||
GroupMemberInfoView(groupInfo: groupInfo, member: member, navigation: true)
|
||||
}
|
||||
RcvMemberImageWithMenu(
|
||||
member: member,
|
||||
groupInfo: groupInfo,
|
||||
composeState: $composeState,
|
||||
selectedMember: $selectedMember
|
||||
)
|
||||
.environmentObject(chat)
|
||||
chatItemWithMenu(ci, maxWidth)
|
||||
}
|
||||
}
|
||||
@@ -464,6 +481,34 @@ struct ChatView: View {
|
||||
.padding(.leading, memberImageSize + 8 + 12)
|
||||
}
|
||||
}
|
||||
} else if case let .groupSnd(directMember) = ci.chatDir {
|
||||
let (prevItem, _) = chatModel.getChatItemNeighbors(ci)
|
||||
if prevItem == nil || showSndScope(directMember, prevItem) {
|
||||
VStack(alignment: .trailing, spacing: 4) {
|
||||
if ci.content.showMemberName {
|
||||
HStack {
|
||||
if let directMember = directMember {
|
||||
Text("**only to** \(directMember.displayName)")
|
||||
ProfileImage(imageStr: directMember.image)
|
||||
.frame(width: 20, height: 20)
|
||||
.onTapGesture { selectedMember = directMember }
|
||||
} else {
|
||||
Text("to group")
|
||||
}
|
||||
}
|
||||
.font(.caption)
|
||||
.foregroundStyle(.secondary)
|
||||
.padding(.top, 7)
|
||||
}
|
||||
chatItemWithMenu(ci, maxWidth)
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 5)
|
||||
} else {
|
||||
chatItemWithMenu(ci, maxWidth)
|
||||
.padding(.horizontal)
|
||||
.padding(.top, 5)
|
||||
}
|
||||
} else {
|
||||
chatItemWithMenu(ci, maxWidth)
|
||||
.padding(.horizontal)
|
||||
@@ -471,6 +516,69 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private struct RcvMemberImageWithMenu: View {
|
||||
var member: GroupMember
|
||||
var groupInfo: GroupInfo
|
||||
@Binding var composeState: ComposeState
|
||||
@Binding var selectedMember: GroupMember?
|
||||
@State private var allowMenu: Bool = true
|
||||
|
||||
var body: some View {
|
||||
let menuItems = memberMenu(member, groupInfo)
|
||||
if menuItems.isEmpty {
|
||||
profileImage()
|
||||
} else {
|
||||
let uiMenu: Binding<UIMenu> = Binding(
|
||||
get: { UIMenu(title: "", children: menuItems) },
|
||||
set: { _ in }
|
||||
)
|
||||
profileImage()
|
||||
.uiKitContextMenu(menu: uiMenu, allowMenu: $allowMenu)
|
||||
}
|
||||
}
|
||||
|
||||
private func profileImage() -> some View {
|
||||
ProfileImage(imageStr: member.memberProfile.image)
|
||||
.frame(width: memberImageSize, height: memberImageSize)
|
||||
.onTapGesture { selectedMember = member }
|
||||
}
|
||||
|
||||
private func memberMenu(_ member: GroupMember, _ groupInfo: GroupInfo) -> [UIMenuElement] {
|
||||
var menu: [UIMenuElement] = []
|
||||
if let memberWithConn = ChatModel.shared.getGroupMember(member.groupMemberId),
|
||||
memberWithConn.allowedToSendDirectlyTo(groupInfo: groupInfo) {
|
||||
menu.append(memberMenuSendDirectlyUIAction(memberWithConn, groupInfo))
|
||||
}
|
||||
return menu
|
||||
}
|
||||
|
||||
private func memberMenuSendDirectlyUIAction(_ toDirectMember: GroupMember, _ groupInfo: GroupInfo) -> UIAction {
|
||||
UIAction(
|
||||
title: NSLocalizedString("Send directly", comment: "chat item action"),
|
||||
image: UIImage(systemName: "arrow.left.arrow.right")
|
||||
) { _ in
|
||||
let canSend = toDirectMember.canSendDirectlyTo(groupInfo: groupInfo)
|
||||
switch canSend {
|
||||
case .notConnected:
|
||||
AlertManager.shared.showAlert(cantSendDirectlyNotConnectedAlert())
|
||||
case .notSupported:
|
||||
AlertManager.shared.showAlert(cantSendDirectlyNotSupportedAlert())
|
||||
case .canSend:
|
||||
withAnimation {
|
||||
if composeState.editing {
|
||||
composeState = ComposeState(directMember: .directMember(groupMember: toDirectMember))
|
||||
} else {
|
||||
composeState = composeState.copy(
|
||||
directMember: .directMember(groupMember: toDirectMember),
|
||||
contextItem: .noContextItem
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func chatItemWithMenu(_ ci: ChatItem, _ maxWidth: CGFloat) -> some View {
|
||||
ChatItemWithMenu(
|
||||
ci: ci,
|
||||
@@ -597,19 +705,27 @@ struct ChatView: View {
|
||||
menu.append(rm)
|
||||
}
|
||||
if ci.meta.itemDeleted == nil && !ci.isLiveDummy && !live {
|
||||
menu.append(replyUIAction())
|
||||
if ci.directMember == nil {
|
||||
menu.append(replyUIAction())
|
||||
}
|
||||
if case let .group(groupInfo) = chat.chatInfo,
|
||||
let toDirectMember = ci.memberToReplyDirectlyTo,
|
||||
let toDirectMemberWithConn = ChatModel.shared.getGroupMember(toDirectMember.groupMemberId),
|
||||
toDirectMemberWithConn.allowedToSendDirectlyTo(groupInfo: groupInfo) {
|
||||
menu.append(replyDirectlyUIAction(toDirectMemberWithConn, groupInfo))
|
||||
}
|
||||
}
|
||||
menu.append(shareUIAction())
|
||||
menu.append(copyUIAction())
|
||||
if let filePath = getLoadedFilePath(ci.file) {
|
||||
if let fileSource = getLoadedFileSource(ci.file) {
|
||||
if case .image = ci.content.msgContent, let image = getLoadedImage(ci.file) {
|
||||
if image.imageData != nil {
|
||||
menu.append(saveFileAction(filePath))
|
||||
menu.append(saveFileAction(fileSource))
|
||||
} else {
|
||||
menu.append(saveImageAction(image))
|
||||
}
|
||||
} else {
|
||||
menu.append(saveFileAction(filePath))
|
||||
menu.append(saveFileAction(fileSource))
|
||||
}
|
||||
}
|
||||
if ci.meta.editable && !mc.isVoice && !live {
|
||||
@@ -646,13 +762,45 @@ struct ChatView: View {
|
||||
private func replyUIAction() -> UIAction {
|
||||
UIAction(
|
||||
title: NSLocalizedString("Reply", comment: "chat item action"),
|
||||
image: UIImage(systemName: "arrowshape.turn.up.left")
|
||||
image: UIImage(systemName: "arrowshape.turn.up.left.2")
|
||||
) { _ in
|
||||
withAnimation {
|
||||
if composeState.editing {
|
||||
composeState = ComposeState(contextItem: .quotedItem(chatItem: ci))
|
||||
} else {
|
||||
composeState = composeState.copy(contextItem: .quotedItem(chatItem: ci))
|
||||
composeState = composeState.copy(
|
||||
directMember: .noDirectMember,
|
||||
contextItem: .quotedItem(chatItem: ci)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func replyDirectlyUIAction(_ toDirectMember: GroupMember, _ groupInfo: GroupInfo) -> UIAction {
|
||||
UIAction(
|
||||
title: NSLocalizedString("Reply directly", comment: "chat item action"),
|
||||
image: UIImage(systemName: "arrowshape.turn.up.left")
|
||||
) { _ in
|
||||
let canSend = toDirectMember.canSendDirectlyTo(groupInfo: groupInfo)
|
||||
switch canSend {
|
||||
case .notConnected:
|
||||
AlertManager.shared.showAlert(cantSendDirectlyNotConnectedAlert())
|
||||
case .notSupported:
|
||||
AlertManager.shared.showAlert(cantSendDirectlyNotSupportedAlert())
|
||||
case .canSend:
|
||||
withAnimation {
|
||||
if composeState.editing {
|
||||
composeState = ComposeState(
|
||||
directMember: .directMember(groupMember: toDirectMember),
|
||||
contextItem: .quotedItem(chatItem: ci)
|
||||
)
|
||||
} else {
|
||||
composeState = composeState.copy(
|
||||
directMember: .directMember(groupMember: toDirectMember),
|
||||
contextItem: .quotedItem(chatItem: ci)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -747,13 +895,12 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func saveFileAction(_ filePath: String) -> UIAction {
|
||||
private func saveFileAction(_ fileSource: CryptoFile) -> UIAction {
|
||||
UIAction(
|
||||
title: NSLocalizedString("Save", comment: "chat item action"),
|
||||
image: UIImage(systemName: "square.and.arrow.down")
|
||||
image: UIImage(systemName: fileSource.cryptoArgs == nil ? "square.and.arrow.down" : "lock.open")
|
||||
) { _ in
|
||||
let fileURL = URL(fileURLWithPath: filePath)
|
||||
showShareSheet(items: [fileURL])
|
||||
saveCryptoFile(fileSource)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -875,10 +1022,33 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func showMemberImage(_ member: GroupMember, _ prevItem: ChatItem?) -> Bool {
|
||||
private static func cantSendDirectlyNotConnectedAlert() -> Alert {
|
||||
Alert(
|
||||
title: Text("Can't send directly!"),
|
||||
message: Text("Member is not connected.")
|
||||
)
|
||||
}
|
||||
|
||||
private static func cantSendDirectlyNotSupportedAlert() -> Alert {
|
||||
Alert(
|
||||
title: Text("Can't send directly!"),
|
||||
message: Text("Member doesn't support this feature, ask them to update.")
|
||||
)
|
||||
}
|
||||
|
||||
private func showMemberImage(_ member: GroupMember, _ msgScope: MessageScope, _ prevItem: ChatItem?) -> Bool {
|
||||
switch (prevItem?.chatDir) {
|
||||
case .groupSnd: return true
|
||||
case let .groupRcv(prevMember): return prevMember.groupMemberId != member.groupMemberId
|
||||
case let .groupRcv(prevMember, prevMsgScope): return prevMember.groupMemberId != member.groupMemberId || prevMsgScope != msgScope
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
private func showSndScope(_ directMember: GroupMember?, _ prevItem: ChatItem?) -> Bool {
|
||||
switch (prevItem?.chatDir) {
|
||||
case .groupRcv: return directMember != nil
|
||||
case let .groupSnd(prevDirectMember):
|
||||
return prevDirectMember?.groupMemberId != directMember?.groupMemberId
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
@@ -11,6 +11,12 @@ import SimpleXChat
|
||||
import SwiftyGif
|
||||
import PhotosUI
|
||||
|
||||
enum ComposeDirectMember {
|
||||
case noDirectMember
|
||||
case directMember(groupMember: GroupMember)
|
||||
case directMemberCancelled
|
||||
}
|
||||
|
||||
enum ComposePreview {
|
||||
case noPreview
|
||||
case linkPreview(linkPreview: LinkPreview?)
|
||||
@@ -40,6 +46,7 @@ struct LiveMessage {
|
||||
struct ComposeState {
|
||||
var message: String
|
||||
var liveMessage: LiveMessage? = nil
|
||||
var directMember: ComposeDirectMember
|
||||
var preview: ComposePreview
|
||||
var contextItem: ComposeContextItem
|
||||
var voiceMessageRecordingState: VoiceMessageRecordingState
|
||||
@@ -49,12 +56,14 @@ struct ComposeState {
|
||||
init(
|
||||
message: String = "",
|
||||
liveMessage: LiveMessage? = nil,
|
||||
directMember: ComposeDirectMember = .noDirectMember,
|
||||
preview: ComposePreview = .noPreview,
|
||||
contextItem: ComposeContextItem = .noContextItem,
|
||||
voiceMessageRecordingState: VoiceMessageRecordingState = .noRecording
|
||||
) {
|
||||
self.message = message
|
||||
self.liveMessage = liveMessage
|
||||
self.directMember = directMember
|
||||
self.preview = preview
|
||||
self.contextItem = contextItem
|
||||
self.voiceMessageRecordingState = voiceMessageRecordingState
|
||||
@@ -62,6 +71,11 @@ struct ComposeState {
|
||||
|
||||
init(editingItem: ChatItem) {
|
||||
self.message = editingItem.content.text
|
||||
if let directMember = editingItem.directMember {
|
||||
self.directMember = .directMember(groupMember: directMember)
|
||||
} else {
|
||||
self.directMember = .noDirectMember
|
||||
}
|
||||
self.preview = chatItemPreview(chatItem: editingItem)
|
||||
self.contextItem = .editingItem(chatItem: editingItem)
|
||||
if let emc = editingItem.content.msgContent,
|
||||
@@ -75,6 +89,7 @@ struct ComposeState {
|
||||
func copy(
|
||||
message: String? = nil,
|
||||
liveMessage: LiveMessage? = nil,
|
||||
directMember: ComposeDirectMember? = nil,
|
||||
preview: ComposePreview? = nil,
|
||||
contextItem: ComposeContextItem? = nil,
|
||||
voiceMessageRecordingState: VoiceMessageRecordingState? = nil
|
||||
@@ -82,6 +97,7 @@ struct ComposeState {
|
||||
ComposeState(
|
||||
message: message ?? self.message,
|
||||
liveMessage: liveMessage ?? self.liveMessage,
|
||||
directMember: directMember ?? self.directMember,
|
||||
preview: preview ?? self.preview,
|
||||
contextItem: contextItem ?? self.contextItem,
|
||||
voiceMessageRecordingState: voiceMessageRecordingState ?? self.voiceMessageRecordingState
|
||||
@@ -167,25 +183,23 @@ struct ComposeState {
|
||||
}
|
||||
|
||||
func chatItemPreview(chatItem: ChatItem) -> ComposePreview {
|
||||
let chatItemPreview: ComposePreview
|
||||
switch chatItem.content.msgContent {
|
||||
case .text:
|
||||
chatItemPreview = .noPreview
|
||||
return .noPreview
|
||||
case let .link(_, preview: preview):
|
||||
chatItemPreview = .linkPreview(linkPreview: preview)
|
||||
return .linkPreview(linkPreview: preview)
|
||||
case let .image(_, image):
|
||||
chatItemPreview = .mediaPreviews(mediaPreviews: [(image, nil)])
|
||||
return .mediaPreviews(mediaPreviews: [(image, nil)])
|
||||
case let .video(_, image, _):
|
||||
chatItemPreview = .mediaPreviews(mediaPreviews: [(image, nil)])
|
||||
return .mediaPreviews(mediaPreviews: [(image, nil)])
|
||||
case let .voice(_, duration):
|
||||
chatItemPreview = .voicePreview(recordingFileName: chatItem.file?.fileName ?? "", duration: duration)
|
||||
return .voicePreview(recordingFileName: chatItem.file?.fileName ?? "", duration: duration)
|
||||
case .file:
|
||||
let fileName = chatItem.file?.fileName ?? ""
|
||||
chatItemPreview = .filePreview(fileName: fileName, file: getAppFilePath(fileName))
|
||||
return .filePreview(fileName: fileName, file: getAppFilePath(fileName))
|
||||
default:
|
||||
chatItemPreview = .noPreview
|
||||
return .noPreview
|
||||
}
|
||||
return chatItemPreview
|
||||
}
|
||||
|
||||
enum UploadContent: Equatable {
|
||||
@@ -259,6 +273,7 @@ struct ComposeView: View {
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
contextDirectMemberView()
|
||||
contextItemView()
|
||||
switch (composeState.editing, composeState.preview) {
|
||||
case (true, .filePreview): EmptyView()
|
||||
@@ -584,6 +599,26 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func contextDirectMemberView() -> some View {
|
||||
switch composeState.directMember {
|
||||
case .noDirectMember:
|
||||
EmptyView()
|
||||
case let .directMember(groupMember):
|
||||
ContextDirectMemberView(
|
||||
directMember: groupMember,
|
||||
cancelDirectMemberContext: { composeState = composeState.copy(
|
||||
directMember: .directMemberCancelled,
|
||||
contextItem: .noContextItem
|
||||
)}
|
||||
)
|
||||
case .directMemberCancelled:
|
||||
ContextDirectMemberView(
|
||||
directMember: nil,
|
||||
cancelDirectMemberContext: { composeState = composeState.copy(directMember: .noDirectMember) }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func contextItemView() -> some View {
|
||||
switch composeState.contextItem {
|
||||
case .noContextItem:
|
||||
@@ -656,10 +691,10 @@ struct ComposeView: View {
|
||||
}
|
||||
case let .voicePreview(recordingFileName, duration):
|
||||
stopPlayback.toggle()
|
||||
chatModel.filesToDelete.remove(getAppFilePath(recordingFileName))
|
||||
sent = await send(.voice(text: msgText, duration: duration), quoted: quoted, file: recordingFileName, ttl: ttl)
|
||||
let file = voiceCryptoFile(recordingFileName)
|
||||
sent = await send(.voice(text: msgText, duration: duration), quoted: quoted, file: file, ttl: ttl)
|
||||
case let .filePreview(_, file):
|
||||
if let savedFile = saveFileFromURL(file) {
|
||||
if let savedFile = saveFileFromURL(file, encrypted: privacyEncryptLocalFilesGroupDefault.get()) {
|
||||
sent = await send(.file(msgText), quoted: quoted, file: savedFile, live: live, ttl: ttl)
|
||||
}
|
||||
}
|
||||
@@ -727,32 +762,65 @@ struct ComposeView: View {
|
||||
|
||||
func sendVideo(_ imageData: (String, UploadContent?), text: String = "", quoted: Int64? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? {
|
||||
let (image, data) = imageData
|
||||
if case let .video(_, url, duration) = data, let savedFile = saveFileFromURLWithoutLoad(url) {
|
||||
if case let .video(_, url, duration) = data, let savedFile = moveTempFileFromURL(url) {
|
||||
return await send(.video(text: text, image: image, duration: duration), quoted: quoted, file: savedFile, live: live, ttl: ttl)
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func send(_ mc: MsgContent, quoted: Int64?, file: String? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? {
|
||||
if let chatItem = await apiSendMessage(
|
||||
type: chat.chatInfo.chatType,
|
||||
id: chat.chatInfo.apiId,
|
||||
file: file,
|
||||
quotedItemId: quoted,
|
||||
msg: mc,
|
||||
live: live,
|
||||
ttl: ttl
|
||||
) {
|
||||
await MainActor.run {
|
||||
chatModel.removeLiveDummy(animated: false)
|
||||
chatModel.addChatItem(chat.chatInfo, chatItem)
|
||||
func voiceCryptoFile(_ fileName: String) -> CryptoFile? {
|
||||
if !privacyEncryptLocalFilesGroupDefault.get() {
|
||||
return CryptoFile.plain(fileName)
|
||||
}
|
||||
let url = getAppFilePath(fileName)
|
||||
let toFile = generateNewFileName("voice", "m4a")
|
||||
let toUrl = getAppFilePath(toFile)
|
||||
if let cfArgs = try? encryptCryptoFile(fromPath: url.path, toPath: toUrl.path) {
|
||||
removeFile(url)
|
||||
return CryptoFile(filePath: toFile, cryptoArgs: cfArgs)
|
||||
} else {
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
func send(_ mc: MsgContent, quoted: Int64?, file: CryptoFile? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? {
|
||||
var sendRef: SendRef?
|
||||
let chatId = chat.chatInfo.apiId
|
||||
switch chat.chatInfo.chatType {
|
||||
case .direct:
|
||||
sendRef = .direct(contactId: chatId)
|
||||
case .group:
|
||||
switch composeState.directMember {
|
||||
case let .directMember(groupMember):
|
||||
sendRef = .group(groupId: chatId, directMemberId: groupMember.groupMemberId)
|
||||
default:
|
||||
sendRef = .group(groupId: chatId, directMemberId: nil)
|
||||
}
|
||||
return chatItem
|
||||
default: sendRef = nil
|
||||
}
|
||||
if let file = file {
|
||||
removeFile(file)
|
||||
if let sendRef = sendRef {
|
||||
if let chatItem = await apiSendMessage(
|
||||
sendRef: sendRef,
|
||||
file: file,
|
||||
quotedItemId: quoted,
|
||||
msg: mc,
|
||||
live: live,
|
||||
ttl: ttl
|
||||
) {
|
||||
await MainActor.run {
|
||||
chatModel.removeLiveDummy(animated: false)
|
||||
chatModel.addChatItem(chat.chatInfo, chatItem)
|
||||
}
|
||||
return chatItem
|
||||
}
|
||||
if let file = file {
|
||||
removeFile(file.filePath)
|
||||
}
|
||||
return nil
|
||||
} else {
|
||||
logger.error("ComposeView send: sendRef is nil")
|
||||
return nil
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkLinkPreview() -> MsgContent {
|
||||
@@ -770,7 +838,7 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func saveAnyImage(_ img: UploadContent) -> String? {
|
||||
func saveAnyImage(_ img: UploadContent) -> CryptoFile? {
|
||||
switch img {
|
||||
case let .simpleImage(image): return saveImage(image)
|
||||
case let .animatedImage(image): return saveAnimImage(image)
|
||||
|
||||
@@ -188,7 +188,7 @@ struct ComposeVoiceView: View {
|
||||
playbackTime = recordingTime // animate progress bar to the end
|
||||
}
|
||||
)
|
||||
audioPlayer?.start(fileName: recordingFileName, at: playbackTime)
|
||||
audioPlayer?.start(fileSource: CryptoFile.plain(recordingFileName), at: playbackTime)
|
||||
playbackState = .playing
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,59 @@
|
||||
//
|
||||
// ContextDirectMemberView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 11.09.2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct ContextDirectMemberView: View {
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
let directMember: GroupMember?
|
||||
let cancelDirectMemberContext: () -> Void
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
if let directMember = directMember {
|
||||
Text("**only to** \(directMember.chatViewName)")
|
||||
.lineLimit(1)
|
||||
if let image = directMember.image {
|
||||
ProfileImage(imageStr: image)
|
||||
.frame(width: 24, height: 24)
|
||||
.padding(.trailing, 2)
|
||||
} else {
|
||||
Image(systemName: "arrow.left.arrow.right")
|
||||
.resizable()
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 16, height: 16)
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.trailing, 2)
|
||||
}
|
||||
} else {
|
||||
// maybe make it disappear after some time?
|
||||
Text("send to all group members")
|
||||
}
|
||||
Spacer()
|
||||
Button {
|
||||
withAnimation {
|
||||
cancelDirectMemberContext()
|
||||
}
|
||||
} label: {
|
||||
Image(systemName: "multiply")
|
||||
}
|
||||
}
|
||||
.padding(12)
|
||||
.frame(minHeight: 50)
|
||||
.frame(maxWidth: .infinity)
|
||||
.background(colorScheme == .light ? sentColorLight : sentColorDark)
|
||||
.padding(.top, 8)
|
||||
}
|
||||
}
|
||||
|
||||
struct ContextDirectMemberView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
ContextDirectMemberView(directMember: GroupMember.sampleData, cancelDirectMemberContext: {})
|
||||
}
|
||||
}
|
||||
@@ -239,7 +239,7 @@ struct GroupMemberInfoView: View {
|
||||
chatModel.chatId = chat.id
|
||||
}
|
||||
} label: {
|
||||
Label("Send direct message", systemImage: "message")
|
||||
Label("Open direct chat", systemImage: "message")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -256,7 +256,7 @@ struct GroupMemberInfoView: View {
|
||||
logger.error("openDirectChatButton apiGetChat error: \(responseError(error))")
|
||||
}
|
||||
} label: {
|
||||
Label("Send direct message", systemImage: "message")
|
||||
Label("Open direct chat", systemImage: "message")
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -8,11 +8,15 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
func showShareSheet(items: [Any]) {
|
||||
func showShareSheet(items: [Any], completed: (() -> Void)? = nil) {
|
||||
let keyWindowScene = UIApplication.shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene
|
||||
if let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first,
|
||||
let presentedViewController = keyWindow.rootViewController?.presentedViewController ?? keyWindow.rootViewController {
|
||||
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
||||
if let completed = completed {
|
||||
let handler: UIActivityViewController.CompletionWithItemsHandler = { _,_,_,_ in completed() }
|
||||
activityViewController.completionWithItemsHandler = handler
|
||||
}
|
||||
presentedViewController.present(activityViewController, animated: true)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -15,6 +15,7 @@ struct PrivacySettings: View {
|
||||
@AppStorage(DEFAULT_PRIVACY_LINK_PREVIEWS) private var useLinkPreviews = true
|
||||
@AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true
|
||||
@AppStorage(DEFAULT_PRIVACY_SAVE_LAST_DRAFT) private var saveLastDraft = true
|
||||
@AppStorage(GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES, store: groupDefaults) private var encryptLocalFiles = true
|
||||
@State private var simplexLinkMode = privacySimplexLinkModeDefault.get()
|
||||
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
|
||||
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
|
||||
@@ -63,6 +64,9 @@ struct PrivacySettings: View {
|
||||
}
|
||||
|
||||
Section {
|
||||
settingsRow("lock.doc") {
|
||||
Toggle("Encrypt local files", isOn: $encryptLocalFiles)
|
||||
}
|
||||
settingsRow("photo") {
|
||||
Toggle("Auto-accept images", isOn: $autoAcceptImages)
|
||||
.onChange(of: autoAcceptImages) {
|
||||
|
||||
@@ -3655,6 +3655,26 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target state="translated">%1$@ في %2$@:</target>
|
||||
<note>copied message info, <sender> at <time></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="## 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="ar" datatype="plaintext">
|
||||
|
||||
@@ -44,14 +44,17 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="# %@" xml:space="preserve">
|
||||
<source># %@</source>
|
||||
<target># %@</target>
|
||||
<note>copied message info title, # <title></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## History" xml:space="preserve">
|
||||
<source>## History</source>
|
||||
<target>## Historie</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## In reply to" xml:space="preserve">
|
||||
<source>## In reply to</source>
|
||||
<target>## Odpovídáno</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="#secret#" xml:space="preserve">
|
||||
@@ -86,6 +89,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve">
|
||||
<source>%@ and %@ connected</source>
|
||||
<target>%@ a %@ připojen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ at %@:" xml:space="preserve">
|
||||
@@ -120,6 +124,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
|
||||
<source>%@, %@ and %lld other members connected</source>
|
||||
<target>%@, %@ a %lld ostatní členové připojeni</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@:" xml:space="preserve">
|
||||
@@ -477,7 +482,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept connection request?" xml:space="preserve">
|
||||
<source>Accept connection request?</source>
|
||||
<target>Přijmout kontakt</target>
|
||||
<target>Přijmout kontakt?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept contact request from %@?" xml:space="preserve">
|
||||
@@ -1063,15 +1068,17 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect directly" xml:space="preserve">
|
||||
<source>Connect directly</source>
|
||||
<target>Připojit přímo</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect incognito" xml:space="preserve">
|
||||
<source>Connect incognito</source>
|
||||
<target>Spojit se inkognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via contact link" xml:space="preserve">
|
||||
<source>Connect via contact link</source>
|
||||
<target>Připojit se přes kontaktní odkaz?</target>
|
||||
<target>Připojit se přes odkaz</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via group link?" xml:space="preserve">
|
||||
@@ -1091,7 +1098,7 @@
|
||||
</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>
|
||||
<target>Připojit se jednorázovým odkazem</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server…" xml:space="preserve">
|
||||
@@ -1569,6 +1576,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Delivery" xml:space="preserve">
|
||||
<source>Delivery</source>
|
||||
<target>Doručenka</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delivery receipts are disabled!" xml:space="preserve">
|
||||
@@ -1811,6 +1819,10 @@
|
||||
<target>Šifrovat databázi?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Zašifrovaná databáze</target>
|
||||
@@ -1941,6 +1953,10 @@
|
||||
<target>Chyba při vytváření profilu!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Chyba při mazání databáze chatu</target>
|
||||
@@ -2583,6 +2599,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Incognito mode protects your privacy by using a new random profile for each contact." xml:space="preserve">
|
||||
<source>Incognito mode protects your privacy by using a new random profile for each contact.</source>
|
||||
<target>Režim inkognito chrání vaše soukromí používáním nového náhodného profilu pro každý kontakt.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incoming audio call" xml:space="preserve">
|
||||
@@ -2659,6 +2676,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid status" xml:space="preserve">
|
||||
<source>Invalid status</source>
|
||||
<target>Neplatný status</target>
|
||||
<note>item status text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invitation expired!" xml:space="preserve">
|
||||
@@ -2744,12 +2762,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Join incognito" xml:space="preserve">
|
||||
<source>Join incognito</source>
|
||||
<target>Připojte se inkognito</target>
|
||||
<target>Připojit se inkognito</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Joining group" xml:space="preserve">
|
||||
<source>Joining group</source>
|
||||
<target>Připojení ke skupině</target>
|
||||
<target>Připojování ke skupině</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
@@ -3009,6 +3027,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Most likely this connection is deleted." xml:space="preserve">
|
||||
<source>Most likely this connection is deleted.</source>
|
||||
<target>Pravděpodobně je toto spojení smazáno.</target>
|
||||
<note>item status description</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
|
||||
@@ -4623,7 +4642,7 @@ Může se to stát kvůli nějaké chybě, nebo pokud je spojení kompromitován
|
||||
</trans-unit>
|
||||
<trans-unit id="They can be overridden in contact and group settings." xml:space="preserve">
|
||||
<source>They can be overridden in contact and group settings.</source>
|
||||
<target>Mohou být přepsány v nastavení kontaktů</target>
|
||||
<target>Mohou být přepsány v nastavení kontaktů.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." xml:space="preserve">
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1819,6 +1819,10 @@
|
||||
<target>Datenbank verschlüsseln?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Verschlüsselte Datenbank</target>
|
||||
@@ -1949,6 +1953,10 @@
|
||||
<target>Fehler beim Erstellen des Profils!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Fehler beim Löschen der Chat-Datenbank</target>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1819,6 +1819,11 @@
|
||||
<target>Encrypt database?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<target>Encrypt local files</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Encrypted database</target>
|
||||
@@ -1949,6 +1954,11 @@
|
||||
<target>Error creating profile!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<target>Error decrypting file</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Error deleting chat database</target>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -466,7 +466,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="About SimpleX address" xml:space="preserve">
|
||||
<source>About SimpleX address</source>
|
||||
<target>Acerca de dirección SimpleX</target>
|
||||
<target>Acerca de la dirección SimpleX</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accent color" xml:space="preserve">
|
||||
@@ -1208,12 +1208,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Create SimpleX address" xml:space="preserve">
|
||||
<source>Create SimpleX address</source>
|
||||
<target>Crear dirección SimpleX</target>
|
||||
<target>Crear tu dirección SimpleX</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create an address to let people connect with you." xml:space="preserve">
|
||||
<source>Create an address to let people connect with you.</source>
|
||||
<target>Crear una dirección para que otras personas se puedan conectar contigo.</target>
|
||||
<target>Crea una dirección para que otras personas puedan conectar contigo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create file" xml:space="preserve">
|
||||
@@ -1248,7 +1248,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Create your profile" xml:space="preserve">
|
||||
<source>Create your profile</source>
|
||||
<target>Crear tu perfil</target>
|
||||
<target>Crea tu perfil</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Created on %@" xml:space="preserve">
|
||||
@@ -1381,7 +1381,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Decentralized" xml:space="preserve">
|
||||
<source>Decentralized</source>
|
||||
<target>Descentralizado</target>
|
||||
<target>Descentralizada</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Decryption error" xml:space="preserve">
|
||||
@@ -1706,7 +1706,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Don't create address" xml:space="preserve">
|
||||
<source>Don't create address</source>
|
||||
<target>No crear dirección</target>
|
||||
<target>No crear dirección SimpleX</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Don't enable" xml:space="preserve">
|
||||
@@ -1819,6 +1819,10 @@
|
||||
<target>¿Cifrar base de datos?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Base de datos cifrada</target>
|
||||
@@ -1949,6 +1953,10 @@
|
||||
<target>¡Error al crear perfil!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Error al eliminar base de datos</target>
|
||||
@@ -2899,7 +2907,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Markdown in messages" xml:space="preserve">
|
||||
<source>Markdown in messages</source>
|
||||
<target>Sintaxis markdown en los mensajes</target>
|
||||
<target>Sintaxis Markdown</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Max 30 seconds, received instantly." xml:space="preserve">
|
||||
@@ -3663,7 +3671,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving address will be changed to a different server. Address change will complete after sender comes online." xml:space="preserve">
|
||||
<source>Receiving address will be changed to a different server. Address change will complete after sender comes online.</source>
|
||||
<target>La dirección de recepción se cambiará. El cambio se completará cuando el remitente esté en línea.</target>
|
||||
<target>La dirección de recepción pasará a otro servidor. El cambio se completará cuando el remitente esté en línea.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
@@ -4383,7 +4391,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Stop chat to enable database actions" xml:space="preserve">
|
||||
<source>Stop chat to enable database actions</source>
|
||||
<target>Para habilitar las acciones sobre la base de datos, previamente debes detener Chat</target>
|
||||
<target>Detén SimpleX para habilitar las acciones sobre la base de datos</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." xml:space="preserve">
|
||||
@@ -4590,7 +4598,7 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="The next generation of private messaging" xml:space="preserve">
|
||||
<source>The next generation of private messaging</source>
|
||||
<target>La próxima generación de mensajería privada</target>
|
||||
<target>La nueva generación de mensajería privada</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
|
||||
@@ -5130,7 +5138,7 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
|
||||
</trans-unit>
|
||||
<trans-unit id="You can create it later" xml:space="preserve">
|
||||
<source>You can create it later</source>
|
||||
<target>Puedes crearlo más tarde</target>
|
||||
<target>Puedes crearla más tarde</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can enable later via Settings" xml:space="preserve">
|
||||
@@ -5330,7 +5338,7 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chat database" xml:space="preserve">
|
||||
<source>Your chat database</source>
|
||||
<target>Base de datos Chat</target>
|
||||
<target>Base de datos</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chat database is not encrypted - set passphrase to encrypt it." xml:space="preserve">
|
||||
@@ -5411,7 +5419,7 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile, contacts and delivered messages are stored on your device." xml:space="preserve">
|
||||
<source>Your profile, contacts and delivered messages are stored on your device.</source>
|
||||
<target>Tu perfil, contactos y mensajes entregados se almacenan en tu dispositivo.</target>
|
||||
<target>Tu perfil, contactos y mensajes se almacenan en tu dispositivo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your random profile" xml:space="preserve">
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"locale" : "fi"
|
||||
}
|
||||
],
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
},
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
File diff suppressed because it is too large
Load Diff
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "0.000",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.533"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
},
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "SimpleX NSE";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX NSE";
|
||||
/* Copyright (human-readable) */
|
||||
"NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved.";
|
||||
@@ -0,0 +1,30 @@
|
||||
/* No comment provided by engineer. */
|
||||
"_italic_" = "\\_italic_";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"*bold*" = "\\*bold*";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"`a + b`" = "\\`a + b`";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"~strike~" = "\\~strike~";
|
||||
|
||||
/* call status */
|
||||
"connecting call" = "connecting call…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server…" = "Connecting to server…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Connecting to server… (error: %@)";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"member connected" = "connected";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No group!" = "Group not found!";
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX";
|
||||
/* Privacy - Camera Usage Description */
|
||||
"NSCameraUsageDescription" = "SimpleX needs camera access to scan QR codes to connect to other users and for video calls.";
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"NSFaceIDUsageDescription" = "SimpleX uses Face ID for local authentication";
|
||||
/* Privacy - Microphone Usage Description */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX needs microphone access for audio and video calls, and to record voice messages.";
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "SimpleX needs access to Photo Library for saving captured and received media";
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"developmentRegion" : "en",
|
||||
"project" : "SimpleX.xcodeproj",
|
||||
"targetLocale" : "fi",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "15A5219j",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "15.0"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -1819,6 +1819,10 @@
|
||||
<target>Chiffrer la base de données ?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Base de données chiffrée</target>
|
||||
@@ -1949,6 +1953,10 @@
|
||||
<target>Erreur lors de la création du profil !</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Erreur lors de la suppression de la base de données du chat</target>
|
||||
@@ -5725,7 +5733,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption ok" xml:space="preserve">
|
||||
<source>encryption ok</source>
|
||||
<target>chiffrement ok</target>
|
||||
<target>chiffrement OK</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption ok for %@" xml:space="preserve">
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -284,7 +284,7 @@ Available in v5.1</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="." xml:space="preserve" approved="no">
|
||||
<source>.</source>
|
||||
<target state="needs-translation">.</target>
|
||||
<target state="translated">.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="1 day" xml:space="preserve" approved="no">
|
||||
@@ -1971,8 +1971,9 @@ Available in v5.1</source>
|
||||
<target state="translated">חברי הקבוצה יכולים לשלוח הודעות קוליות.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group message:" xml:space="preserve">
|
||||
<trans-unit id="Group message:" xml:space="preserve" approved="no">
|
||||
<source>Group message:</source>
|
||||
<target state="translated">הודעה קבוצתית:</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group moderation" xml:space="preserve" approved="no">
|
||||
@@ -2377,262 +2378,327 @@ Available in v5.1</source>
|
||||
<target state="translated">נתוני פרופיל מקומיים בלבד</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Lock after" xml:space="preserve">
|
||||
<trans-unit id="Lock after" xml:space="preserve" approved="no">
|
||||
<source>Lock after</source>
|
||||
<target state="translated">נעל אחרי</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Lock mode" xml:space="preserve">
|
||||
<trans-unit id="Lock mode" xml:space="preserve" approved="no">
|
||||
<source>Lock mode</source>
|
||||
<target state="translated">מצב נעילה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make a private connection" xml:space="preserve">
|
||||
<trans-unit id="Make a private connection" xml:space="preserve" approved="no">
|
||||
<source>Make a private connection</source>
|
||||
<target state="translated">צור חיבור פרטי</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make profile private!" xml:space="preserve">
|
||||
<trans-unit id="Make profile private!" xml:space="preserve" approved="no">
|
||||
<source>Make profile private!</source>
|
||||
<target state="translated">הפוך את הפרופיל לפרטי!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." xml:space="preserve">
|
||||
<trans-unit id="Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@)." xml:space="preserve" approved="no">
|
||||
<source>Make sure %@ server addresses are in correct format, line separated and are not duplicated (%@).</source>
|
||||
<target state="translated">ודא שכתובות השרת %@ הן בפורמט הנכון, מופרדות בשורה ואינן משוכפלות (%@).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." xml:space="preserve">
|
||||
<trans-unit id="Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated." xml:space="preserve" approved="no">
|
||||
<source>Make sure WebRTC ICE server addresses are in correct format, line separated and are not duplicated.</source>
|
||||
<target state="translated">ודאו שכתובות שרתי ה־WebRTC ICE הן בפורמט הנכון, מופרדות בשורה ולא משוכפלות.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" xml:space="preserve">
|
||||
<trans-unit id="Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" xml:space="preserve" approved="no">
|
||||
<source>Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*</source>
|
||||
<target state="translated">אנשים רבים שאלו: *אם ל-SimpleX אין מזהי משתמש, איך הוא יכול לשלוח הודעות?*</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Mark deleted for everyone" xml:space="preserve">
|
||||
<trans-unit id="Mark deleted for everyone" xml:space="preserve" approved="no">
|
||||
<source>Mark deleted for everyone</source>
|
||||
<target state="translated">לסמן נמחק לכולם</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Mark read" xml:space="preserve">
|
||||
<trans-unit id="Mark read" xml:space="preserve" approved="no">
|
||||
<source>Mark read</source>
|
||||
<target state="translated">סמן כנקרא</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Mark verified" xml:space="preserve">
|
||||
<trans-unit id="Mark verified" xml:space="preserve" approved="no">
|
||||
<source>Mark verified</source>
|
||||
<target state="translated">סמן מאומת</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Markdown in messages" xml:space="preserve">
|
||||
<trans-unit id="Markdown in messages" xml:space="preserve" approved="no">
|
||||
<source>Markdown in messages</source>
|
||||
<target state="translated">מרקדאון בהודעות</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Max 30 seconds, received instantly." xml:space="preserve">
|
||||
<trans-unit id="Max 30 seconds, received instantly." xml:space="preserve" approved="no">
|
||||
<source>Max 30 seconds, received instantly.</source>
|
||||
<target state="translated">מקסימום 30 שניות, התקבל באופן מיידי.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Member" xml:space="preserve">
|
||||
<trans-unit id="Member" xml:space="preserve" approved="no">
|
||||
<source>Member</source>
|
||||
<target state="translated">חבר קבוצה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Member role will be changed to "%@". All group members will be notified." xml:space="preserve">
|
||||
<trans-unit id="Member role will be changed to "%@". All group members will be notified." xml:space="preserve" approved="no">
|
||||
<source>Member role will be changed to "%@". All group members will be notified.</source>
|
||||
<target state="translated">תפקיד חבר הקבוצה ישתנה ל-"%@". כל חברי הקבוצה יקבלו הודעה.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Member role will be changed to "%@". The member will receive a new invitation." xml:space="preserve">
|
||||
<trans-unit id="Member role will be changed to "%@". The member will receive a new invitation." xml:space="preserve" approved="no">
|
||||
<source>Member role will be changed to "%@". The member will receive a new invitation.</source>
|
||||
<target state="translated">תפקיד חבר הקבוצה ישתנה ל-"%@". חבר הקבוצה יקבל הזמנה חדשה.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Member will be removed from group - this cannot be undone!" xml:space="preserve">
|
||||
<trans-unit id="Member will be removed from group - this cannot be undone!" xml:space="preserve" approved="no">
|
||||
<source>Member will be removed from group - this cannot be undone!</source>
|
||||
<target state="translated">חבר הקבוצה יוסר מהקבוצה – לא ניתן לבטל זאת!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message delivery error" xml:space="preserve">
|
||||
<trans-unit id="Message delivery error" xml:space="preserve" approved="no">
|
||||
<source>Message delivery error</source>
|
||||
<target state="translated">שגיאת מסירת הודעה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message draft" xml:space="preserve">
|
||||
<trans-unit id="Message draft" xml:space="preserve" approved="no">
|
||||
<source>Message draft</source>
|
||||
<target state="translated">טיוטת הודעה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message text" xml:space="preserve">
|
||||
<trans-unit id="Message text" xml:space="preserve" approved="no">
|
||||
<source>Message text</source>
|
||||
<target state="translated">טקסט הודעה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Messages" xml:space="preserve">
|
||||
<trans-unit id="Messages" xml:space="preserve" approved="no">
|
||||
<source>Messages</source>
|
||||
<target state="translated">הודעות</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Messages & files" xml:space="preserve">
|
||||
<trans-unit id="Messages & files" xml:space="preserve" approved="no">
|
||||
<source>Messages & files</source>
|
||||
<target state="translated">הודעות וקבצים</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migrating database archive..." xml:space="preserve">
|
||||
<source>Migrating database archive...</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migration error:" xml:space="preserve">
|
||||
<trans-unit id="Migration error:" xml:space="preserve" approved="no">
|
||||
<source>Migration error:</source>
|
||||
<target state="translated">שגיאת העברה:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." xml:space="preserve">
|
||||
<trans-unit id="Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat)." xml:space="preserve" approved="no">
|
||||
<source>Migration failed. Tap **Skip** below to continue using the current database. Please report the issue to the app developers via chat or email [chat@simplex.chat](mailto:chat@simplex.chat).</source>
|
||||
<target state="translated">ההעברה נכשלה. הקש על **דלג** למטה כדי להמשיך להשתמש במסד הנתונים הנוכחי. אנא דווח על הבעיה למפתחי האפליקציה באמצעות צ'אט או דוא"ל [chat@simplex.chat](mailto:chat@simplex.chat).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migration is completed" xml:space="preserve">
|
||||
<trans-unit id="Migration is completed" xml:space="preserve" approved="no">
|
||||
<source>Migration is completed</source>
|
||||
<target state="translated">ההעברה הושלמה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migrations: %@" xml:space="preserve">
|
||||
<trans-unit id="Migrations: %@" xml:space="preserve" approved="no">
|
||||
<source>Migrations: %@</source>
|
||||
<target state="translated">העברות: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Moderate" xml:space="preserve">
|
||||
<trans-unit id="Moderate" xml:space="preserve" approved="no">
|
||||
<source>Moderate</source>
|
||||
<target state="translated">חסימת הודעה</target>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="More improvements are coming soon!" xml:space="preserve">
|
||||
<trans-unit id="More improvements are coming soon!" xml:space="preserve" approved="no">
|
||||
<source>More improvements are coming soon!</source>
|
||||
<target state="translated">שיפורים נוספים יגיעו בקרוב!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
|
||||
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve" approved="no">
|
||||
<source>Most likely this contact has deleted the connection with you.</source>
|
||||
<target state="translated">ככל הנראה איש קשר זה מחק את החיבור איתך.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Multiple chat profiles" xml:space="preserve">
|
||||
<trans-unit id="Multiple chat profiles" xml:space="preserve" approved="no">
|
||||
<source>Multiple chat profiles</source>
|
||||
<target state="translated">פרופילי צ׳אט מרובים</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Mute" xml:space="preserve">
|
||||
<trans-unit id="Mute" xml:space="preserve" approved="no">
|
||||
<source>Mute</source>
|
||||
<target state="translated">השתק</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Muted when inactive!" xml:space="preserve">
|
||||
<trans-unit id="Muted when inactive!" xml:space="preserve" approved="no">
|
||||
<source>Muted when inactive!</source>
|
||||
<target state="translated">מושתק כאשר אין פעילות!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Name" xml:space="preserve">
|
||||
<trans-unit id="Name" xml:space="preserve" approved="no">
|
||||
<source>Name</source>
|
||||
<target state="translated">שם</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network & servers" xml:space="preserve">
|
||||
<trans-unit id="Network & servers" xml:space="preserve" approved="no">
|
||||
<source>Network & servers</source>
|
||||
<target state="translated">רשת ושרתים</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<trans-unit id="Network settings" xml:space="preserve" approved="no">
|
||||
<source>Network settings</source>
|
||||
<target state="translated">הגדרות רשת</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network status" xml:space="preserve">
|
||||
<trans-unit id="Network status" xml:space="preserve" approved="no">
|
||||
<source>Network status</source>
|
||||
<target state="translated">מצב רשת</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New Passcode" xml:space="preserve">
|
||||
<trans-unit id="New Passcode" xml:space="preserve" approved="no">
|
||||
<source>New Passcode</source>
|
||||
<target state="translated">קוד גישה חדש</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<trans-unit id="New contact request" xml:space="preserve" approved="no">
|
||||
<source>New contact request</source>
|
||||
<target state="translated">בקשה חדשה ליצירת קשר</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact:" xml:space="preserve">
|
||||
<trans-unit id="New contact:" xml:space="preserve" approved="no">
|
||||
<source>New contact:</source>
|
||||
<target state="translated">איש קשר חדש:</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New database archive" xml:space="preserve">
|
||||
<trans-unit id="New database archive" xml:space="preserve" approved="no">
|
||||
<source>New database archive</source>
|
||||
<target state="translated">ארכיון מסד נתונים חדש</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New in %@" xml:space="preserve">
|
||||
<trans-unit id="New in %@" xml:space="preserve" approved="no">
|
||||
<source>New in %@</source>
|
||||
<target state="translated">חדש ב %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New member role" xml:space="preserve">
|
||||
<trans-unit id="New member role" xml:space="preserve" approved="no">
|
||||
<source>New member role</source>
|
||||
<target state="translated">תפקיד חבר קבוצה חדש</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New message" xml:space="preserve">
|
||||
<trans-unit id="New message" xml:space="preserve" approved="no">
|
||||
<source>New message</source>
|
||||
<target state="translated">הודעה חדשה</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New passphrase…" xml:space="preserve">
|
||||
<trans-unit id="New passphrase…" xml:space="preserve" approved="no">
|
||||
<source>New passphrase…</source>
|
||||
<target state="translated">סיסמה חדשה…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No" xml:space="preserve">
|
||||
<trans-unit id="No" xml:space="preserve" approved="no">
|
||||
<source>No</source>
|
||||
<target state="translated">לא</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No app password" xml:space="preserve">
|
||||
<trans-unit id="No app password" xml:space="preserve" approved="no">
|
||||
<source>No app password</source>
|
||||
<target state="translated">אין סיסמה לאפליקציה</target>
|
||||
<note>Authentication unavailable</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No contacts selected" xml:space="preserve">
|
||||
<trans-unit id="No contacts selected" xml:space="preserve" approved="no">
|
||||
<source>No contacts selected</source>
|
||||
<target state="translated">לא נבחרו אנשי קשר</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No contacts to add" xml:space="preserve">
|
||||
<trans-unit id="No contacts to add" xml:space="preserve" approved="no">
|
||||
<source>No contacts to add</source>
|
||||
<target state="translated">אין אנשי קשר להוסיף</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No device token!" xml:space="preserve">
|
||||
<trans-unit id="No device token!" xml:space="preserve" approved="no">
|
||||
<source>No device token!</source>
|
||||
<target state="translated">אין אסימון מכשיר!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No group!" xml:space="preserve">
|
||||
<trans-unit id="No group!" xml:space="preserve" approved="no">
|
||||
<source>Group not found!</source>
|
||||
<target state="translated">קבוצה לא נמצאה!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve" approved="no">
|
||||
<source>No permission to record voice message</source>
|
||||
<target state="translated">אין הרשאה להקליט הודעה קולית</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No received or sent files" xml:space="preserve">
|
||||
<trans-unit id="No received or sent files" xml:space="preserve" approved="no">
|
||||
<source>No received or sent files</source>
|
||||
<target state="translated">לא התקבלו או נשלחו קבצים</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notifications" xml:space="preserve">
|
||||
<trans-unit id="Notifications" xml:space="preserve" approved="no">
|
||||
<source>Notifications</source>
|
||||
<target state="translated">התראות</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notifications are disabled!" xml:space="preserve">
|
||||
<trans-unit id="Notifications are disabled!" xml:space="preserve" approved="no">
|
||||
<source>Notifications are disabled!</source>
|
||||
<target state="translated">ההתראות מושבתות!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Now admins can: - delete members' messages. - disable members ("observer" role)" xml:space="preserve">
|
||||
<trans-unit id="Now admins can: - delete members' messages. - disable members ("observer" role)" xml:space="preserve" approved="no">
|
||||
<source>Now admins can:
|
||||
- delete members' messages.
|
||||
- disable members ("observer" role)</source>
|
||||
<target state="translated">כעת מנהלים יכולים:
|
||||
- למחוק הודעות של חברי קבוצה.
|
||||
- להשבית חברי קבוצה (תפקיד ”צופה”)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off" xml:space="preserve">
|
||||
<trans-unit id="Off" xml:space="preserve" approved="no">
|
||||
<source>Off</source>
|
||||
<target state="translated">כבוי</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Off (Local)" xml:space="preserve">
|
||||
<trans-unit id="Off (Local)" xml:space="preserve" approved="no">
|
||||
<source>Off (Local)</source>
|
||||
<target state="translated">כבוי (מקומי)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Ok" xml:space="preserve">
|
||||
<trans-unit id="Ok" xml:space="preserve" approved="no">
|
||||
<source>Ok</source>
|
||||
<target state="translated">אישור</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Old database" xml:space="preserve">
|
||||
<trans-unit id="Old database" xml:space="preserve" approved="no">
|
||||
<source>Old database</source>
|
||||
<target state="translated">מסד נתונים ישן</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Old database archive" xml:space="preserve">
|
||||
<trans-unit id="Old database archive" xml:space="preserve" approved="no">
|
||||
<source>Old database archive</source>
|
||||
<target state="translated">ארכיון מסד נתונים ישן</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="One-time invitation link" xml:space="preserve">
|
||||
<trans-unit id="One-time invitation link" xml:space="preserve" approved="no">
|
||||
<source>One-time invitation link</source>
|
||||
<target state="translated">קישור הזמנה חד־פעמי</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
|
||||
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve" approved="no">
|
||||
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
|
||||
<target state="translated">לחיבור יידרשו מארחי Onion. דורש הפעלת VPN.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
|
||||
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve" approved="no">
|
||||
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
|
||||
<target state="translated">מארחי Onion ישומשו כאשר יהיו זמינים. דורש הפעלת VPN.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
|
||||
<trans-unit id="Onion hosts will not be used." xml:space="preserve" approved="no">
|
||||
<source>Onion hosts will not be used.</source>
|
||||
<target state="translated">לא ייעשה שימוש במארחי Onion.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
|
||||
@@ -4981,6 +5047,270 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target state="translated">%1$@ בזמן %2$@:</target>
|
||||
<note>copied message info, <sender> at <time></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="## 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="- more stable message delivery. - a bit better groups. - and more!" xml:space="preserve" approved="no">
|
||||
<source>- more stable message delivery.
|
||||
- a bit better groups.
|
||||
- and more!</source>
|
||||
<target state="translated">- שליחת הודעות יציבה יותר.
|
||||
- קבוצות קצת יותר טובות.
|
||||
- ועוד!</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">עוד כמה דברים</target>
|
||||
<note>No comment provided by engineer.</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">ישותף פרופיל אקראי חדש.</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">לאשר בקשת חיבור?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect directly" xml:space="preserve" approved="no">
|
||||
<source>Connect directly</source>
|
||||
<target state="translated">התחבר ישירות</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect incognito" xml:space="preserve" approved="no">
|
||||
<source>Connect incognito</source>
|
||||
<target state="translated">התחבר בזהות נסתרת</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via one-time link" xml:space="preserve" approved="no">
|
||||
<source>Connect via one-time link</source>
|
||||
<target state="translated">התחבר באמצעות קישור חד־פעמי</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contacts" xml:space="preserve" approved="no">
|
||||
<source>Contacts</source>
|
||||
<target state="translated">אנשי קשר</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delivery" xml:space="preserve" approved="no">
|
||||
<source>Delivery</source>
|
||||
<target state="translated">מסירה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error synchronizing connection" xml:space="preserve" approved="no">
|
||||
<source>Error synchronizing connection</source>
|
||||
<target state="translated">שגיאה בסנכרון החיבור</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix encryption after restoring backups." xml:space="preserve" approved="no">
|
||||
<source>Fix encryption after restoring backups.</source>
|
||||
<target state="translated">תקן הצפנה לאחר שחזור גיבויים.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix not supported by group member" xml:space="preserve" approved="no">
|
||||
<source>Fix not supported by group member</source>
|
||||
<target state="translated">תיקון אינו נתמך על ידי חבר הקבוצה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid status" xml:space="preserve" approved="no">
|
||||
<source>Invalid status</source>
|
||||
<target state="translated">סטטוס לא חוקי</target>
|
||||
<note>item status text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Migrating database archive…" xml:space="preserve" approved="no">
|
||||
<source>Migrating database archive…</source>
|
||||
<target state="translated">מעביר את ארכיון מסד הנתונים…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Moderated at: %@" xml:space="preserve" approved="no">
|
||||
<source>Moderated at: %@</source>
|
||||
<target state="translated">נחסם ב: %@</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Most likely this connection is deleted." xml:space="preserve" approved="no">
|
||||
<source>Most likely this connection is deleted.</source>
|
||||
<target state="translated">סביר להניח שהחיבור הזה נמחק.</target>
|
||||
<note>item status description</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>
|
||||
<trans-unit id="Connect via contact link" xml:space="preserve" approved="no">
|
||||
<source>Connect via contact link</source>
|
||||
<target state="translated">התחבר באמצעות קישור איש קשר</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delivery receipts!" xml:space="preserve" approved="no">
|
||||
<source>Delivery receipts!</source>
|
||||
<target state="translated">קבלות על המשלוח!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disable for all" xml:space="preserve" approved="no">
|
||||
<source>Disable for all</source>
|
||||
<target state="translated">השבת לכולם</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error enabling delivery receipts!" xml:space="preserve" approved="no">
|
||||
<source>Error enabling delivery receipts!</source>
|
||||
<target state="translated">שגיאה בהפעלת קבלות משלוח!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Even when disabled in the conversation." xml:space="preserve" approved="no">
|
||||
<source>Even when disabled in the conversation.</source>
|
||||
<target state="translated">גם אם הוא מושבת בשיחה.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix" xml:space="preserve" approved="no">
|
||||
<source>Fix</source>
|
||||
<target state="translated">תקן</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix connection" xml:space="preserve" approved="no">
|
||||
<source>Fix connection</source>
|
||||
<target state="translated">תקן את החיבור</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Find chats faster" xml:space="preserve" approved="no">
|
||||
<source>Find chats faster</source>
|
||||
<target state="translated">מצא צ'אטים מהר יותר</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix connection?" xml:space="preserve" approved="no">
|
||||
<source>Fix connection?</source>
|
||||
<target state="translated">לתקן את החיבור?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make one message disappear" xml:space="preserve" approved="no">
|
||||
<source>Make one message disappear</source>
|
||||
<target state="translated">העלם הודעה אחת</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix not supported by contact" xml:space="preserve" approved="no">
|
||||
<source>Fix not supported by contact</source>
|
||||
<target state="translated">תיקון לא נתמך על ידי איש קשר</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incognito mode protects your privacy by using a new random profile for each contact." xml:space="preserve" approved="no">
|
||||
<source>Incognito mode protects your privacy by using a new random profile for each contact.</source>
|
||||
<target state="translated">מצב זהות נסתרת מגן על הפרטיות שלך על ידי שימוש בפרופיל אקראי חדש עבור כל איש קשר.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="In reply to" xml:space="preserve" approved="no">
|
||||
<source>In reply to</source>
|
||||
<target state="translated">בתגובה ל</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve" approved="no">
|
||||
<source>Keep your connections</source>
|
||||
<target state="translated">שימרו על הקשרים שלכם</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message delivery receipts!" xml:space="preserve" approved="no">
|
||||
<source>Message delivery receipts!</source>
|
||||
<target state="translated">קבלות על הודעות!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message reactions are prohibited in this chat." xml:space="preserve" approved="no">
|
||||
<source>Message reactions are prohibited in this chat.</source>
|
||||
<target state="translated">תגובות אמוג׳י להודעות אסורות בצ׳אט זה.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message reactions are prohibited in this group." xml:space="preserve" approved="no">
|
||||
<source>Message reactions are prohibited in this group.</source>
|
||||
<target state="translated">תגובות אמוג׳י להודעות אסורות בקבוצה זו.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delivery receipts are disabled!" xml:space="preserve" approved="no">
|
||||
<source>Delivery receipts are disabled!</source>
|
||||
<target state="translated">קבלות על משלוח מושבתות!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disable (keep overrides)" xml:space="preserve" approved="no">
|
||||
<source>Disable (keep overrides)</source>
|
||||
<target state="translated">השבת (שמור עקיפות)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Don't enable" xml:space="preserve" approved="no">
|
||||
<source>Don't enable</source>
|
||||
<target state="translated">אל תפעיל</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable (keep overrides)" xml:space="preserve" approved="no">
|
||||
<source>Enable (keep overrides)</source>
|
||||
<target state="translated">הפעל (שמור עקיפות)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve" approved="no">
|
||||
<source>Enable for all</source>
|
||||
<target state="translated">הפעל עבור כולם</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error setting delivery receipts!" xml:space="preserve" approved="no">
|
||||
<source>Error setting delivery receipts!</source>
|
||||
<target state="translated">שגיאה בהגדרת קבלות משלוח!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Filter unread and favorite chats." xml:space="preserve" approved="no">
|
||||
<source>Filter unread and favorite chats.</source>
|
||||
<target state="translated">סנן צ'אטים שלא נקראו וצ'אטים מועדפים.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Moderated at" xml:space="preserve" approved="no">
|
||||
<source>Moderated at</source>
|
||||
<target state="translated">נחסם</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message reactions" xml:space="preserve" approved="no">
|
||||
<source>Message reactions</source>
|
||||
<target state="translated">תגובות אמוג׳י להודעות</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Exporting database archive…" xml:space="preserve" approved="no">
|
||||
<source>Exporting database archive…</source>
|
||||
<target state="translated">מייצא את ארכיון מסד הנתונים…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve" approved="no">
|
||||
<source>%@, %@ and %lld other members connected</source>
|
||||
<target state="translated">%@, %@ ו-%lld חברים אחרים מחוברים</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New display name" xml:space="preserve" approved="no">
|
||||
<source>New display name</source>
|
||||
<target state="translated">שם תצוגה חדש</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No delivery information" xml:space="preserve" approved="no">
|
||||
<source>No delivery information</source>
|
||||
<target state="translated">אין מידע על מסירה</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No filtered chats" xml:space="preserve" approved="no">
|
||||
<source>No filtered chats</source>
|
||||
<target state="translated">אין צ'אטים מסוננים</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No history" xml:space="preserve" approved="no">
|
||||
<source>No history</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="he" datatype="plaintext">
|
||||
|
||||
@@ -1819,6 +1819,10 @@
|
||||
<target>Crittografare il database?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Database crittografato</target>
|
||||
@@ -1949,6 +1953,10 @@
|
||||
<target>Errore nella creazione del profilo!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Errore nell'eliminazione del database della chat</target>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -44,14 +44,17 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="# %@" xml:space="preserve">
|
||||
<source># %@</source>
|
||||
<target># %@</target>
|
||||
<note>copied message info title, # <title></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## History" xml:space="preserve">
|
||||
<source>## History</source>
|
||||
<target>## 履歴</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## In reply to" xml:space="preserve">
|
||||
<source>## In reply to</source>
|
||||
<target>## 返信先</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="#secret#" xml:space="preserve">
|
||||
@@ -86,10 +89,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve">
|
||||
<source>%@ and %@ connected</source>
|
||||
<target>%@ と %@ は接続中</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ at %@:" xml:space="preserve">
|
||||
<source>%1$@ at %2$@:</source>
|
||||
<target>%1$@ at %2$@:</target>
|
||||
<note>copied message info, <sender> at <time></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve">
|
||||
@@ -119,6 +124,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
|
||||
<source>%@, %@ and %lld other members connected</source>
|
||||
<target>%@, %@ および %lld 人のメンバーが接続中</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@:" xml:space="preserve">
|
||||
@@ -325,6 +331,9 @@
|
||||
<source>- more stable message delivery.
|
||||
- a bit better groups.
|
||||
- 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">
|
||||
@@ -405,6 +414,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="A few more things" xml:space="preserve">
|
||||
<source>A few more things</source>
|
||||
<target>その他</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="A new contact" xml:space="preserve">
|
||||
@@ -414,6 +424,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="A new random profile will be shared." xml:space="preserve">
|
||||
<source>A new random profile will be shared.</source>
|
||||
<target>新しいランダムなプロファイルが共有されます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="A separate TCP connection will be used **for each chat profile you have in the app**." xml:space="preserve">
|
||||
@@ -430,14 +441,17 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Abort" xml:space="preserve">
|
||||
<source>Abort</source>
|
||||
<target>中止</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Abort changing address" xml:space="preserve">
|
||||
<source>Abort changing address</source>
|
||||
<target>アドレス変更の中止</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Abort changing address?" xml:space="preserve">
|
||||
<source>Abort changing address?</source>
|
||||
<target>アドレス変更を中止しますか?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="About SimpleX" xml:space="preserve">
|
||||
@@ -523,6 +537,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Address change will be aborted. Old receiving address will be used." xml:space="preserve">
|
||||
<source>Address change will be aborted. Old receiving address will be used.</source>
|
||||
<target>アドレス変更は中止されます。古い受信アドレスが使用されます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Admins can create the links to join groups." xml:space="preserve">
|
||||
@@ -617,6 +632,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>ファイルやメディアの送信を許可する。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send voice messages." xml:space="preserve">
|
||||
@@ -791,6 +807,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Better messages" xml:space="preserve">
|
||||
<source>Better messages</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">
|
||||
@@ -1051,10 +1068,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect directly" xml:space="preserve">
|
||||
<source>Connect directly</source>
|
||||
<target>直接接続する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect incognito" xml:space="preserve">
|
||||
<source>Connect incognito</source>
|
||||
<target>シークレットモードで接続</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via contact link" xml:space="preserve">
|
||||
@@ -1159,6 +1178,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Contacts" xml:space="preserve">
|
||||
<source>Contacts</source>
|
||||
<target>連絡先</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contacts can mark messages for deletion; you will be able to view them." xml:space="preserve">
|
||||
@@ -1556,10 +1576,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Delivery" xml:space="preserve">
|
||||
<source>Delivery</source>
|
||||
<target>Delivery</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delivery receipts are disabled!" xml:space="preserve">
|
||||
<source>Delivery receipts are disabled!</source>
|
||||
<target>Delivery receipts are disabled!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delivery receipts!" xml:space="preserve">
|
||||
@@ -1613,6 +1635,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Disable (keep overrides)" xml:space="preserve">
|
||||
<source>Disable (keep overrides)</source>
|
||||
<target>無効にする(設定の優先を維持)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disable SimpleX Lock" xml:space="preserve">
|
||||
@@ -1622,6 +1645,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Disable for all" xml:space="preserve">
|
||||
<source>Disable for all</source>
|
||||
<target>すべて無効</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Disappearing message" xml:space="preserve">
|
||||
@@ -1686,6 +1710,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Don't enable" xml:space="preserve">
|
||||
<source>Don't enable</source>
|
||||
<target>有効にしない</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Don't show again" xml:space="preserve">
|
||||
@@ -1730,6 +1755,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable (keep overrides)" xml:space="preserve">
|
||||
<source>Enable (keep overrides)</source>
|
||||
<target>有効にする(設定の優先を維持)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable SimpleX Lock" xml:space="preserve">
|
||||
@@ -1749,6 +1775,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable for all" xml:space="preserve">
|
||||
<source>Enable for all</source>
|
||||
<target>すべて有効</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enable instant notifications?" xml:space="preserve">
|
||||
@@ -1791,6 +1818,10 @@
|
||||
<target>データベースを暗号化しますか?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>暗号化済みデータベース</target>
|
||||
@@ -1868,6 +1899,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Error aborting address change" xml:space="preserve">
|
||||
<source>Error aborting address change</source>
|
||||
<target>アドレス変更中止エラー</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error accepting contact request" xml:space="preserve">
|
||||
@@ -1920,6 +1952,10 @@
|
||||
<target>プロフィール作成にエラー発生!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>チャットデータベース削除にエラー発生</target>
|
||||
@@ -2065,6 +2101,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Error synchronizing connection" xml:space="preserve">
|
||||
<source>Error synchronizing connection</source>
|
||||
<target>接続の同期エラー</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error updating group link" xml:space="preserve">
|
||||
@@ -2109,6 +2146,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Even when disabled in the conversation." xml:space="preserve">
|
||||
<source>Even when disabled in the conversation.</source>
|
||||
<target>会話中に無効になっている場合でも。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Exit without saving" xml:space="preserve">
|
||||
@@ -2148,6 +2186,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Favorite" xml:space="preserve">
|
||||
<source>Favorite</source>
|
||||
<target>お気に入り</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="File will be deleted from servers." xml:space="preserve">
|
||||
@@ -2177,50 +2216,62 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media" xml:space="preserve">
|
||||
<source>Files and media</source>
|
||||
<target>ファイルとメディア</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media are prohibited in this group." xml:space="preserve">
|
||||
<source>Files and media are prohibited in this group.</source>
|
||||
<target>このグループでは、ファイルとメディアは禁止されています。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>ファイルとメディアは禁止されています!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Filter unread and favorite chats." xml:space="preserve">
|
||||
<source>Filter unread and favorite chats.</source>
|
||||
<target>未読とお気に入りをフィルターします。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Finally, we have them! 🚀" xml:space="preserve">
|
||||
<source>Finally, we have them! 🚀</source>
|
||||
<target>ついに、私たちはそれらを手に入れました! 🚀</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Find chats faster" xml:space="preserve">
|
||||
<source>Find chats faster</source>
|
||||
<target>チャットを素早く検索</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix" xml:space="preserve">
|
||||
<source>Fix</source>
|
||||
<target>修正</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix connection" xml:space="preserve">
|
||||
<source>Fix connection</source>
|
||||
<target>接続を修正</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix connection?" xml:space="preserve">
|
||||
<source>Fix connection?</source>
|
||||
<target>接続を修正しますか?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix encryption after restoring backups." xml:space="preserve">
|
||||
<source>Fix encryption after restoring backups.</source>
|
||||
<target>バックアップの復元後に暗号化を修正します。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix not supported by contact" xml:space="preserve">
|
||||
<source>Fix not supported by contact</source>
|
||||
<target>連絡先による修正はサポートされていません</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Fix not supported by group member" xml:space="preserve">
|
||||
<source>Fix not supported by group member</source>
|
||||
<target>グループメンバーによる修正はサポートされていません</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="For console" xml:space="preserve">
|
||||
@@ -2330,6 +2381,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send files and media." xml:space="preserve">
|
||||
<source>Group members can send files and media.</source>
|
||||
<target>グループメンバーはファイルやメディアを送信できます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send voice messages." xml:space="preserve">
|
||||
@@ -2529,6 +2581,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="In reply to" xml:space="preserve">
|
||||
<source>In reply to</source>
|
||||
<target>返信先</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incognito" xml:space="preserve">
|
||||
@@ -2543,6 +2596,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Incognito mode protects your privacy by using a new random profile for each contact." xml:space="preserve">
|
||||
<source>Incognito mode protects your privacy by using a new random profile for each contact.</source>
|
||||
<target>シークレットモードとは、メインのプロフィールとプロフィール画像を守るために、新しい連絡先を追加する時に、その連絡先に対してランダムなプロフィールが作成されるという対策です。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Incoming audio call" xml:space="preserve">
|
||||
@@ -2619,6 +2673,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid status" xml:space="preserve">
|
||||
<source>Invalid status</source>
|
||||
<target>無効なステータス</target>
|
||||
<note>item status text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invitation expired!" xml:space="preserve">
|
||||
@@ -2714,6 +2769,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Keep your connections" xml:space="preserve">
|
||||
<source>Keep your connections</source>
|
||||
<target>接続を維持</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="KeyChain error" xml:space="preserve">
|
||||
@@ -2808,6 +2864,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Make one message disappear" xml:space="preserve">
|
||||
<source>Make one message disappear</source>
|
||||
<target>メッセージを1つ消す</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make profile private!" xml:space="preserve">
|
||||
@@ -2966,6 +3023,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Most likely this connection is deleted." xml:space="preserve">
|
||||
<source>Most likely this connection is deleted.</source>
|
||||
<target>おそらく、この接続は削除されています。</target>
|
||||
<note>item status description</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
|
||||
@@ -3075,6 +3133,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="No delivery information" xml:space="preserve">
|
||||
<source>No delivery information</source>
|
||||
<target>送信情報なし</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No device token!" xml:space="preserve">
|
||||
@@ -3084,6 +3143,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="No filtered chats" xml:space="preserve">
|
||||
<source>No filtered chats</source>
|
||||
<target>フィルタされたチャットはありません</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No group!" xml:space="preserve">
|
||||
@@ -3093,6 +3153,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="No history" xml:space="preserve">
|
||||
<source>No history</source>
|
||||
<target>履歴はありません</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
@@ -3181,6 +3242,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Only group owners can enable files and media." xml:space="preserve">
|
||||
<source>Only group owners can enable files and media.</source>
|
||||
<target>ファイルやメディアを有効にできるのは、グループオーナーだけです。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Only group owners can enable voice messages." xml:space="preserve">
|
||||
@@ -3505,6 +3567,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending files and media." xml:space="preserve">
|
||||
<source>Prohibit sending files and media.</source>
|
||||
<target>ファイルやメディアの送信を禁止します。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending voice messages." xml:space="preserve">
|
||||
@@ -3529,6 +3592,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Protocol timeout per KB" xml:space="preserve">
|
||||
<source>Protocol timeout per KB</source>
|
||||
<target>KB あたりのプロトコル タイムアウト</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Push notifications" xml:space="preserve">
|
||||
@@ -3543,6 +3607,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="React…" xml:space="preserve">
|
||||
<source>React…</source>
|
||||
<target>反応する…</target>
|
||||
<note>chat item menu</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read" xml:space="preserve">
|
||||
@@ -3601,6 +3666,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving address will be changed to a different server. Address change will complete after sender comes online." xml:space="preserve">
|
||||
<source>Receiving address will be changed to a different server. Address change will complete after sender comes online.</source>
|
||||
<target>開発中の機能です!相手のクライアントが4.2でなければ機能しません。アドレス変更が完了すると、会話にメッセージが出ます。連絡相手 (またはグループのメンバー) からメッセージを受信できないかをご確認ください。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
@@ -3620,10 +3686,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Reconnect all connected servers to force message delivery. It uses additional traffic." xml:space="preserve">
|
||||
<source>Reconnect all connected servers to force message delivery. It uses additional traffic.</source>
|
||||
<target>接続されているすべてのサーバーを再接続して、メッセージを強制的に配信します。 追加のトラフィックを使用します。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reconnect servers?" xml:space="preserve">
|
||||
<source>Reconnect servers?</source>
|
||||
<target>サーバーに再接続しますか?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Record updated at" xml:space="preserve">
|
||||
@@ -3688,14 +3756,17 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Renegotiate" xml:space="preserve">
|
||||
<source>Renegotiate</source>
|
||||
<target>再ネゴシエート</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Renegotiate encryption" xml:space="preserve">
|
||||
<source>Renegotiate encryption</source>
|
||||
<target>暗号化の再ネゴシエート</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Renegotiate encryption?" xml:space="preserve">
|
||||
<source>Renegotiate encryption?</source>
|
||||
<target>暗号化を再ネゴシエートしますか?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reply" xml:space="preserve">
|
||||
@@ -4182,6 +4253,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Show last messages" xml:space="preserve">
|
||||
<source>Show last messages</source>
|
||||
<target>最新のメッセージを表示</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show preview" xml:space="preserve">
|
||||
@@ -4266,10 +4338,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Small groups (max 20)" xml:space="preserve">
|
||||
<source>Small groups (max 20)</source>
|
||||
<target>小グループ(最大20名)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
|
||||
<source>Some non-fatal errors occurred during import - you may see Chat console for more details.</source>
|
||||
<target>インポート中に致命的でないエラーが発生しました - 詳細はチャットコンソールを参照してください。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Somebody" xml:space="preserve">
|
||||
@@ -4486,6 +4560,7 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
</trans-unit>
|
||||
<trans-unit id="The encryption is working and the new encryption agreement is not required. It may result in connection errors!" xml:space="preserve">
|
||||
<source>The encryption is working and the new encryption agreement is not required. It may result in connection errors!</source>
|
||||
<target>暗号化は機能しており、新しい暗号化への同意は必要ありません。接続エラーが発生する可能性があります!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The group is fully decentralized – it is visible only to the members." xml:space="preserve">
|
||||
@@ -4525,6 +4600,7 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
</trans-unit>
|
||||
<trans-unit id="The second tick we missed! ✅" xml:space="preserve">
|
||||
<source>The second tick we missed! ✅</source>
|
||||
<target>長らくお待たせしました! ✅</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The sender will NOT be notified" xml:space="preserve">
|
||||
@@ -4554,10 +4630,12 @@ It can happen because of some bug or when the connection is compromised.</source
|
||||
</trans-unit>
|
||||
<trans-unit id="These settings are for your current profile **%@**." xml:space="preserve">
|
||||
<source>These settings are for your current profile **%@**.</source>
|
||||
<target>これらの設定は現在のプロファイル **%@** 用です。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="They can be overridden in contact and group settings." xml:space="preserve">
|
||||
<source>They can be overridden in contact and group settings.</source>
|
||||
<target>これらは連絡先の設定が優先します。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." xml:space="preserve">
|
||||
@@ -4688,6 +4766,7 @@ You will be prompted to complete authentication before this feature is enabled.<
|
||||
</trans-unit>
|
||||
<trans-unit id="Unfav." xml:space="preserve">
|
||||
<source>Unfav.</source>
|
||||
<target>お気に入りを取り消す。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unhide" xml:space="preserve">
|
||||
@@ -4819,6 +4898,7 @@ To connect, please ask your contact to create another connection link and check
|
||||
</trans-unit>
|
||||
<trans-unit id="Use current profile" xml:space="preserve">
|
||||
<source>Use current profile</source>
|
||||
<target>現在のプロファイルを使用する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use for new connections" xml:space="preserve">
|
||||
@@ -4833,6 +4913,7 @@ To connect, please ask your contact to create another connection link and check
|
||||
</trans-unit>
|
||||
<trans-unit id="Use new incognito profile" xml:space="preserve">
|
||||
<source>Use new incognito profile</source>
|
||||
<target>新しいシークレットプロファイルを使用する</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
@@ -5047,10 +5128,12 @@ To connect, please ask your contact to create another connection link and check
|
||||
</trans-unit>
|
||||
<trans-unit id="You can enable later via Settings" xml:space="preserve">
|
||||
<source>You can enable later via Settings</source>
|
||||
<target>あとで設定から有効にできます</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can enable them later via app Privacy & Security settings." xml:space="preserve">
|
||||
<source>You can enable them later via app Privacy & Security settings.</source>
|
||||
<target>あとでアプリのプライバシーとセキュリティの設定から有効にすることができます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can hide or mute a user profile - swipe it to the right." xml:space="preserve">
|
||||
@@ -5309,6 +5392,7 @@ You can change it in Settings.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile **%@** will be shared." xml:space="preserve">
|
||||
<source>Your profile **%@** will be shared.</source>
|
||||
<target>あなたのプロファイル **%@** が共有されます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." xml:space="preserve">
|
||||
@@ -5385,10 +5469,12 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>%@の暗号化に同意しています…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption…" xml:space="preserve">
|
||||
<source>agreeing encryption…</source>
|
||||
<target>暗号化に同意しています…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
@@ -5453,10 +5539,12 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
</trans-unit>
|
||||
<trans-unit id="changing address for %@…" xml:space="preserve">
|
||||
<source>changing address for %@…</source>
|
||||
<target>%@ のアドレスを変更しています…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="changing address…" xml:space="preserve">
|
||||
<source>changing address…</source>
|
||||
<target>アドレスを変更しています…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="colored" xml:space="preserve">
|
||||
@@ -5561,10 +5649,12 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
</trans-unit>
|
||||
<trans-unit id="default (no)" xml:space="preserve">
|
||||
<source>default (no)</source>
|
||||
<target>デフォルト(いいえ)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="default (yes)" xml:space="preserve">
|
||||
<source>default (yes)</source>
|
||||
<target>デフォルト(はい)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="deleted" xml:space="preserve">
|
||||
@@ -5589,6 +5679,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
</trans-unit>
|
||||
<trans-unit id="disabled" xml:space="preserve">
|
||||
<source>disabled</source>
|
||||
<target>無効</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="duplicate message" xml:space="preserve">
|
||||
@@ -5618,34 +5709,42 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption agreed" xml:space="preserve">
|
||||
<source>encryption agreed</source>
|
||||
<target>暗号化に同意しました</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption agreed for %@" xml:space="preserve">
|
||||
<source>encryption agreed for %@</source>
|
||||
<target>%@ の暗号化に同意しました</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption ok" xml:space="preserve">
|
||||
<source>encryption ok</source>
|
||||
<target>暗号化OK</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption ok for %@" xml:space="preserve">
|
||||
<source>encryption ok for %@</source>
|
||||
<target>%@ の暗号化OK</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption re-negotiation allowed" xml:space="preserve">
|
||||
<source>encryption re-negotiation allowed</source>
|
||||
<target>暗号化の再ネゴシエーションを許可</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption re-negotiation allowed for %@" xml:space="preserve">
|
||||
<source>encryption re-negotiation allowed for %@</source>
|
||||
<target>%@ の暗号化の再ネゴシエーションを許可</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption re-negotiation required" xml:space="preserve">
|
||||
<source>encryption re-negotiation required</source>
|
||||
<target>暗号化の再ネゴシエーションが必要</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="encryption re-negotiation required for %@" xml:space="preserve">
|
||||
<source>encryption re-negotiation required for %@</source>
|
||||
<target>%@ の暗号化の再ネゴシエーションが必要</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="ended" xml:space="preserve">
|
||||
@@ -5665,6 +5764,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
</trans-unit>
|
||||
<trans-unit id="event happened" xml:space="preserve">
|
||||
<source>event happened</source>
|
||||
<target>イベント発生</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
@@ -5834,6 +5934,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
</trans-unit>
|
||||
<trans-unit id="no text" xml:space="preserve">
|
||||
<source>no text</source>
|
||||
<target>テキストなし</target>
|
||||
<note>copied message info in history</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="observer" xml:space="preserve">
|
||||
@@ -5924,6 +6025,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
</trans-unit>
|
||||
<trans-unit id="security code changed" xml:space="preserve">
|
||||
<source>security code changed</source>
|
||||
<target>セキュリティコードが変更されました</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="starting…" xml:space="preserve">
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -244,7 +244,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%u messages failed to decrypt." xml:space="preserve">
|
||||
<source>%u messages failed to decrypt.</source>
|
||||
<target>%u-berichten kunnen niet worden gedecodeerd.</target>
|
||||
<target>%u berichten kunnen niet worden ontsleuteld.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%u messages skipped." xml:space="preserve">
|
||||
@@ -1819,6 +1819,10 @@
|
||||
<target>Database versleutelen?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Versleutelde database</target>
|
||||
@@ -1949,6 +1953,10 @@
|
||||
<target>Fout bij aanmaken van profiel!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Fout bij het verwijderen van de chat database</target>
|
||||
@@ -2713,7 +2721,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="It can happen when you or your connection used the old database backup." xml:space="preserve">
|
||||
<source>It can happen when you or your connection used the old database backup.</source>
|
||||
<target>Het kan gebeuren wanneer u of uw verbinding de oude databaseback-up gebruikte.</target>
|
||||
<target>Het kan gebeuren wanneer u of de ander een oude databaseback-up gebruikt.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="It can happen when: 1. The messages expired in the sending client after 2 days or on the server after 30 days. 2. Message decryption failed, because you or your contact used old database backup. 3. The connection was compromised." xml:space="preserve">
|
||||
@@ -4919,7 +4927,7 @@ Om verbinding te maken, vraagt u uw contactpersoon om een andere verbinding link
|
||||
</trans-unit>
|
||||
<trans-unit id="Use new incognito profile" xml:space="preserve">
|
||||
<source>Use new incognito profile</source>
|
||||
<target>Gebruik een nieuw incognito -profiel</target>
|
||||
<target>Gebruik een nieuw incognitoprofiel</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use server" xml:space="preserve">
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -89,6 +89,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve">
|
||||
<source>%@ and %@ connected</source>
|
||||
<target>%@ i %@ połączeni</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ at %@:" xml:space="preserve">
|
||||
@@ -123,6 +124,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
|
||||
<source>%@, %@ and %lld other members connected</source>
|
||||
<target>%@, %@ i %lld innych członków połączeni</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@:" xml:space="preserve">
|
||||
@@ -1817,6 +1819,10 @@
|
||||
<target>Zaszyfrować bazę danych?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Zaszyfrowana baza danych</target>
|
||||
@@ -1947,6 +1953,10 @@
|
||||
<target>Błąd tworzenia profilu!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Błąd usuwania bazy danych czatu</target>
|
||||
@@ -4256,6 +4266,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Show last messages" xml:space="preserve">
|
||||
<source>Show last messages</source>
|
||||
<target>Pokaż ostatnie wiadomości</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show preview" xml:space="preserve">
|
||||
@@ -5767,6 +5778,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
</trans-unit>
|
||||
<trans-unit id="event happened" xml:space="preserve">
|
||||
<source>event happened</source>
|
||||
<target>nowe wydarzenie</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1819,6 +1819,10 @@
|
||||
<target>Зашифровать базу данных?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>База данных зашифрована</target>
|
||||
@@ -1949,6 +1953,10 @@
|
||||
<target>Ошибка создания профиля!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>Ошибка при удалении данных чата</target>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -1807,6 +1807,10 @@
|
||||
<target>Encrypt ฐานข้อมูล?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>Encrypt ฐานข้อมูลเรียบร้อยแล้ว</target>
|
||||
@@ -1937,6 +1941,10 @@
|
||||
<target>เกิดข้อผิดพลาดในการสร้างโปรไฟล์!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>เกิดข้อผิดพลาดในการลบฐานข้อมูลแชท</target>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"locale" : "uk"
|
||||
}
|
||||
],
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
},
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -1891,7 +1891,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" xml:space="preserve" approved="no">
|
||||
<source>Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)</source>
|
||||
<target state="translated">Встановіть [SimpleX Chat для терміналу] (https://github.com/simplex-chat/simplex-chat)</target>
|
||||
<target state="translated">Встановіть [SimpleX Chat для терміналу](https://github.com/simplex-chat/simplex-chat)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Instant push notifications will be hidden! " xml:space="preserve" approved="no">
|
||||
@@ -2586,7 +2586,7 @@ We will be adding server redundancy to prevent lost messages.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." xml:space="preserve" approved="no">
|
||||
<source>Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme).</source>
|
||||
<target state="translated">Читайте більше в нашому [GitHub репозиторії] (https://github.com/simplex-chat/simplex-chat#readme).</target>
|
||||
<target state="translated">Читайте більше в нашому [GitHub репозиторії](https://github.com/simplex-chat/simplex-chat#readme).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Received file event" xml:space="preserve" approved="no">
|
||||
@@ -3892,17 +3892,17 @@ SimpleX servers cannot see your profile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)" xml:space="preserve" approved="no">
|
||||
<source>[Contribute](https://github.com/simplex-chat/simplex-chat#contribute)</source>
|
||||
<target state="translated">[Внесок] (https://github.com/simplex-chat/simplex-chat#contribute)</target>
|
||||
<target state="translated">[Внесок](https://github.com/simplex-chat/simplex-chat#contribute)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="[Send us email](mailto:chat@simplex.chat)" xml:space="preserve" approved="no">
|
||||
<source>[Send us email](mailto:chat@simplex.chat)</source>
|
||||
<target state="translated">[Напишіть нам електронною поштою] (mailto:chat@simplex.chat)</target>
|
||||
<target state="translated">[Напишіть нам електронною поштою](mailto:chat@simplex.chat)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="[Star on GitHub](https://github.com/simplex-chat/simplex-chat)" xml:space="preserve" approved="no">
|
||||
<source>[Star on GitHub](https://github.com/simplex-chat/simplex-chat)</source>
|
||||
<target state="translated">[Зірка на GitHub] (https://github.com/simplex-chat/simplex-chat)</target>
|
||||
<target state="translated">[Зірка на 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" approved="no">
|
||||
@@ -5369,7 +5369,7 @@ SimpleX servers cannot see your profile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve" approved="no">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
|
||||
<target state="translated">Читайте більше в [Посібнику користувача] (https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
<target state="translated">Читайте більше в [Посібнику користувача](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve" approved="no">
|
||||
@@ -5419,7 +5419,7 @@ SimpleX servers cannot see your profile.</source>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." xml:space="preserve" approved="no">
|
||||
<source>Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</source>
|
||||
<target state="translated">Читайте більше в [Посібнику користувача] (https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
|
||||
<target state="translated">Читайте більше в [Посібнику користувача](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="Moderated at" xml:space="preserve" approved="no">
|
||||
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "0.000",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.533"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
},
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "SimpleX NSE";
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX NSE";
|
||||
/* Copyright (human-readable) */
|
||||
"NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. All rights reserved.";
|
||||
@@ -0,0 +1,30 @@
|
||||
/* No comment provided by engineer. */
|
||||
"_italic_" = "\\_italic_";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"**Add new contact**: to create your one-time QR Code for your contact." = "**Add new contact**: to create your one-time QR Code or link for your contact.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"*bold*" = "\\*bold*";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"`a + b`" = "\\`a + b`";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"~strike~" = "\\~strike~";
|
||||
|
||||
/* call status */
|
||||
"connecting call" = "connecting call…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server…" = "Connecting to server…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Connecting to server… (error: %@)";
|
||||
|
||||
/* rcv group event chat item */
|
||||
"member connected" = "connected";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No group!" = "Group not found!";
|
||||
|
||||
+10
@@ -0,0 +1,10 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX";
|
||||
/* Privacy - Camera Usage Description */
|
||||
"NSCameraUsageDescription" = "SimpleX needs camera access to scan QR codes to connect to other users and for video calls.";
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"NSFaceIDUsageDescription" = "SimpleX uses Face ID for local authentication";
|
||||
/* Privacy - Microphone Usage Description */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX needs microphone access for audio and video calls, and to record voice messages.";
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "SimpleX needs access to Photo Library for saving captured and received media";
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"developmentRegion" : "en",
|
||||
"project" : "SimpleX.xcodeproj",
|
||||
"targetLocale" : "uk",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "15A5219j",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "15.0"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -44,14 +44,17 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="# %@" xml:space="preserve">
|
||||
<source># %@</source>
|
||||
<target># %@</target>
|
||||
<note>copied message info title, # <title></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## History" xml:space="preserve">
|
||||
<source>## History</source>
|
||||
<target>## 历史</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="## In reply to" xml:space="preserve">
|
||||
<source>## In reply to</source>
|
||||
<target>## 回复</target>
|
||||
<note>copied message info</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="#secret#" xml:space="preserve">
|
||||
@@ -86,10 +89,12 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ and %@ connected" xml:space="preserve">
|
||||
<source>%@ and %@ connected</source>
|
||||
<target>%@ 和%@ 以建立连接</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ at %@:" xml:space="preserve">
|
||||
<source>%1$@ at %2$@:</source>
|
||||
<target>%2$@:</target>
|
||||
<note>copied message info, <sender> at <time></note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve">
|
||||
@@ -119,6 +124,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="%@, %@ and %lld other members connected" xml:space="preserve">
|
||||
<source>%@, %@ and %lld other members connected</source>
|
||||
<target>%@, %@ 和 %lld 个成员</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@:" xml:space="preserve">
|
||||
@@ -325,6 +331,9 @@
|
||||
<source>- more stable message delivery.
|
||||
- a bit better groups.
|
||||
- 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">
|
||||
@@ -405,6 +414,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="A few more things" xml:space="preserve">
|
||||
<source>A few more things</source>
|
||||
<target/>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="A new contact" xml:space="preserve">
|
||||
@@ -414,6 +424,7 @@
|
||||
</trans-unit>
|
||||
<trans-unit id="A new random profile will be shared." xml:space="preserve">
|
||||
<source>A new random profile will be shared.</source>
|
||||
<target>创建一个随机的共享文件</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="A separate TCP connection will be used **for each chat profile you have in the app**." xml:space="preserve">
|
||||
@@ -1797,6 +1808,10 @@
|
||||
<target>加密数据库?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt local files" xml:space="preserve">
|
||||
<source>Encrypt local files</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypted database" xml:space="preserve">
|
||||
<source>Encrypted database</source>
|
||||
<target>加密数据库</target>
|
||||
@@ -1927,6 +1942,10 @@
|
||||
<target>创建资料错误!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error decrypting file" xml:space="preserve">
|
||||
<source>Error decrypting file</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error deleting chat database" xml:space="preserve">
|
||||
<source>Error deleting chat database</source>
|
||||
<target>删除聊天数据库错误</target>
|
||||
|
||||
-1
@@ -1 +0,0 @@
|
||||
|
||||
@@ -271,7 +271,7 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
ntfBadgeCountGroupDefault.set(max(0, ntfBadgeCountGroupDefault.get() - 1))
|
||||
}
|
||||
if let file = cItem.autoReceiveFile() {
|
||||
cItem = autoReceiveFile(file) ?? cItem
|
||||
cItem = autoReceiveFile(file, encrypted: cItem.encryptLocalFile) ?? cItem
|
||||
}
|
||||
let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(notification: createMessageReceivedNtf(user, cInfo, cItem)) : .empty
|
||||
return cItem.showNotification ? (aChatItem.chatId, ntf) : nil
|
||||
@@ -367,25 +367,25 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiReceiveFile(fileId: Int64, inline: Bool? = nil) -> AChatItem? {
|
||||
let r = sendSimpleXCmd(.receiveFile(fileId: fileId, inline: inline))
|
||||
func apiReceiveFile(fileId: Int64, encrypted: Bool, inline: Bool? = nil) -> AChatItem? {
|
||||
let r = sendSimpleXCmd(.receiveFile(fileId: fileId, encrypted: encrypted, inline: inline))
|
||||
if case let .rcvFileAccepted(_, chatItem) = r { return chatItem }
|
||||
logger.error("receiveFile error: \(responseError(r))")
|
||||
return nil
|
||||
}
|
||||
|
||||
func apiSetFileToReceive(fileId: Int64) {
|
||||
let r = sendSimpleXCmd(.setFileToReceive(fileId: fileId))
|
||||
func apiSetFileToReceive(fileId: Int64, encrypted: Bool) {
|
||||
let r = sendSimpleXCmd(.setFileToReceive(fileId: fileId, encrypted: encrypted))
|
||||
if case .cmdOk = r { return }
|
||||
logger.error("setFileToReceive error: \(responseError(r))")
|
||||
}
|
||||
|
||||
func autoReceiveFile(_ file: CIFile) -> ChatItem? {
|
||||
func autoReceiveFile(_ file: CIFile, encrypted: Bool) -> ChatItem? {
|
||||
switch file.fileProtocol {
|
||||
case .smp:
|
||||
return apiReceiveFile(fileId: file.fileId)?.chatItem
|
||||
return apiReceiveFile(fileId: file.fileId, encrypted: false)?.chatItem
|
||||
case .xftp:
|
||||
apiSetFileToReceive(fileId: file.fileId)
|
||||
apiSetFileToReceive(fileId: file.fileId, encrypted: encrypted)
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "SimpleX NSE";
|
||||
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX NSE";
|
||||
|
||||
/* Copyright (human-readable) */
|
||||
"NSHumanReadableCopyright" = "Copyright © 2022 SimpleX Chat. Kaikki oikeudet pidätetään.";
|
||||
|
||||
@@ -0,0 +1,9 @@
|
||||
/* Bundle display name */
|
||||
"CFBundleDisplayName" = "SimpleX NSE";
|
||||
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX NSE";
|
||||
|
||||
/* Copyright (human-readable) */
|
||||
"NSHumanReadableCopyright" = "Авторське право © 2022 SimpleX Chat. Всі права захищені.";
|
||||
|
||||
@@ -77,11 +77,12 @@
|
||||
5C9CC7A928C532AB00BEF955 /* DatabaseErrorView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9CC7A828C532AB00BEF955 /* DatabaseErrorView.swift */; };
|
||||
5C9CC7AD28C55D7800BEF955 /* DatabaseEncryptionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9CC7AC28C55D7800BEF955 /* DatabaseEncryptionView.swift */; };
|
||||
5C9D13A3282187BB00AB8B43 /* WebRTC.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9D13A2282187BB00AB8B43 /* WebRTC.swift */; };
|
||||
5C9F83F42A9A7D98009AD0AA /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F83EF2A9A7D98009AD0AA /* libffi.a */; };
|
||||
5C9F83F52A9A7D98009AD0AA /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F83F02A9A7D98009AD0AA /* libgmp.a */; };
|
||||
5C9F83F62A9A7D98009AD0AA /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F83F12A9A7D98009AD0AA /* libgmpxx.a */; };
|
||||
5C9F83F72A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F83F22A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h-ghc8.10.7.a */; };
|
||||
5C9F83F82A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9F83F32A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h.a */; };
|
||||
5C9D811A2AA8727A001D49FD /* CryptoFile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */; };
|
||||
5C9E127E2AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9E12792AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t-ghc8.10.7.a */; };
|
||||
5C9E127F2AAE62A500C9D8FF /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9E127A2AAE62A500C9D8FF /* libgmpxx.a */; };
|
||||
5C9E12802AAE62A500C9D8FF /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9E127B2AAE62A500C9D8FF /* libgmp.a */; };
|
||||
5C9E12812AAE62A500C9D8FF /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9E127C2AAE62A500C9D8FF /* libffi.a */; };
|
||||
5C9E12822AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C9E127D2AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t.a */; };
|
||||
5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */; };
|
||||
5CA059DC279559F40002BEB4 /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DB279559F40002BEB4 /* Tests_iOS.swift */; };
|
||||
5CA059DE279559F40002BEB4 /* Tests_iOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DD279559F40002BEB4 /* Tests_iOSLaunchTests.swift */; };
|
||||
@@ -171,6 +172,7 @@
|
||||
648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; };
|
||||
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
|
||||
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
|
||||
64A2723E2AAF16CD00BE1136 /* ContextDirectMemberView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64A2723D2AAF16CD00BE1136 /* ContextDirectMemberView.swift */; };
|
||||
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
|
||||
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
|
||||
64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */; };
|
||||
@@ -267,6 +269,8 @@
|
||||
5C10D88728EED12E00E58BF0 /* ContactConnectionInfo.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionInfo.swift; sourceTree = "<group>"; };
|
||||
5C10D88928F187F300E58BF0 /* FullScreenMediaView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FullScreenMediaView.swift; sourceTree = "<group>"; };
|
||||
5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestView.swift; sourceTree = "<group>"; };
|
||||
5C136D8E2AAB3D14006DE2FC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = "fi.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5C136D8F2AAB3D14006DE2FC /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@@ -295,6 +299,8 @@
|
||||
5C5E5D3C282447AB00B0488A /* CallTypes.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallTypes.swift; sourceTree = "<group>"; };
|
||||
5C5F2B6C27EBC3FE006A9D5F /* ImagePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ImagePicker.swift; sourceTree = "<group>"; };
|
||||
5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ProfileImage.swift; sourceTree = "<group>"; };
|
||||
5C636F662AAB3D2400751C84 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = "uk.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5C636F672AAB3D2400751C84 /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
5C65DAE429C77136003CEE45 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/Localizable.strings"; sourceTree = "<group>"; };
|
||||
5C65DAE629C771B9003CEE45 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5C65DAE729C771B9003CEE45 /* zh-Hans */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = "zh-Hans"; path = "zh-Hans.lproj/InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
@@ -331,11 +337,12 @@
|
||||
5C9CC7A828C532AB00BEF955 /* DatabaseErrorView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseErrorView.swift; sourceTree = "<group>"; };
|
||||
5C9CC7AC28C55D7800BEF955 /* DatabaseEncryptionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DatabaseEncryptionView.swift; sourceTree = "<group>"; };
|
||||
5C9D13A2282187BB00AB8B43 /* WebRTC.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WebRTC.swift; sourceTree = "<group>"; };
|
||||
5C9F83EF2A9A7D98009AD0AA /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C9F83F02A9A7D98009AD0AA /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C9F83F12A9A7D98009AD0AA /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C9F83F22A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5C9F83F32A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h.a"; sourceTree = "<group>"; };
|
||||
5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CryptoFile.swift; sourceTree = "<group>"; };
|
||||
5C9E12792AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5C9E127A2AAE62A500C9D8FF /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C9E127B2AAE62A500C9D8FF /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C9E127C2AAE62A500C9D8FF /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C9E127D2AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t.a"; sourceTree = "<group>"; };
|
||||
5C9FD96A27A56D4D0075386C /* JSON.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JSON.swift; sourceTree = "<group>"; };
|
||||
5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SendMessageView.swift; sourceTree = "<group>"; };
|
||||
5CA059C3279559F40002BEB4 /* SimpleXApp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SimpleXApp.swift; sourceTree = "<group>"; };
|
||||
@@ -413,6 +420,8 @@
|
||||
5CE2BA96284537A800EC33A6 /* dummy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = dummy.m; sourceTree = "<group>"; };
|
||||
5CE4407127ADB1D0007B033A /* Emoji.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Emoji.swift; sourceTree = "<group>"; };
|
||||
5CE4407827ADB701007B033A /* EmojiItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EmojiItemView.swift; sourceTree = "<group>"; };
|
||||
5CE6C7B32AAB1515007F345C /* fi */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = fi; path = fi.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CE6C7B42AAB1527007F345C /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CEACCE227DE9246000BD591 /* ComposeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeView.swift; sourceTree = "<group>"; };
|
||||
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>"; };
|
||||
@@ -441,6 +450,7 @@
|
||||
6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; };
|
||||
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
|
||||
64A2723D2AAF16CD00BE1136 /* ContextDirectMemberView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextDirectMemberView.swift; sourceTree = "<group>"; };
|
||||
64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.swift; sourceTree = "<group>"; };
|
||||
64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = "<group>"; };
|
||||
64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemInfoView.swift; sourceTree = "<group>"; };
|
||||
@@ -492,13 +502,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C9F83F82A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h.a in Frameworks */,
|
||||
5C9E127F2AAE62A500C9D8FF /* libgmpxx.a in Frameworks */,
|
||||
5C9E12812AAE62A500C9D8FF /* libffi.a in Frameworks */,
|
||||
5C9E12802AAE62A500C9D8FF /* libgmp.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5C9F83F72A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h-ghc8.10.7.a in Frameworks */,
|
||||
5C9F83F62A9A7D98009AD0AA /* libgmpxx.a in Frameworks */,
|
||||
5C9F83F42A9A7D98009AD0AA /* libffi.a in Frameworks */,
|
||||
5C9F83F52A9A7D98009AD0AA /* libgmp.a in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5C9E127E2AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t-ghc8.10.7.a in Frameworks */,
|
||||
5C9E12822AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -559,11 +569,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5C9F83EF2A9A7D98009AD0AA /* libffi.a */,
|
||||
5C9F83F02A9A7D98009AD0AA /* libgmp.a */,
|
||||
5C9F83F12A9A7D98009AD0AA /* libgmpxx.a */,
|
||||
5C9F83F22A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h-ghc8.10.7.a */,
|
||||
5C9F83F32A9A7D98009AD0AA /* libHSsimplex-chat-5.3.0.6-5utBXdHr6QFBiN3wb3H70h.a */,
|
||||
5C9E127C2AAE62A500C9D8FF /* libffi.a */,
|
||||
5C9E127B2AAE62A500C9D8FF /* libgmp.a */,
|
||||
5C9E127A2AAE62A500C9D8FF /* libgmpxx.a */,
|
||||
5C9E12792AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t-ghc8.10.7.a */,
|
||||
5C9E127D2AAE62A500C9D8FF /* libHSsimplex-chat-5.3.0.7-6JlIR0UqFTrEzd5R0Y6B8t.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -723,10 +733,10 @@
|
||||
5CADE79929211BB900072E13 /* PreferencesView.swift */,
|
||||
5C5DB70D289ABDD200730FFF /* AppearanceSettings.swift */,
|
||||
5C05DF522840AA1D00C683F9 /* CallSettings.swift */,
|
||||
5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */,
|
||||
5CB924E027A867BA00ACCCDD /* UserProfile.swift */,
|
||||
5CC036DF29C488D500C0EF20 /* HiddenProfileView.swift */,
|
||||
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */,
|
||||
5C3F1D57284363C400EC8A82 /* PrivacySettings.swift */,
|
||||
5C93292E29239A170090FFF9 /* ProtocolServersView.swift */,
|
||||
5C93293029239BED0090FFF9 /* ProtocolServerView.swift */,
|
||||
5C9329402929248A0090FFF9 /* ScanProtocolServer.swift */,
|
||||
@@ -779,6 +789,7 @@
|
||||
5CDCAD7D2818941F00503DA2 /* API.swift */,
|
||||
5CDCAD80281A7E2700503DA2 /* Notifications.swift */,
|
||||
64DAE1502809D9F5000DA960 /* FileUtils.swift */,
|
||||
5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */,
|
||||
5C00168028C4FE760094D739 /* KeyChain.swift */,
|
||||
5CE2BA76284530BF00EC33A6 /* SimpleXChat.h */,
|
||||
5CE2BA8A2845332200EC33A6 /* SimpleX.h */,
|
||||
@@ -828,6 +839,7 @@
|
||||
3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */,
|
||||
644EFFDD292BCD9D00525D5B /* ComposeVoiceView.swift */,
|
||||
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */,
|
||||
64A2723D2AAF16CD00BE1136 /* ContextDirectMemberView.swift */,
|
||||
);
|
||||
path = ComposeMessage;
|
||||
sourceTree = "<group>";
|
||||
@@ -1003,6 +1015,8 @@
|
||||
pl,
|
||||
ja,
|
||||
th,
|
||||
fi,
|
||||
uk,
|
||||
);
|
||||
mainGroup = 5CA059BD279559F40002BEB4;
|
||||
packageReferences = (
|
||||
@@ -1108,6 +1122,7 @@
|
||||
64D0C2C629FAC1EC00B38D5F /* AddContactLearnMore.swift in Sources */,
|
||||
5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */,
|
||||
5C65F343297D45E100B67AF3 /* VersionView.swift in Sources */,
|
||||
64A2723E2AAF16CD00BE1136 /* ContextDirectMemberView.swift in Sources */,
|
||||
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */,
|
||||
5CB0BA90282713D900B3292C /* SimpleXInfo.swift in Sources */,
|
||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
||||
@@ -1236,6 +1251,7 @@
|
||||
5CE2BA90284533A300EC33A6 /* JSON.swift in Sources */,
|
||||
5CE2BA8B284533A300EC33A6 /* ChatTypes.swift in Sources */,
|
||||
5CE2BA8F284533A300EC33A6 /* APITypes.swift in Sources */,
|
||||
5C9D811A2AA8727A001D49FD /* CryptoFile.swift in Sources */,
|
||||
5CE2BA8C284533A300EC33A6 /* AppGroup.swift in Sources */,
|
||||
5CE2BA8D284533A300EC33A6 /* CallTypes.swift in Sources */,
|
||||
5CE2BA8E284533A300EC33A6 /* API.swift in Sources */,
|
||||
@@ -1283,6 +1299,8 @@
|
||||
5C6D183329E93FBA00D430B3 /* pl */,
|
||||
5CAC411B2A192DE800C331A2 /* ja */,
|
||||
5CA3ED502A9422D1005D71E2 /* th */,
|
||||
5C136D8F2AAB3D14006DE2FC /* fi */,
|
||||
5C636F672AAB3D2400751C84 /* uk */,
|
||||
);
|
||||
name = InfoPlist.strings;
|
||||
sourceTree = "<group>";
|
||||
@@ -1302,6 +1320,8 @@
|
||||
5CAB912529E93F9400F34A95 /* pl */,
|
||||
5CAC41182A192D8400C331A2 /* ja */,
|
||||
5CA3ED4D2A942170005D71E2 /* th */,
|
||||
5CE6C7B32AAB1515007F345C /* fi */,
|
||||
5CE6C7B42AAB1527007F345C /* uk */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
@@ -1320,6 +1340,8 @@
|
||||
5C6D183229E93FBA00D430B3 /* pl */,
|
||||
5CAC411A2A192DE800C331A2 /* ja */,
|
||||
5CA3ED4F2A9422D1005D71E2 /* th */,
|
||||
5C136D8E2AAB3D14006DE2FC /* fi */,
|
||||
5C636F662AAB3D2400751C84 /* uk */,
|
||||
);
|
||||
name = "SimpleX--iOS--InfoPlist.strings";
|
||||
sourceTree = "<group>";
|
||||
@@ -1453,7 +1475,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 169;
|
||||
CURRENT_PROJECT_VERSION = 170;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1495,7 +1517,7 @@
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 169;
|
||||
CURRENT_PROJECT_VERSION = 170;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
@@ -1575,7 +1597,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 169;
|
||||
CURRENT_PROJECT_VERSION = 170;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1607,7 +1629,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 169;
|
||||
CURRENT_PROJECT_VERSION = 170;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
@@ -1639,7 +1661,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 169;
|
||||
CURRENT_PROJECT_VERSION = 170;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1685,7 +1707,7 @@
|
||||
APPLICATION_EXTENSION_API_ONLY = YES;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 169;
|
||||
CURRENT_PROJECT_VERSION = 170;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
|
||||
@@ -9,7 +9,7 @@
|
||||
import Foundation
|
||||
import SwiftUI
|
||||
|
||||
let jsonDecoder = getJSONDecoder()
|
||||
public let jsonDecoder = getJSONDecoder()
|
||||
let jsonEncoder = getJSONEncoder()
|
||||
|
||||
public enum ChatCommand {
|
||||
@@ -39,7 +39,7 @@ public enum ChatCommand {
|
||||
case apiGetChats(userId: Int64)
|
||||
case apiGetChat(type: ChatType, id: Int64, pagination: ChatPagination, search: String)
|
||||
case apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64)
|
||||
case apiSendMessage(type: ChatType, id: Int64, file: String?, quotedItemId: Int64?, msg: MsgContent, live: Bool, ttl: Int?)
|
||||
case apiSendMessage(sendRef: SendRef, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool, ttl: Int?)
|
||||
case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool)
|
||||
case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode)
|
||||
case apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64)
|
||||
@@ -110,8 +110,8 @@ public enum ChatCommand {
|
||||
case apiCallStatus(contact: Contact, callStatus: WebRTCCallStatus)
|
||||
case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64))
|
||||
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
|
||||
case receiveFile(fileId: Int64, inline: Bool?)
|
||||
case setFileToReceive(fileId: Int64)
|
||||
case receiveFile(fileId: Int64, encrypted: Bool, inline: Bool?)
|
||||
case setFileToReceive(fileId: Int64, encrypted: Bool)
|
||||
case cancelFile(fileId: Int64)
|
||||
case showVersion
|
||||
case string(String)
|
||||
@@ -156,10 +156,10 @@ public enum ChatCommand {
|
||||
case let .apiGetChat(type, id, pagination, search): return "/_get chat \(ref(type, id)) \(pagination.cmdString)" +
|
||||
(search == "" ? "" : " search=\(search)")
|
||||
case let .apiGetChatItemInfo(type, id, itemId): return "/_get item info \(ref(type, id)) \(itemId)"
|
||||
case let .apiSendMessage(type, id, file, quotedItemId, mc, live, ttl):
|
||||
let msg = encodeJSON(ComposedMessage(filePath: file, quotedItemId: quotedItemId, msgContent: mc))
|
||||
case let .apiSendMessage(sendRef, file, quotedItemId, mc, live, ttl):
|
||||
let msg = encodeJSON(ComposedMessage(fileSource: file, quotedItemId: quotedItemId, msgContent: mc))
|
||||
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
|
||||
return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msg)"
|
||||
return "/_send \(sendRefStr(sendRef)) live=\(onOff(live)) ttl=\(ttlStr) json \(msg)"
|
||||
case let .apiUpdateChatItem(type, id, itemId, mc, live): return "/_update item \(ref(type, id)) \(itemId) live=\(onOff(live)) \(mc.cmdString)"
|
||||
case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)"
|
||||
case let .apiDeleteMemberChatItem(groupId, groupMemberId, itemId): return "/_delete member item #\(groupId) \(groupMemberId) \(itemId)"
|
||||
@@ -239,12 +239,13 @@ public enum ChatCommand {
|
||||
case let .apiCallStatus(contact, callStatus): return "/_call status @\(contact.apiId) \(callStatus.rawValue)"
|
||||
case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)"
|
||||
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
|
||||
case let .receiveFile(fileId, inline):
|
||||
case let .receiveFile(fileId, encrypted, inline):
|
||||
let s = "/freceive \(fileId) encrypt=\(onOff(encrypted))"
|
||||
if let inline = inline {
|
||||
return "/freceive \(fileId) inline=\(onOff(inline))"
|
||||
return s + " inline=\(onOff(inline))"
|
||||
}
|
||||
return "/freceive \(fileId)"
|
||||
case let .setFileToReceive(fileId): return "/_set_file_to_receive \(fileId)"
|
||||
return s
|
||||
case let .setFileToReceive(fileId, encrypted): return "/_set_file_to_receive \(fileId) encrypt=\(onOff(encrypted))"
|
||||
case let .cancelFile(fileId): return "/fcancel \(fileId)"
|
||||
case .showVersion: return "/version"
|
||||
case let .string(str): return str
|
||||
@@ -364,6 +365,14 @@ public enum ChatCommand {
|
||||
"\(type.rawValue)\(id)"
|
||||
}
|
||||
|
||||
func sendRefStr(_ sendRef: SendRef) -> String {
|
||||
switch sendRef {
|
||||
case let .direct(contactId): return "@\(contactId)"
|
||||
case let .group(groupId, .none): return "#\(groupId)"
|
||||
case let .group(groupId, .some(directMemberId)): return "#\(groupId) @\(directMemberId)"
|
||||
}
|
||||
}
|
||||
|
||||
func protoServersStr(_ servers: [ServerCfg]) -> String {
|
||||
encodeJSON(ProtoServersConfig(servers: servers))
|
||||
}
|
||||
@@ -481,7 +490,7 @@ public enum ChatResponse: Decodable, Error {
|
||||
case groupEmpty(user: UserRef, groupInfo: GroupInfo)
|
||||
case userContactLinkSubscribed
|
||||
case newChatItem(user: UserRef, chatItem: AChatItem)
|
||||
case chatItemStatusUpdated(UserRef: User, chatItem: AChatItem)
|
||||
case chatItemStatusUpdated(user: UserRef, chatItem: AChatItem)
|
||||
case chatItemUpdated(user: UserRef, chatItem: AChatItem)
|
||||
case chatItemNotChanged(user: UserRef, chatItem: AChatItem)
|
||||
case chatItemReaction(user: UserRef, added: Bool, reaction: ACIReaction)
|
||||
@@ -824,6 +833,11 @@ public enum ChatResponse: Decodable, Error {
|
||||
}
|
||||
}
|
||||
|
||||
public enum SendRef {
|
||||
case direct(contactId: Int64)
|
||||
case group(groupId: Int64, directMemberId: Int64?)
|
||||
}
|
||||
|
||||
public func chatError(_ chatResponse: ChatResponse) -> ChatErrorType? {
|
||||
switch chatResponse {
|
||||
case let .chatCmdError(_, .error(error)): return error
|
||||
@@ -853,7 +867,7 @@ public enum ChatPagination {
|
||||
}
|
||||
|
||||
struct ComposedMessage: Encodable {
|
||||
var filePath: String?
|
||||
var fileSource: CryptoFile?
|
||||
var quotedItemId: Int64?
|
||||
var msgContent: MsgContent
|
||||
}
|
||||
@@ -1453,6 +1467,7 @@ public enum ChatErrorType: Decodable {
|
||||
case agentCommandError(message: String)
|
||||
case invalidFileDescription(message: String)
|
||||
case connectionIncognitoChangeProhibited
|
||||
case peerChatVRangeIncompatible
|
||||
case internalError(message: String)
|
||||
case exception(message: String)
|
||||
}
|
||||
@@ -1467,6 +1482,7 @@ public enum StoreError: Decodable {
|
||||
case userNotFoundByContactRequestId(contactRequestId: Int64)
|
||||
case contactNotFound(contactId: Int64)
|
||||
case contactNotFoundByName(contactName: ContactName)
|
||||
case contactNotFoundByMemberId(groupMemberId: Int64)
|
||||
case contactNotReady(contactName: ContactName)
|
||||
case duplicateContactLink
|
||||
case userContactLinkNotFound
|
||||
@@ -1494,6 +1510,7 @@ public enum StoreError: Decodable {
|
||||
case rcvFileNotFoundXFTP(agentRcvFileId: String)
|
||||
case connectionNotFound(agentConnId: String)
|
||||
case connectionNotFoundById(connId: Int64)
|
||||
case connectionNotFoundByMemberId(groupMemberId: Int64)
|
||||
case pendingConnectionNotFound(connId: Int64)
|
||||
case introNotFound
|
||||
case uniqueID
|
||||
|
||||
@@ -17,6 +17,7 @@ public let GROUP_DEFAULT_NTF_ENABLE_LOCAL = "ntfEnableLocal"
|
||||
public let GROUP_DEFAULT_NTF_ENABLE_PERIODIC = "ntfEnablePeriodic"
|
||||
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"
|
||||
let GROUP_DEFAULT_NTF_BADGE_COUNT = "ntgBadgeCount"
|
||||
let GROUP_DEFAULT_NETWORK_USE_ONION_HOSTS = "networkUseOnionHosts"
|
||||
let GROUP_DEFAULT_NETWORK_SESSION_MODE = "networkSessionMode"
|
||||
@@ -59,6 +60,7 @@ public func registerGroupDefaults() {
|
||||
GROUP_DEFAULT_INITIAL_RANDOM_DB_PASSPHRASE: false,
|
||||
GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES: true,
|
||||
GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE: false,
|
||||
GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES: true,
|
||||
GROUP_DEFAULT_CONFIRM_DB_UPGRADES: false,
|
||||
GROUP_DEFAULT_CALL_KIT_ENABLED: true,
|
||||
])
|
||||
@@ -113,7 +115,7 @@ public let ntfEnablePeriodicGroupDefault = BoolDefault(defaults: groupDefaults,
|
||||
|
||||
public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES)
|
||||
|
||||
public let privacyTransferImagesInlineGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_TRANSFER_IMAGES_INLINE)
|
||||
public let privacyEncryptLocalFilesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ENCRYPT_LOCAL_FILES)
|
||||
|
||||
public let ntfBadgeCountGroupDefault = IntDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_BADGE_COUNT)
|
||||
|
||||
|
||||
@@ -1449,6 +1449,7 @@ public struct ContactSubStatus: Decodable {
|
||||
public struct Connection: Decodable {
|
||||
public var connId: Int64
|
||||
public var agentConnId: String
|
||||
// public var peerChatVRange: VersionRange
|
||||
var connStatus: ConnStatus
|
||||
public var connLevel: Int
|
||||
public var viaGroupLink: Bool
|
||||
@@ -1459,6 +1460,7 @@ public struct Connection: Decodable {
|
||||
|
||||
private enum CodingKeys: String, CodingKey {
|
||||
case connId, agentConnId, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode
|
||||
// case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode
|
||||
}
|
||||
|
||||
public var id: ChatId { get { ":\(connId)" } }
|
||||
@@ -1466,12 +1468,22 @@ public struct Connection: Decodable {
|
||||
static let sampleData = Connection(
|
||||
connId: 1,
|
||||
agentConnId: "abc",
|
||||
// peerChatVRange: VersionRange(minVersion: 1, maxVersion: 1),
|
||||
connStatus: .ready,
|
||||
connLevel: 0,
|
||||
viaGroupLink: false
|
||||
)
|
||||
}
|
||||
|
||||
public struct VersionRange: Decodable {
|
||||
public var minVersion: Int
|
||||
public var maxVersion: Int
|
||||
|
||||
public func isCompatibleRange(_ range: VersionRange) -> Bool {
|
||||
minVersion <= range.maxVersion && range.minVersion <= maxVersion
|
||||
}
|
||||
}
|
||||
|
||||
public struct SecurityCode: Decodable, Equatable {
|
||||
public init(securityCode: String, verifiedAt: Date) {
|
||||
self.securityCode = securityCode
|
||||
@@ -1807,6 +1819,30 @@ public struct GroupMember: Identifiable, Decodable {
|
||||
return GroupMemberRole.allCases.filter { $0 <= userRole }
|
||||
}
|
||||
|
||||
public func allowedToSendDirectlyTo(groupInfo: GroupInfo) -> Bool {
|
||||
let userRole = groupInfo.membership.memberRole
|
||||
return (
|
||||
(groupInfo.fullGroupPreferences.directMessages.on && userRole >= .author)
|
||||
|| userRole >= .admin
|
||||
|| memberRole >= .admin
|
||||
)
|
||||
}
|
||||
|
||||
public enum CanSendDirectlyTo {
|
||||
case canSend
|
||||
case notConnected
|
||||
case notSupported
|
||||
}
|
||||
|
||||
public func canSendDirectlyTo(groupInfo: GroupInfo) -> CanSendDirectlyTo {
|
||||
// TODO group-direct: check version
|
||||
if let activeConn = activeConn {
|
||||
return .canSend
|
||||
} else {
|
||||
return .notConnected
|
||||
}
|
||||
}
|
||||
|
||||
public var memberIncognito: Bool {
|
||||
memberProfile.profileId != memberContactProfileId
|
||||
}
|
||||
@@ -1834,6 +1870,7 @@ public struct GroupMemberRef: Decodable {
|
||||
|
||||
public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Decodable {
|
||||
case observer = "observer"
|
||||
case author = "author"
|
||||
case member = "member"
|
||||
case admin = "admin"
|
||||
case owner = "owner"
|
||||
@@ -1843,6 +1880,7 @@ public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Dec
|
||||
public var text: String {
|
||||
switch self {
|
||||
case .observer: return NSLocalizedString("observer", comment: "member role")
|
||||
case .author: return NSLocalizedString("author", comment: "member role")
|
||||
case .member: return NSLocalizedString("member", comment: "member role")
|
||||
case .admin: return NSLocalizedString("admin", comment: "member role")
|
||||
case .owner: return NSLocalizedString("owner", comment: "member role")
|
||||
@@ -1852,9 +1890,10 @@ public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Dec
|
||||
private var comparisonValue: Int {
|
||||
switch self {
|
||||
case .observer: return 0
|
||||
case .member: return 1
|
||||
case .admin: return 2
|
||||
case .owner: return 3
|
||||
case .author: return 1
|
||||
case .member: return 2
|
||||
case .admin: return 3
|
||||
case .owner: return 4
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1968,7 +2007,7 @@ public struct AChatItem: Decodable {
|
||||
public var chatItem: ChatItem
|
||||
|
||||
public var chatId: String {
|
||||
if case let .groupRcv(groupMember) = chatItem.chatDir {
|
||||
if case let .groupRcv(groupMember, _) = chatItem.chatDir {
|
||||
return groupMember.id
|
||||
}
|
||||
return chatInfo.id
|
||||
@@ -2041,7 +2080,7 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
|
||||
public var memberConnected: GroupMember? {
|
||||
switch chatDir {
|
||||
case .groupRcv(let groupMember):
|
||||
case let .groupRcv(groupMember, _):
|
||||
switch content {
|
||||
case .rcvGroupEvent(rcvGroupEvent: .memberConnected): return groupMember
|
||||
default: return nil
|
||||
@@ -2112,9 +2151,20 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
return nil
|
||||
}
|
||||
|
||||
public var encryptedFile: Bool? {
|
||||
guard let fileSource = file?.fileSource else { return nil }
|
||||
return fileSource.cryptoArgs != nil
|
||||
}
|
||||
|
||||
public var encryptLocalFile: Bool {
|
||||
file?.fileProtocol == .xftp &&
|
||||
content.msgContent?.isVideo == false &&
|
||||
privacyEncryptLocalFilesGroupDefault.get()
|
||||
}
|
||||
|
||||
public var memberDisplayName: String? {
|
||||
get {
|
||||
if case let .groupRcv(groupMember) = chatDir {
|
||||
if case let .groupRcv(groupMember, _) = chatDir {
|
||||
return groupMember.displayName
|
||||
} else {
|
||||
return nil
|
||||
@@ -2122,9 +2172,25 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
public var directMember: GroupMember? {
|
||||
switch chatDir {
|
||||
case let .groupSnd(directMember): return directMember
|
||||
case let .groupRcv(groupMember, .msDirect): return groupMember
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
public var memberToReplyDirectlyTo: GroupMember? {
|
||||
switch chatDir {
|
||||
case let .groupSnd(directMember): return directMember
|
||||
case let .groupRcv(groupMember, _): return groupMember
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
|
||||
public func memberToModerate(_ chatInfo: ChatInfo) -> (GroupInfo, GroupMember)? {
|
||||
switch (chatInfo, chatDir) {
|
||||
case let (.group(groupInfo), .groupRcv(groupMember)):
|
||||
case let (.group(groupInfo), .groupRcv(groupMember, .msGroup)):
|
||||
let m = groupInfo.membership
|
||||
return m.memberRole >= .admin && m.memberRole >= groupMember.memberRole && meta.itemDeleted == nil
|
||||
? (groupInfo, groupMember)
|
||||
@@ -2237,7 +2303,7 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
|
||||
public static func liveDummy(_ chatType: ChatType) -> ChatItem {
|
||||
var item = ChatItem(
|
||||
chatDir: chatType == ChatType.direct ? CIDirection.directSnd : CIDirection.groupSnd,
|
||||
chatDir: chatType == ChatType.direct ? CIDirection.directSnd : CIDirection.groupSnd(directMember: nil),
|
||||
meta: CIMeta(
|
||||
itemId: -2,
|
||||
itemTs: .now,
|
||||
@@ -2269,11 +2335,34 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
public enum MessageScope: String, Decodable {
|
||||
case msGroup = "group"
|
||||
case msDirect = "direct"
|
||||
}
|
||||
|
||||
public enum CIDirection: Decodable {
|
||||
case directSnd
|
||||
case directRcv
|
||||
case groupSnd
|
||||
case groupRcv(groupMember: GroupMember)
|
||||
case groupSnd(directMember: GroupMember?)
|
||||
case groupRcv(groupMember: GroupMember, messageScope: MessageScope)
|
||||
|
||||
public var sent: Bool {
|
||||
get {
|
||||
switch self {
|
||||
case .directSnd: return true
|
||||
case .directRcv: return false
|
||||
case .groupSnd: return true
|
||||
case .groupRcv: return false
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public enum CIQDirection: Decodable {
|
||||
case directSnd
|
||||
case directRcv
|
||||
case groupSnd(messageScope: MessageScope)
|
||||
case groupRcv(groupMember: GroupMember, messageScope: MessageScope)
|
||||
|
||||
public var sent: Bool {
|
||||
get {
|
||||
@@ -2552,12 +2641,17 @@ public enum CIContent: Decodable, ItemContent {
|
||||
public var showMemberName: Bool {
|
||||
switch self {
|
||||
case .rcvMsgContent: return true
|
||||
case .sndMsgContent: return true
|
||||
case .rcvDeleted: return true
|
||||
case .sndDeleted: return true
|
||||
case .rcvCall: return true
|
||||
case .sndCall: return true
|
||||
case .rcvIntegrityError: return true
|
||||
case .rcvDecryptionError: return true
|
||||
case .rcvGroupInvitation: return true
|
||||
case .sndGroupInvitation: return true
|
||||
case .rcvModerated: return true
|
||||
case .sndModerated: return true
|
||||
case .invalidJSON: return true
|
||||
default: return false
|
||||
}
|
||||
@@ -2581,7 +2675,7 @@ public enum MsgDecryptError: String, Decodable {
|
||||
}
|
||||
|
||||
public struct CIQuote: Decodable, ItemContent {
|
||||
public var chatDir: CIDirection?
|
||||
public var chatDir: CIQDirection?
|
||||
public var itemId: Int64?
|
||||
var sharedMsgId: String? = nil
|
||||
public var sentAt: Date
|
||||
@@ -2596,16 +2690,35 @@ public struct CIQuote: Decodable, ItemContent {
|
||||
}
|
||||
|
||||
public func getSender(_ membership: GroupMember?) -> String? {
|
||||
switch (chatDir) {
|
||||
case .directSnd: return "you"
|
||||
case .directRcv: return nil
|
||||
case .groupSnd: return membership?.displayName ?? "you"
|
||||
case let .groupRcv(member): return member.displayName
|
||||
switch chatDir {
|
||||
case .directSnd:
|
||||
return NSLocalizedString("you", comment: "quote sender")
|
||||
case .directRcv:
|
||||
return nil
|
||||
case .groupSnd(.msGroup):
|
||||
if let membershipName = membership?.displayName {
|
||||
return membershipName
|
||||
} else {
|
||||
return NSLocalizedString("you", comment: "quote sender")
|
||||
}
|
||||
case .groupSnd(.msDirect):
|
||||
// TODO don't quite understand this condition - this is always user's message, why we show the name, and when "you" will be shown?
|
||||
// Maybe better to repeat "only to <member>" as it was on the original message?
|
||||
// Or "<user name> only to <member>"?
|
||||
if let membershipName = membership?.displayName {
|
||||
return String.localizedStringWithFormat(NSLocalizedString("%@, directly", comment: "quote sender"), membershipName)
|
||||
} else {
|
||||
return NSLocalizedString("you, directly", comment: "quote sender")
|
||||
}
|
||||
case let .groupRcv(member, .msGroup):
|
||||
return member.displayName
|
||||
case let .groupRcv(member, .msDirect):
|
||||
return String.localizedStringWithFormat(NSLocalizedString("%@ only to you", comment: "quote sender"), member.displayName)
|
||||
case nil: return nil
|
||||
}
|
||||
}
|
||||
|
||||
public static func getSample(_ itemId: Int64?, _ sentAt: Date, _ text: String, chatDir: CIDirection?, image: String? = nil) -> CIQuote {
|
||||
public static func getSample(_ itemId: Int64?, _ sentAt: Date, _ text: String, chatDir: CIQDirection?, image: String? = nil) -> CIQuote {
|
||||
let mc: MsgContent
|
||||
if let image = image {
|
||||
mc = .image(text: text, image: image)
|
||||
@@ -2690,12 +2803,18 @@ public struct CIFile: Decodable {
|
||||
public var fileId: Int64
|
||||
public var fileName: String
|
||||
public var fileSize: Int64
|
||||
public var filePath: String?
|
||||
public var fileSource: CryptoFile?
|
||||
public var fileStatus: CIFileStatus
|
||||
public var fileProtocol: FileProtocol
|
||||
|
||||
public static func getSample(fileId: Int64 = 1, fileName: String = "test.txt", fileSize: Int64 = 100, filePath: String? = "test.txt", fileStatus: CIFileStatus = .rcvComplete) -> CIFile {
|
||||
CIFile(fileId: fileId, fileName: fileName, fileSize: fileSize, filePath: filePath, fileStatus: fileStatus, fileProtocol: .xftp)
|
||||
let f: CryptoFile?
|
||||
if let filePath = filePath {
|
||||
f = CryptoFile.plain(filePath)
|
||||
} else {
|
||||
f = nil
|
||||
}
|
||||
return CIFile(fileId: fileId, fileName: fileName, fileSize: fileSize, fileSource: f, fileStatus: fileStatus, fileProtocol: .xftp)
|
||||
}
|
||||
|
||||
public var loaded: Bool {
|
||||
@@ -2742,6 +2861,25 @@ public struct CIFile: Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
public struct CryptoFile: Codable {
|
||||
public var filePath: String // the name of the file, not a full path
|
||||
public var cryptoArgs: CryptoFileArgs?
|
||||
|
||||
public init(filePath: String, cryptoArgs: CryptoFileArgs?) {
|
||||
self.filePath = filePath
|
||||
self.cryptoArgs = cryptoArgs
|
||||
}
|
||||
|
||||
public static func plain(_ f: String) -> CryptoFile {
|
||||
CryptoFile(filePath: f, cryptoArgs: nil)
|
||||
}
|
||||
}
|
||||
|
||||
public struct CryptoFileArgs: Codable {
|
||||
public var fileKey: String
|
||||
public var fileNonce: String
|
||||
}
|
||||
|
||||
public struct CancelAction {
|
||||
public var uiAction: String
|
||||
public var alert: AlertInfo
|
||||
|
||||
@@ -0,0 +1,69 @@
|
||||
//
|
||||
// CryptoFile.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Evgeny on 05/09/2023.
|
||||
// Copyright © 2023 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
|
||||
enum WriteFileResult: Decodable {
|
||||
case result(cryptoArgs: CryptoFileArgs)
|
||||
case error(writeError: String)
|
||||
}
|
||||
|
||||
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 d = fromCString(cjson).data(using: .utf8)!
|
||||
switch try jsonDecoder.decode(WriteFileResult.self, from: d) {
|
||||
case let .result(cfArgs): return cfArgs
|
||||
case let .error(err): throw RuntimeError(err)
|
||||
}
|
||||
}
|
||||
|
||||
public func readCryptoFile(path: String, cryptoArgs: CryptoFileArgs) throws -> Data {
|
||||
var cPath = path.cString(using: .utf8)!
|
||||
var cKey = cryptoArgs.fileKey.cString(using: .utf8)!
|
||||
var cNonce = cryptoArgs.fileNonce.cString(using: .utf8)!
|
||||
let ptr = chat_read_file(&cPath, &cKey, &cNonce)!
|
||||
let status = UInt8(ptr.pointee)
|
||||
switch status {
|
||||
case 0: // ok
|
||||
let dLen = Data(bytes: ptr.advanced(by: 1), count: 4)
|
||||
let len = dLen.withUnsafeBytes { $0.load(as: UInt32.self) }
|
||||
let d = Data(bytes: ptr.advanced(by: 5), count: Int(len))
|
||||
free(ptr)
|
||||
return d
|
||||
case 1: // error
|
||||
let err = String.init(cString: ptr)
|
||||
free(ptr)
|
||||
throw RuntimeError(err)
|
||||
default:
|
||||
throw RuntimeError("unexpected chat_read_file status: \(status)")
|
||||
}
|
||||
}
|
||||
|
||||
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 d = fromCString(cjson).data(using: .utf8)!
|
||||
switch try jsonDecoder.decode(WriteFileResult.self, from: d) {
|
||||
case let .result(cfArgs): return cfArgs
|
||||
case let .error(err): throw RuntimeError(err)
|
||||
}
|
||||
}
|
||||
|
||||
public func decryptCryptoFile(fromPath: String, cryptoArgs: CryptoFileArgs, toPath: String) throws {
|
||||
var cFromPath = fromPath.cString(using: .utf8)!
|
||||
var cKey = cryptoArgs.fileKey.cString(using: .utf8)!
|
||||
var cNonce = cryptoArgs.fileNonce.cString(using: .utf8)!
|
||||
var cToPath = toPath.cString(using: .utf8)!
|
||||
let cErr = chat_decrypt_file(&cFromPath, &cKey, &cNonce, &cToPath)!
|
||||
let err = fromCString(cErr)
|
||||
if err != "" { throw RuntimeError(err) }
|
||||
}
|
||||
@@ -173,11 +173,16 @@ public func getAppFilePath(_ fileName: String) -> URL {
|
||||
getAppFilesDirectory().appendingPathComponent(fileName)
|
||||
}
|
||||
|
||||
public func saveFile(_ data: Data, _ fileName: String) -> String? {
|
||||
public func saveFile(_ data: Data, _ fileName: String, encrypted: Bool) -> CryptoFile? {
|
||||
let filePath = getAppFilePath(fileName)
|
||||
do {
|
||||
try data.write(to: filePath)
|
||||
return fileName
|
||||
if encrypted {
|
||||
let cfArgs = try writeCryptoFile(path: filePath.path, data: data)
|
||||
return CryptoFile(filePath: fileName, cryptoArgs: cfArgs)
|
||||
} else {
|
||||
try data.write(to: filePath)
|
||||
return CryptoFile.plain(fileName)
|
||||
}
|
||||
} catch {
|
||||
logger.error("FileUtils.saveFile error: \(error.localizedDescription)")
|
||||
return nil
|
||||
@@ -210,7 +215,7 @@ public func cleanupFile(_ aChatItem: AChatItem) {
|
||||
let cItem = aChatItem.chatItem
|
||||
let mc = cItem.content.msgContent
|
||||
if case .file = mc,
|
||||
let fileName = cItem.file?.filePath {
|
||||
let fileName = cItem.file?.fileSource?.filePath {
|
||||
removeFile(fileName)
|
||||
}
|
||||
}
|
||||
@@ -221,3 +226,15 @@ public func getMaxFileSize(_ fileProtocol: FileProtocol) -> Int64 {
|
||||
case .smp: return MAX_FILE_SIZE_SMP
|
||||
}
|
||||
}
|
||||
|
||||
public struct RuntimeError: Error {
|
||||
let message: String
|
||||
|
||||
public init(_ message: String) {
|
||||
self.message = message
|
||||
}
|
||||
|
||||
public var localizedDescription: String {
|
||||
return message
|
||||
}
|
||||
}
|
||||
|
||||
@@ -59,7 +59,7 @@ public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact)
|
||||
public func createMessageReceivedNtf(_ user: any UserLike, _ cInfo: ChatInfo, _ cItem: ChatItem) -> UNMutableNotificationContent {
|
||||
let previewMode = ntfPreviewModeGroupDefault.get()
|
||||
var title: String
|
||||
if case let .group(groupInfo) = cInfo, case let .groupRcv(groupMember) = cItem.chatDir {
|
||||
if case let .group(groupInfo) = cInfo, case let .groupRcv(groupMember, _) = cItem.chatDir {
|
||||
title = groupMsgNtfTitle(groupInfo, groupMember, hideContent: previewMode == .hidden)
|
||||
} else {
|
||||
title = previewMode == .hidden ? contactHidden : "\(cInfo.chatViewName):"
|
||||
|
||||
@@ -25,3 +25,18 @@ extern char *chat_parse_server(char *str);
|
||||
extern char *chat_password_hash(char *pwd, char *salt);
|
||||
extern char *chat_encrypt_media(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);
|
||||
|
||||
// chat_read_file returns a buffer with:
|
||||
// result status (1 byte), then if
|
||||
// status == 0 (success): buffer length (uint32, 4 bytes), buffer of specified length.
|
||||
// status == 1 (error): null-terminated error message string.
|
||||
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);
|
||||
|
||||
// chat_decrypt_file returns null-terminated string with the error message
|
||||
extern char *chat_decrypt_file(char *fromPath, char *key, char *nonce, char *toPath);
|
||||
|
||||
@@ -88,6 +88,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"*bold*" = "\\*tučně*";
|
||||
|
||||
/* copied message info title, # <title> */
|
||||
"# %@" = "# %@";
|
||||
|
||||
/* copied message info */
|
||||
"## History" = "## Historie";
|
||||
|
||||
/* copied message info */
|
||||
"## In reply to" = "## Odpovídáno";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"#secret#" = "#tajný#";
|
||||
|
||||
@@ -106,6 +115,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"%@ %@" = "%@ %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ and %@ connected" = "%@ a %@ připojen";
|
||||
|
||||
/* copied message info, <sender> at <time> */
|
||||
"%@ at %@:" = "%1$@ na %2$@:";
|
||||
|
||||
@@ -124,6 +136,9 @@
|
||||
/* notification title */
|
||||
"%@ wants to connect!" = "%@ se chce připojit!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@, %@ and %lld other members connected" = "%@, %@ a %lld ostatní členové připojeni";
|
||||
|
||||
/* copied message info */
|
||||
"%@:" = "%@:";
|
||||
|
||||
@@ -282,7 +297,7 @@
|
||||
"Accept" = "Přijmout";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Accept connection request?" = "Přijmout kontakt";
|
||||
"Accept connection request?" = "Přijmout kontakt?";
|
||||
|
||||
/* notification body */
|
||||
"Accept contact request from %@?" = "Přijmout žádost o kontakt od %@?";
|
||||
@@ -693,11 +708,17 @@
|
||||
/* server test step */
|
||||
"Connect" = "Připojit";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect directly" = "Připojit přímo";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "Spojit se inkognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "připojit se k vývojářům SimpleX Chat.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via contact link" = "Připojit se přes kontaktní odkaz?";
|
||||
"Connect via contact link" = "Připojit se přes odkaz";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via group link?" = "Připojit se přes odkaz skupiny?";
|
||||
@@ -709,7 +730,7 @@
|
||||
"Connect via link / QR code" = "Připojit se prostřednictvím odkazu / QR kódu";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via one-time link" = "Připojit se jednorázovým odkazem?";
|
||||
"Connect via one-time link" = "Připojit se jednorázovým odkazem";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connected" = "připojeno";
|
||||
@@ -1053,6 +1074,9 @@
|
||||
/* rcv group event chat item */
|
||||
"deleted group" = "odstraněna skupina";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delivery" = "Doručenka";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delivery receipts are disabled!" = "Potvrzení o doručení jsou vypnuté!";
|
||||
|
||||
@@ -1722,6 +1746,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "Režim inkognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode protects your privacy by using a new random profile for each contact." = "Režim inkognito chrání vaše soukromí používáním nového náhodného profilu pro každý kontakt.";
|
||||
|
||||
/* chat list item description */
|
||||
"incognito via contact address link" = "inkognito přes odkaz na kontaktní adresu";
|
||||
|
||||
@@ -1785,6 +1812,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid server address!" = "Neplatná adresa serveru!";
|
||||
|
||||
/* item status text */
|
||||
"Invalid status" = "Neplatný status";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Invitation expired!" = "Platnost pozvánky vypršela!";
|
||||
|
||||
@@ -1858,10 +1888,10 @@
|
||||
"Join group" = "Připojit ke skupině";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Join incognito" = "Připojte se inkognito";
|
||||
"Join incognito" = "Připojit se inkognito";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Joining group" = "Připojení ke skupině";
|
||||
"Joining group" = "Připojování ke skupině";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Keep your connections" = "Zachovat vaše připojení";
|
||||
@@ -2046,6 +2076,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"More improvements are coming soon!" = "Další vylepšení se chystají již brzy!";
|
||||
|
||||
/* item status description */
|
||||
"Most likely this connection is deleted." = "Pravděpodobně je toto spojení smazáno.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Most likely this contact has deleted the connection with you." = "Tento kontakt s největší pravděpodobností smazal spojení s vámi.";
|
||||
|
||||
@@ -3071,7 +3104,7 @@
|
||||
"These settings are for your current profile **%@**." = "Toto nastavení je pro váš aktuální profil **%@**.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"They can be overridden in contact and group settings." = "Mohou být přepsány v nastavení kontaktů";
|
||||
"They can be overridden in contact and group settings." = "Mohou být přepsány v nastavení kontaktů.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "Tuto akci nelze vrátit zpět - všechny přijaté a odeslané soubory a média budou smazány. Obrázky s nízkým rozlišením zůstanou zachovány.";
|
||||
|
||||
@@ -281,7 +281,7 @@
|
||||
"About SimpleX" = "Acerca de SimpleX";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"About SimpleX address" = "Acerca de dirección SimpleX";
|
||||
"About SimpleX address" = "Acerca de la dirección SimpleX";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"About SimpleX Chat" = "Sobre SimpleX Chat";
|
||||
@@ -832,7 +832,7 @@
|
||||
"Create" = "Crear";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create an address to let people connect with you." = "Crear una dirección para que otras personas se puedan conectar contigo.";
|
||||
"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";
|
||||
@@ -853,10 +853,10 @@
|
||||
"Create secret group" = "Crea grupo secreto";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create SimpleX address" = "Crear dirección SimpleX";
|
||||
"Create SimpleX address" = "Crear tu dirección SimpleX";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create your profile" = "Crear tu perfil";
|
||||
"Create your profile" = "Crea tu perfil";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Created on %@" = "Creado en %@";
|
||||
@@ -943,7 +943,7 @@
|
||||
"days" = "días";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Decentralized" = "Descentralizado";
|
||||
"Decentralized" = "Descentralizada";
|
||||
|
||||
/* message decrypt error item */
|
||||
"Decryption error" = "Error descifrado";
|
||||
@@ -1162,7 +1162,7 @@
|
||||
"Do NOT use SimpleX for emergency calls." = "NO uses SimpleX para llamadas de emergencia.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Don't create address" = "No crear dirección";
|
||||
"Don't create address" = "No crear dirección SimpleX";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Don't enable" = "No activar";
|
||||
@@ -1981,7 +1981,7 @@
|
||||
"Mark verified" = "Marcar como verificado";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Markdown in messages" = "Sintaxis markdown en los mensajes";
|
||||
"Markdown in messages" = "Sintaxis Markdown";
|
||||
|
||||
/* marked deleted chat item preview text */
|
||||
"marked deleted" = "marcado eliminado";
|
||||
@@ -2510,7 +2510,7 @@
|
||||
"Received message" = "Mensaje entrante";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "La dirección de recepción se cambiará. El cambio se completará cuando el remitente esté en línea.";
|
||||
"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "La dirección de recepción pasará a otro servidor. El cambio se completará cuando el remitente esté en línea.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Receiving file will be stopped." = "Se detendrá la recepción del archivo.";
|
||||
@@ -2966,7 +2966,7 @@
|
||||
"Stop" = "Detener";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Stop chat to enable database actions" = "Para habilitar las acciones sobre la base de datos, previamente debes detener Chat";
|
||||
"Stop chat to enable database actions" = "Detén SimpleX para habilitar las acciones sobre la base de datos";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Stop chat to export, import or delete chat database. You will not be able to receive and send messages while the chat is stopped." = "Para poder exportar, importar o eliminar la base de datos primero debes detener Chat. Durante el tiempo que esté detenido no podrás recibir ni enviar mensajes.";
|
||||
@@ -3095,7 +3095,7 @@
|
||||
"The message will be marked as moderated for all members." = "El mensaje será marcado como moderado para todos los miembros.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The next generation of private messaging" = "La próxima generación de mensajería privada";
|
||||
"The next generation of private messaging" = "La nueva generación de mensajería privada";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The old database was not removed during the migration, it can be deleted." = "La base de datos antigua no se eliminó durante la migración, puede eliminarse.";
|
||||
@@ -3464,7 +3464,7 @@
|
||||
"You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "También puedes conectarte haciendo clic en el enlace. Si se abre en el navegador, haz clic en el botón **Abrir en aplicación móvil**.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You can create it later" = "Puedes crearlo más tarde";
|
||||
"You can create it later" = "Puedes crearla más tarde";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You can enable later via Settings" = "Puedes activar más tarde en Configuración";
|
||||
@@ -3599,7 +3599,7 @@
|
||||
"Your calls" = "Llamadas";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your chat database" = "Base de datos Chat";
|
||||
"Your chat database" = "Base de datos";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your chat database is not encrypted - set passphrase to encrypt it." = "La base de datos no está cifrada - establece una contraseña para cifrarla.";
|
||||
@@ -3647,7 +3647,7 @@
|
||||
"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Tu perfil se almacena en tu dispositivo y sólo se comparte con tus contactos.\nLos servidores de SimpleX no pueden ver tu perfil.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile, contacts and delivered messages are stored on your device." = "Tu perfil, contactos y mensajes entregados se almacenan en tu dispositivo.";
|
||||
"Your profile, contacts and delivered messages are stored on your device." = "Tu perfil, contactos y mensajes se almacenan en tu dispositivo.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your random profile" = "Tu perfil aleatorio";
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX";
|
||||
|
||||
/* Privacy - Camera Usage Description */
|
||||
"NSCameraUsageDescription" = "SimpleX tarvitsee pääsyn kameraan, jotta se voi skannata QR-koodeja muodostaakseen yhteyden muihin käyttäjiin ja videopuheluita varten.";
|
||||
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"NSFaceIDUsageDescription" = "SimpleX käyttää Face ID:tä paikalliseen todennukseen";
|
||||
|
||||
/* Privacy - Microphone Usage Description */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX tarvitsee mikrofonia ääni- ja videopuheluita ja ääniviestien tallentamista varten.";
|
||||
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "SimpleX tarvitsee pääsyn valokuvakirjastoon kuvattujen ja vastaanotettujen medioiden tallentamista varten";
|
||||
|
||||
@@ -1273,7 +1273,7 @@
|
||||
"encryption agreed for %@" = "chiffrement accepté pour %@";
|
||||
|
||||
/* chat item text */
|
||||
"encryption ok" = "chiffrement ok";
|
||||
"encryption ok" = "chiffrement OK";
|
||||
|
||||
/* chat item text */
|
||||
"encryption ok for %@" = "chiffrement ok pour %@";
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"_italic_" = "\\_斜体_";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- より安定したメッセージ配信。\n- 改良されたグループ。\n- などなど!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- 最長 5 分間の音声メッセージ。\n- 消えるまでのカスタム時間。\n- 編集履歴。";
|
||||
|
||||
@@ -85,6 +88,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"*bold*" = "\\*太文字*";
|
||||
|
||||
/* copied message info title, # <title> */
|
||||
"# %@" = "# %@";
|
||||
|
||||
/* copied message info */
|
||||
"## History" = "## 履歴";
|
||||
|
||||
/* copied message info */
|
||||
"## In reply to" = "## 返信先";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"#secret#" = "シークレット";
|
||||
|
||||
@@ -103,6 +115,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"%@ %@" = "%@ %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ and %@ connected" = "%@ と %@ は接続中";
|
||||
|
||||
/* copied message info, <sender> at <time> */
|
||||
"%@ at %@:" = "%1$@ at %2$@:";
|
||||
|
||||
/* notification title */
|
||||
"%@ is connected!" = "%@ 接続中!";
|
||||
|
||||
@@ -118,6 +136,9 @@
|
||||
/* notification title */
|
||||
"%@ wants to connect!" = "%@ が接続を希望しています!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@, %@ and %lld other members connected" = "%@, %@ および %lld 人のメンバーが接続中";
|
||||
|
||||
/* copied message info */
|
||||
"%@:" = "%@:";
|
||||
|
||||
@@ -232,15 +253,30 @@
|
||||
/* No comment provided by engineer. */
|
||||
"30 seconds" = "30秒";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"A few more things" = "その他";
|
||||
|
||||
/* notification title */
|
||||
"A new contact" = "新しい連絡先";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"A new random profile will be shared." = "新しいランダムなプロファイルが共有されます。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"A separate TCP connection will be used **for each chat profile you have in the app**." = "**アプリ内のチャット プロフィールごとに**、個別の TCP 接続が使用されます。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"A separate TCP connection will be used **for each contact and group member**.\n**Please note**: if you have many connections, your battery and traffic consumption can be substantially higher and some connections may fail." = "**アプリ内のチャット プロファイルごとに**、個別の TCP 接続が使用されます。\n**注意**:多くの接続がある場合、バッテリーと通信量の消費が大幅に増加し、一部の接続に失敗することがあります。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Abort" = "中止";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Abort changing address" = "アドレス変更の中止";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Abort changing address?" = "アドレス変更を中止しますか?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"About SimpleX" = "SimpleXについて";
|
||||
|
||||
@@ -296,6 +332,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Address" = "アドレス";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Address change will be aborted. Old receiving address will be used." = "アドレス変更は中止されます。古い受信アドレスが使用されます。";
|
||||
|
||||
/* member role */
|
||||
"admin" = "管理者";
|
||||
|
||||
@@ -305,6 +344,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Advanced network settings" = "ネットワーク詳細設定";
|
||||
|
||||
/* chat item text */
|
||||
"agreeing encryption for %@…" = "%@の暗号化に同意しています…";
|
||||
|
||||
/* chat item text */
|
||||
"agreeing encryption…" = "暗号化に同意しています…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"All app data is deleted." = "すべてのアプリデータが削除されます。";
|
||||
|
||||
@@ -353,6 +398,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Allow to irreversibly delete sent messages." = "送信済みメッセージの永久削除を許可する。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Allow to send files and media." = "ファイルやメディアの送信を許可する。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Allow to send voice messages." = "音声メッセージの送信を許可する。";
|
||||
|
||||
@@ -467,6 +515,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Bad message ID" = "メッセージ ID が正しくありません";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Better messages" = "より良いメッセージ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"bold" = "太文字";
|
||||
|
||||
@@ -564,6 +615,12 @@
|
||||
/* rcv group event chat item */
|
||||
"changed your role to %@" = "あなたの役割を %@ に変更しました";
|
||||
|
||||
/* chat item text */
|
||||
"changing address for %@…" = "%@ のアドレスを変更しています…";
|
||||
|
||||
/* chat item text */
|
||||
"changing address…" = "アドレスを変更しています…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Chat archive" = "チャットのアーカイブ";
|
||||
|
||||
@@ -651,6 +708,12 @@
|
||||
/* server test step */
|
||||
"Connect" = "接続";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect directly" = "直接接続する";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect incognito" = "シークレットモードで接続";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "SimpleX Chat 開発者に接続します。";
|
||||
|
||||
@@ -750,6 +813,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Contact preferences" = "連絡先の設定";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Contacts" = "連絡先";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Contacts can mark messages for deletion; you will be able to view them." = "連絡先はメッセージを削除対象とすることができます。あなたには閲覧可能です。";
|
||||
|
||||
@@ -885,6 +951,12 @@
|
||||
/* pref value */
|
||||
"default (%@)" = "デフォルト (%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"default (no)" = "デフォルト(いいえ)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"default (yes)" = "デフォルト(はい)";
|
||||
|
||||
/* chat item action */
|
||||
"Delete" = "削除";
|
||||
|
||||
@@ -1002,6 +1074,12 @@
|
||||
/* rcv group event chat item */
|
||||
"deleted group" = "削除されたグループ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delivery" = "Delivery";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delivery receipts are disabled!" = "Delivery receipts are disabled!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Description" = "説明";
|
||||
|
||||
@@ -1035,9 +1113,18 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Direct messages between members are prohibited in this group." = "このグループではメンバー間のダイレクトメッセージが使用禁止です。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disable (keep overrides)" = "無効にする(設定の優先を維持)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disable for all" = "すべて無効";
|
||||
|
||||
/* authentication reason */
|
||||
"Disable SimpleX Lock" = "SimpleXロックを無効にする";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"disabled" = "無効";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Disappearing message" = "消えるメッセージ";
|
||||
|
||||
@@ -1074,6 +1161,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Don't create address" = "アドレスを作成しないでください";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Don't enable" = "有効にしない";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Don't show again" = "次から表示しない";
|
||||
|
||||
@@ -1104,9 +1194,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Enable" = "有効";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enable (keep overrides)" = "有効にする(設定の優先を維持)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enable automatic message deletion?" = "自動メッセージ削除を有効にしますか?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enable for all" = "すべて有効";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enable instant notifications?" = "即時通知を有効にしますか?";
|
||||
|
||||
@@ -1167,6 +1263,30 @@
|
||||
/* notification */
|
||||
"Encrypted message: unexpected error" = "暗号化されたメッセージ : 予期しないエラー";
|
||||
|
||||
/* chat item text */
|
||||
"encryption agreed" = "暗号化に同意しました";
|
||||
|
||||
/* chat item text */
|
||||
"encryption agreed for %@" = "%@ の暗号化に同意しました";
|
||||
|
||||
/* chat item text */
|
||||
"encryption ok" = "暗号化OK";
|
||||
|
||||
/* chat item text */
|
||||
"encryption ok for %@" = "%@ の暗号化OK";
|
||||
|
||||
/* chat item text */
|
||||
"encryption re-negotiation allowed" = "暗号化の再ネゴシエーションを許可";
|
||||
|
||||
/* chat item text */
|
||||
"encryption re-negotiation allowed for %@" = "%@ の暗号化の再ネゴシエーションを許可";
|
||||
|
||||
/* chat item text */
|
||||
"encryption re-negotiation required" = "暗号化の再ネゴシエーションが必要";
|
||||
|
||||
/* chat item text */
|
||||
"encryption re-negotiation required for %@" = "%@ の暗号化の再ネゴシエーションが必要";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"ended" = "終了";
|
||||
|
||||
@@ -1200,6 +1320,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Error" = "エラー";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error aborting address change" = "アドレス変更中止エラー";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error accepting contact request" = "連絡先リクエストの承諾にエラー発生";
|
||||
|
||||
@@ -1311,6 +1434,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Error switching profile!" = "プロフィール切り替えにエラー発生!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error synchronizing connection" = "接続の同期エラー";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error updating group link" = "グループのリンクのアップデートにエラー発生";
|
||||
|
||||
@@ -1335,6 +1461,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Error: URL is invalid" = "エラー: 無効なURL";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Even when disabled in the conversation." = "会話中に無効になっている場合でも。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"event happened" = "イベント発生";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Exit without saving" = "保存せずに閉じる";
|
||||
|
||||
@@ -1356,6 +1488,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Fast and no wait until the sender is online!" = "送信者がオンラインになるまでの待ち時間がなく、速い!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Favorite" = "お気に入り";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"File will be deleted from servers." = "ファイルはサーバーから削除されます。";
|
||||
|
||||
@@ -1371,6 +1506,42 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Files & media" = "ファイルとメディア";
|
||||
|
||||
/* chat feature */
|
||||
"Files and media" = "ファイルとメディア";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Files and media are prohibited in this group." = "このグループでは、ファイルとメディアは禁止されています。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Files and media prohibited!" = "ファイルとメディアは禁止されています!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Filter unread and favorite chats." = "未読とお気に入りをフィルターします。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Finally, we have them! 🚀" = "ついに、私たちはそれらを手に入れました! 🚀";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Find chats faster" = "チャットを素早く検索";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fix" = "修正";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fix connection" = "接続を修正";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fix connection?" = "接続を修正しますか?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fix encryption after restoring backups." = "バックアップの復元後に暗号化を修正します。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fix not supported by contact" = "連絡先による修正はサポートされていません";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Fix not supported by group member" = "グループメンバーによる修正はサポートされていません";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"For console" = "コンソール";
|
||||
|
||||
@@ -1437,6 +1608,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Group members can send disappearing messages." = "グループのメンバーが消えるメッセージを送信できます。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Group members can send files and media." = "グループメンバーはファイルやメディアを送信できます。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Group members can send voice messages." = "グループのメンバーが音声メッセージを送信できます。";
|
||||
|
||||
@@ -1560,12 +1734,18 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Improved server configuration" = "サーバ設定の向上";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"In reply to" = "返信先";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito" = "シークレットモード";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode" = "シークレットモード";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Incognito mode protects your privacy by using a new random profile for each contact." = "シークレットモードとは、メインのプロフィールとプロフィール画像を守るために、新しい連絡先を追加する時に、その連絡先に対してランダムなプロフィールが作成されるという対策です。";
|
||||
|
||||
/* chat list item description */
|
||||
"incognito via contact address link" = "連絡先リンク経由でシークレットモード";
|
||||
|
||||
@@ -1629,6 +1809,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid server address!" = "無効なサーバアドレス!";
|
||||
|
||||
/* item status text */
|
||||
"Invalid status" = "無効なステータス";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Invitation expired!" = "招待が期限切れました!";
|
||||
|
||||
@@ -1707,6 +1890,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Joining group" = "グループに参加";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Keep your connections" = "接続を維持";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Keychain error" = "キーチェーンのエラー";
|
||||
|
||||
@@ -1764,6 +1950,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Make a private connection" = "プライベートな接続をする";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Make one message disappear" = "メッセージを1つ消す";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Make profile private!" = "プロフィールを非表示にできます!";
|
||||
|
||||
@@ -1881,6 +2070,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"More improvements are coming soon!" = "まだまだ改善してまいります!";
|
||||
|
||||
/* item status description */
|
||||
"Most likely this connection is deleted." = "おそらく、この接続は削除されています。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Most likely this contact has deleted the connection with you." = "恐らくこの連絡先があなたとの接続を削除しました。";
|
||||
|
||||
@@ -1953,21 +2145,33 @@
|
||||
/* No comment provided by engineer. */
|
||||
"No contacts to add" = "追加できる連絡先がありません";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No delivery information" = "送信情報なし";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No device token!" = "デバイストークンがありません!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"no e2e encryption" = "エンドツーエンド暗号化がありません";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No filtered chats" = "フィルタされたチャットはありません";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No group!" = "グループが見つかりません!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No history" = "履歴はありません";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No permission to record voice message" = "音声メッセージを録音する権限がありません";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"No received or sent files" = "送受信済みのファイルがありません";
|
||||
|
||||
/* copied message info in history */
|
||||
"no text" = "テキストなし";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Notifications" = "通知";
|
||||
|
||||
@@ -2026,6 +2230,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Only group owners can change group preferences." = "グループ設定を変えられるのはグループのオーナーだけです。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Only group owners can enable files and media." = "ファイルやメディアを有効にできるのは、グループオーナーだけです。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Only group owners can enable voice messages." = "音声メッセージを利用可能に設定できるのはグループのオーナーだけです。";
|
||||
|
||||
@@ -2227,6 +2434,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Prohibit sending disappearing messages." = "消えるメッセージを使用禁止にする。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Prohibit sending files and media." = "ファイルやメディアの送信を禁止します。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Prohibit sending voice messages." = "音声メッセージを使用禁止にする。";
|
||||
|
||||
@@ -2239,12 +2449,18 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Protocol timeout" = "プロトコル・タイムアウト";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Protocol timeout per KB" = "KB あたりのプロトコル タイムアウト";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Push notifications" = "プッシュ通知";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Rate the app" = "アプリを評価";
|
||||
|
||||
/* chat item menu */
|
||||
"React…" = "反応する…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Read" = "読む";
|
||||
|
||||
@@ -2281,6 +2497,9 @@
|
||||
/* message info title */
|
||||
"Received message" = "受信したメッセージ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "開発中の機能です!相手のクライアントが4.2でなければ機能しません。アドレス変更が完了すると、会話にメッセージが出ます。連絡相手 (またはグループのメンバー) からメッセージを受信できないかをご確認ください。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Receiving file will be stopped." = "ファイルの受信を停止します。";
|
||||
|
||||
@@ -2290,6 +2509,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Recipients see updates as you type them." = "受信者には、入力時に更新内容が表示されます。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Reconnect all connected servers to force message delivery. It uses additional traffic." = "接続されているすべてのサーバーを再接続して、メッセージを強制的に配信します。 追加のトラフィックを使用します。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Reconnect servers?" = "サーバーに再接続しますか?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Record updated at" = "レコード更新日時";
|
||||
|
||||
@@ -2338,6 +2563,15 @@
|
||||
/* rcv group event chat item */
|
||||
"removed you" = "あなたを除名しました";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Renegotiate" = "再ネゴシエート";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Renegotiate encryption" = "暗号化の再ネゴシエート";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Renegotiate encryption?" = "暗号化を再ネゴシエートしますか?";
|
||||
|
||||
/* chat item action */
|
||||
"Reply" = "返信";
|
||||
|
||||
@@ -2476,6 +2710,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Security code" = "セキュリティコード";
|
||||
|
||||
/* chat item text */
|
||||
"security code changed" = "セキュリティコードが変更されました";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Select" = "選択";
|
||||
|
||||
@@ -2614,6 +2851,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Show developer options" = "開発者向けオプションを表示";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Show last messages" = "最新のメッセージを表示";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Show preview" = "プレビューを表示";
|
||||
|
||||
@@ -2662,9 +2902,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Skipped messages" = "飛ばしたメッセージ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Small groups (max 20)" = "小グループ(最大20名)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"SMP servers" = "SMPサーバ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Some non-fatal errors occurred during import - you may see Chat console for more details." = "インポート中に致命的でないエラーが発生しました - 詳細はチャットコンソールを参照してください。";
|
||||
|
||||
/* notification title */
|
||||
"Somebody" = "誰か";
|
||||
|
||||
@@ -2794,6 +3040,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"The created archive is available via app Settings / Database / Old database archive." = "作成されたアーカイブは、アプリの設定/データベース/過去のデータベースアーカイブから利用できます。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "暗号化は機能しており、新しい暗号化への同意は必要ありません。接続エラーが発生する可能性があります!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The group is fully decentralized – it is visible only to the members." = "グループは完全分散型で、メンバーしか内容を見れません。";
|
||||
|
||||
@@ -2818,6 +3067,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"The profile is only shared with your contacts." = "プロフィールは連絡先にしか共有されません。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The second tick we missed! ✅" = "長らくお待たせしました! ✅";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The sender will NOT be notified" = "送信者には通知されません";
|
||||
|
||||
@@ -2833,6 +3085,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"There should be at least one visible user profile." = "少なくとも1つのユーザープロフィールが表示されている必要があります。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"These settings are for your current profile **%@**." = "これらの設定は現在のプロファイル **%@** 用です。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"They can be overridden in contact and group settings." = "これらは連絡先の設定が優先します。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"This action cannot be undone - all received and sent files and media will be deleted. Low resolution pictures will remain." = "ファイルとメディアが全て削除されます (※元に戻せません※)。低解像度の画像が残ります。";
|
||||
|
||||
@@ -2908,6 +3166,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Unexpected migration state" = "予期しない移行状態";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unfav." = "お気に入りを取り消す。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unhide" = "表示にする";
|
||||
|
||||
@@ -2986,12 +3247,18 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Use chat" = "チャット";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use current profile" = "現在のプロファイルを使用する";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use for new connections" = "新しい接続に使う";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use iOS call interface" = "iOS通話インターフェースを使用する";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use new incognito profile" = "新しいシークレットプロファイルを使用する";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use server" = "サーバを使う";
|
||||
|
||||
@@ -3160,6 +3427,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"You can create it later" = "後からでも作成できます";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You can enable later via Settings" = "あとで設定から有効にできます";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You can enable them later via app Privacy & Security settings." = "あとでアプリのプライバシーとセキュリティの設定から有効にすることができます。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You can hide or mute a user profile - swipe it to the right." = "ユーザープロファイルを右にスワイプすると、非表示またはミュートにすることができます。";
|
||||
|
||||
@@ -3328,6 +3601,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Your privacy" = "あなたのプライバシー";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile **%@** will be shared." = "あなたのプロファイル **%@** が共有されます。";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "プロフィールはデバイスに保存され、連絡先とのみ共有されます。\nSimpleX サーバーはあなたのプロファイルを参照できません。";
|
||||
|
||||
|
||||
@@ -209,7 +209,7 @@
|
||||
"%lldw" = "%lldw";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%u messages failed to decrypt." = "%u-berichten kunnen niet worden gedecodeerd.";
|
||||
"%u messages failed to decrypt." = "%u berichten kunnen niet worden ontsleuteld.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%u messages skipped." = "%u berichten zijn overgeslagen.";
|
||||
@@ -1867,7 +1867,7 @@
|
||||
"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Het maakt het mogelijk om veel anonieme verbindingen te hebben zonder enige gedeelde gegevens tussen hen in een enkel chat profiel.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"It can happen when you or your connection used the old database backup." = "Het kan gebeuren wanneer u of uw verbinding de oude databaseback-up gebruikte.";
|
||||
"It can happen when you or your connection used the old database backup." = "Het kan gebeuren wanneer u of de ander een oude databaseback-up gebruikt.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"It can happen when:\n1. The messages expired in the sending client after 2 days or on the server after 30 days.\n2. Message decryption failed, because you or your contact used old database backup.\n3. The connection was compromised." = "Het kan gebeuren wanneer:\n1. De berichten zijn na 2 dagen verlopen bij de verzendende client of na 30 dagen op de server.\n2. Decodering van het bericht is mislukt, omdat u of uw contactpersoon een oude databaseback-up heeft gebruikt.\n3. De verbinding is verbroken.";
|
||||
@@ -3299,7 +3299,7 @@
|
||||
"Use iOS call interface" = "De iOS-oproepinterface gebruiken";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use new incognito profile" = "Gebruik een nieuw incognito -profiel";
|
||||
"Use new incognito profile" = "Gebruik een nieuw incognitoprofiel";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use server" = "Gebruik server";
|
||||
|
||||
@@ -115,6 +115,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"%@ %@" = "%@ %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ and %@ connected" = "%@ i %@ połączeni";
|
||||
|
||||
/* copied message info, <sender> at <time> */
|
||||
"%@ at %@:" = "%1$@ o %2$@:";
|
||||
|
||||
@@ -133,6 +136,9 @@
|
||||
/* notification title */
|
||||
"%@ wants to connect!" = "%@ chce się połączyć!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@, %@ and %lld other members connected" = "%@, %@ i %lld innych członków połączeni";
|
||||
|
||||
/* copied message info */
|
||||
"%@:" = "%@:";
|
||||
|
||||
@@ -1467,6 +1473,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Even when disabled in the conversation." = "Nawet po wyłączeniu w rozmowie.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"event happened" = "nowe wydarzenie";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Exit without saving" = "Wyjdź bez zapisywania";
|
||||
|
||||
@@ -2881,6 +2890,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"Show developer options" = "Pokaż opcje dewelopera";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Show last messages" = "Pokaż ostatnie wiadomości";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Show preview" = "Pokaż podgląd";
|
||||
|
||||
|
||||
File diff suppressed because it is too large
Load Diff
@@ -0,0 +1,15 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX";
|
||||
|
||||
/* Privacy - Camera Usage Description */
|
||||
"NSCameraUsageDescription" = "SimpleX потребує доступу до камери, щоб сканувати QR-коди для з'єднання з іншими користувачами та для відеодзвінків.";
|
||||
|
||||
/* Privacy - Face ID Usage Description */
|
||||
"NSFaceIDUsageDescription" = "SimpleX використовує Face ID для локальної автентифікації";
|
||||
|
||||
/* Privacy - Microphone Usage Description */
|
||||
"NSMicrophoneUsageDescription" = "SimpleX потребує доступу до мікрофона для аудіо та відео дзвінків, а також для запису голосових повідомлень.";
|
||||
|
||||
/* Privacy - Photo Library Additions Usage Description */
|
||||
"NSPhotoLibraryAddUsageDescription" = "SimpleX потребує доступу до фототеки для збереження захоплених та отриманих медіафайлів";
|
||||
|
||||
@@ -19,6 +19,9 @@
|
||||
/* No comment provided by engineer. */
|
||||
"_italic_" = "\\_斜体_";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"- more stable message delivery.\n- a bit better groups.\n- and more!" = "- 更稳定的传输!\n- 更好的社群!\n- 以及更多!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"- voice messages up to 5 minutes.\n- custom time to disappear.\n- editing history." = "- 语音消息最长5分钟。\n- 自定义限时消息。\n- 编辑消息历史。";
|
||||
|
||||
@@ -85,6 +88,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"*bold*" = "\\*加粗*";
|
||||
|
||||
/* copied message info title, # <title> */
|
||||
"# %@" = "# %@";
|
||||
|
||||
/* copied message info */
|
||||
"## History" = "## 历史";
|
||||
|
||||
/* copied message info */
|
||||
"## In reply to" = "## 回复";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"#secret#" = "#秘密#";
|
||||
|
||||
@@ -103,6 +115,12 @@
|
||||
/* No comment provided by engineer. */
|
||||
"%@ %@" = "%@ %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@ and %@ connected" = "%@ 和%@ 以建立连接";
|
||||
|
||||
/* copied message info, <sender> at <time> */
|
||||
"%@ at %@:" = "%2$@:";
|
||||
|
||||
/* notification title */
|
||||
"%@ is connected!" = "%@ 已连接!";
|
||||
|
||||
@@ -118,6 +136,9 @@
|
||||
/* notification title */
|
||||
"%@ wants to connect!" = "%@ 要连接!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%@, %@ and %lld other members connected" = "%@, %@ 和 %lld 个成员";
|
||||
|
||||
/* copied message info */
|
||||
"%@:" = "%@:";
|
||||
|
||||
@@ -232,9 +253,15 @@
|
||||
/* No comment provided by engineer. */
|
||||
"30 seconds" = "30秒";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"A few more things" = "";
|
||||
|
||||
/* notification title */
|
||||
"A new contact" = "新联系人";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"A new random profile will be shared." = "创建一个随机的共享文件";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"A separate TCP connection will be used **for each chat profile you have in the app**." = "一个单独的 TCP 连接将被用于**您在应用程序中的每个聊天资料**。";
|
||||
|
||||
|
||||
@@ -83,12 +83,15 @@ android {
|
||||
// Comma separated list of languages that will be included in the apk
|
||||
android.defaultConfig.resConfigs(
|
||||
"en",
|
||||
"ar",
|
||||
"bg",
|
||||
"cs",
|
||||
"de",
|
||||
"es",
|
||||
"fi",
|
||||
"fr",
|
||||
"it",
|
||||
"iw",
|
||||
"ja",
|
||||
"nl",
|
||||
"pl",
|
||||
|
||||
@@ -141,7 +141,12 @@ fun processExternalIntent(intent: Intent?) {
|
||||
when {
|
||||
intent.type == "text/plain" -> {
|
||||
val text = intent.getStringExtra(Intent.EXTRA_TEXT)
|
||||
if (text != null) {
|
||||
val uri = intent.getParcelableExtra<Parcelable>(Intent.EXTRA_STREAM) as? Uri
|
||||
if (uri != null) {
|
||||
// Shared file that contains plain text, like `*.log` file
|
||||
chatModel.sharedContent.value = SharedContent.File(text ?: "", uri.toURI())
|
||||
} else if (text != null) {
|
||||
// Shared just a text
|
||||
chatModel.sharedContent.value = SharedContent.Text(text)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -71,7 +71,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
}
|
||||
Lifecycle.Event.ON_RESUME -> {
|
||||
isAppOnForeground = true
|
||||
if (chatModel.onboardingStage.value == OnboardingStage.OnboardingComplete) {
|
||||
if (chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete) {
|
||||
SimplexService.showBackgroundServiceNoticeIfNeeded()
|
||||
}
|
||||
/**
|
||||
@@ -80,7 +80,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
* It can happen when app was started and a user enables battery optimization while app in background
|
||||
* */
|
||||
if (chatModel.chatRunning.value != false &&
|
||||
chatModel.onboardingStage.value == OnboardingStage.OnboardingComplete &&
|
||||
chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete &&
|
||||
appPrefs.notificationsMode.get() == NotificationsMode.SERVICE
|
||||
) {
|
||||
SimplexService.start()
|
||||
@@ -191,7 +191,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
|
||||
override fun androidChatInitializedAndStarted() {
|
||||
// Prevents from showing "Enable notifications" alert when onboarding wasn't complete yet
|
||||
if (chatModel.onboardingStage.value == OnboardingStage.OnboardingComplete) {
|
||||
if (chatModel.controller.appPrefs.onboardingStage.get() == OnboardingStage.OnboardingComplete) {
|
||||
SimplexService.showBackgroundServiceNoticeIfNeeded()
|
||||
if (appPrefs.notificationsMode.get() == NotificationsMode.SERVICE)
|
||||
withBGApi {
|
||||
|
||||
@@ -46,7 +46,7 @@ buildscript {
|
||||
classpath("com.android.tools.build:gradle:${rootProject.extra["gradle.plugin.version"]}")
|
||||
classpath(kotlin("gradle-plugin", version = rootProject.extra["kotlin.version"] as String))
|
||||
classpath("org.jetbrains.kotlin:kotlin-serialization:1.3.2")
|
||||
classpath("dev.icerock.moko:resources-generator:0.22.3")
|
||||
classpath("dev.icerock.moko:resources-generator:0.23.0")
|
||||
|
||||
// NOTE: Do not place your application dependencies here; they belong
|
||||
// in the individual module build.gradle files
|
||||
|
||||
@@ -39,7 +39,7 @@ kotlin {
|
||||
api("org.jetbrains.kotlinx:kotlinx-datetime:0.3.2")
|
||||
api("com.russhwolf:multiplatform-settings:1.0.0")
|
||||
api("com.charleskorn.kaml:kaml:0.43.0")
|
||||
api("dev.icerock.moko:resources-compose:0.22.3")
|
||||
api("dev.icerock.moko:resources-compose:0.23.0")
|
||||
api("org.jetbrains.compose.ui:ui-text:${rootProject.extra["compose.version"] as String}")
|
||||
implementation("org.jetbrains.compose.components:components-animatedimage:${rootProject.extra["compose.version"] as String}")
|
||||
//Barcode
|
||||
@@ -48,7 +48,7 @@ kotlin {
|
||||
// Link Previews
|
||||
implementation("org.jsoup:jsoup:1.13.1")
|
||||
// Resources
|
||||
implementation("dev.icerock.moko:resources:0.22.3")
|
||||
implementation("dev.icerock.moko:resources:0.23.0")
|
||||
}
|
||||
}
|
||||
val commonTest by getting {
|
||||
@@ -62,7 +62,7 @@ kotlin {
|
||||
val work_version = "2.7.1"
|
||||
implementation("androidx.work:work-runtime-ktx:$work_version")
|
||||
implementation("com.google.accompanist:accompanist-insets:0.23.0")
|
||||
implementation("dev.icerock.moko:resources:0.22.3")
|
||||
implementation("dev.icerock.moko:resources:0.23.0")
|
||||
|
||||
// Video support
|
||||
implementation("com.google.android.exoplayer:exoplayer:2.17.1")
|
||||
|
||||
+2
@@ -23,6 +23,8 @@ actual val agentDatabaseFileName: String = "files_agent.db"
|
||||
|
||||
actual val databaseExportDir: File = androidAppContext.cacheDir
|
||||
|
||||
actual fun desktopOpenDatabaseDir() {}
|
||||
|
||||
@Composable
|
||||
actual fun rememberFileChooserLauncher(getContent: Boolean, rememberedValue: Any?, onResult: (URI?) -> Unit): FileChooserLauncher {
|
||||
val launcher = rememberLauncherForActivityResult(
|
||||
|
||||
+33
-11
@@ -1,6 +1,5 @@
|
||||
package chat.simplex.common.platform
|
||||
|
||||
import android.app.Application
|
||||
import android.content.Context
|
||||
import android.media.*
|
||||
import android.media.AudioManager.AudioPlaybackCallback
|
||||
@@ -8,10 +7,10 @@ import android.media.MediaRecorder.MEDIA_RECORDER_INFO_MAX_DURATION_REACHED
|
||||
import android.media.MediaRecorder.MEDIA_RECORDER_INFO_MAX_FILESIZE_REACHED
|
||||
import android.os.Build
|
||||
import androidx.compose.runtime.*
|
||||
import chat.simplex.res.MR
|
||||
import chat.simplex.common.model.ChatItem
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.AudioPlayer.duration
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
import java.io.*
|
||||
|
||||
@@ -134,20 +133,25 @@ actual object AudioPlayer: AudioPlayerInterface {
|
||||
}
|
||||
|
||||
// Returns real duration of the track
|
||||
private fun start(filePath: String, seek: Int? = null, onProgressUpdate: (position: Int?, state: TrackState) -> Unit): Int? {
|
||||
if (!File(filePath).exists()) {
|
||||
Log.e(TAG, "No such file: $filePath")
|
||||
private fun start(fileSource: CryptoFile, seek: Int? = null, onProgressUpdate: (position: Int?, state: TrackState) -> Unit): Int? {
|
||||
val absoluteFilePath = getAppFilePath(fileSource.filePath)
|
||||
if (!File(absoluteFilePath).exists()) {
|
||||
Log.e(TAG, "No such file: ${fileSource.filePath}")
|
||||
return null
|
||||
}
|
||||
|
||||
VideoPlayer.stopAll()
|
||||
RecorderInterface.stopRecording?.invoke()
|
||||
val current = currentlyPlaying.value
|
||||
if (current == null || current.first != filePath) {
|
||||
if (current == null || current.first != fileSource.filePath) {
|
||||
stopListener()
|
||||
player.reset()
|
||||
runCatching {
|
||||
player.setDataSource(filePath)
|
||||
if (fileSource.cryptoArgs != null) {
|
||||
player.setDataSource(CryptoMediaSource(readCryptoFile(absoluteFilePath, fileSource.cryptoArgs)))
|
||||
} else {
|
||||
player.setDataSource(absoluteFilePath)
|
||||
}
|
||||
}.onFailure {
|
||||
Log.e(TAG, it.stackTraceToString())
|
||||
AlertManager.shared.showAlertMsg(generalGetString(MR.strings.unknown_error), it.message)
|
||||
@@ -162,7 +166,7 @@ actual object AudioPlayer: AudioPlayerInterface {
|
||||
}
|
||||
if (seek != null) player.seekTo(seek)
|
||||
player.start()
|
||||
currentlyPlaying.value = filePath to onProgressUpdate
|
||||
currentlyPlaying.value = fileSource.filePath to onProgressUpdate
|
||||
progressJob = CoroutineScope(Dispatchers.Default).launch {
|
||||
onProgressUpdate(player.currentPosition, TrackState.PLAYING)
|
||||
while(isActive && player.isPlaying) {
|
||||
@@ -229,7 +233,7 @@ actual object AudioPlayer: AudioPlayerInterface {
|
||||
}
|
||||
|
||||
override fun play(
|
||||
filePath: String?,
|
||||
fileSource: CryptoFile,
|
||||
audioPlaying: MutableState<Boolean>,
|
||||
progress: MutableState<Int>,
|
||||
duration: MutableState<Int>,
|
||||
@@ -238,7 +242,7 @@ actual object AudioPlayer: AudioPlayerInterface {
|
||||
if (progress.value == duration.value) {
|
||||
progress.value = 0
|
||||
}
|
||||
val realDuration = start(filePath ?: return, progress.value) { pro, state ->
|
||||
val realDuration = start(fileSource, progress.value) { pro, state ->
|
||||
if (pro != null) {
|
||||
progress.value = pro
|
||||
}
|
||||
@@ -283,3 +287,21 @@ actual object AudioPlayer: AudioPlayerInterface {
|
||||
}
|
||||
|
||||
actual typealias SoundPlayer = chat.simplex.common.helpers.SoundPlayer
|
||||
|
||||
class CryptoMediaSource(val data: ByteArray) : MediaDataSource() {
|
||||
override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
|
||||
if (position >= data.size) return -1
|
||||
|
||||
val endPosition: Int = (position + size).toInt()
|
||||
var sizeLeft: Int = size
|
||||
if (endPosition > data.size) {
|
||||
sizeLeft -= endPosition - data.size
|
||||
}
|
||||
|
||||
System.arraycopy(data, position.toInt(), buffer, offset, sizeLeft)
|
||||
return sizeLeft
|
||||
}
|
||||
|
||||
override fun getSize(): Long = data.size.toLong()
|
||||
override fun close() {}
|
||||
}
|
||||
|
||||
+27
-9
@@ -8,13 +8,15 @@ import android.provider.MediaStore
|
||||
import android.webkit.MimeTypeMap
|
||||
import androidx.compose.ui.platform.ClipboardManager
|
||||
import androidx.compose.ui.platform.UriHandler
|
||||
import chat.simplex.common.helpers.toUri
|
||||
import chat.simplex.common.model.CIFile
|
||||
import chat.simplex.common.views.helpers.generalGetString
|
||||
import chat.simplex.common.views.helpers.getAppFileUri
|
||||
import androidx.core.content.FileProvider
|
||||
import androidx.core.net.toUri
|
||||
import chat.simplex.common.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import java.io.BufferedOutputStream
|
||||
import java.io.File
|
||||
import chat.simplex.res.MR
|
||||
import java.io.ByteArrayOutputStream
|
||||
|
||||
actual fun ClipboardManager.shareText(text: String) {
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
@@ -28,9 +30,17 @@ actual fun ClipboardManager.shareText(text: String) {
|
||||
androidAppContext.startActivity(shareIntent)
|
||||
}
|
||||
|
||||
actual fun shareFile(text: String, filePath: String) {
|
||||
val uri = getAppFileUri(filePath.substringAfterLast(File.separator))
|
||||
val ext = filePath.substringAfterLast(".")
|
||||
actual fun shareFile(text: String, fileSource: CryptoFile) {
|
||||
val uri = if (fileSource.cryptoArgs != null) {
|
||||
val tmpFile = File(tmpDir, fileSource.filePath)
|
||||
tmpFile.deleteOnExit()
|
||||
ChatModel.filesToDelete.add(tmpFile)
|
||||
decryptCryptoFile(getAppFilePath(fileSource.filePath), fileSource.cryptoArgs, tmpFile.absolutePath)
|
||||
FileProvider.getUriForFile(androidAppContext, "$APPLICATION_ID.provider", File(tmpFile.absolutePath)).toURI()
|
||||
} else {
|
||||
getAppFileUri(fileSource.filePath)
|
||||
}
|
||||
val ext = fileSource.filePath.substringAfterLast(".")
|
||||
val mimeType = MimeTypeMap.getSingleton().getMimeTypeFromExtension(ext) ?: return
|
||||
val sendIntent: Intent = Intent().apply {
|
||||
action = Intent.ACTION_SEND
|
||||
@@ -84,8 +94,16 @@ fun saveImage(ciFile: CIFile?) {
|
||||
uri?.let {
|
||||
androidAppContext.contentResolver.openOutputStream(uri)?.let { stream ->
|
||||
val outputStream = BufferedOutputStream(stream)
|
||||
File(filePath).inputStream().use { it.copyTo(outputStream) }
|
||||
outputStream.close()
|
||||
if (ciFile.fileSource?.cryptoArgs != null) {
|
||||
createTmpFileAndDelete { tmpFile ->
|
||||
decryptCryptoFile(filePath, ciFile.fileSource.cryptoArgs, tmpFile.absolutePath)
|
||||
tmpFile.inputStream().use { it.copyTo(outputStream) }
|
||||
}
|
||||
outputStream.close()
|
||||
} else {
|
||||
File(filePath).inputStream().use { it.copyTo(outputStream) }
|
||||
outputStream.close()
|
||||
}
|
||||
showToast(generalGetString(MR.strings.image_saved))
|
||||
}
|
||||
}
|
||||
|
||||
+2
-2
@@ -19,7 +19,7 @@ import java.net.URI
|
||||
|
||||
@Composable
|
||||
actual fun SimpleAndAnimatedImageView(
|
||||
uri: URI,
|
||||
data: ByteArray,
|
||||
imageBitmap: ImageBitmap,
|
||||
file: CIFile?,
|
||||
imageProvider: () -> ImageGalleryProvider,
|
||||
@@ -27,7 +27,7 @@ actual fun SimpleAndAnimatedImageView(
|
||||
) {
|
||||
val context = LocalContext.current
|
||||
val imagePainter = rememberAsyncImagePainter(
|
||||
ImageRequest.Builder(context).data(data = uri.toUri()).size(coil.size.Size.ORIGINAL).build(),
|
||||
ImageRequest.Builder(context).data(data = data).size(coil.size.Size.ORIGINAL).build(),
|
||||
placeholder = BitmapPainter(imageBitmap), // show original image while it's still loading by coil
|
||||
imageLoader = imageLoader
|
||||
)
|
||||
|
||||
+1
-1
@@ -26,7 +26,7 @@ actual fun ReactionIcon(text: String, fontSize: TextUnit) {
|
||||
@Composable
|
||||
actual fun SaveContentItemAction(cItem: ChatItem, saveFileLauncher: FileChooserLauncher, showMenu: MutableState<Boolean>) {
|
||||
val writePermissionState = rememberPermissionState(permission = Manifest.permission.WRITE_EXTERNAL_STORAGE)
|
||||
ItemAction(stringResource(MR.strings.save_verb), painterResource(MR.images.ic_download), onClick = {
|
||||
ItemAction(stringResource(MR.strings.save_verb), painterResource(if (cItem.file?.fileSource?.cryptoArgs == null) MR.images.ic_download else MR.images.ic_lock_open_right), onClick = {
|
||||
when (cItem.content.msgContent) {
|
||||
is MsgContent.MCImage -> {
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.R || writePermissionState.hasPermission) {
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user