mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Compare commits
120 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| ab87d274b0 | |||
| 1f50e580ed | |||
| 4447b66b4e | |||
| eed216cd75 | |||
| f18d776c2d | |||
| d3141a26a5 | |||
| ccf2db000c | |||
| 0a2f1408f3 | |||
| ef70e1ce15 | |||
| 4cfda91124 | |||
| 7aec147cec | |||
| f6f2044675 | |||
| 5f0b5c5a9f | |||
| dfdb4af646 | |||
| 2ab5f14119 | |||
| 46d774a822 | |||
| acf2f1fbbe | |||
| 1a853d4eea | |||
| 3ea8279451 | |||
| 87c55dbbf7 | |||
| fb4475027d | |||
| 388609563d | |||
| 0cb568d206 | |||
| 8f6e9741e7 | |||
| e00001b571 | |||
| 351cfcbcbc | |||
| 74b837bf9a | |||
| 691cd489ea | |||
| 5ed701402b | |||
| 1839dab17b | |||
| 06939343a1 | |||
| b2ebb81fcf | |||
| bccbb9900f | |||
| 1f3355921c | |||
| 9e45f68a6e | |||
| ebb5d629e7 | |||
| e002f33c53 | |||
| 71bea947a5 | |||
| 90d5abdff1 | |||
| 6407d5de63 | |||
| 9cba96082d | |||
| 2089fd8539 | |||
| 31c4ff2705 | |||
| 33895b0330 | |||
| fe20a43232 | |||
| 087519c39d | |||
| 3b49817f17 | |||
| f94f0dea08 | |||
| d68a3ba80d | |||
| 7a4dca1e4c | |||
| fff29f8548 | |||
| 8cc03b6c21 | |||
| 7a5b04d523 | |||
| d7ab0aef14 | |||
| 41cb734d56 | |||
| 6adf8f29b0 | |||
| 4ca1b57e1b | |||
| 9432a5e5cd | |||
| a9ec1f9ec1 | |||
| 122387d180 | |||
| 23f54c1022 | |||
| 0b0b78293f | |||
| eef1e97ecc | |||
| 564b137f95 | |||
| 6edea46dad | |||
| 2fe3acf4df | |||
| 1c64b17545 | |||
| 700918f0ca | |||
| 94c552ca12 | |||
| dfe16991d0 | |||
| acb372a4ce | |||
| 121eaf6073 | |||
| 8cc075eda8 | |||
| 05e7f35037 | |||
| e582d2d742 | |||
| 043a4ed915 | |||
| 76cb9013f5 | |||
| f1e8c65aa1 | |||
| 8a6bf05773 | |||
| 0118e64ab4 | |||
| 0477b1aad3 | |||
| ae850c8ce8 | |||
| 94ae571ec3 | |||
| 4552860345 | |||
| a1579810bb | |||
| 4574198990 | |||
| 4d18174b11 | |||
| defd095a4f | |||
| ed60f28e56 | |||
| f0b889ffcf | |||
| c07df9e05f | |||
| efe8ed1739 | |||
| bcd50019be | |||
| 7b48c59f9f | |||
| 04033fc0b5 | |||
| 7b90e01b3a | |||
| ef1897f865 | |||
| f587179045 | |||
| 791489e943 | |||
| c485837910 | |||
| a95415fa1a | |||
| 8d48c4b14c | |||
| 9f44242e4c | |||
| 7e7d93c596 | |||
| 073818db55 | |||
| 519dd9e219 | |||
| 94218a1a7e | |||
| 996c6efddd | |||
| fd9c080103 | |||
| 5cb8badb22 | |||
| 0438f35539 | |||
| d5eb7b7811 | |||
| e04f74738e | |||
| 5f0ccb9f17 | |||
| b2d18f6960 | |||
| b52dfee078 | |||
| 70991debfd | |||
| 885aa9cfa5 | |||
| 75a468434c | |||
| 3b98032371 |
@@ -5,12 +5,22 @@ on:
|
|||||||
branches:
|
branches:
|
||||||
- master
|
- master
|
||||||
- stable
|
- stable
|
||||||
- users
|
|
||||||
tags:
|
tags:
|
||||||
- "v*"
|
- "v*"
|
||||||
- "!*-fdroid"
|
- "!*-fdroid"
|
||||||
- "!*-armv7a"
|
- "!*-armv7a"
|
||||||
pull_request:
|
pull_request:
|
||||||
|
paths-ignore:
|
||||||
|
- "apps/ios"
|
||||||
|
- "apps/multiplatform"
|
||||||
|
- "blog"
|
||||||
|
- "docs"
|
||||||
|
- "fastlane"
|
||||||
|
- "images"
|
||||||
|
- "packages"
|
||||||
|
- "website"
|
||||||
|
- "README.md"
|
||||||
|
- "PRIVACY.md"
|
||||||
|
|
||||||
jobs:
|
jobs:
|
||||||
prepare-release:
|
prepare-release:
|
||||||
|
|||||||
@@ -61,6 +61,7 @@ website/package/generated*
|
|||||||
# Ignore build tool output, e.g. code coverage
|
# Ignore build tool output, e.g. code coverage
|
||||||
website/.nyc_output/
|
website/.nyc_output/
|
||||||
website/coverage/
|
website/coverage/
|
||||||
|
result
|
||||||
|
|
||||||
# Ignore API documentation
|
# Ignore API documentation
|
||||||
website/api-docs/
|
website/api-docs/
|
||||||
|
|||||||
@@ -300,25 +300,27 @@ What is already implemented:
|
|||||||
1. Instead of user profile identifiers used by all other platforms, even the most private ones, SimpleX uses [pairwise per-queue identifiers](./docs/GLOSSARY.md#pairwise-pseudonymous-identifier) (2 addresses for each unidirectional message queue, with an optional 3rd address for push notifications on iOS, 2 queues in each connection between the users). It makes observing the network graph on the application level more difficult, as for `n` users there can be up to `n * (n-1)` message queues.
|
1. Instead of user profile identifiers used by all other platforms, even the most private ones, SimpleX uses [pairwise per-queue identifiers](./docs/GLOSSARY.md#pairwise-pseudonymous-identifier) (2 addresses for each unidirectional message queue, with an optional 3rd address for push notifications on iOS, 2 queues in each connection between the users). It makes observing the network graph on the application level more difficult, as for `n` users there can be up to `n * (n-1)` message queues.
|
||||||
2. [End-to-end encryption](./docs/GLOSSARY.md#end-to-end-encryption) in each message queue using [NaCl cryptobox](https://nacl.cr.yp.to/box.html). This is added to allow redundancy in the future (passing each message via several servers), to avoid having the same ciphertext in different queues (that would only be visible to the attacker if TLS is compromised). The encryption keys used for this encryption are not rotated, instead we are planning to rotate the queues. Curve25519 keys are used for key negotiation.
|
2. [End-to-end encryption](./docs/GLOSSARY.md#end-to-end-encryption) in each message queue using [NaCl cryptobox](https://nacl.cr.yp.to/box.html). This is added to allow redundancy in the future (passing each message via several servers), to avoid having the same ciphertext in different queues (that would only be visible to the attacker if TLS is compromised). The encryption keys used for this encryption are not rotated, instead we are planning to rotate the queues. Curve25519 keys are used for key negotiation.
|
||||||
3. [Double ratchet](./docs/GLOSSARY.md#double-ratchet-algorithm) end-to-end encryption in each conversation between two users (or group members). This is the same algorithm that is used in Signal and many other messaging apps; it provides OTR messaging with [forward secrecy](./docs/GLOSSARY.md#forward-secrecy) (each message is encrypted by its own ephemeral key) and [break-in recovery](./docs/GLOSSARY.md#post-compromise-security) (the keys are frequently re-negotiated as part of the message exchange). Two pairs of Curve448 keys are used for the initial [key agreement](./docs/GLOSSARY.md#key-agreement-protocol), initiating party passes these keys via the connection link, accepting side - in the header of the confirmation message.
|
3. [Double ratchet](./docs/GLOSSARY.md#double-ratchet-algorithm) end-to-end encryption in each conversation between two users (or group members). This is the same algorithm that is used in Signal and many other messaging apps; it provides OTR messaging with [forward secrecy](./docs/GLOSSARY.md#forward-secrecy) (each message is encrypted by its own ephemeral key) and [break-in recovery](./docs/GLOSSARY.md#post-compromise-security) (the keys are frequently re-negotiated as part of the message exchange). Two pairs of Curve448 keys are used for the initial [key agreement](./docs/GLOSSARY.md#key-agreement-protocol), initiating party passes these keys via the connection link, accepting side - in the header of the confirmation message.
|
||||||
4. Additional layer of encryption using NaCL cryptobox for the messages delivered from the server to the recipient. This layer avoids having any ciphertext in common between sent and received traffic of the server inside TLS (and there are no identifiers in common as well).
|
4. [Post-quantum resistant key exchange](./docs/GLOSSARY.md#post-quantum-cryptography) in double ratchet protocol *on every ratchet step*. Read more in [this post](./blog/20240314-simplex-chat-v5-6-quantum-resistance-signal-double-ratchet-algorithm.md) and also see this [publication by Apple]( https://security.apple.com/blog/imessage-pq3/) explaining the need for post-quantum key rotation.
|
||||||
5. Several levels of [content padding](./docs/GLOSSARY.md#message-padding) to frustrate message size attacks.
|
5. Additional layer of encryption using NaCL cryptobox for the messages delivered from the server to the recipient. This layer avoids having any ciphertext in common between sent and received traffic of the server inside TLS (and there are no identifiers in common as well).
|
||||||
6. All message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed.
|
6. Several levels of [content padding](./docs/GLOSSARY.md#message-padding) to frustrate message size attacks.
|
||||||
7. Only TLS 1.2/1.3 are allowed for client-server connections, limited to cryptographic algorithms: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448.
|
7. All message metadata, including the time when the message was received by the server (rounded to a second) is sent to the recipients inside an encrypted envelope, so even if TLS is compromised it cannot be observed.
|
||||||
8. To protect against replay attacks SimpleX servers require [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) as session ID in each client command signed with per-queue ephemeral key.
|
8. Only TLS 1.2/1.3 are allowed for client-server connections, limited to cryptographic algorithms: CHACHA20POLY1305_SHA256, Ed25519/Ed448, Curve25519/Curve448.
|
||||||
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.
|
9. To protect against replay attacks SimpleX servers require [tlsunique channel binding](https://www.rfc-editor.org/rfc/rfc5929.html) as session ID in each client command signed with per-queue ephemeral key.
|
||||||
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.
|
10. To protect your IP address from unknown messaging relays, and for per-message transport anonymity (compared with Tor/VPN per-connection anonymity), from v6.0 all SimpleX Chat clients use private message routing by default. Read more in [this post](./blog/20240604-simplex-chat-v5.8-private-message-routing-chat-themes.md#private-message-routing).
|
||||||
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.
|
11. To protect your IP address from unknown file relays, when SOCKS proxy is not enabled SimpleX Chat clients ask for a confirmation before downloading the files from unknown servers.
|
||||||
12. Manual messaging queue rotations to move conversation to another SMP relay.
|
12. To protect your IP address from known servers 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.
|
||||||
13. Sending end-to-end encrypted files using [XFTP protocol](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html).
|
13. 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.
|
||||||
14. Local files encryption.
|
14. Transport isolation - different TCP connections and Tor circuits are used for traffic of different user profiles, optionally - for different contacts and group member connections.
|
||||||
|
15. Manual messaging queue rotations to move conversation to another SMP relay.
|
||||||
|
16. Sending end-to-end encrypted files using [XFTP protocol](https://simplex.chat/blog/20230301-simplex-file-transfer-protocol.html).
|
||||||
|
17. Local files encryption.
|
||||||
|
|
||||||
We plan to add:
|
We plan to add:
|
||||||
|
|
||||||
1. Senders' SMP relays and recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party.
|
1. 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).
|
||||||
2. Post-quantum resistant key exchange in double ratchet protocol.
|
2. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time.
|
||||||
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).
|
3. 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.
|
||||||
4. Message "mixing" - adding latency to message delivery, to protect against traffic correlation by message time.
|
4. Recipients' XFTP relays to reduce traffic and conceal IP addresses from the relays chosen, and potentially controlled, by another party.
|
||||||
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
|
## For developers
|
||||||
|
|
||||||
|
|||||||
@@ -15,16 +15,11 @@ class AppDelegate: NSObject, UIApplicationDelegate {
|
|||||||
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
func application(_ application: UIApplication, didFinishLaunchingWithOptions launchOptions: [UIApplication.LaunchOptionsKey : Any]? = nil) -> Bool {
|
||||||
logger.debug("AppDelegate: didFinishLaunchingWithOptions")
|
logger.debug("AppDelegate: didFinishLaunchingWithOptions")
|
||||||
application.registerForRemoteNotifications()
|
application.registerForRemoteNotifications()
|
||||||
NotificationCenter.default.addObserver(self, selector: #selector(pasteboardChanged), name: UIPasteboard.changedNotification, object: nil)
|
|
||||||
removePasscodesIfReinstalled()
|
removePasscodesIfReinstalled()
|
||||||
prepareForLaunch()
|
prepareForLaunch()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@objc func pasteboardChanged() {
|
|
||||||
ChatModel.shared.pasteboardHasStrings = UIPasteboard.general.hasStrings
|
|
||||||
}
|
|
||||||
|
|
||||||
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
func application(_ application: UIApplication, didRegisterForRemoteNotificationsWithDeviceToken deviceToken: Data) {
|
||||||
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
|
let token = deviceToken.map { String(format: "%02hhx", $0) }.joined()
|
||||||
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
|
logger.debug("AppDelegate: didRegisterForRemoteNotificationsWithDeviceToken \(token)")
|
||||||
|
|||||||
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"symbols": [
|
||||||
|
{
|
||||||
|
"filename": "checkmark.2.svg",
|
||||||
|
"idiom": "universal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,227 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="3300px" height="2200px" viewBox="0 0 3300 2200" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>checkmark.2</title>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="double.checkmark">
|
||||||
|
<g id="Notes">
|
||||||
|
<rect id="artboard" fill="#FFFFFF" fill-rule="nonzero" x="0" y="0" width="3300" height="2200"></rect>
|
||||||
|
<line x1="263" y1="292" x2="3036" y2="292" id="Path" stroke="#000000" stroke-width="0.5"></line>
|
||||||
|
<text id="Weight/Scale-Variations" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||||
|
font-size="13" font-weight="normal">
|
||||||
|
<tspan x="263" y="322">Weight/Scale Variations</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Ultralight" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="533.711" y="322">Ultralight</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Thin" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="843.422" y="322">Thin</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Light" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="1138.63" y="322">Light</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Regular" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="1426.84" y="322">Regular</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Medium" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="1723.06" y="322">Medium</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Semibold" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2015.77" y="322">Semibold</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Bold" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2326.48" y="322">Bold</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Heavy" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2618.19" y="322">Heavy</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Black" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2917.4" y="322">Black</tspan>
|
||||||
|
</text>
|
||||||
|
<line x1="263" y1="1903" x2="3036" y2="1903" id="Path" stroke="#000000" stroke-width="0.5"></line>
|
||||||
|
<g id="Group" transform="translate(264.3672, 1918.0684)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M7.88088,15.76172 C12.18752,15.76172 15.7715,12.1875 15.7715,7.88086 C15.7715,3.57422 12.17774,0 7.8711,0 C3.57422,0 0,3.57422 0,7.88086 C0,12.1875 3.58398,15.76172 7.88086,15.76172 L7.88088,15.76172 Z M7.88088,14.277344 C4.33596,14.277344 1.50392,11.435544 1.50392,7.880864 C1.50392,4.326184 4.32618,1.484384 7.8711,1.484384 C11.42578,1.484384 14.27734,4.326184 14.27734,7.880864 C14.27734,11.435544 11.43554,14.277344 7.88086,14.277344 L7.88088,14.277344 Z M4.28712,7.880864 C4.28712,8.310552 4.589854,8.60352 5.039074,8.60352 L7.138674,8.60352 L7.138674,10.72266 C7.138674,11.162114 7.431642,11.464848 7.86133,11.464848 C8.310548,11.464848 8.603518,11.162114 8.603518,10.72266 L8.603518,8.60352 L10.722658,8.60352 C11.162112,8.60352 11.464846,8.310552 11.464846,7.880864 C11.464846,7.44141 11.162112,7.138676 10.722658,7.138676 L8.603518,7.138676 L8.603518,5.029296 C8.603518,4.580078 8.31055,4.277342 7.86133,4.277342 C7.431642,4.277342 7.138674,4.580076 7.138674,5.029296 L7.138674,7.138676 L5.039074,7.138676 C4.589856,7.138676 4.28712,7.44141 4.28712,7.880864 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Group" transform="translate(283.254, 1915.9883)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M9.96094,19.92188 C15.41016,19.92188 19.92188,15.4004 19.92188,9.96094 C19.92188,4.51172 15.4004,0 9.95118,0 C4.51172,0 0,4.51172 0,9.96094 C0,15.4004 4.52148,19.92188 9.96094,19.92188 Z M9.96094,18.261724 C5.35156,18.261724 1.66992,14.570324 1.66992,9.960944 C1.66992,5.351564 5.3418,1.660164 9.95116,1.660164 C14.56052,1.660164 18.2617,5.351564 18.2617,9.960944 C18.2617,14.570324 14.5703,18.261724 9.96092,18.261724 L9.96094,18.261724 Z M5.4297,9.960944 C5.4297,10.43946 5.761732,10.761726 6.259778,10.761726 L9.130878,10.761726 L9.130878,13.642586 C9.130878,14.130868 9.46291,14.472664 9.941424,14.472664 C10.4297,14.472664 10.771502,14.140632 10.771502,13.642586 L10.771502,10.761726 L13.652362,10.761726 C14.140644,10.761726 14.48244,10.43946 14.48244,9.960944 C14.48244,9.472662 14.140644,9.130866 13.652362,9.130866 L10.771502,9.130866 L10.771502,6.259766 C10.771502,5.76172 10.4297,5.419922 9.941424,5.419922 C9.462908,5.419922 9.130878,5.761718 9.130878,6.259766 L9.130878,9.130866 L6.259778,9.130866 C5.761732,9.130866 5.4297,9.472662 5.4297,9.960944 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Group" transform="translate(307.1798, 1913.2246)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M12.71486,25.43944 C19.67776,25.43944 25.43946,19.67772 25.43946,12.7246 C25.43946,5.7617 19.66798,0 12.70508,0 C5.75196,0 -1.42108547e-15,5.76172 -1.42108547e-15,12.7246 C-1.42108547e-15,19.67772 5.76172,25.43944 12.71484,25.43944 L12.71486,25.43944 Z M12.71486,23.623034 C6.6797,23.623034 1.82618,18.759754 1.82618,12.724594 C1.82618,6.679674 6.66994,1.826154 12.70508,1.826154 C18.75,1.826154 23.61328,6.679674 23.61328,12.724594 C23.61328,18.759754 18.75976,23.623034 12.71484,23.623034 L12.71486,23.623034 Z M6.94338,12.724594 C6.94338,13.242172 7.314474,13.6035 7.861348,13.6035 L11.806668,13.6035 L11.806668,17.55858 C11.806668,18.09569 12.177762,18.476548 12.69534,18.476548 C13.23245,18.476548 13.603544,18.105454 13.603544,17.55858 L13.603544,13.6035 L17.558624,13.6035 C18.095734,13.6035 18.476592,13.242172 18.476592,12.724594 C18.476592,12.177718 18.105498,11.806626 17.558624,11.806626 L13.603544,11.806626 L13.603544,7.861306 C13.603544,7.31443 13.23245,6.933572 12.69534,6.933572 C12.177762,6.933572 11.806668,7.314432 11.806668,7.861306 L11.806668,11.806626 L7.861348,11.806626 C7.314472,11.806626 6.94338,12.17772 6.94338,12.724594 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<text id="Design-Variations" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="263" y="1953">Design Variations</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Symbols-are-supported-in-up-to-nine-weights-and-three-scales." fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="263" y="1971">Symbols are supported in up to nine weights and three scales.</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="For-optimal-layout-with-text-and-other-symbols,-vertically-align" fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="263" y="1989">For optimal layout with text and other symbols, vertically align</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="symbols-with-the-adjacent-text." fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||||
|
font-size="13" font-weight="normal">
|
||||||
|
<tspan x="263" y="2007">symbols with the adjacent text.</tspan>
|
||||||
|
</text>
|
||||||
|
<line x1="776" y1="1919" x2="776" y2="1933" id="Path" stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<g id="Group" transform="translate(778.4902, 1918.7324)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0.8203116,14.423832 C1.3378896,14.423832 1.5917956,14.2285116 1.7773436,13.681636 L3.0371096,10.234376 L8.7988296,10.234376 L10.0585956,13.681636 C10.2441424,14.228512 10.4980496,14.423832 11.0058616,14.423832 C11.5234396,14.423832 11.8554716,14.111324 11.8554716,13.623042 C11.8554716,13.4570264 11.8261748,13.300776 11.7480498,13.095698 L7.1679698,0.898438 C6.9433598,0.302734 6.5429698,0 5.9179698,0 C5.3125018,0 4.9023458,0.292968 4.6875018,0.888672 L0.1074218,13.105472 C0.0292968,13.31055 -3.55271368e-16,13.4668 -3.55271368e-16,13.632816 C-3.55271368e-16,14.121098 0.3125,14.423832 0.820312,14.423832 L0.8203116,14.423832 Z M3.5156316,8.750004 L5.8886716,2.177744 L5.9374998,2.177744 L8.3105398,8.750004 L3.5156316,8.750004 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<line x1="792.836" y1="1919" x2="792.836" y2="1933" id="Path" stroke="#00AEEF" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<text id="Margins" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="776" y="1953">Margins</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Leading-and-trailing-margins-on-the-left-and-right-side-of-each-symbol" fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="776" y="1971">Leading and trailing margins on the left and right side of each symbol
|
||||||
|
</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="can-be-adjusted-by-modifying-the-x-location-of-the-margin-guidelines." fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="776" y="1989">can be adjusted by modifying the x-location of the margin guidelines.
|
||||||
|
</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Modifications-are-automatically-applied-proportionally-to-all" fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="776" y="2007">Modifications are automatically applied proportionally to all</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="scales-and-weights." fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="776" y="2025">scales and weights.</tspan>
|
||||||
|
</text>
|
||||||
|
<g id="Group" transform="translate(1291.2481, 1914.5174)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0.593687825,20.3477978 L2.29290583,22.0567818 C3.15228183,22.9259218 4.13860983,22.8673278 5.06634583,21.8419378 L15.7597058,10.0548378 L14.7929098,9.07827578 L4.17766983,20.7579558 C3.82610783,21.1583458 3.49407583,21.2560018 3.02532583,20.7872526 L1.85344983,19.6251426 C1.38469983,19.1661586 1.49212183,18.8243606 1.89251223,18.4630326 L13.3671122,7.66225258 L12.3905502,6.69545658 L0.798750225,17.5841366 C-0.187577775,18.5021046 -0.265703775,19.4786686 0.593672225,20.3478166 L0.593687825,20.3477978 Z M7.00970783,2.15443778 C6.58978583,2.56459378 6.56048983,3.14076578 6.79486383,3.53139178 C7.02923983,3.89271978 7.48822383,4.12709578 8.13275383,3.96107978 C9.59759783,3.61928378 11.1210338,3.56068978 12.5468138,4.49818978 L11.9608758,5.95326778 C11.6190798,6.78334578 11.7948602,7.36928378 12.3319698,7.91615778 L14.6268898,10.2306178 C15.1151718,10.7188998 15.5253278,10.7384298 16.0917338,10.6407738 L17.1561878,10.4454614 L17.8202498,11.1192894 L17.7811874,11.6759294 C17.742125,12.1739754 17.869078,12.5548354 18.3573594,13.0333514 L19.1190774,13.7755394 C19.5975934,14.2540554 20.2128274,14.2833514 20.6815774,13.8146018 L23.5917374,10.8946818 C24.0604874,10.4259318 24.0409554,9.83022778 23.5624406,9.35171378 L22.7909566,8.58999578 C22.3124406,8.11147978 21.9413466,7.95522978 21.4628326,7.99429178 L20.8866606,8.04311998 L20.2421286,7.40835398 L20.4862686,6.28530798 C20.6132218,5.71890198 20.4569718,5.27944798 19.8710346,4.69351198 L17.6737746,2.50601198 C14.3339346,-0.814308021 9.90033463,-0.736168021 7.00971463,2.15444998 L7.00970783,2.15443778 Z M8.50384783,2.52553178 C10.9354878,0.748187779 14.2265078,1.05092178 16.4530678,3.27748578 L18.8847078,5.68958578 C19.1190838,5.92396178 19.1581458,6.10950778 19.0897858,6.45130378 L18.7675198,7.93567978 L20.2714258,9.42005578 L21.2577538,9.36146198 C21.5116598,9.35169636 21.5897858,9.3712276 21.7850978,9.56653998 L22.3612698,10.142712 L19.9198698,12.584112 L19.3436978,12.00794 C19.1483854,11.8126276 19.1190878,11.734502 19.1288538,11.47083 L19.1972132,10.494268 L17.7030732,9.00989198 L16.1796352,9.26379798 C15.8573692,9.33215738 15.7108852,9.30286038 15.4667452,9.06848558 L13.4647852,7.06652558 C13.2108792,6.83214958 13.1815812,6.66613558 13.337832,6.29504158 L14.216738,4.20520158 C12.654238,2.75012358 10.622978,2.12512158 8.59173803,2.72082558 C8.43548803,2.75988798 8.37689403,2.63293498 8.50384743,2.52551318 L8.50384783,2.52553178 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<text id="Exporting" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="1289" y="1953">Exporting</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Symbols-should-be-outlined-when-exporting-to-ensure-the" fill="#000000" fill-rule="nonzero"
|
||||||
|
font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="1289" y="1971">Symbols should be outlined when exporting to ensure the</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="design-is-preserved-when-submitting-to-Xcode." fill="#000000" fill-rule="nonzero"
|
||||||
|
font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="1289" y="1989">design is preserved when submitting to Xcode.</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="template-version" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2952" y="1933">Template v.5.0</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Requires-Xcode-15-or-greater" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||||
|
font-size="13" font-weight="normal">
|
||||||
|
<tspan x="2865" y="1951">Requires Xcode 15 or greater</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="descriptive-name" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2835" y="1969">Generated from double.checkmark</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Typeset-at-100.0-points" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||||
|
font-size="13" font-weight="normal">
|
||||||
|
<tspan x="2901" y="1987">Typeset at 100.0 points</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Small" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="263" y="726">Small</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Medium" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="263" y="1156">Medium</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Large" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="263" y="1586">Large</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Guides" transform="translate(263, 600.785)">
|
||||||
|
<g id="H-reference" transform="translate(76.9937, 24.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<line x1="0" y1="95.215" x2="2773" y2="95.215" id="Baseline-S" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<line x1="0" y1="24.756" x2="2773" y2="24.756" id="Capline-S" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<g id="H-reference" transform="translate(76.9937, 454.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<line x1="0" y1="525.215" x2="2773" y2="525.215" id="Baseline-M" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<line x1="0" y1="454.755" x2="2773" y2="454.755" id="Capline-M" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<g id="H-reference" transform="translate(76.9937, 884.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<line x1="0" y1="955.215" x2="2773" y2="955.215" id="Baseline-L" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<line x1="0" y1="884.755" x2="2773" y2="884.755" id="Capline-L" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<line x1="256.625" y1="1.13686838e-13" x2="256.625" y2="119.336" id="left-margin-Ultralight-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="348.798" y1="1.13686838e-13" x2="348.798" y2="119.336" id="right-margin-Ultralight-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="1143.53" y1="1.13686838e-13" x2="1143.53" y2="119.336" id="left-margin-Regular-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="1257.15" y1="1.13686838e-13" x2="1257.15" y2="119.336" id="right-margin-Regular-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="2622.62" y1="1.13686838e-13" x2="2622.62" y2="119.336" id="left-margin-Black-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="2760.18" y1="1.13686838e-13" x2="2760.18" y2="119.336" id="right-margin-Black-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
</g>
|
||||||
|
<g id="Symbols" transform="translate(529.3906, 625.2969)" stroke="#000000" stroke-width="0.5">
|
||||||
|
<g id="Black-S" transform="translate(2365.995, 0)">
|
||||||
|
<path
|
||||||
|
d="M67.46878,71.191381 C71.17968,71.191381 74.06058,69.873022 76.01368,66.99216 L111.07228,15.2343 C112.43948,13.2324 113.02538,11.1328 113.02538,9.2773 C113.02538,4.0039 108.82618,0 103.35738,0 C99.69528,0 97.30278,1.3183 95.05668,4.834 L67.32228,47.9492 L53.69918,32.6172 C51.79488,30.4687 49.54888,29.4433 46.52148,29.4433 C41.05278,29.4433 37,33.4472 37,38.7695 C37,41.2109 37.63478,43.1152 39.73438,45.459 L59.36328,67.67576 C61.51168,70.117162 64.14848,71.191381 67.46878,71.191381 Z"
|
||||||
|
id="Path"></path>
|
||||||
|
<path
|
||||||
|
d="M9.52148,29.4433 C12.54888,29.4433 14.79488,30.4687 16.69918,32.6172 L30.32228,47.9492 L32.291,44.888 L44.484,58.915 L39.01368,66.99216 C37.1235832,69.780091 34.3645791,71.1046997 30.825305,71.1872572 L30.46878,71.191381 C27.14848,71.191381 24.51168,70.117162 22.36328,67.67576 L2.73438,45.459 C0.63478,43.1152 0,41.2109 0,38.7695 C0,33.4472 4.05278,29.4433 9.52148,29.4433 Z M66.35738,0 C71.82618,0 76.02538,4.0039 76.02538,9.2773 C76.02538,11.1328 75.43948,13.2324 74.07228,15.2343 L61.951,33.129 L49.252,18.52 L58.05668,4.834 C60.2386057,1.41874857 62.5586852,0.0771252245 66.0465687,0.00324899359 Z"
|
||||||
|
id="Combined-Shape"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Regular-S" transform="translate(886.905, 3.7109)">
|
||||||
|
<path
|
||||||
|
d="M55.87888,66.113294 C57.78318,66.113294 59.29688,65.28322 60.37108,63.62306 L95.96678,7.3242 C96.79688,6.0547 97.08988,5.0293 97.08988,4.0039 C97.08988,1.6113 95.52738,0 93.08598,0 C91.32818,0 90.35158,0.586 89.27738,2.2949 L55.68358,56.2012 L38.00778,32.3731 C36.88478,30.8594 35.81058,30.2246 34.19918,30.2246 C31.75778,30.2246 30,31.9336 30,34.375 C30,35.4004 30.43948,36.5234 31.26958,37.5977 L51.24028,63.5254 C52.60738,65.28322 53.97458,66.113294 55.87888,66.113294 Z"
|
||||||
|
id="Path"></path>
|
||||||
|
<path
|
||||||
|
d="M4.19918,30.2246 C5.81058,30.2246 6.88478,30.8594 8.00778,32.3731 L25.68358,56.2012 L30.332,48.741 L35.554,55.426 L30.37108,63.62306 C29.3457073,65.2077582 27.919881,66.0361177 26.1361316,66.1081489 L25.87888,66.113294 C23.97458,66.113294 22.60738,65.28322 21.24028,63.5254 L1.26958,37.5977 C0.43948,36.5234 0,35.4004 0,34.375 C0,31.9336 1.75778,30.2246 4.19918,30.2246 Z M63.08598,0 C65.52738,0 67.08988,1.6113 67.08988,4.0039 C67.08988,5.0293 66.79688,6.0547 65.96678,7.3242 L48.893,34.328 L43.564,27.508 L59.27738,2.2949 C60.3068217,0.657204167 61.2466272,0.0507828125 62.8702602,0.00309092159 Z"
|
||||||
|
id="Combined-Shape"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Ultralight-S" transform="translate(0, 4.4375)">
|
||||||
|
<path
|
||||||
|
d="M36.79298,62.07178 C37.24418,62.07178 37.53178,61.87744 37.78868,61.53417 L76.33598,2.0112 C76.57578,1.6045 76.64168,1.3965 76.64168,1.1885 C76.64168,0.5215 76.12358,0 75.45318,0 C75.01228,0 74.71678,0.1772 74.50538,0.5693 L36.73398,58.97114 L18.24078,37.7768 C17.98048,37.3984 17.67828,37.2177 17.20218,37.2177 C16.48638,37.2177 16,37.7006 16,38.371 C16,38.6699 16.12159,38.9755 16.40678,39.2778 L35.65088,61.52733 C36.01908,61.96826 36.29638,62.07178 36.79298,62.07178 Z"
|
||||||
|
id="Path"></path>
|
||||||
|
<path
|
||||||
|
d="M1.20218,37.2177 C1.67828,37.2177 1.98048,37.3984 2.24078,37.7768 L20.73398,58.97114 L23.078,55.345 L24.636,57.137 L21.78868,61.53417 C21.5603244,61.8392989 21.3077121,62.0267547 20.937503,62.0646445 L20.79298,62.07178 C20.29638,62.07178 20.01908,61.96826 19.65088,61.52733 L0.40678,39.2778 C0.12159,38.9755 0,38.6699 0,38.371 C0,37.7006 0.48638,37.2177 1.20218,37.2177 Z M59.45318,0 C60.12358,0 60.64168,0.5215 60.64168,1.1885 C60.64168,1.3965 60.57578,1.6045 60.33598,2.0112 L32.216,45.432 L30.652,43.634 L58.50538,0.5693 C58.6932911,0.220766667 58.9476516,0.0420308642 59.3115144,0.00661467764 Z"
|
||||||
|
id="Combined-Shape"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 24 KiB |
@@ -0,0 +1,12 @@
|
|||||||
|
{
|
||||||
|
"info": {
|
||||||
|
"author": "xcode",
|
||||||
|
"version": 1
|
||||||
|
},
|
||||||
|
"symbols": [
|
||||||
|
{
|
||||||
|
"filename": "checkmark.wide.svg",
|
||||||
|
"idiom": "universal"
|
||||||
|
}
|
||||||
|
]
|
||||||
|
}
|
||||||
@@ -0,0 +1,218 @@
|
|||||||
|
<?xml version="1.0" encoding="UTF-8"?>
|
||||||
|
<svg width="3300px" height="2200px" viewBox="0 0 3300 2200" version="1.1" xmlns="http://www.w3.org/2000/svg"
|
||||||
|
xmlns:xlink="http://www.w3.org/1999/xlink">
|
||||||
|
<title>checkmark.wide</title>
|
||||||
|
<g id="Page-1" stroke="none" stroke-width="1" fill="none" fill-rule="evenodd">
|
||||||
|
<g id="double.checkmark">
|
||||||
|
<g id="Notes">
|
||||||
|
<rect id="artboard" fill="#FFFFFF" fill-rule="nonzero" x="0" y="0" width="3300" height="2200"></rect>
|
||||||
|
<line x1="263" y1="292" x2="3036" y2="292" id="Path" stroke="#000000" stroke-width="0.5"></line>
|
||||||
|
<text id="Weight/Scale-Variations" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||||
|
font-size="13" font-weight="normal">
|
||||||
|
<tspan x="263" y="322">Weight/Scale Variations</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Ultralight" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="533.711" y="322">Ultralight</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Thin" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="843.422" y="322">Thin</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Light" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="1138.63" y="322">Light</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Regular" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="1426.84" y="322">Regular</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Medium" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="1723.06" y="322">Medium</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Semibold" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2015.77" y="322">Semibold</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Bold" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2326.48" y="322">Bold</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Heavy" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2618.19" y="322">Heavy</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Black" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2917.4" y="322">Black</tspan>
|
||||||
|
</text>
|
||||||
|
<line x1="263" y1="1903" x2="3036" y2="1903" id="Path" stroke="#000000" stroke-width="0.5"></line>
|
||||||
|
<g id="Group" transform="translate(264.3672, 1918.0684)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M7.88088,15.76172 C12.18752,15.76172 15.7715,12.1875 15.7715,7.88086 C15.7715,3.57422 12.17774,0 7.8711,0 C3.57422,0 0,3.57422 0,7.88086 C0,12.1875 3.58398,15.76172 7.88086,15.76172 L7.88088,15.76172 Z M7.88088,14.277344 C4.33596,14.277344 1.50392,11.435544 1.50392,7.880864 C1.50392,4.326184 4.32618,1.484384 7.8711,1.484384 C11.42578,1.484384 14.27734,4.326184 14.27734,7.880864 C14.27734,11.435544 11.43554,14.277344 7.88086,14.277344 L7.88088,14.277344 Z M4.28712,7.880864 C4.28712,8.310552 4.589854,8.60352 5.039074,8.60352 L7.138674,8.60352 L7.138674,10.72266 C7.138674,11.162114 7.431642,11.464848 7.86133,11.464848 C8.310548,11.464848 8.603518,11.162114 8.603518,10.72266 L8.603518,8.60352 L10.722658,8.60352 C11.162112,8.60352 11.464846,8.310552 11.464846,7.880864 C11.464846,7.44141 11.162112,7.138676 10.722658,7.138676 L8.603518,7.138676 L8.603518,5.029296 C8.603518,4.580078 8.31055,4.277342 7.86133,4.277342 C7.431642,4.277342 7.138674,4.580076 7.138674,5.029296 L7.138674,7.138676 L5.039074,7.138676 C4.589856,7.138676 4.28712,7.44141 4.28712,7.880864 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Group" transform="translate(283.254, 1915.9883)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M9.96094,19.92188 C15.41016,19.92188 19.92188,15.4004 19.92188,9.96094 C19.92188,4.51172 15.4004,0 9.95118,0 C4.51172,0 0,4.51172 0,9.96094 C0,15.4004 4.52148,19.92188 9.96094,19.92188 Z M9.96094,18.261724 C5.35156,18.261724 1.66992,14.570324 1.66992,9.960944 C1.66992,5.351564 5.3418,1.660164 9.95116,1.660164 C14.56052,1.660164 18.2617,5.351564 18.2617,9.960944 C18.2617,14.570324 14.5703,18.261724 9.96092,18.261724 L9.96094,18.261724 Z M5.4297,9.960944 C5.4297,10.43946 5.761732,10.761726 6.259778,10.761726 L9.130878,10.761726 L9.130878,13.642586 C9.130878,14.130868 9.46291,14.472664 9.941424,14.472664 C10.4297,14.472664 10.771502,14.140632 10.771502,13.642586 L10.771502,10.761726 L13.652362,10.761726 C14.140644,10.761726 14.48244,10.43946 14.48244,9.960944 C14.48244,9.472662 14.140644,9.130866 13.652362,9.130866 L10.771502,9.130866 L10.771502,6.259766 C10.771502,5.76172 10.4297,5.419922 9.941424,5.419922 C9.462908,5.419922 9.130878,5.761718 9.130878,6.259766 L9.130878,9.130866 L6.259778,9.130866 C5.761732,9.130866 5.4297,9.472662 5.4297,9.960944 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Group" transform="translate(307.1798, 1913.2246)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M12.71486,25.43944 C19.67776,25.43944 25.43946,19.67772 25.43946,12.7246 C25.43946,5.7617 19.66798,0 12.70508,0 C5.75196,0 -1.42108547e-15,5.76172 -1.42108547e-15,12.7246 C-1.42108547e-15,19.67772 5.76172,25.43944 12.71484,25.43944 L12.71486,25.43944 Z M12.71486,23.623034 C6.6797,23.623034 1.82618,18.759754 1.82618,12.724594 C1.82618,6.679674 6.66994,1.826154 12.70508,1.826154 C18.75,1.826154 23.61328,6.679674 23.61328,12.724594 C23.61328,18.759754 18.75976,23.623034 12.71484,23.623034 L12.71486,23.623034 Z M6.94338,12.724594 C6.94338,13.242172 7.314474,13.6035 7.861348,13.6035 L11.806668,13.6035 L11.806668,17.55858 C11.806668,18.09569 12.177762,18.476548 12.69534,18.476548 C13.23245,18.476548 13.603544,18.105454 13.603544,17.55858 L13.603544,13.6035 L17.558624,13.6035 C18.095734,13.6035 18.476592,13.242172 18.476592,12.724594 C18.476592,12.177718 18.105498,11.806626 17.558624,11.806626 L13.603544,11.806626 L13.603544,7.861306 C13.603544,7.31443 13.23245,6.933572 12.69534,6.933572 C12.177762,6.933572 11.806668,7.314432 11.806668,7.861306 L11.806668,11.806626 L7.861348,11.806626 C7.314472,11.806626 6.94338,12.17772 6.94338,12.724594 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<text id="Design-Variations" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="263" y="1953">Design Variations</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Symbols-are-supported-in-up-to-nine-weights-and-three-scales." fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="263" y="1971">Symbols are supported in up to nine weights and three scales.</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="For-optimal-layout-with-text-and-other-symbols,-vertically-align" fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="263" y="1989">For optimal layout with text and other symbols, vertically align</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="symbols-with-the-adjacent-text." fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||||
|
font-size="13" font-weight="normal">
|
||||||
|
<tspan x="263" y="2007">symbols with the adjacent text.</tspan>
|
||||||
|
</text>
|
||||||
|
<line x1="776" y1="1919" x2="776" y2="1933" id="Path" stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<g id="Group" transform="translate(778.4902, 1918.7324)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0.8203116,14.423832 C1.3378896,14.423832 1.5917956,14.2285116 1.7773436,13.681636 L3.0371096,10.234376 L8.7988296,10.234376 L10.0585956,13.681636 C10.2441424,14.228512 10.4980496,14.423832 11.0058616,14.423832 C11.5234396,14.423832 11.8554716,14.111324 11.8554716,13.623042 C11.8554716,13.4570264 11.8261748,13.300776 11.7480498,13.095698 L7.1679698,0.898438 C6.9433598,0.302734 6.5429698,0 5.9179698,0 C5.3125018,0 4.9023458,0.292968 4.6875018,0.888672 L0.1074218,13.105472 C0.0292968,13.31055 -3.55271368e-16,13.4668 -3.55271368e-16,13.632816 C-3.55271368e-16,14.121098 0.3125,14.423832 0.820312,14.423832 L0.8203116,14.423832 Z M3.5156316,8.750004 L5.8886716,2.177744 L5.9374998,2.177744 L8.3105398,8.750004 L3.5156316,8.750004 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<line x1="792.836" y1="1919" x2="792.836" y2="1933" id="Path" stroke="#00AEEF" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<text id="Margins" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="776" y="1953">Margins</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Leading-and-trailing-margins-on-the-left-and-right-side-of-each-symbol" fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="776" y="1971">Leading and trailing margins on the left and right side of each symbol
|
||||||
|
</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="can-be-adjusted-by-modifying-the-x-location-of-the-margin-guidelines." fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="776" y="1989">can be adjusted by modifying the x-location of the margin guidelines.
|
||||||
|
</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Modifications-are-automatically-applied-proportionally-to-all" fill="#000000"
|
||||||
|
fill-rule="nonzero" font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="776" y="2007">Modifications are automatically applied proportionally to all</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="scales-and-weights." fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="776" y="2025">scales and weights.</tspan>
|
||||||
|
</text>
|
||||||
|
<g id="Group" transform="translate(1291.2481, 1914.5174)" fill="#000000" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0.593687825,20.3477978 L2.29290583,22.0567818 C3.15228183,22.9259218 4.13860983,22.8673278 5.06634583,21.8419378 L15.7597058,10.0548378 L14.7929098,9.07827578 L4.17766983,20.7579558 C3.82610783,21.1583458 3.49407583,21.2560018 3.02532583,20.7872526 L1.85344983,19.6251426 C1.38469983,19.1661586 1.49212183,18.8243606 1.89251223,18.4630326 L13.3671122,7.66225258 L12.3905502,6.69545658 L0.798750225,17.5841366 C-0.187577775,18.5021046 -0.265703775,19.4786686 0.593672225,20.3478166 L0.593687825,20.3477978 Z M7.00970783,2.15443778 C6.58978583,2.56459378 6.56048983,3.14076578 6.79486383,3.53139178 C7.02923983,3.89271978 7.48822383,4.12709578 8.13275383,3.96107978 C9.59759783,3.61928378 11.1210338,3.56068978 12.5468138,4.49818978 L11.9608758,5.95326778 C11.6190798,6.78334578 11.7948602,7.36928378 12.3319698,7.91615778 L14.6268898,10.2306178 C15.1151718,10.7188998 15.5253278,10.7384298 16.0917338,10.6407738 L17.1561878,10.4454614 L17.8202498,11.1192894 L17.7811874,11.6759294 C17.742125,12.1739754 17.869078,12.5548354 18.3573594,13.0333514 L19.1190774,13.7755394 C19.5975934,14.2540554 20.2128274,14.2833514 20.6815774,13.8146018 L23.5917374,10.8946818 C24.0604874,10.4259318 24.0409554,9.83022778 23.5624406,9.35171378 L22.7909566,8.58999578 C22.3124406,8.11147978 21.9413466,7.95522978 21.4628326,7.99429178 L20.8866606,8.04311998 L20.2421286,7.40835398 L20.4862686,6.28530798 C20.6132218,5.71890198 20.4569718,5.27944798 19.8710346,4.69351198 L17.6737746,2.50601198 C14.3339346,-0.814308021 9.90033463,-0.736168021 7.00971463,2.15444998 L7.00970783,2.15443778 Z M8.50384783,2.52553178 C10.9354878,0.748187779 14.2265078,1.05092178 16.4530678,3.27748578 L18.8847078,5.68958578 C19.1190838,5.92396178 19.1581458,6.10950778 19.0897858,6.45130378 L18.7675198,7.93567978 L20.2714258,9.42005578 L21.2577538,9.36146198 C21.5116598,9.35169636 21.5897858,9.3712276 21.7850978,9.56653998 L22.3612698,10.142712 L19.9198698,12.584112 L19.3436978,12.00794 C19.1483854,11.8126276 19.1190878,11.734502 19.1288538,11.47083 L19.1972132,10.494268 L17.7030732,9.00989198 L16.1796352,9.26379798 C15.8573692,9.33215738 15.7108852,9.30286038 15.4667452,9.06848558 L13.4647852,7.06652558 C13.2108792,6.83214958 13.1815812,6.66613558 13.337832,6.29504158 L14.216738,4.20520158 C12.654238,2.75012358 10.622978,2.12512158 8.59173803,2.72082558 C8.43548803,2.75988798 8.37689403,2.63293498 8.50384743,2.52551318 L8.50384783,2.52553178 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<text id="Exporting" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="1289" y="1953">Exporting</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Symbols-should-be-outlined-when-exporting-to-ensure-the" fill="#000000" fill-rule="nonzero"
|
||||||
|
font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="1289" y="1971">Symbols should be outlined when exporting to ensure the</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="design-is-preserved-when-submitting-to-Xcode." fill="#000000" fill-rule="nonzero"
|
||||||
|
font-family="Helvetica" font-size="13" font-weight="normal">
|
||||||
|
<tspan x="1289" y="1989">design is preserved when submitting to Xcode.</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="template-version" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2952" y="1933">Template v.5.0</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Requires-Xcode-15-or-greater" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||||
|
font-size="13" font-weight="normal">
|
||||||
|
<tspan x="2865" y="1951">Requires Xcode 15 or greater</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="descriptive-name" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="2835" y="1969">Generated from double.checkmark</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Typeset-at-100.0-points" fill="#000000" fill-rule="nonzero" font-family="Helvetica"
|
||||||
|
font-size="13" font-weight="normal">
|
||||||
|
<tspan x="2901" y="1987">Typeset at 100.0 points</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Small" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="263" y="726">Small</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Medium" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="263" y="1156">Medium</tspan>
|
||||||
|
</text>
|
||||||
|
<text id="Large" fill="#000000" fill-rule="nonzero" font-family="Helvetica" font-size="13"
|
||||||
|
font-weight="normal">
|
||||||
|
<tspan x="263" y="1586">Large</tspan>
|
||||||
|
</text>
|
||||||
|
</g>
|
||||||
|
<g id="Guides" transform="translate(263, 600.785)">
|
||||||
|
<g id="H-reference" transform="translate(76.9937, 24.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<line x1="0" y1="95.215" x2="2773" y2="95.215" id="Baseline-S" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<line x1="0" y1="24.756" x2="2773" y2="24.756" id="Capline-S" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<g id="H-reference" transform="translate(76.9937, 454.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<line x1="0" y1="525.215" x2="2773" y2="525.215" id="Baseline-M" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<line x1="0" y1="454.755" x2="2773" y2="454.755" id="Capline-M" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<g id="H-reference" transform="translate(76.9937, 884.756)" fill="#27AAE1" fill-rule="nonzero">
|
||||||
|
<path
|
||||||
|
d="M0,70.459 L2.644096,70.459 L28.334446,3.3267 L29.036646,3.3267 L29.036646,0 L27.128946,0 L0,70.459 Z M10.694846,45.9791 L45.987846,45.9791 L45.237846,43.7305 L11.444846,43.7305 L10.694846,45.9791 Z M54.125946,70.459 L56.770046,70.459 L29.644546,0 L28.438946,0 L28.438946,3.3267 L54.125946,70.459 Z"
|
||||||
|
id="Shape"></path>
|
||||||
|
</g>
|
||||||
|
<line x1="0" y1="955.215" x2="2773" y2="955.215" id="Baseline-L" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<line x1="0" y1="884.755" x2="2773" y2="884.755" id="Capline-L" stroke="#27AAE1" stroke-width="0.5">
|
||||||
|
</line>
|
||||||
|
<line x1="256.625" y1="1.13686838e-13" x2="256.625" y2="119.336" id="left-margin-Ultralight-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="348.798" y1="1.13686838e-13" x2="348.798" y2="119.336" id="right-margin-Ultralight-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="1143.53" y1="1.13686838e-13" x2="1143.53" y2="119.336" id="left-margin-Regular-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="1257.15" y1="1.13686838e-13" x2="1257.15" y2="119.336" id="right-margin-Regular-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="2622.62" y1="1.13686838e-13" x2="2622.62" y2="119.336" id="left-margin-Black-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
<line x1="2760.18" y1="1.13686838e-13" x2="2760.18" y2="119.336" id="right-margin-Black-S"
|
||||||
|
stroke="#00AEEF" stroke-width="0.5"></line>
|
||||||
|
</g>
|
||||||
|
<g id="Symbols" transform="translate(529.3906, 625.2969)" stroke="#000000" stroke-width="0.5">
|
||||||
|
<g id="Black-S" transform="translate(2365.995, 0)">
|
||||||
|
<path
|
||||||
|
d="M30.46878,71.191381 C34.17968,71.191381 37.06058,69.873022 39.01368,66.99216 L74.07228,15.2343 C75.43948,13.2324 76.02538,11.1328 76.02538,9.2773 C76.02538,4.0039 71.82618,0 66.35738,0 C62.69528,0 60.30278,1.3183 58.05668,4.834 L30.32228,47.9492 L16.69918,32.6172 C14.79488,30.4687 12.54888,29.4433 9.52148,29.4433 C4.05278,29.4433 0,33.4472 0,38.7695 C0,41.2109 0.63478,43.1152 2.73438,45.459 L22.36328,67.67576 C24.51168,70.117162 27.14848,71.191381 30.46878,71.191381 Z"
|
||||||
|
id="Path"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Regular-S" transform="translate(886.905, 3.7109)">
|
||||||
|
<path
|
||||||
|
d="M25.87888,66.113294 C27.78318,66.113294 29.29688,65.28322 30.37108,63.62306 L65.96678,7.3242 C66.79688,6.0547 67.08988,5.0293 67.08988,4.0039 C67.08988,1.6113 65.52738,0 63.08598,0 C61.32818,0 60.35158,0.586 59.27738,2.2949 L25.68358,56.2012 L8.00778,32.3731 C6.88478,30.8594 5.81058,30.2246 4.19918,30.2246 C1.75778,30.2246 0,31.9336 0,34.375 C0,35.4004 0.43948,36.5234 1.26958,37.5977 L21.24028,63.5254 C22.60738,65.28322 23.97458,66.113294 25.87888,66.113294 Z"
|
||||||
|
id="Path"></path>
|
||||||
|
</g>
|
||||||
|
<g id="Ultralight-S" transform="translate(0, 4.4375)">
|
||||||
|
<path
|
||||||
|
d="M20.79298,62.07178 C21.24418,62.07178 21.53178,61.87744 21.78868,61.53417 L60.33598,2.0112 C60.57578,1.6045 60.64168,1.3965 60.64168,1.1885 C60.64168,0.5215 60.12358,0 59.45318,0 C59.01228,0 58.71678,0.1772 58.50538,0.5693 L20.73398,58.97114 L2.24078,37.7768 C1.98048,37.3984 1.67828,37.2177 1.20218,37.2177 C0.48638,37.2177 0,37.7006 0,38.371 C0,38.6699 0.12159,38.9755 0.40678,39.2778 L19.65088,61.52733 C20.01908,61.96826 20.29638,62.07178 20.79298,62.07178 Z"
|
||||||
|
id="Path"></path>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</g>
|
||||||
|
</svg>
|
||||||
|
After Width: | Height: | Size: 22 KiB |
@@ -36,7 +36,7 @@ struct ContentView: View {
|
|||||||
@State private var waitingForOrPassedAuth = true
|
@State private var waitingForOrPassedAuth = true
|
||||||
@State private var chatListActionSheet: ChatListActionSheet? = nil
|
@State private var chatListActionSheet: ChatListActionSheet? = nil
|
||||||
|
|
||||||
private let callTopPadding: CGFloat = 50
|
private let callTopPadding: CGFloat = 40
|
||||||
|
|
||||||
private enum ChatListActionSheet: Identifiable {
|
private enum ChatListActionSheet: Identifiable {
|
||||||
case planAndConnectSheet(sheet: PlanAndConnectActionSheet)
|
case planAndConnectSheet(sheet: PlanAndConnectActionSheet)
|
||||||
@@ -151,12 +151,12 @@ struct ContentView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
reactOnDarkThemeChanges()
|
reactOnDarkThemeChanges(systemInDarkThemeCurrently)
|
||||||
}
|
}
|
||||||
.onChange(of: colorScheme) { scheme in
|
.onChange(of: colorScheme) { scheme in
|
||||||
// It's needed to update UI colors when iOS wants to make screenshot after going to background,
|
// It's needed to update UI colors when iOS wants to make screenshot after going to background,
|
||||||
// so when a user changes his global theme from dark to light or back, the app will adapt to it
|
// so when a user changes his global theme from dark to light or back, the app will adapt to it
|
||||||
reactOnDarkThemeChanges()
|
reactOnDarkThemeChanges(scheme == .dark)
|
||||||
}
|
}
|
||||||
.onChange(of: theme.name) { _ in
|
.onChange(of: theme.name) { _ in
|
||||||
ThemeManager.adjustWindowStyle()
|
ThemeManager.adjustWindowStyle()
|
||||||
@@ -207,7 +207,7 @@ struct ContentView: View {
|
|||||||
CallDuration(call: call)
|
CallDuration(call: call)
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
.frame(height: callTopPadding - 10)
|
.frame(height: callTopPadding)
|
||||||
.background(Color(uiColor: UIColor(red: 47/255, green: 208/255, blue: 88/255, alpha: 1)))
|
.background(Color(uiColor: UIColor(red: 47/255, green: 208/255, blue: 88/255, alpha: 1)))
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
chatModel.activeCallViewIsCollapsed = false
|
chatModel.activeCallViewIsCollapsed = false
|
||||||
|
|||||||
@@ -61,7 +61,7 @@ class ItemsModel: ObservableObject {
|
|||||||
|
|
||||||
init() {
|
init() {
|
||||||
publisher
|
publisher
|
||||||
.throttle(for: 0.25, scheduler: DispatchQueue.main, latest: true)
|
.throttle(for: 0.2, scheduler: DispatchQueue.main, latest: true)
|
||||||
.sink { self.objectWillChange.send() }
|
.sink { self.objectWillChange.send() }
|
||||||
.store(in: &bag)
|
.store(in: &bag)
|
||||||
}
|
}
|
||||||
@@ -123,6 +123,14 @@ class NetworkModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
/// ChatItemWithMenu can depend on previous or next item for it's appearance
|
||||||
|
/// This dummy model is used to force an update of all chat items,
|
||||||
|
/// when they might have changed appearance.
|
||||||
|
class ChatItemDummyModel: ObservableObject {
|
||||||
|
static let shared = ChatItemDummyModel()
|
||||||
|
func sendUpdate() { objectWillChange.send() }
|
||||||
|
}
|
||||||
|
|
||||||
final class ChatModel: ObservableObject {
|
final class ChatModel: ObservableObject {
|
||||||
@Published var onboardingStage: OnboardingStage?
|
@Published var onboardingStage: OnboardingStage?
|
||||||
@Published var setDeliveryReceipts = false
|
@Published var setDeliveryReceipts = false
|
||||||
@@ -143,7 +151,7 @@ final class ChatModel: ObservableObject {
|
|||||||
@Published var contentViewAccessAuthenticated: Bool = false
|
@Published var contentViewAccessAuthenticated: Bool = false
|
||||||
@Published var laRequest: LocalAuthRequest?
|
@Published var laRequest: LocalAuthRequest?
|
||||||
// list of chat "previews"
|
// list of chat "previews"
|
||||||
@Published var chats: [Chat] = []
|
@Published private(set) var chats: [Chat] = []
|
||||||
@Published var deletedChats: Set<String> = []
|
@Published var deletedChats: Set<String> = []
|
||||||
// current chat
|
// current chat
|
||||||
@Published var chatId: String?
|
@Published var chatId: String?
|
||||||
@@ -183,7 +191,6 @@ final class ChatModel: ObservableObject {
|
|||||||
@Published var stopPreviousRecPlay: URL? = nil // coordinates currently playing source
|
@Published var stopPreviousRecPlay: URL? = nil // coordinates currently playing source
|
||||||
@Published var draft: ComposeState?
|
@Published var draft: ComposeState?
|
||||||
@Published var draftChatId: String?
|
@Published var draftChatId: String?
|
||||||
@Published var pasteboardHasStrings: Bool = UIPasteboard.general.hasStrings
|
|
||||||
@Published var networkInfo = UserNetworkInfo(networkType: .other, online: true)
|
@Published var networkInfo = UserNetworkInfo(networkType: .other, online: true)
|
||||||
|
|
||||||
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
|
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
|
||||||
@@ -357,25 +364,8 @@ final class ChatModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func updateChats(with newChats: [ChatData]) {
|
func updateChats(_ newChats: [ChatData]) {
|
||||||
for i in 0..<newChats.count {
|
chats = newChats.map { Chat($0) }
|
||||||
let c = newChats[i]
|
|
||||||
if let j = getChatIndex(c.id) {
|
|
||||||
let chat = chats[j]
|
|
||||||
chat.chatInfo = c.chatInfo
|
|
||||||
chat.chatItems = c.chatItems
|
|
||||||
chat.chatStats = c.chatStats
|
|
||||||
if i != j {
|
|
||||||
if chatId != c.chatInfo.id {
|
|
||||||
popChat_(j, to: i)
|
|
||||||
} else if i == 0 {
|
|
||||||
chatToTop = c.chatInfo.id
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
addChat_(Chat(c), at: i)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
NtfManager.shared.setNtfBadgeCount(totalUnreadCountForAllUsers())
|
NtfManager.shared.setNtfBadgeCount(totalUnreadCountForAllUsers())
|
||||||
popChatCollector.clear()
|
popChatCollector.clear()
|
||||||
}
|
}
|
||||||
@@ -445,19 +435,17 @@ final class ChatModel: ObservableObject {
|
|||||||
|
|
||||||
private func _upsertChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool {
|
private func _upsertChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) -> Bool {
|
||||||
if let i = getChatItemIndex(cItem) {
|
if let i = getChatItemIndex(cItem) {
|
||||||
withConditionalAnimation {
|
_updateChatItem(at: i, with: cItem)
|
||||||
_updateChatItem(at: i, with: cItem)
|
ChatItemDummyModel.shared.sendUpdate()
|
||||||
}
|
|
||||||
return false
|
return false
|
||||||
} else {
|
} else {
|
||||||
withConditionalAnimation(itemAnimation()) {
|
var ci = cItem
|
||||||
var ci = cItem
|
if let status = chatItemStatuses.removeValue(forKey: ci.id), case .sndNew = ci.meta.itemStatus {
|
||||||
if let status = chatItemStatuses.removeValue(forKey: ci.id), case .sndNew = ci.meta.itemStatus {
|
ci.meta.itemStatus = status
|
||||||
ci.meta.itemStatus = status
|
|
||||||
}
|
|
||||||
im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0)
|
|
||||||
im.itemAdded = true
|
|
||||||
}
|
}
|
||||||
|
im.reversedChatItems.insert(ci, at: hasLiveDummy ? 1 : 0)
|
||||||
|
im.itemAdded = true
|
||||||
|
ChatItemDummyModel.shared.sendUpdate()
|
||||||
return true
|
return true
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -574,6 +562,7 @@ final class ChatModel: ObservableObject {
|
|||||||
// update preview
|
// update preview
|
||||||
_updateChat(cInfo.id) { chat in
|
_updateChat(cInfo.id) { chat in
|
||||||
self.decreaseUnreadCounter(user: self.currentUser!, by: chat.chatStats.unreadCount)
|
self.decreaseUnreadCounter(user: self.currentUser!, by: chat.chatStats.unreadCount)
|
||||||
|
self.updateFloatingButtons(unreadCount: 0)
|
||||||
chat.chatStats = ChatStats()
|
chat.chatStats = ChatStats()
|
||||||
}
|
}
|
||||||
// update current chat
|
// update current chat
|
||||||
@@ -590,6 +579,12 @@ final class ChatModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func updateFloatingButtons(unreadCount: Int) {
|
||||||
|
let fbm = ChatView.FloatingButtonModel.shared
|
||||||
|
fbm.totalUnread = unreadCount
|
||||||
|
fbm.objectWillChange.send()
|
||||||
|
}
|
||||||
|
|
||||||
func markChatItemsRead(_ cInfo: ChatInfo, aboveItem: ChatItem? = nil) {
|
func markChatItemsRead(_ cInfo: ChatInfo, aboveItem: ChatItem? = nil) {
|
||||||
if let cItem = aboveItem {
|
if let cItem = aboveItem {
|
||||||
if chatId == cInfo.id, let i = getChatItemIndex(cItem) {
|
if chatId == cInfo.id, let i = getChatItemIndex(cItem) {
|
||||||
@@ -608,6 +603,7 @@ final class ChatModel: ObservableObject {
|
|||||||
if markedCount > 0 {
|
if markedCount > 0 {
|
||||||
chat.chatStats.unreadCount -= markedCount
|
chat.chatStats.unreadCount -= markedCount
|
||||||
self.decreaseUnreadCounter(user: self.currentUser!, by: markedCount)
|
self.decreaseUnreadCounter(user: self.currentUser!, by: markedCount)
|
||||||
|
self.updateFloatingButtons(unreadCount: chat.chatStats.unreadCount)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -637,19 +633,15 @@ final class ChatModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func markChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async {
|
func markChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID]) {
|
||||||
if chatId == cInfo.id,
|
if self.chatId == cInfo.id {
|
||||||
let itemIndex = getChatItemIndex(cItem),
|
for itemId in itemIds {
|
||||||
im.reversedChatItems[itemIndex].isRcvNew {
|
if let i = im.reversedChatItems.firstIndex(where: { $0.id == itemId }) {
|
||||||
await MainActor.run {
|
markChatItemRead_(i)
|
||||||
withTransaction(Transaction()) {
|
|
||||||
// update current chat
|
|
||||||
markChatItemRead_(itemIndex)
|
|
||||||
// update preview
|
|
||||||
unreadCollector.changeUnreadCounter(cInfo.id, by: -1)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
self.unreadCollector.changeUnreadCounter(cInfo.id, by: -itemIds.count)
|
||||||
}
|
}
|
||||||
|
|
||||||
private let unreadCollector = UnreadCollector()
|
private let unreadCollector = UnreadCollector()
|
||||||
@@ -675,9 +667,10 @@ final class ChatModel: ObservableObject {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func changeUnreadCounter(_ chatId: ChatId, by count: Int) {
|
func changeUnreadCounter(_ chatId: ChatId, by count: Int) {
|
||||||
DispatchQueue.main.async {
|
if chatId == ChatModel.shared.chatId {
|
||||||
self.unreadCounts[chatId] = (self.unreadCounts[chatId] ?? 0) + count
|
ChatView.FloatingButtonModel.shared.totalUnread += count
|
||||||
}
|
}
|
||||||
|
self.unreadCounts[chatId] = (self.unreadCounts[chatId] ?? 0) + count
|
||||||
subject.send()
|
subject.send()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -899,35 +892,6 @@ final class ChatModel: ObservableObject {
|
|||||||
_ = upsertGroupMember(groupInfo, updatedMember)
|
_ = upsertGroupMember(groupInfo, updatedMember)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func unreadChatItemCounts(itemsInView: Set<String>) -> UnreadChatItemCounts {
|
|
||||||
var i = 0
|
|
||||||
var totalBelow = 0
|
|
||||||
var unreadBelow = 0
|
|
||||||
while i < im.reversedChatItems.count - 1 && !itemsInView.contains(im.reversedChatItems[i].viewId) {
|
|
||||||
totalBelow += 1
|
|
||||||
if im.reversedChatItems[i].isRcvNew {
|
|
||||||
unreadBelow += 1
|
|
||||||
}
|
|
||||||
i += 1
|
|
||||||
}
|
|
||||||
return UnreadChatItemCounts(
|
|
||||||
// TODO these thresholds account for the fact that items are still "visible" while
|
|
||||||
// covered by compose area, they should be replaced with the actual height in pixels below the screen.
|
|
||||||
isNearBottom: totalBelow < 15,
|
|
||||||
isReallyNearBottom: totalBelow < 2,
|
|
||||||
unreadBelow: unreadBelow
|
|
||||||
)
|
|
||||||
}
|
|
||||||
|
|
||||||
func topItemInView(itemsInView: Set<String>) -> ChatItem? {
|
|
||||||
let maxIx = im.reversedChatItems.count - 1
|
|
||||||
var i = 0
|
|
||||||
let inView = { itemsInView.contains(self.im.reversedChatItems[$0].viewId) }
|
|
||||||
while i < maxIx && !inView(i) { i += 1 }
|
|
||||||
while i < maxIx && inView(i) { i += 1 }
|
|
||||||
return im.reversedChatItems[min(i - 1, maxIx)]
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ShowingInvitation {
|
struct ShowingInvitation {
|
||||||
@@ -940,12 +904,6 @@ struct NTFContactRequest {
|
|||||||
var chatId: String
|
var chatId: String
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UnreadChatItemCounts: Equatable {
|
|
||||||
var isNearBottom: Bool
|
|
||||||
var isReallyNearBottom: Bool
|
|
||||||
var unreadBelow: Int
|
|
||||||
}
|
|
||||||
|
|
||||||
final class Chat: ObservableObject, Identifiable, ChatLike {
|
final class Chat: ObservableObject, Identifiable, ChatLike {
|
||||||
@Published var chatInfo: ChatInfo
|
@Published var chatInfo: ChatInfo
|
||||||
@Published var chatItems: [ChatItem]
|
@Published var chatItems: [ChatItem]
|
||||||
|
|||||||
@@ -112,9 +112,9 @@ func chatSendCmdSync(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? =
|
|||||||
return resp
|
return resp
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatSendCmd(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, _ ctrl: chat_ctrl? = nil) async -> ChatResponse {
|
func chatSendCmd(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? = nil, _ ctrl: chat_ctrl? = nil, log: Bool = true) async -> ChatResponse {
|
||||||
await withCheckedContinuation { cont in
|
await withCheckedContinuation { cont in
|
||||||
cont.resume(returning: chatSendCmdSync(cmd, bgTask: bgTask, bgDelay: bgDelay, ctrl))
|
cont.resume(returning: chatSendCmdSync(cmd, bgTask: bgTask, bgDelay: bgDelay, ctrl, log: log))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -357,17 +357,17 @@ func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws -
|
|||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64, ttl: Int?) async -> ChatItem? {
|
func apiForwardChatItems(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemIds: [Int64], ttl: Int?) async -> [ChatItem]? {
|
||||||
let cmd: ChatCommand = .apiForwardChatItem(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemId: itemId, ttl: ttl)
|
let cmd: ChatCommand = .apiForwardChatItems(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemIds: itemIds, ttl: ttl)
|
||||||
return await processSendMessageCmd(toChatType: toChatType, cmd: cmd)
|
return await processSendMessageCmd(toChatType: toChatType, cmd: cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? {
|
func apiSendMessages(type: ChatType, id: Int64, live: Bool = false, ttl: Int? = nil, composedMessages: [ComposedMessage]) async -> [ChatItem]? {
|
||||||
let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl)
|
let cmd: ChatCommand = .apiSendMessages(type: type, id: id, live: live, ttl: ttl, composedMessages: composedMessages)
|
||||||
return await processSendMessageCmd(toChatType: type, cmd: cmd)
|
return await processSendMessageCmd(toChatType: type, cmd: cmd)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async -> ChatItem? {
|
private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async -> [ChatItem]? {
|
||||||
let chatModel = ChatModel.shared
|
let chatModel = ChatModel.shared
|
||||||
let r: ChatResponse
|
let r: ChatResponse
|
||||||
if toChatType == .direct {
|
if toChatType == .direct {
|
||||||
@@ -380,10 +380,13 @@ private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async
|
|||||||
}
|
}
|
||||||
})
|
})
|
||||||
r = await chatSendCmd(cmd, bgTask: false)
|
r = await chatSendCmd(cmd, bgTask: false)
|
||||||
if case let .newChatItem(_, aChatItem) = r {
|
if case let .newChatItems(_, aChatItems) = r {
|
||||||
cItem = aChatItem.chatItem
|
let cItems = aChatItems.map { $0.chatItem }
|
||||||
chatModel.messageDelivery[aChatItem.chatItem.id] = endTask
|
if let cItemLast = cItems.last {
|
||||||
return cItem
|
cItem = cItemLast
|
||||||
|
chatModel.messageDelivery[cItemLast.id] = endTask
|
||||||
|
}
|
||||||
|
return cItems
|
||||||
}
|
}
|
||||||
if let networkErrorAlert = networkErrorAlert(r) {
|
if let networkErrorAlert = networkErrorAlert(r) {
|
||||||
AlertManager.shared.showAlert(networkErrorAlert)
|
AlertManager.shared.showAlert(networkErrorAlert)
|
||||||
@@ -394,18 +397,18 @@ private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async
|
|||||||
return nil
|
return nil
|
||||||
} else {
|
} else {
|
||||||
r = await chatSendCmd(cmd, bgDelay: msgDelay)
|
r = await chatSendCmd(cmd, bgDelay: msgDelay)
|
||||||
if case let .newChatItem(_, aChatItem) = r {
|
if case let .newChatItems(_, aChatItems) = r {
|
||||||
return aChatItem.chatItem
|
return aChatItems.map { $0.chatItem }
|
||||||
}
|
}
|
||||||
sendMessageErrorAlert(r)
|
sendMessageErrorAlert(r)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent) async -> ChatItem? {
|
func apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage]) async -> [ChatItem]? {
|
||||||
let r = await chatSendCmd(.apiCreateChatItem(noteFolderId: noteFolderId, file: file, msg: msg))
|
let r = await chatSendCmd(.apiCreateChatItems(noteFolderId: noteFolderId, composedMessages: composedMessages))
|
||||||
if case let .newChatItem(_, aChatItem) = r { return aChatItem.chatItem }
|
if case let .newChatItems(_, aChatItems) = r { return aChatItems.map { $0.chatItem } }
|
||||||
createChatItemErrorAlert(r)
|
createChatItemsErrorAlert(r)
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -417,8 +420,8 @@ private func sendMessageErrorAlert(_ r: ChatResponse) {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func createChatItemErrorAlert(_ r: ChatResponse) {
|
private func createChatItemsErrorAlert(_ r: ChatResponse) {
|
||||||
logger.error("apiCreateChatItem error: \(String(describing: r))")
|
logger.error("apiCreateChatItems error: \(String(describing: r))")
|
||||||
AlertManager.shared.showAlertMsg(
|
AlertManager.shared.showAlertMsg(
|
||||||
title: "Error creating message",
|
title: "Error creating message",
|
||||||
message: "Error: \(responseError(r))"
|
message: "Error: \(responseError(r))"
|
||||||
@@ -582,13 +585,13 @@ func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) throws -> (Gro
|
|||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiContactQueueInfo(_ contactId: Int64) async throws -> (RcvMsgInfo?, QueueInfo) {
|
func apiContactQueueInfo(_ contactId: Int64) async throws -> (RcvMsgInfo?, ServerQueueInfo) {
|
||||||
let r = await chatSendCmd(.apiContactQueueInfo(contactId: contactId))
|
let r = await chatSendCmd(.apiContactQueueInfo(contactId: contactId))
|
||||||
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
|
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiGroupMemberQueueInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (RcvMsgInfo?, QueueInfo) {
|
func apiGroupMemberQueueInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (RcvMsgInfo?, ServerQueueInfo) {
|
||||||
let r = await chatSendCmd(.apiGroupMemberQueueInfo(groupId: groupId, groupMemberId: groupMemberId))
|
let r = await chatSendCmd(.apiGroupMemberQueueInfo(groupId: groupId, groupMemberId: groupMemberId))
|
||||||
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
|
if case let .queueInfo(_, rcvMsgInfo, queueInfo) = r { return (rcvMsgInfo, queueInfo) }
|
||||||
throw r
|
throw r
|
||||||
@@ -673,6 +676,13 @@ func apiSetConnectionIncognito(connId: Int64, incognito: Bool) async throws -> P
|
|||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiChangeConnectionUser(connId: Int64, userId: Int64) async throws -> PendingContactConnection? {
|
||||||
|
let r = await chatSendCmd(.apiChangeConnectionUser(connId: connId, userId: userId))
|
||||||
|
|
||||||
|
if case let .connectionUserChanged(_, _, toConnection, _) = r {return toConnection}
|
||||||
|
throw r
|
||||||
|
}
|
||||||
|
|
||||||
func apiConnectPlan(connReq: String) async throws -> ConnectionPlan {
|
func apiConnectPlan(connReq: String) async throws -> ConnectionPlan {
|
||||||
let userId = try currentUserId("apiConnectPlan")
|
let userId = try currentUserId("apiConnectPlan")
|
||||||
let r = await chatSendCmd(.apiConnectPlan(userId: userId, connReq: connReq))
|
let r = await chatSendCmd(.apiConnectPlan(userId: userId, connReq: connReq))
|
||||||
@@ -990,6 +1000,10 @@ func apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64)) async thr
|
|||||||
try await sendCommandOkResp(.apiChatRead(type: type, id: id, itemRange: itemRange))
|
try await sendCommandOkResp(.apiChatRead(type: type, id: id, itemRange: itemRange))
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64]) async throws {
|
||||||
|
try await sendCommandOkResp(.apiChatItemsRead(type: type, id: id, itemIds: itemIds))
|
||||||
|
}
|
||||||
|
|
||||||
func apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) async throws {
|
func apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool) async throws {
|
||||||
try await sendCommandOkResp(.apiChatUnread(type: type, id: id, unreadChat: unreadChat))
|
try await sendCommandOkResp(.apiChatUnread(type: type, id: id, unreadChat: unreadChat))
|
||||||
}
|
}
|
||||||
@@ -1218,12 +1232,18 @@ func apiEndCall(_ contact: Contact) async throws {
|
|||||||
try await sendCommandOkResp(.apiEndCall(contact: contact))
|
try await sendCommandOkResp(.apiEndCall(contact: contact))
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiGetCallInvitations() throws -> [RcvCallInvitation] {
|
func apiGetCallInvitationsSync() throws -> [RcvCallInvitation] {
|
||||||
let r = chatSendCmdSync(.apiGetCallInvitations)
|
let r = chatSendCmdSync(.apiGetCallInvitations)
|
||||||
if case let .callInvitations(invs) = r { return invs }
|
if case let .callInvitations(invs) = r { return invs }
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func apiGetCallInvitations() async throws -> [RcvCallInvitation] {
|
||||||
|
let r = await chatSendCmd(.apiGetCallInvitations)
|
||||||
|
if case let .callInvitations(invs) = r { return invs }
|
||||||
|
throw r
|
||||||
|
}
|
||||||
|
|
||||||
func apiCallStatus(_ contact: Contact, _ status: String) async throws {
|
func apiCallStatus(_ contact: Contact, _ status: String) async throws {
|
||||||
if let callStatus = WebRTCCallStatus.init(rawValue: status) {
|
if let callStatus = WebRTCCallStatus.init(rawValue: status) {
|
||||||
try await sendCommandOkResp(.apiCallStatus(contact: contact, callStatus: callStatus))
|
try await sendCommandOkResp(.apiCallStatus(contact: contact, callStatus: callStatus))
|
||||||
@@ -1271,11 +1291,23 @@ func markChatUnread(_ chat: Chat, unreadChat: Bool = true) async {
|
|||||||
|
|
||||||
func apiMarkChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async {
|
func apiMarkChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async {
|
||||||
do {
|
do {
|
||||||
logger.debug("apiMarkChatItemRead: \(cItem.id)")
|
|
||||||
try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, itemRange: (cItem.id, cItem.id))
|
try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, itemRange: (cItem.id, cItem.id))
|
||||||
await ChatModel.shared.markChatItemRead(cInfo, cItem)
|
DispatchQueue.main.async {
|
||||||
|
ChatModel.shared.markChatItemsRead(cInfo, [cItem.id])
|
||||||
|
}
|
||||||
} catch {
|
} catch {
|
||||||
logger.error("apiMarkChatItemRead apiChatRead error: \(responseError(error))")
|
logger.error("apiChatRead error: \(responseError(error))")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func apiMarkChatItemsRead(_ cInfo: ChatInfo, _ itemIds: [ChatItem.ID]) async {
|
||||||
|
do {
|
||||||
|
try await apiChatItemsRead(type: cInfo.chatType, id: cInfo.apiId, itemIds: itemIds)
|
||||||
|
DispatchQueue.main.async {
|
||||||
|
ChatModel.shared.markChatItemsRead(cInfo, itemIds)
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
logger.error("apiChatItemsRead error: \(responseError(error))")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -1422,7 +1454,7 @@ func apiGetVersion() throws -> CoreVersionInfo {
|
|||||||
|
|
||||||
func getAgentSubsTotal() async throws -> (SMPServerSubs, Bool) {
|
func getAgentSubsTotal() async throws -> (SMPServerSubs, Bool) {
|
||||||
let userId = try currentUserId("getAgentSubsTotal")
|
let userId = try currentUserId("getAgentSubsTotal")
|
||||||
let r = await chatSendCmd(.getAgentSubsTotal(userId: userId))
|
let r = await chatSendCmd(.getAgentSubsTotal(userId: userId), log: false)
|
||||||
if case let .agentSubsTotal(_, subsTotal, hasSession) = r { return (subsTotal, hasSession) }
|
if case let .agentSubsTotal(_, subsTotal, hasSession) = r { return (subsTotal, hasSession) }
|
||||||
logger.error("getAgentSubsTotal error: \(String(describing: r))")
|
logger.error("getAgentSubsTotal error: \(String(describing: r))")
|
||||||
throw r
|
throw r
|
||||||
@@ -1517,7 +1549,7 @@ func startChat(refreshInvitations: Bool = true) throws {
|
|||||||
try getUserChatData()
|
try getUserChatData()
|
||||||
NtfManager.shared.setNtfBadgeCount(m.totalUnreadCountForAllUsers())
|
NtfManager.shared.setNtfBadgeCount(m.totalUnreadCountForAllUsers())
|
||||||
if (refreshInvitations) {
|
if (refreshInvitations) {
|
||||||
try refreshCallInvitations()
|
Task { try await refreshCallInvitations() }
|
||||||
}
|
}
|
||||||
(m.savedToken, m.tokenStatus, m.notificationMode, m.notificationServer) = apiGetNtfToken()
|
(m.savedToken, m.tokenStatus, m.notificationMode, m.notificationServer) = apiGetNtfToken()
|
||||||
_ = try apiStartChat()
|
_ = try apiStartChat()
|
||||||
@@ -1591,8 +1623,7 @@ func getUserChatData() throws {
|
|||||||
m.userAddress = try apiGetUserAddress()
|
m.userAddress = try apiGetUserAddress()
|
||||||
m.chatItemTTL = try getChatItemTTL()
|
m.chatItemTTL = try getChatItemTTL()
|
||||||
let chats = try apiGetChats()
|
let chats = try apiGetChats()
|
||||||
m.chats = chats.map { Chat.init($0) }
|
m.updateChats(chats)
|
||||||
m.popChatCollector.clear()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getUserChatDataAsync() async throws {
|
private func getUserChatDataAsync() async throws {
|
||||||
@@ -1604,14 +1635,12 @@ private func getUserChatDataAsync() async throws {
|
|||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
m.userAddress = userAddress
|
m.userAddress = userAddress
|
||||||
m.chatItemTTL = chatItemTTL
|
m.chatItemTTL = chatItemTTL
|
||||||
m.chats = chats.map { Chat.init($0) }
|
m.updateChats(chats)
|
||||||
m.popChatCollector.clear()
|
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
m.userAddress = nil
|
m.userAddress = nil
|
||||||
m.chats = []
|
m.updateChats([])
|
||||||
m.popChatCollector.clear()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -1772,23 +1801,25 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
|||||||
n.networkStatuses = ns
|
n.networkStatuses = ns
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .newChatItem(user, aChatItem):
|
case let .newChatItems(user, chatItems):
|
||||||
let cInfo = aChatItem.chatInfo
|
for chatItem in chatItems {
|
||||||
let cItem = aChatItem.chatItem
|
let cInfo = chatItem.chatInfo
|
||||||
await MainActor.run {
|
let cItem = chatItem.chatItem
|
||||||
if active(user) {
|
await MainActor.run {
|
||||||
m.addChatItem(cInfo, cItem)
|
if active(user) {
|
||||||
} else if cItem.isRcvNew && cInfo.ntfsEnabled {
|
m.addChatItem(cInfo, cItem)
|
||||||
m.increaseUnreadCounter(user: user)
|
} else if cItem.isRcvNew && cInfo.ntfsEnabled {
|
||||||
|
m.increaseUnreadCounter(user: user)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
if let file = cItem.autoReceiveFile() {
|
||||||
if let file = cItem.autoReceiveFile() {
|
Task {
|
||||||
Task {
|
await receiveFile(user: user, fileId: file.fileId, auto: true)
|
||||||
await receiveFile(user: user, fileId: file.fileId, auto: true)
|
}
|
||||||
|
}
|
||||||
|
if cItem.showNotification {
|
||||||
|
NtfManager.shared.notifyMessageReceived(user, cInfo, cItem)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
if cItem.showNotification {
|
|
||||||
NtfManager.shared.notifyMessageReceived(user, cInfo, cItem)
|
|
||||||
}
|
}
|
||||||
case let .chatItemStatusUpdated(user, aChatItem):
|
case let .chatItemStatusUpdated(user, aChatItem):
|
||||||
let cInfo = aChatItem.chatInfo
|
let cInfo = aChatItem.chatInfo
|
||||||
@@ -1798,10 +1829,15 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
|||||||
}
|
}
|
||||||
if let endTask = m.messageDelivery[cItem.id] {
|
if let endTask = m.messageDelivery[cItem.id] {
|
||||||
switch cItem.meta.itemStatus {
|
switch cItem.meta.itemStatus {
|
||||||
|
case .sndNew: ()
|
||||||
case .sndSent: endTask()
|
case .sndSent: endTask()
|
||||||
|
case .sndRcvd: endTask()
|
||||||
case .sndErrorAuth: endTask()
|
case .sndErrorAuth: endTask()
|
||||||
case .sndError: endTask()
|
case .sndError: endTask()
|
||||||
default: ()
|
case .sndWarning: endTask()
|
||||||
|
case .rcvNew: ()
|
||||||
|
case .rcvRead: ()
|
||||||
|
case .invalid: ()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
case let .chatItemUpdated(user, aChatItem):
|
case let .chatItemUpdated(user, aChatItem):
|
||||||
@@ -2164,32 +2200,42 @@ func chatItemSimpleUpdate(_ user: any UserLike, _ aChatItem: AChatItem) async {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func refreshCallInvitations() throws {
|
func refreshCallInvitations() async throws {
|
||||||
let m = ChatModel.shared
|
let m = ChatModel.shared
|
||||||
let callInvitations = try justRefreshCallInvitations()
|
let callInvitations = try await apiGetCallInvitations()
|
||||||
if let (chatId, ntfAction) = m.ntfCallInvitationAction,
|
await MainActor.run {
|
||||||
let invitation = m.callInvitations.removeValue(forKey: chatId) {
|
m.callInvitations = callsByChat(callInvitations)
|
||||||
m.ntfCallInvitationAction = nil
|
if let (chatId, ntfAction) = m.ntfCallInvitationAction,
|
||||||
CallController.shared.callAction(invitation: invitation, action: ntfAction)
|
let invitation = m.callInvitations.removeValue(forKey: chatId) {
|
||||||
} else if let invitation = callInvitations.last(where: { $0.user.showNotifications }) {
|
m.ntfCallInvitationAction = nil
|
||||||
activateCall(invitation)
|
CallController.shared.callAction(invitation: invitation, action: ntfAction)
|
||||||
|
} else if let invitation = callInvitations.last(where: { $0.user.showNotifications }) {
|
||||||
|
activateCall(invitation)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func justRefreshCallInvitations() throws -> [RcvCallInvitation] {
|
func justRefreshCallInvitations() async throws {
|
||||||
let m = ChatModel.shared
|
let callInvitations = try apiGetCallInvitationsSync()
|
||||||
let callInvitations = try apiGetCallInvitations()
|
await MainActor.run {
|
||||||
m.callInvitations = callInvitations.reduce(into: [ChatId: RcvCallInvitation]()) { result, inv in result[inv.contact.id] = inv }
|
ChatModel.shared.callInvitations = callsByChat(callInvitations)
|
||||||
return callInvitations
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func callsByChat(_ callInvitations: [RcvCallInvitation]) -> [ChatId: RcvCallInvitation] {
|
||||||
|
callInvitations.reduce(into: [ChatId: RcvCallInvitation]()) {
|
||||||
|
result, inv in result[inv.contact.id] = inv
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func activateCall(_ callInvitation: RcvCallInvitation) {
|
func activateCall(_ callInvitation: RcvCallInvitation) {
|
||||||
if !callInvitation.user.showNotifications { return }
|
|
||||||
let m = ChatModel.shared
|
let m = ChatModel.shared
|
||||||
|
logger.debug("reportNewIncomingCall activeCallUUID \(String(describing: m.activeCall?.callUUID)) invitationUUID \(String(describing: callInvitation.callUUID))")
|
||||||
|
if !callInvitation.user.showNotifications || m.activeCall?.callUUID == callInvitation.callUUID { return }
|
||||||
CallController.shared.reportNewIncomingCall(invitation: callInvitation) { error in
|
CallController.shared.reportNewIncomingCall(invitation: callInvitation) { error in
|
||||||
if let error = error {
|
if let error = error {
|
||||||
DispatchQueue.main.async {
|
DispatchQueue.main.async {
|
||||||
m.callInvitations[callInvitation.contact.id]?.callkitUUID = nil
|
m.callInvitations[callInvitation.contact.id]?.callUUID = nil
|
||||||
}
|
}
|
||||||
logger.error("reportNewIncomingCall error: \(error.localizedDescription)")
|
logger.error("reportNewIncomingCall error: \(error.localizedDescription)")
|
||||||
} else {
|
} else {
|
||||||
|
|||||||
@@ -83,9 +83,11 @@ struct SimpleXApp: App {
|
|||||||
if appState != .stopped {
|
if appState != .stopped {
|
||||||
startChatAndActivate {
|
startChatAndActivate {
|
||||||
if appState.inactive && chatModel.chatRunning == true {
|
if appState.inactive && chatModel.chatRunning == true {
|
||||||
updateChats()
|
Task {
|
||||||
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
|
await updateChats()
|
||||||
updateCallInvitations()
|
if !chatModel.showCallView && !CallController.shared.hasActiveCalls() {
|
||||||
|
await updateCallInvitations()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -130,16 +132,16 @@ struct SimpleXApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateChats() {
|
private func updateChats() async {
|
||||||
do {
|
do {
|
||||||
let chats = try apiGetChats()
|
let chats = try await apiGetChatsAsync()
|
||||||
chatModel.updateChats(with: chats)
|
await MainActor.run { chatModel.updateChats(chats) }
|
||||||
if let id = chatModel.chatId,
|
if let id = chatModel.chatId,
|
||||||
let chat = chatModel.getChat(id) {
|
let chat = chatModel.getChat(id) {
|
||||||
Task { await loadChat(chat: chat, clearItems: false) }
|
Task { await loadChat(chat: chat, clearItems: false) }
|
||||||
}
|
}
|
||||||
if let ncr = chatModel.ntfContactRequest {
|
if let ncr = chatModel.ntfContactRequest {
|
||||||
chatModel.ntfContactRequest = nil
|
await MainActor.run { chatModel.ntfContactRequest = nil }
|
||||||
if case let .contactRequest(contactRequest) = chatModel.getChat(ncr.chatId)?.chatInfo {
|
if case let .contactRequest(contactRequest) = chatModel.getChat(ncr.chatId)?.chatInfo {
|
||||||
Task { await acceptContactRequest(incognito: ncr.incognito, contactRequest: contactRequest) }
|
Task { await acceptContactRequest(incognito: ncr.incognito, contactRequest: contactRequest) }
|
||||||
}
|
}
|
||||||
@@ -149,9 +151,9 @@ struct SimpleXApp: App {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func updateCallInvitations() {
|
private func updateCallInvitations() async {
|
||||||
do {
|
do {
|
||||||
try refreshCallInvitations()
|
try await refreshCallInvitations()
|
||||||
} catch let error {
|
} catch let error {
|
||||||
logger.error("apiGetCallInvitations: cannot update call invitations \(responseError(error))")
|
logger.error("apiGetCallInvitations: cannot update call invitations \(responseError(error))")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -91,8 +91,8 @@ var systemInDarkThemeCurrently: Bool {
|
|||||||
return UITraitCollection.current.userInterfaceStyle == .dark
|
return UITraitCollection.current.userInterfaceStyle == .dark
|
||||||
}
|
}
|
||||||
|
|
||||||
func reactOnDarkThemeChanges() {
|
func reactOnDarkThemeChanges(_ inDarkNow: Bool) {
|
||||||
if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == systemInDarkThemeCurrently {
|
if currentThemeDefault.get() == DefaultTheme.SYSTEM_THEME_NAME && CurrentColors.colors.isLight == inDarkNow {
|
||||||
// Change active colors from light to dark and back based on system theme
|
// Change active colors from light to dark and back based on system theme
|
||||||
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
|
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
|
||||||
}
|
}
|
||||||
@@ -102,7 +102,7 @@ extension ThemeWallpaper {
|
|||||||
public func importFromString() -> ThemeWallpaper {
|
public func importFromString() -> ThemeWallpaper {
|
||||||
if preset == nil, let image {
|
if preset == nil, let image {
|
||||||
// Need to save image from string and to save its path
|
// Need to save image from string and to save its path
|
||||||
if let parsed = UIImage(base64Encoded: image),
|
if let parsed = imageFromBase64(image),
|
||||||
let filename = saveWallpaperFile(image: parsed) {
|
let filename = saveWallpaperFile(image: parsed) {
|
||||||
var copy = self
|
var copy = self
|
||||||
copy.image = nil
|
copy.image = nil
|
||||||
|
|||||||
@@ -53,7 +53,7 @@ class ThemeManager {
|
|||||||
return perUserTheme
|
return perUserTheme
|
||||||
}
|
}
|
||||||
let defaultTheme = defaultActiveTheme(appSettingsTheme)
|
let defaultTheme = defaultActiveTheme(appSettingsTheme)
|
||||||
return ThemeModeOverride(mode: CurrentColors.base.mode, colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper)
|
return ThemeModeOverride(mode: CurrentColors.base.mode, colors: defaultTheme?.colors ?? ThemeColors(), wallpaper: defaultTheme?.wallpaper ?? ThemeWallpaper.from(PresetWallpaper.school.toType(CurrentColors.base), nil, nil))
|
||||||
}
|
}
|
||||||
|
|
||||||
static func currentColors(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ActiveTheme {
|
static func currentColors(_ themeOverridesForType: WallpaperType?, _ perChatTheme: ThemeModeOverride?, _ perUserTheme: ThemeModeOverrides?, _ appSettingsTheme: [ThemeOverrides]) -> ActiveTheme {
|
||||||
|
|||||||
@@ -185,7 +185,7 @@ struct ActiveCallView: View {
|
|||||||
case .ended:
|
case .ended:
|
||||||
closeCallView(client)
|
closeCallView(client)
|
||||||
call.callState = .ended
|
call.callState = .ended
|
||||||
if let uuid = call.callkitUUID {
|
if let uuid = call.callUUID {
|
||||||
CallController.shared.endCall(callUUID: uuid)
|
CallController.shared.endCall(callUUID: uuid)
|
||||||
}
|
}
|
||||||
case .ok:
|
case .ok:
|
||||||
@@ -382,7 +382,7 @@ struct ActiveCallOverlay: View {
|
|||||||
private func endCallButton() -> some View {
|
private func endCallButton() -> some View {
|
||||||
let cc = CallController.shared
|
let cc = CallController.shared
|
||||||
return callButton("phone.down.fill", width: 60, height: 60) {
|
return callButton("phone.down.fill", width: 60, height: 60) {
|
||||||
if let uuid = call.callkitUUID {
|
if let uuid = call.callUUID {
|
||||||
cc.endCall(callUUID: uuid)
|
cc.endCall(callUUID: uuid)
|
||||||
} else {
|
} else {
|
||||||
cc.endCall(call: call) {}
|
cc.endCall(call: call) {}
|
||||||
@@ -462,9 +462,9 @@ struct ActiveCallOverlay: View {
|
|||||||
struct ActiveCallOverlay_Previews: PreviewProvider {
|
struct ActiveCallOverlay_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
Group{
|
Group{
|
||||||
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callkitUUID: UUID(), callState: .offerSent, localMedia: .video), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
|
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, localMedia: .video), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
|
||||||
.background(.black)
|
.background(.black)
|
||||||
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callkitUUID: UUID(), callState: .offerSent, localMedia: .audio), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
|
ActiveCallOverlay(call: Call(direction: .incoming, contact: Contact.sampleData, callUUID: UUID().uuidString.lowercased(), callState: .offerSent, localMedia: .audio), client: WebRTCClient(Binding.constant(nil), { _ in }, Binding.constant(nil)))
|
||||||
.background(.black)
|
.background(.black)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -51,7 +51,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
|
|
||||||
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
func provider(_ provider: CXProvider, perform action: CXStartCallAction) {
|
||||||
logger.debug("CallController.provider CXStartCallAction")
|
logger.debug("CallController.provider CXStartCallAction")
|
||||||
if callManager.startOutgoingCall(callUUID: action.callUUID) {
|
if callManager.startOutgoingCall(callUUID: action.callUUID.uuidString.lowercased()) {
|
||||||
action.fulfill()
|
action.fulfill()
|
||||||
provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil)
|
provider.reportOutgoingCall(with: action.callUUID, startedConnectingAt: nil)
|
||||||
} else {
|
} else {
|
||||||
@@ -61,12 +61,30 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
|
|
||||||
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
func provider(_ provider: CXProvider, perform action: CXAnswerCallAction) {
|
||||||
logger.debug("CallController.provider CXAnswerCallAction")
|
logger.debug("CallController.provider CXAnswerCallAction")
|
||||||
if callManager.answerIncomingCall(callUUID: action.callUUID) {
|
Task {
|
||||||
// WebRTC call should be in connected state to fulfill.
|
let chatIsReady = await waitUntilChatStarted(timeoutMs: 30_000, stepMs: 500)
|
||||||
// Otherwise no audio and mic working on lockscreen
|
logger.debug("CallController chat started \(chatIsReady) \(ChatModel.shared.chatInitialized) \(ChatModel.shared.chatRunning == true) \(String(describing: AppChatState.shared.value))")
|
||||||
fulfillOnConnect = action
|
if !chatIsReady {
|
||||||
} else {
|
action.fail()
|
||||||
action.fail()
|
return
|
||||||
|
}
|
||||||
|
if !ChatModel.shared.callInvitations.values.contains(where: { inv in inv.callUUID == action.callUUID.uuidString.lowercased() }) {
|
||||||
|
try? await justRefreshCallInvitations()
|
||||||
|
logger.debug("CallController: updated call invitations chat")
|
||||||
|
}
|
||||||
|
await MainActor.run {
|
||||||
|
logger.debug("CallController.provider will answer on call")
|
||||||
|
|
||||||
|
if callManager.answerIncomingCall(callUUID: action.callUUID.uuidString.lowercased()) {
|
||||||
|
logger.debug("CallController.provider answered on call")
|
||||||
|
// WebRTC call should be in connected state to fulfill.
|
||||||
|
// Otherwise no audio and mic working on lockscreen
|
||||||
|
fulfillOnConnect = action
|
||||||
|
} else {
|
||||||
|
logger.debug("CallController.provider will fail the call")
|
||||||
|
action.fail()
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -75,7 +93,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
// Should be nil here if connection was in connected state
|
// Should be nil here if connection was in connected state
|
||||||
fulfillOnConnect?.fail()
|
fulfillOnConnect?.fail()
|
||||||
fulfillOnConnect = nil
|
fulfillOnConnect = nil
|
||||||
callManager.endCall(callUUID: action.callUUID) { ok in
|
callManager.endCall(callUUID: action.callUUID.uuidString.lowercased()) { ok in
|
||||||
if ok {
|
if ok {
|
||||||
action.fulfill()
|
action.fulfill()
|
||||||
} else {
|
} else {
|
||||||
@@ -86,7 +104,7 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
}
|
}
|
||||||
|
|
||||||
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
func provider(_ provider: CXProvider, perform action: CXSetMutedCallAction) {
|
||||||
if callManager.enableMedia(media: .audio, enable: !action.isMuted, callUUID: action.callUUID) {
|
if callManager.enableMedia(media: .audio, enable: !action.isMuted, callUUID: action.callUUID.uuidString.lowercased()) {
|
||||||
action.fulfill()
|
action.fulfill()
|
||||||
} else {
|
} else {
|
||||||
action.fail()
|
action.fail()
|
||||||
@@ -156,6 +174,19 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func waitUntilChatStarted(timeoutMs: UInt64, stepMs: UInt64) async -> Bool {
|
||||||
|
logger.debug("CallController waiting until chat started")
|
||||||
|
var t: UInt64 = 0
|
||||||
|
repeat {
|
||||||
|
if ChatModel.shared.chatInitialized, ChatModel.shared.chatRunning == true, case .active = AppChatState.shared.value {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
_ = try? await Task.sleep(nanoseconds: stepMs * 1000000)
|
||||||
|
t += stepMs
|
||||||
|
} while t < timeoutMs
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
@objc(pushRegistry:didUpdatePushCredentials:forType:)
|
@objc(pushRegistry:didUpdatePushCredentials:forType:)
|
||||||
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
|
func pushRegistry(_ registry: PKPushRegistry, didUpdate pushCredentials: PKPushCredentials, for type: PKPushType) {
|
||||||
logger.debug("CallController: didUpdate push credentials for type \(type.rawValue)")
|
logger.debug("CallController: didUpdate push credentials for type \(type.rawValue)")
|
||||||
@@ -171,32 +202,19 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
self.reportExpiredCall(payload: payload, completion)
|
self.reportExpiredCall(payload: payload, completion)
|
||||||
return
|
return
|
||||||
}
|
}
|
||||||
if (!ChatModel.shared.chatInitialized) {
|
|
||||||
logger.debug("CallController: initializing chat")
|
|
||||||
do {
|
|
||||||
try initializeChat(start: true, refreshInvitations: false)
|
|
||||||
} catch let error {
|
|
||||||
logger.error("CallController: initializing chat error: \(error)")
|
|
||||||
self.reportExpiredCall(payload: payload, completion)
|
|
||||||
return
|
|
||||||
}
|
|
||||||
}
|
|
||||||
logger.debug("CallController: initialized chat")
|
|
||||||
startChatForCall()
|
|
||||||
logger.debug("CallController: started chat")
|
|
||||||
self.shouldSuspendChat = true
|
|
||||||
// There are no invitations in the model, as it was processed by NSE
|
|
||||||
_ = try? justRefreshCallInvitations()
|
|
||||||
logger.debug("CallController: updated call invitations chat")
|
|
||||||
// logger.debug("CallController justRefreshCallInvitations: \(String(describing: m.callInvitations))")
|
|
||||||
// Extract the call information from the push notification payload
|
// Extract the call information from the push notification payload
|
||||||
let m = ChatModel.shared
|
let m = ChatModel.shared
|
||||||
if let contactId = payload.dictionaryPayload["contactId"] as? String,
|
if let contactId = payload.dictionaryPayload["contactId"] as? String,
|
||||||
let invitation = m.callInvitations[contactId] {
|
let displayName = payload.dictionaryPayload["displayName"] as? String,
|
||||||
let update = self.cxCallUpdate(invitation: invitation)
|
let callUUID = payload.dictionaryPayload["callUUID"] as? String,
|
||||||
if let uuid = invitation.callkitUUID {
|
let uuid = UUID(uuidString: callUUID),
|
||||||
|
let callTsInterval = payload.dictionaryPayload["callTs"] as? TimeInterval,
|
||||||
|
let mediaStr = payload.dictionaryPayload["media"] as? String,
|
||||||
|
let media = CallMediaType(rawValue: mediaStr) {
|
||||||
|
let update = self.cxCallUpdate(contactId, displayName, media)
|
||||||
|
let callTs = Date(timeIntervalSince1970: callTsInterval)
|
||||||
|
if callTs.timeIntervalSinceNow >= -180 {
|
||||||
logger.debug("CallController: report pushkit call via CallKit")
|
logger.debug("CallController: report pushkit call via CallKit")
|
||||||
let update = self.cxCallUpdate(invitation: invitation)
|
|
||||||
self.provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
self.provider.reportNewIncomingCall(with: uuid, update: update) { error in
|
||||||
if error != nil {
|
if error != nil {
|
||||||
m.callInvitations.removeValue(forKey: contactId)
|
m.callInvitations.removeValue(forKey: contactId)
|
||||||
@@ -205,11 +223,31 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
completion()
|
completion()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
logger.debug("CallController will expire call 1")
|
||||||
self.reportExpiredCall(update: update, completion)
|
self.reportExpiredCall(update: update, completion)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
|
logger.debug("CallController will expire call 2")
|
||||||
self.reportExpiredCall(payload: payload, completion)
|
self.reportExpiredCall(payload: payload, completion)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
//DispatchQueue.main.asyncAfter(deadline: .now() + 10) {
|
||||||
|
if (!ChatModel.shared.chatInitialized) {
|
||||||
|
logger.debug("CallController: initializing chat")
|
||||||
|
do {
|
||||||
|
try initializeChat(start: true, refreshInvitations: false)
|
||||||
|
} catch let error {
|
||||||
|
logger.error("CallController: initializing chat error: \(error)")
|
||||||
|
if let call = ChatModel.shared.activeCall {
|
||||||
|
self.endCall(call: call, completed: completion)
|
||||||
|
}
|
||||||
|
return
|
||||||
|
}
|
||||||
|
}
|
||||||
|
logger.debug("CallController: initialized chat")
|
||||||
|
startChatForCall()
|
||||||
|
logger.debug("CallController: started chat")
|
||||||
|
self.shouldSuspendChat = true
|
||||||
}
|
}
|
||||||
|
|
||||||
// This function fulfils the requirement to always report a call when PushKit notification is received,
|
// This function fulfils the requirement to always report a call when PushKit notification is received,
|
||||||
@@ -239,8 +277,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
}
|
}
|
||||||
|
|
||||||
func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) {
|
func reportNewIncomingCall(invitation: RcvCallInvitation, completion: @escaping (Error?) -> Void) {
|
||||||
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callkitUUID))")
|
logger.debug("CallController.reportNewIncomingCall, UUID=\(String(describing: invitation.callUUID))")
|
||||||
if CallController.useCallKit(), let uuid = invitation.callkitUUID {
|
if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) {
|
||||||
if invitation.callTs.timeIntervalSinceNow >= -180 {
|
if invitation.callTs.timeIntervalSinceNow >= -180 {
|
||||||
let update = cxCallUpdate(invitation: invitation)
|
let update = cxCallUpdate(invitation: invitation)
|
||||||
provider.reportNewIncomingCall(with: uuid, update: update, completion: completion)
|
provider.reportNewIncomingCall(with: uuid, update: update, completion: completion)
|
||||||
@@ -261,6 +299,14 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
return update
|
return update
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func cxCallUpdate(_ contactId: String, _ displayName: String, _ media: CallMediaType) -> CXCallUpdate {
|
||||||
|
let update = CXCallUpdate()
|
||||||
|
update.remoteHandle = CXHandle(type: .generic, value: contactId)
|
||||||
|
update.hasVideo = media == .video
|
||||||
|
update.localizedCallerName = displayName
|
||||||
|
return update
|
||||||
|
}
|
||||||
|
|
||||||
func reportIncomingCall(call: Call, connectedAt dateConnected: Date?) {
|
func reportIncomingCall(call: Call, connectedAt dateConnected: Date?) {
|
||||||
logger.debug("CallController: reporting incoming call connected")
|
logger.debug("CallController: reporting incoming call connected")
|
||||||
if CallController.useCallKit() {
|
if CallController.useCallKit() {
|
||||||
@@ -272,14 +318,14 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
|
|
||||||
func reportOutgoingCall(call: Call, connectedAt dateConnected: Date?) {
|
func reportOutgoingCall(call: Call, connectedAt dateConnected: Date?) {
|
||||||
logger.debug("CallController: reporting outgoing call connected")
|
logger.debug("CallController: reporting outgoing call connected")
|
||||||
if CallController.useCallKit(), let uuid = call.callkitUUID {
|
if CallController.useCallKit(), let callUUID = call.callUUID, let uuid = UUID(uuidString: callUUID) {
|
||||||
provider.reportOutgoingCall(with: uuid, connectedAt: dateConnected)
|
provider.reportOutgoingCall(with: uuid, connectedAt: dateConnected)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func reportCallRemoteEnded(invitation: RcvCallInvitation) {
|
func reportCallRemoteEnded(invitation: RcvCallInvitation) {
|
||||||
logger.debug("CallController: reporting remote ended")
|
logger.debug("CallController: reporting remote ended")
|
||||||
if CallController.useCallKit(), let uuid = invitation.callkitUUID {
|
if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) {
|
||||||
provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
|
provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
|
||||||
} else if invitation.contact.id == activeCallInvitation?.contact.id {
|
} else if invitation.contact.id == activeCallInvitation?.contact.id {
|
||||||
activeCallInvitation = nil
|
activeCallInvitation = nil
|
||||||
@@ -288,14 +334,17 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
|
|
||||||
func reportCallRemoteEnded(call: Call) {
|
func reportCallRemoteEnded(call: Call) {
|
||||||
logger.debug("CallController: reporting remote ended")
|
logger.debug("CallController: reporting remote ended")
|
||||||
if CallController.useCallKit(), let uuid = call.callkitUUID {
|
if CallController.useCallKit(), let callUUID = call.callUUID, let uuid = UUID(uuidString: callUUID) {
|
||||||
provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
|
provider.reportCall(with: uuid, endedAt: nil, reason: .remoteEnded)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func startCall(_ contact: Contact, _ media: CallMediaType) {
|
func startCall(_ contact: Contact, _ media: CallMediaType) {
|
||||||
logger.debug("CallController.startCall")
|
logger.debug("CallController.startCall")
|
||||||
let uuid = callManager.newOutgoingCall(contact, media)
|
let callUUID = callManager.newOutgoingCall(contact, media)
|
||||||
|
guard let uuid = UUID(uuidString: callUUID) else {
|
||||||
|
return
|
||||||
|
}
|
||||||
if CallController.useCallKit() {
|
if CallController.useCallKit() {
|
||||||
let handle = CXHandle(type: .generic, value: contact.id)
|
let handle = CXHandle(type: .generic, value: contact.id)
|
||||||
let action = CXStartCallAction(call: uuid, handle: handle)
|
let action = CXStartCallAction(call: uuid, handle: handle)
|
||||||
@@ -307,8 +356,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
update.localizedCallerName = contact.displayName
|
update.localizedCallerName = contact.displayName
|
||||||
self.provider.reportCall(with: uuid, updated: update)
|
self.provider.reportCall(with: uuid, updated: update)
|
||||||
}
|
}
|
||||||
} else if callManager.startOutgoingCall(callUUID: uuid) {
|
} else if callManager.startOutgoingCall(callUUID: callUUID) {
|
||||||
if callManager.startOutgoingCall(callUUID: uuid) {
|
if callManager.startOutgoingCall(callUUID: callUUID) {
|
||||||
logger.debug("CallController.startCall: call started")
|
logger.debug("CallController.startCall: call started")
|
||||||
} else {
|
} else {
|
||||||
logger.error("CallController.startCall: no active call")
|
logger.error("CallController.startCall: no active call")
|
||||||
@@ -318,8 +367,8 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
|
|
||||||
func answerCall(invitation: RcvCallInvitation) {
|
func answerCall(invitation: RcvCallInvitation) {
|
||||||
logger.debug("CallController: answering a call")
|
logger.debug("CallController: answering a call")
|
||||||
if CallController.useCallKit(), let callUUID = invitation.callkitUUID {
|
if CallController.useCallKit(), let callUUID = invitation.callUUID, let uuid = UUID(uuidString: callUUID) {
|
||||||
requestTransaction(with: CXAnswerCallAction(call: callUUID))
|
requestTransaction(with: CXAnswerCallAction(call: uuid))
|
||||||
} else {
|
} else {
|
||||||
callManager.answerIncomingCall(invitation: invitation)
|
callManager.answerIncomingCall(invitation: invitation)
|
||||||
}
|
}
|
||||||
@@ -328,10 +377,13 @@ class CallController: NSObject, CXProviderDelegate, PKPushRegistryDelegate, Obse
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func endCall(callUUID: UUID) {
|
func endCall(callUUID: String) {
|
||||||
logger.debug("CallController: ending the call with UUID \(callUUID.uuidString)")
|
let uuid = UUID(uuidString: callUUID)
|
||||||
|
logger.debug("CallController: ending the call with UUID \(callUUID)")
|
||||||
if CallController.useCallKit() {
|
if CallController.useCallKit() {
|
||||||
requestTransaction(with: CXEndCallAction(call: callUUID))
|
if let uuid {
|
||||||
|
requestTransaction(with: CXEndCallAction(call: uuid))
|
||||||
|
}
|
||||||
} else {
|
} else {
|
||||||
callManager.endCall(callUUID: callUUID) { ok in
|
callManager.endCall(callUUID: callUUID) { ok in
|
||||||
if ok {
|
if ok {
|
||||||
|
|||||||
@@ -10,17 +10,17 @@ import Foundation
|
|||||||
import SimpleXChat
|
import SimpleXChat
|
||||||
|
|
||||||
class CallManager {
|
class CallManager {
|
||||||
func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> UUID {
|
func newOutgoingCall(_ contact: Contact, _ media: CallMediaType) -> String {
|
||||||
let uuid = UUID()
|
let uuid = UUID().uuidString.lowercased()
|
||||||
let call = Call(direction: .outgoing, contact: contact, callkitUUID: uuid, callState: .waitCapabilities, localMedia: media)
|
let call = Call(direction: .outgoing, contact: contact, callUUID: uuid, callState: .waitCapabilities, localMedia: media)
|
||||||
call.speakerEnabled = media == .video
|
call.speakerEnabled = media == .video
|
||||||
ChatModel.shared.activeCall = call
|
ChatModel.shared.activeCall = call
|
||||||
return uuid
|
return uuid
|
||||||
}
|
}
|
||||||
|
|
||||||
func startOutgoingCall(callUUID: UUID) -> Bool {
|
func startOutgoingCall(callUUID: String) -> Bool {
|
||||||
let m = ChatModel.shared
|
let m = ChatModel.shared
|
||||||
if let call = m.activeCall, call.callkitUUID == callUUID {
|
if let call = m.activeCall, call.callUUID == callUUID {
|
||||||
m.showCallView = true
|
m.showCallView = true
|
||||||
Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) }
|
Task { await m.callCommand.processCommand(.capabilities(media: call.localMedia)) }
|
||||||
return true
|
return true
|
||||||
@@ -28,7 +28,7 @@ class CallManager {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func answerIncomingCall(callUUID: UUID) -> Bool {
|
func answerIncomingCall(callUUID: String) -> Bool {
|
||||||
if let invitation = getCallInvitation(callUUID) {
|
if let invitation = getCallInvitation(callUUID) {
|
||||||
answerIncomingCall(invitation: invitation)
|
answerIncomingCall(invitation: invitation)
|
||||||
return true
|
return true
|
||||||
@@ -42,7 +42,7 @@ class CallManager {
|
|||||||
let call = Call(
|
let call = Call(
|
||||||
direction: .incoming,
|
direction: .incoming,
|
||||||
contact: invitation.contact,
|
contact: invitation.contact,
|
||||||
callkitUUID: invitation.callkitUUID,
|
callUUID: invitation.callUUID,
|
||||||
callState: .invitationAccepted,
|
callState: .invitationAccepted,
|
||||||
localMedia: invitation.callType.media,
|
localMedia: invitation.callType.media,
|
||||||
sharedKey: invitation.sharedKey
|
sharedKey: invitation.sharedKey
|
||||||
@@ -68,8 +68,8 @@ class CallManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func enableMedia(media: CallMediaType, enable: Bool, callUUID: UUID) -> Bool {
|
func enableMedia(media: CallMediaType, enable: Bool, callUUID: String) -> Bool {
|
||||||
if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID {
|
if let call = ChatModel.shared.activeCall, call.callUUID == callUUID {
|
||||||
let m = ChatModel.shared
|
let m = ChatModel.shared
|
||||||
Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) }
|
Task { await m.callCommand.processCommand(.media(media: media, enable: enable)) }
|
||||||
return true
|
return true
|
||||||
@@ -77,8 +77,8 @@ class CallManager {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
func endCall(callUUID: UUID, completed: @escaping (Bool) -> Void) {
|
func endCall(callUUID: String, completed: @escaping (Bool) -> Void) {
|
||||||
if let call = ChatModel.shared.activeCall, call.callkitUUID == callUUID {
|
if let call = ChatModel.shared.activeCall, call.callUUID == callUUID {
|
||||||
endCall(call: call) { completed(true) }
|
endCall(call: call) { completed(true) }
|
||||||
} else if let invitation = getCallInvitation(callUUID) {
|
} else if let invitation = getCallInvitation(callUUID) {
|
||||||
endCall(invitation: invitation) { completed(true) }
|
endCall(invitation: invitation) { completed(true) }
|
||||||
@@ -126,8 +126,8 @@ class CallManager {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func getCallInvitation(_ callUUID: UUID) -> RcvCallInvitation? {
|
private func getCallInvitation(_ callUUID: String) -> RcvCallInvitation? {
|
||||||
if let (_, invitation) = ChatModel.shared.callInvitations.first(where: { (_, inv) in inv.callkitUUID == callUUID }) {
|
if let (_, invitation) = ChatModel.shared.callInvitations.first(where: { (_, inv) in inv.callUUID == callUUID }) {
|
||||||
return invitation
|
return invitation
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -38,6 +38,7 @@ struct IncomingCallView: View {
|
|||||||
}
|
}
|
||||||
HStack {
|
HStack {
|
||||||
ProfilePreview(profileOf: invitation.contact, color: .white)
|
ProfilePreview(profileOf: invitation.contact, color: .white)
|
||||||
|
.padding(.vertical, 6)
|
||||||
Spacer()
|
Spacer()
|
||||||
|
|
||||||
callButton("Reject", "phone.down.fill", .red) {
|
callButton("Reject", "phone.down.fill", .red) {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ class Call: ObservableObject, Equatable {
|
|||||||
|
|
||||||
var direction: CallDirection
|
var direction: CallDirection
|
||||||
var contact: Contact
|
var contact: Contact
|
||||||
var callkitUUID: UUID?
|
var callUUID: String?
|
||||||
var localMedia: CallMediaType
|
var localMedia: CallMediaType
|
||||||
@Published var callState: CallState
|
@Published var callState: CallState
|
||||||
@Published var localCapabilities: CallCapabilities?
|
@Published var localCapabilities: CallCapabilities?
|
||||||
@@ -33,14 +33,14 @@ class Call: ObservableObject, Equatable {
|
|||||||
init(
|
init(
|
||||||
direction: CallDirection,
|
direction: CallDirection,
|
||||||
contact: Contact,
|
contact: Contact,
|
||||||
callkitUUID: UUID?,
|
callUUID: String?,
|
||||||
callState: CallState,
|
callState: CallState,
|
||||||
localMedia: CallMediaType,
|
localMedia: CallMediaType,
|
||||||
sharedKey: String? = nil
|
sharedKey: String? = nil
|
||||||
) {
|
) {
|
||||||
self.direction = direction
|
self.direction = direction
|
||||||
self.contact = contact
|
self.contact = contact
|
||||||
self.callkitUUID = callkitUUID
|
self.callUUID = callUUID
|
||||||
self.callState = callState
|
self.callState = callState
|
||||||
self.localMedia = localMedia
|
self.localMedia = localMedia
|
||||||
self.sharedKey = sharedKey
|
self.sharedKey = sharedKey
|
||||||
|
|||||||
@@ -411,6 +411,15 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
|||||||
}
|
}
|
||||||
|
|
||||||
func endCall() {
|
func endCall() {
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
_endCall()
|
||||||
|
} else {
|
||||||
|
// Fixes `connection.close()` getting locked up in iOS15
|
||||||
|
DispatchQueue.global(qos: .utility).async { self._endCall() }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func _endCall() {
|
||||||
guard let call = activeCall.wrappedValue else { return }
|
guard let call = activeCall.wrappedValue else { return }
|
||||||
logger.debug("WebRTCClient: ending the call")
|
logger.debug("WebRTCClient: ending the call")
|
||||||
activeCall.wrappedValue = nil
|
activeCall.wrappedValue = nil
|
||||||
|
|||||||
@@ -671,7 +671,14 @@ private struct CallButton: View {
|
|||||||
|
|
||||||
InfoViewButton(image: image, title: title, disabledLook: !canCall, width: width) {
|
InfoViewButton(image: image, title: title, disabledLook: !canCall, width: width) {
|
||||||
if canCall {
|
if canCall {
|
||||||
CallController.shared.startCall(contact, mediaType)
|
if CallController.useCallKit() {
|
||||||
|
CallController.shared.startCall(contact, mediaType)
|
||||||
|
} else {
|
||||||
|
// When CallKit is not used, colorscheme will be changed and it will be visible if not hiding sheets first
|
||||||
|
dismissAllSheets(animated: true) {
|
||||||
|
CallController.shared.startCall(contact, mediaType)
|
||||||
|
}
|
||||||
|
}
|
||||||
} else if contact.nextSendGrpInv {
|
} else if contact.nextSendGrpInv {
|
||||||
showAlert(SomeAlert(
|
showAlert(SomeAlert(
|
||||||
alert: mkAlert(
|
alert: mkAlert(
|
||||||
@@ -935,7 +942,7 @@ func syncConnectionForceAlert(_ syncConnectionForce: @escaping () -> Void) -> Al
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
func queueInfoText(_ info: (RcvMsgInfo?, QueueInfo)) -> String {
|
func queueInfoText(_ info: (RcvMsgInfo?, ServerQueueInfo)) -> String {
|
||||||
let (rcvMsgInfo, qInfo) = info
|
let (rcvMsgInfo, qInfo) = info
|
||||||
var msgInfo: String
|
var msgInfo: String
|
||||||
if let rcvMsgInfo { msgInfo = encodeJSON(rcvMsgInfo) } else { msgInfo = "none" }
|
if let rcvMsgInfo { msgInfo = encodeJSON(rcvMsgInfo) } else { msgInfo = "none" }
|
||||||
|
|||||||
@@ -12,6 +12,7 @@ import SimpleXChat
|
|||||||
struct CIGroupInvitationView: View {
|
struct CIGroupInvitationView: View {
|
||||||
@EnvironmentObject var chatModel: ChatModel
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
|
@Environment(\.showTimestamp) var showTimestamp: Bool
|
||||||
@ObservedObject var chat: Chat
|
@ObservedObject var chat: Chat
|
||||||
var chatItem: ChatItem
|
var chatItem: ChatItem
|
||||||
var groupInvitation: CIGroupInvitation
|
var groupInvitation: CIGroupInvitation
|
||||||
@@ -45,7 +46,7 @@ struct CIGroupInvitationView: View {
|
|||||||
.foregroundColor(inProgress ? theme.colors.secondary : chatIncognito ? .indigo : theme.colors.primary)
|
.foregroundColor(inProgress ? theme.colors.secondary : chatIncognito ? .indigo : theme.colors.primary)
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
+ Text(" ")
|
+ Text(" ")
|
||||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy)
|
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
|
||||||
)
|
)
|
||||||
.overlay(DetermineWidth())
|
.overlay(DetermineWidth())
|
||||||
}
|
}
|
||||||
@@ -53,7 +54,7 @@ struct CIGroupInvitationView: View {
|
|||||||
(
|
(
|
||||||
groupInvitationText()
|
groupInvitationText()
|
||||||
+ Text(" ")
|
+ Text(" ")
|
||||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy)
|
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showStatus: false, showEdited: false, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
|
||||||
)
|
)
|
||||||
.overlay(DetermineWidth())
|
.overlay(DetermineWidth())
|
||||||
}
|
}
|
||||||
@@ -69,7 +70,7 @@ struct CIGroupInvitationView: View {
|
|||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
.background(chatItemFrameColor(chatItem, theme))
|
.background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) }
|
||||||
.textSelection(.disabled)
|
.textSelection(.disabled)
|
||||||
.onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 }
|
.onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 }
|
||||||
.onChange(of: inProgress) { inProgress in
|
.onChange(of: inProgress) { inProgress in
|
||||||
|
|||||||
@@ -165,9 +165,9 @@ struct CIImageView: View {
|
|||||||
private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View {
|
private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View {
|
||||||
Image(systemName: icon)
|
Image(systemName: icon)
|
||||||
.resizable()
|
.resizable()
|
||||||
|
.invertedForegroundStyle()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
.foregroundColor(.white)
|
|
||||||
.padding(padding)
|
.padding(padding)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -16,7 +16,7 @@ struct CILinkView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .center, spacing: 6) {
|
VStack(alignment: .center, spacing: 6) {
|
||||||
if let uiImage = UIImage(base64Encoded: linkPreview.image) {
|
if let uiImage = imageFromBase64(linkPreview.image) {
|
||||||
Image(uiImage: uiImage)
|
Image(uiImage: uiImage)
|
||||||
.resizable()
|
.resizable()
|
||||||
.scaledToFit()
|
.scaledToFit()
|
||||||
|
|||||||
@@ -12,11 +12,13 @@ import SimpleXChat
|
|||||||
struct CIMetaView: View {
|
struct CIMetaView: View {
|
||||||
@ObservedObject var chat: Chat
|
@ObservedObject var chat: Chat
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
|
@Environment(\.showTimestamp) var showTimestamp: Bool
|
||||||
var chatItem: ChatItem
|
var chatItem: ChatItem
|
||||||
var metaColor: Color
|
var metaColor: Color
|
||||||
var paleMetaColor = Color(UIColor.tertiaryLabel)
|
var paleMetaColor = Color(UIColor.tertiaryLabel)
|
||||||
var showStatus = true
|
var showStatus = true
|
||||||
var showEdited = true
|
var showEdited = true
|
||||||
|
var invertedMaterial = false
|
||||||
|
|
||||||
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
|
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
|
||||||
|
|
||||||
@@ -24,93 +26,138 @@ struct CIMetaView: View {
|
|||||||
if chatItem.isDeletedContent {
|
if chatItem.isDeletedContent {
|
||||||
chatItem.timestampText.font(.caption).foregroundColor(metaColor)
|
chatItem.timestampText.font(.caption).foregroundColor(metaColor)
|
||||||
} else {
|
} else {
|
||||||
let meta = chatItem.meta
|
ZStack {
|
||||||
let ttl = chat.chatInfo.timedMessagesTTL
|
ciMetaText(
|
||||||
let encrypted = chatItem.encryptedFile
|
chatItem.meta,
|
||||||
switch meta.itemStatus {
|
chatTTL: chat.chatInfo.timedMessagesTTL,
|
||||||
case let .sndSent(sndProgress):
|
encrypted: chatItem.encryptedFile,
|
||||||
switch sndProgress {
|
color: metaColor,
|
||||||
case .complete: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .sent, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
|
paleColor: paleMetaColor,
|
||||||
case .partial: ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .sent, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
|
colorMode: invertedMaterial
|
||||||
|
? .invertedMaterial
|
||||||
|
: .normal,
|
||||||
|
showStatus: showStatus,
|
||||||
|
showEdited: showEdited,
|
||||||
|
showViaProxy: showSentViaProxy,
|
||||||
|
showTimesamp: showTimestamp
|
||||||
|
).invertedForegroundStyle(enabled: invertedMaterial)
|
||||||
|
if invertedMaterial {
|
||||||
|
ciMetaText(
|
||||||
|
chatItem.meta,
|
||||||
|
chatTTL: chat.chatInfo.timedMessagesTTL,
|
||||||
|
encrypted: chatItem.encryptedFile,
|
||||||
|
colorMode: .normal,
|
||||||
|
onlyOverrides: true,
|
||||||
|
showStatus: showStatus,
|
||||||
|
showEdited: showEdited,
|
||||||
|
showViaProxy: showSentViaProxy,
|
||||||
|
showTimesamp: showTimestamp
|
||||||
|
)
|
||||||
}
|
}
|
||||||
case let .sndRcvd(_, sndProgress):
|
|
||||||
switch sndProgress {
|
|
||||||
case .complete:
|
|
||||||
ZStack {
|
|
||||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd1, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
|
|
||||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, sent: .rcvd2, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
|
|
||||||
}
|
|
||||||
case .partial:
|
|
||||||
ZStack {
|
|
||||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd1, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
|
|
||||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: paleMetaColor, sent: .rcvd2, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
ciMetaText(meta, chatTTL: ttl, encrypted: encrypted, color: metaColor, showStatus: showStatus, showEdited: showEdited, showViaProxy: showSentViaProxy)
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
enum SentCheckmark {
|
enum MetaColorMode {
|
||||||
case sent
|
// Renders provided colours
|
||||||
case rcvd1
|
case normal
|
||||||
case rcvd2
|
// Fully transparent meta - used for reserving space
|
||||||
|
case transparent
|
||||||
|
// Renders white on dark backgrounds and black on light ones
|
||||||
|
case invertedMaterial
|
||||||
|
|
||||||
|
func resolve(_ c: Color?) -> Color? {
|
||||||
|
switch self {
|
||||||
|
case .normal: c
|
||||||
|
case .transparent: .clear
|
||||||
|
case .invertedMaterial: nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var statusSpacer: Text {
|
||||||
|
switch self {
|
||||||
|
case .normal, .transparent: Text(Image(systemName: "circlebadge.fill")).foregroundColor(.clear)
|
||||||
|
case .invertedMaterial: Text(" ").kerning(13)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func ciMetaText(
|
func ciMetaText(
|
||||||
_ meta: CIMeta,
|
_ meta: CIMeta,
|
||||||
chatTTL: Int?,
|
chatTTL: Int?,
|
||||||
encrypted: Bool?,
|
encrypted: Bool?,
|
||||||
color: Color = .clear,
|
color: Color = .clear, // we use this function to reserve space without rendering meta
|
||||||
|
paleColor: Color? = nil,
|
||||||
primaryColor: Color = .accentColor,
|
primaryColor: Color = .accentColor,
|
||||||
transparent: Bool = false,
|
colorMode: MetaColorMode = .normal,
|
||||||
sent: SentCheckmark? = nil,
|
onlyOverrides: Bool = false, // only render colors that differ from base
|
||||||
showStatus: Bool = true,
|
showStatus: Bool = true,
|
||||||
showEdited: Bool = true,
|
showEdited: Bool = true,
|
||||||
showViaProxy: Bool
|
showViaProxy: Bool,
|
||||||
|
showTimesamp: Bool
|
||||||
) -> Text {
|
) -> Text {
|
||||||
var r = Text("")
|
var r = Text("")
|
||||||
|
var space: Text? = nil
|
||||||
|
let appendSpace = {
|
||||||
|
if let sp = space {
|
||||||
|
r = r + sp
|
||||||
|
space = nil
|
||||||
|
}
|
||||||
|
}
|
||||||
|
let resolved = colorMode.resolve(color)
|
||||||
if showEdited, meta.itemEdited {
|
if showEdited, meta.itemEdited {
|
||||||
r = r + statusIconText("pencil", color)
|
r = r + statusIconText("pencil", resolved)
|
||||||
}
|
}
|
||||||
if meta.disappearing {
|
if meta.disappearing {
|
||||||
r = r + statusIconText("timer", color).font(.caption2)
|
r = r + statusIconText("timer", resolved).font(.caption2)
|
||||||
let ttl = meta.itemTimed?.ttl
|
let ttl = meta.itemTimed?.ttl
|
||||||
if ttl != chatTTL {
|
if ttl != chatTTL {
|
||||||
r = r + Text(shortTimeText(ttl)).foregroundColor(color)
|
r = r + colored(Text(shortTimeText(ttl)), resolved)
|
||||||
}
|
}
|
||||||
r = r + Text(" ")
|
space = Text(" ")
|
||||||
}
|
}
|
||||||
if showViaProxy, meta.sentViaProxy == true {
|
if showViaProxy, meta.sentViaProxy == true {
|
||||||
r = r + statusIconText("arrow.forward", color.opacity(0.67)).font(.caption2)
|
appendSpace()
|
||||||
|
r = r + statusIconText("arrow.forward", resolved?.opacity(0.67)).font(.caption2)
|
||||||
}
|
}
|
||||||
if showStatus {
|
if showStatus {
|
||||||
if let (icon, statusColor) = meta.statusIcon(color, primaryColor) {
|
appendSpace()
|
||||||
let t = Text(Image(systemName: icon)).font(.caption2)
|
if let (image, statusColor) = meta.itemStatus.statusIcon(color, paleColor ?? color, primaryColor) {
|
||||||
let gap = Text(" ").kerning(-1.25)
|
let metaColor = if onlyOverrides && statusColor == color {
|
||||||
let t1 = t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67))
|
Color.clear
|
||||||
switch sent {
|
} else {
|
||||||
case nil: r = r + t1
|
colorMode.resolve(statusColor)
|
||||||
case .sent: r = r + t1 + gap
|
|
||||||
case .rcvd1: r = r + t.foregroundColor(transparent ? .clear : statusColor.opacity(0.67)) + gap
|
|
||||||
case .rcvd2: r = r + gap + t1
|
|
||||||
}
|
}
|
||||||
r = r + Text(" ")
|
r = r + colored(Text(image), metaColor)
|
||||||
|
space = Text(" ")
|
||||||
} else if !meta.disappearing {
|
} else if !meta.disappearing {
|
||||||
r = r + statusIconText("circlebadge.fill", .clear) + Text(" ")
|
space = colorMode.statusSpacer + Text(" ")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if let enc = encrypted {
|
if let enc = encrypted {
|
||||||
r = r + statusIconText(enc ? "lock" : "lock.open", color) + Text(" ")
|
appendSpace()
|
||||||
|
r = r + statusIconText(enc ? "lock" : "lock.open", resolved)
|
||||||
|
space = Text(" ")
|
||||||
|
}
|
||||||
|
if showTimesamp {
|
||||||
|
appendSpace()
|
||||||
|
r = r + colored(meta.timestampText, resolved)
|
||||||
}
|
}
|
||||||
r = r + meta.timestampText.foregroundColor(color)
|
|
||||||
return r.font(.caption)
|
return r.font(.caption)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func statusIconText(_ icon: String, _ color: Color) -> Text {
|
private func statusIconText(_ icon: String, _ color: Color?) -> Text {
|
||||||
Text(Image(systemName: icon)).foregroundColor(color)
|
colored(Text(Image(systemName: icon)), color)
|
||||||
|
}
|
||||||
|
|
||||||
|
// Applying `foregroundColor(nil)` breaks `.invertedForegroundStyle` modifier
|
||||||
|
private func colored(_ t: Text, _ color: Color?) -> Text {
|
||||||
|
if let color {
|
||||||
|
t.foregroundColor(color)
|
||||||
|
} else {
|
||||||
|
t
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct CIMetaView_Previews: PreviewProvider {
|
struct CIMetaView_Previews: PreviewProvider {
|
||||||
|
|||||||
@@ -15,6 +15,7 @@ struct CIRcvDecryptionError: View {
|
|||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@ObservedObject var chat: Chat
|
@ObservedObject var chat: Chat
|
||||||
|
@Environment(\.showTimestamp) var showTimestamp: Bool
|
||||||
var msgDecryptError: MsgDecryptError
|
var msgDecryptError: MsgDecryptError
|
||||||
var msgCount: UInt32
|
var msgCount: UInt32
|
||||||
var chatItem: ChatItem
|
var chatItem: ChatItem
|
||||||
@@ -68,37 +69,40 @@ struct CIRcvDecryptionError: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private func viewBody() -> some View {
|
@ViewBuilder private func viewBody() -> some View {
|
||||||
if case let .direct(contact) = chat.chatInfo,
|
Group {
|
||||||
let contactStats = contact.activeConn?.connectionStats {
|
if case let .direct(contact) = chat.chatInfo,
|
||||||
if contactStats.ratchetSyncAllowed {
|
let contactStats = contact.activeConn?.connectionStats {
|
||||||
decryptionErrorItemFixButton(syncSupported: true) {
|
if contactStats.ratchetSyncAllowed {
|
||||||
alert = .syncAllowedAlert { syncContactConnection(contact) }
|
decryptionErrorItemFixButton(syncSupported: true) {
|
||||||
|
alert = .syncAllowedAlert { syncContactConnection(contact) }
|
||||||
|
}
|
||||||
|
} else if !contactStats.ratchetSyncSupported {
|
||||||
|
decryptionErrorItemFixButton(syncSupported: false) {
|
||||||
|
alert = .syncNotSupportedContactAlert
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
basicDecryptionErrorItem()
|
||||||
}
|
}
|
||||||
} else if !contactStats.ratchetSyncSupported {
|
} else if case let .group(groupInfo) = chat.chatInfo,
|
||||||
decryptionErrorItemFixButton(syncSupported: false) {
|
case let .groupRcv(groupMember) = chatItem.chatDir,
|
||||||
alert = .syncNotSupportedContactAlert
|
let mem = m.getGroupMember(groupMember.groupMemberId),
|
||||||
|
let memberStats = mem.wrapped.activeConn?.connectionStats {
|
||||||
|
if memberStats.ratchetSyncAllowed {
|
||||||
|
decryptionErrorItemFixButton(syncSupported: true) {
|
||||||
|
alert = .syncAllowedAlert { syncMemberConnection(groupInfo, groupMember) }
|
||||||
|
}
|
||||||
|
} else if !memberStats.ratchetSyncSupported {
|
||||||
|
decryptionErrorItemFixButton(syncSupported: false) {
|
||||||
|
alert = .syncNotSupportedMemberAlert
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
basicDecryptionErrorItem()
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
basicDecryptionErrorItem()
|
basicDecryptionErrorItem()
|
||||||
}
|
}
|
||||||
} else if case let .group(groupInfo) = chat.chatInfo,
|
|
||||||
case let .groupRcv(groupMember) = chatItem.chatDir,
|
|
||||||
let mem = m.getGroupMember(groupMember.groupMemberId),
|
|
||||||
let memberStats = mem.wrapped.activeConn?.connectionStats {
|
|
||||||
if memberStats.ratchetSyncAllowed {
|
|
||||||
decryptionErrorItemFixButton(syncSupported: true) {
|
|
||||||
alert = .syncAllowedAlert { syncMemberConnection(groupInfo, groupMember) }
|
|
||||||
}
|
|
||||||
} else if !memberStats.ratchetSyncSupported {
|
|
||||||
decryptionErrorItemFixButton(syncSupported: false) {
|
|
||||||
alert = .syncNotSupportedMemberAlert
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
basicDecryptionErrorItem()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
basicDecryptionErrorItem()
|
|
||||||
}
|
}
|
||||||
|
.background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) }
|
||||||
}
|
}
|
||||||
|
|
||||||
private func basicDecryptionErrorItem() -> some View {
|
private func basicDecryptionErrorItem() -> some View {
|
||||||
@@ -122,7 +126,7 @@ struct CIRcvDecryptionError: View {
|
|||||||
.foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary)
|
.foregroundColor(syncSupported ? theme.colors.primary : theme.colors.secondary)
|
||||||
.font(.callout)
|
.font(.callout)
|
||||||
+ Text(" ")
|
+ Text(" ")
|
||||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy)
|
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
@@ -131,7 +135,6 @@ struct CIRcvDecryptionError: View {
|
|||||||
}
|
}
|
||||||
.onTapGesture(perform: { onClick() })
|
.onTapGesture(perform: { onClick() })
|
||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
.background(Color(uiColor: .tertiarySystemGroupedBackground))
|
|
||||||
.textSelection(.disabled)
|
.textSelection(.disabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -142,7 +145,7 @@ struct CIRcvDecryptionError: View {
|
|||||||
.foregroundColor(.red)
|
.foregroundColor(.red)
|
||||||
.italic()
|
.italic()
|
||||||
+ Text(" ")
|
+ Text(" ")
|
||||||
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy)
|
+ ciMetaText(chatItem.meta, chatTTL: nil, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
|
||||||
}
|
}
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
|
CIMetaView(chat: chat, chatItem: chatItem, metaColor: theme.colors.secondary)
|
||||||
@@ -150,7 +153,6 @@ struct CIRcvDecryptionError: View {
|
|||||||
}
|
}
|
||||||
.onTapGesture(perform: { onClick() })
|
.onTapGesture(perform: { onClick() })
|
||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
.background(Color(uiColor: .tertiarySystemGroupedBackground))
|
|
||||||
.textSelection(.disabled)
|
.textSelection(.disabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -292,30 +292,22 @@ struct CIVideoView: View {
|
|||||||
.clipShape(Circle())
|
.clipShape(Circle())
|
||||||
}
|
}
|
||||||
|
|
||||||
private func durationProgress() -> some View {
|
private var fileSizeString: String {
|
||||||
HStack {
|
if let file = chatItem.file, !videoPlaying {
|
||||||
Text("\(durationText(videoPlaying ? progress : duration))")
|
" " + ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary)
|
||||||
.foregroundColor(.white)
|
} else {
|
||||||
.font(.caption)
|
""
|
||||||
.padding(.vertical, 3)
|
|
||||||
.padding(.horizontal, 6)
|
|
||||||
.background(Color.black.opacity(0.35))
|
|
||||||
.cornerRadius(10)
|
|
||||||
.padding([.top, .leading], 6)
|
|
||||||
|
|
||||||
if let file = chatItem.file, !videoPlaying {
|
|
||||||
Text("\(ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary))")
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.font(.caption)
|
|
||||||
.padding(.vertical, 3)
|
|
||||||
.padding(.horizontal, 6)
|
|
||||||
.background(Color.black.opacity(0.35))
|
|
||||||
.cornerRadius(10)
|
|
||||||
.padding(.top, 6)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func durationProgress() -> some View {
|
||||||
|
Text((durationText(videoPlaying ? progress : duration)) + fileSizeString)
|
||||||
|
.invertedForegroundStyle()
|
||||||
|
.font(.caption)
|
||||||
|
.padding(.vertical, 6)
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
}
|
||||||
|
|
||||||
private func imageView(_ img: UIImage) -> some View {
|
private func imageView(_ img: UIImage) -> some View {
|
||||||
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth
|
let w = img.size.width <= img.size.height ? maxWidth * 0.75 : maxWidth
|
||||||
return ZStack(alignment: .topTrailing) {
|
return ZStack(alignment: .topTrailing) {
|
||||||
@@ -411,9 +403,9 @@ struct CIVideoView: View {
|
|||||||
private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View {
|
private func fileIcon(_ icon: String, _ size: CGFloat, _ padding: CGFloat) -> some View {
|
||||||
Image(systemName: icon)
|
Image(systemName: icon)
|
||||||
.resizable()
|
.resizable()
|
||||||
|
.invertedForegroundStyle()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
.foregroundColor(.white)
|
|
||||||
.padding(smallView ? 0 : padding)
|
.padding(smallView ? 0 : padding)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -428,10 +420,8 @@ struct CIVideoView: View {
|
|||||||
private func progressCircle(_ progress: Int64, _ total: Int64) -> some View {
|
private func progressCircle(_ progress: Int64, _ total: Int64) -> some View {
|
||||||
Circle()
|
Circle()
|
||||||
.trim(from: 0, to: Double(progress) / Double(total))
|
.trim(from: 0, to: Double(progress) / Double(total))
|
||||||
.stroke(
|
.stroke(style: StrokeStyle(lineWidth: 2))
|
||||||
Color(uiColor: .white),
|
.invertedForegroundStyle()
|
||||||
style: StrokeStyle(lineWidth: 2)
|
|
||||||
)
|
|
||||||
.rotationEffect(.degrees(-90))
|
.rotationEffect(.degrees(-90))
|
||||||
.frame(width: 16, height: 16)
|
.frame(width: 16, height: 16)
|
||||||
.padding([.trailing, .top], smallView ? 0 : 11)
|
.padding([.trailing, .top], smallView ? 0 : 11)
|
||||||
|
|||||||
@@ -12,7 +12,7 @@ import SimpleXChat
|
|||||||
struct FramedItemView: View {
|
struct FramedItemView: View {
|
||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@EnvironmentObject var scrollModel: ReverseListScrollModel<ChatItem>
|
@EnvironmentObject var scrollModel: ReverseListScrollModel
|
||||||
@ObservedObject var chat: Chat
|
@ObservedObject var chat: Chat
|
||||||
var chatItem: ChatItem
|
var chatItem: ChatItem
|
||||||
var preview: UIImage?
|
var preview: UIImage?
|
||||||
@@ -64,15 +64,20 @@ struct FramedItemView: View {
|
|||||||
.overlay(DetermineWidth())
|
.overlay(DetermineWidth())
|
||||||
}
|
}
|
||||||
|
|
||||||
if chatItem.content.msgContent != nil {
|
if let content = chatItem.content.msgContent {
|
||||||
CIMetaView(chat: chat, chatItem: chatItem, metaColor: useWhiteMetaColor ? Color.white : theme.colors.secondary)
|
CIMetaView(
|
||||||
.padding(.horizontal, 12)
|
chat: chat,
|
||||||
.padding(.bottom, 6)
|
chatItem: chatItem,
|
||||||
.overlay(DetermineWidth())
|
metaColor: theme.colors.secondary,
|
||||||
.accessibilityLabel("")
|
invertedMaterial: useWhiteMetaColor
|
||||||
|
)
|
||||||
|
.padding(.horizontal, 12)
|
||||||
|
.padding(.bottom, 6)
|
||||||
|
.overlay(DetermineWidth())
|
||||||
|
.accessibilityLabel("")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.background(chatItemFrameColorMaybeImageOrVideo(chatItem, theme))
|
.background { chatItemFrameColorMaybeImageOrVideo(chatItem, theme).modifier(ChatTailPadding()) }
|
||||||
.onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 }
|
.onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 }
|
||||||
|
|
||||||
if let (title, text) = chatItem.meta.itemStatus.statusInfo {
|
if let (title, text) = chatItem.meta.itemStatus.statusInfo {
|
||||||
@@ -185,7 +190,7 @@ struct FramedItemView: View {
|
|||||||
let v = ZStack(alignment: .topTrailing) {
|
let v = ZStack(alignment: .topTrailing) {
|
||||||
switch (qi.content) {
|
switch (qi.content) {
|
||||||
case let .image(_, image):
|
case let .image(_, image):
|
||||||
if let uiImage = UIImage(base64Encoded: image) {
|
if let uiImage = imageFromBase64(image) {
|
||||||
ciQuotedMsgView(qi)
|
ciQuotedMsgView(qi)
|
||||||
.padding(.trailing, 70).frame(minWidth: msgWidth, alignment: .leading)
|
.padding(.trailing, 70).frame(minWidth: msgWidth, alignment: .leading)
|
||||||
Image(uiImage: uiImage)
|
Image(uiImage: uiImage)
|
||||||
@@ -197,7 +202,7 @@ struct FramedItemView: View {
|
|||||||
ciQuotedMsgView(qi)
|
ciQuotedMsgView(qi)
|
||||||
}
|
}
|
||||||
case let .video(_, image, _):
|
case let .video(_, image, _):
|
||||||
if let uiImage = UIImage(base64Encoded: image) {
|
if let uiImage = imageFromBase64(image) {
|
||||||
ciQuotedMsgView(qi)
|
ciQuotedMsgView(qi)
|
||||||
.padding(.trailing, 70).frame(minWidth: msgWidth, alignment: .leading)
|
.padding(.trailing, 70).frame(minWidth: msgWidth, alignment: .leading)
|
||||||
Image(uiImage: uiImage)
|
Image(uiImage: uiImage)
|
||||||
|
|||||||
@@ -13,7 +13,7 @@ import AVKit
|
|||||||
|
|
||||||
struct FullScreenMediaView: View {
|
struct FullScreenMediaView: View {
|
||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var scrollModel: ReverseListScrollModel<ChatItem>
|
@EnvironmentObject var scrollModel: ReverseListScrollModel
|
||||||
@State var chatItem: ChatItem
|
@State var chatItem: ChatItem
|
||||||
@State var image: UIImage?
|
@State var image: UIImage?
|
||||||
@State var player: AVPlayer? = nil
|
@State var player: AVPlayer? = nil
|
||||||
|
|||||||
@@ -69,7 +69,7 @@ struct CIMsgError: View {
|
|||||||
}
|
}
|
||||||
.padding(.leading, 12)
|
.padding(.leading, 12)
|
||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
.background(Color(uiColor: .tertiarySystemGroupedBackground))
|
.background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) }
|
||||||
.textSelection(.disabled)
|
.textSelection(.disabled)
|
||||||
.onTapGesture(perform: onTap)
|
.onTapGesture(perform: onTap)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -22,7 +22,7 @@ struct MarkedDeletedItemView: View {
|
|||||||
.foregroundColor(theme.colors.secondary)
|
.foregroundColor(theme.colors.secondary)
|
||||||
.padding(.horizontal, 12)
|
.padding(.horizontal, 12)
|
||||||
.padding(.vertical, 6)
|
.padding(.vertical, 6)
|
||||||
.background(chatItemFrameColor(chatItem, theme))
|
.background { chatItemFrameColor(chatItem, theme).modifier(ChatTailPadding()) }
|
||||||
.textSelection(.disabled)
|
.textSelection(.disabled)
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ private func typing(_ w: Font.Weight = .light) -> Text {
|
|||||||
|
|
||||||
struct MsgContentView: View {
|
struct MsgContentView: View {
|
||||||
@ObservedObject var chat: Chat
|
@ObservedObject var chat: Chat
|
||||||
|
@Environment(\.showTimestamp) var showTimestamp: Bool
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
var text: String
|
var text: String
|
||||||
var formattedText: [FormattedText]? = nil
|
var formattedText: [FormattedText]? = nil
|
||||||
@@ -84,7 +85,7 @@ struct MsgContentView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private func reserveSpaceForMeta(_ mt: CIMeta) -> Text {
|
private func reserveSpaceForMeta(_ mt: CIMeta) -> Text {
|
||||||
(rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, transparent: true, showViaProxy: showSentViaProxy)
|
(rightToLeft ? Text("\n") : Text(" ")) + ciMetaText(mt, chatTTL: chat.chatInfo.timedMessagesTTL, encrypted: nil, colorMode: .transparent, showViaProxy: showSentViaProxy, showTimesamp: showTimestamp)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -450,20 +450,8 @@ struct ChatItemInfoView: View {
|
|||||||
.foregroundColor(theme.colors.secondary).opacity(0.67)
|
.foregroundColor(theme.colors.secondary).opacity(0.67)
|
||||||
}
|
}
|
||||||
let v = Group {
|
let v = Group {
|
||||||
let (icon, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary)
|
let (image, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary)
|
||||||
switch status {
|
image.foregroundColor(statusColor)
|
||||||
case .rcvd:
|
|
||||||
ZStack(alignment: .trailing) {
|
|
||||||
Image(systemName: icon)
|
|
||||||
.foregroundColor(statusColor.opacity(0.67))
|
|
||||||
.padding(.trailing, 6)
|
|
||||||
Image(systemName: icon)
|
|
||||||
.foregroundColor(statusColor.opacity(0.67))
|
|
||||||
}
|
|
||||||
default:
|
|
||||||
Image(systemName: icon)
|
|
||||||
.foregroundColor(statusColor)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
if let (title, text) = status.statusInfo {
|
if let (title, text) = status.statusInfo {
|
||||||
|
|||||||
@@ -9,9 +9,21 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SimpleXChat
|
import SimpleXChat
|
||||||
|
|
||||||
|
extension EnvironmentValues {
|
||||||
|
struct ShowTimestamp: EnvironmentKey {
|
||||||
|
static let defaultValue: Bool = true
|
||||||
|
}
|
||||||
|
|
||||||
|
var showTimestamp: Bool {
|
||||||
|
get { self[ShowTimestamp.self] }
|
||||||
|
set { self[ShowTimestamp.self] = newValue }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
struct ChatItemView: View {
|
struct ChatItemView: View {
|
||||||
@ObservedObject var chat: Chat
|
@ObservedObject var chat: Chat
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
|
@Environment(\.showTimestamp) var showTimestamp: Bool
|
||||||
var chatItem: ChatItem
|
var chatItem: ChatItem
|
||||||
var maxWidth: CGFloat = .infinity
|
var maxWidth: CGFloat = .infinity
|
||||||
@Binding var revealed: Bool
|
@Binding var revealed: Bool
|
||||||
@@ -60,7 +72,7 @@ struct ChatItemView: View {
|
|||||||
default: nil
|
default: nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.flatMap { UIImage(base64Encoded: $0) }
|
.flatMap { imageFromBase64($0) }
|
||||||
let adjustedMaxWidth = {
|
let adjustedMaxWidth = {
|
||||||
if let preview, preview.size.width <= preview.size.height {
|
if let preview, preview.size.width <= preview.size.height {
|
||||||
maxWidth * 0.75
|
maxWidth * 0.75
|
||||||
|
|||||||
@@ -22,8 +22,7 @@ struct ChatView: View {
|
|||||||
@Environment(\.presentationMode) var presentationMode
|
@Environment(\.presentationMode) var presentationMode
|
||||||
@Environment(\.scenePhase) var scenePhase
|
@Environment(\.scenePhase) var scenePhase
|
||||||
@State @ObservedObject var chat: Chat
|
@State @ObservedObject var chat: Chat
|
||||||
@StateObject private var scrollModel = ReverseListScrollModel<ChatItem>()
|
@StateObject private var scrollModel = ReverseListScrollModel()
|
||||||
@StateObject private var floatingButtonModel = FloatingButtonModel()
|
|
||||||
@State private var showChatInfoSheet: Bool = false
|
@State private var showChatInfoSheet: Bool = false
|
||||||
@State private var showAddMembersSheet: Bool = false
|
@State private var showAddMembersSheet: Bool = false
|
||||||
@State private var composeState = ComposeState()
|
@State private var composeState = ComposeState()
|
||||||
@@ -76,7 +75,7 @@ struct ChatView: View {
|
|||||||
VStack(spacing: 0) {
|
VStack(spacing: 0) {
|
||||||
ZStack(alignment: .bottomTrailing) {
|
ZStack(alignment: .bottomTrailing) {
|
||||||
chatItemsList()
|
chatItemsList()
|
||||||
floatingButtons(counts: floatingButtonModel.unreadChatItemCounts)
|
FloatingButtons(theme: theme, scrollModel: scrollModel, chat: chat)
|
||||||
}
|
}
|
||||||
connectingText()
|
connectingText()
|
||||||
if selectedChatItems == nil {
|
if selectedChatItems == nil {
|
||||||
@@ -340,6 +339,7 @@ struct ChatView: View {
|
|||||||
await markChatUnread(chat, unreadChat: false)
|
await markChatUnread(chat, unreadChat: false)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
ChatView.FloatingButtonModel.shared.totalUnread = chat.chatStats.unreadCount
|
||||||
}
|
}
|
||||||
|
|
||||||
private func searchToolbar() -> some View {
|
private func searchToolbar() -> some View {
|
||||||
@@ -413,12 +413,6 @@ struct ChatView: View {
|
|||||||
revealedChatItem: $revealedChatItem,
|
revealedChatItem: $revealedChatItem,
|
||||||
selectedChatItems: $selectedChatItems
|
selectedChatItems: $selectedChatItems
|
||||||
)
|
)
|
||||||
.onAppear {
|
|
||||||
floatingButtonModel.appeared(viewId: ci.viewId)
|
|
||||||
}
|
|
||||||
.onDisappear {
|
|
||||||
floatingButtonModel.disappeared(viewId: ci.viewId)
|
|
||||||
}
|
|
||||||
.id(ci.id) // Required to trigger `onAppear` on iOS15
|
.id(ci.id) // Required to trigger `onAppear` on iOS15
|
||||||
} loadPage: {
|
} loadPage: {
|
||||||
loadChatItems(cInfo)
|
loadChatItems(cInfo)
|
||||||
@@ -429,13 +423,10 @@ struct ChatView: View {
|
|||||||
.onChange(of: searchText) { _ in
|
.onChange(of: searchText) { _ in
|
||||||
Task { await loadChat(chat: chat, search: searchText) }
|
Task { await loadChat(chat: chat, search: searchText) }
|
||||||
}
|
}
|
||||||
.onChange(of: im.reversedChatItems) { _ in
|
|
||||||
floatingButtonModel.chatItemsChanged()
|
|
||||||
}
|
|
||||||
.onChange(of: im.itemAdded) { added in
|
.onChange(of: im.itemAdded) { added in
|
||||||
if added {
|
if added {
|
||||||
im.itemAdded = false
|
im.itemAdded = false
|
||||||
if floatingButtonModel.unreadChatItemCounts.isReallyNearBottom {
|
if FloatingButtonModel.shared.isReallyNearBottom {
|
||||||
scrollModel.scrollToBottom()
|
scrollModel.scrollToBottom()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -458,103 +449,165 @@ struct ChatView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
class FloatingButtonModel: ObservableObject {
|
class FloatingButtonModel: ObservableObject {
|
||||||
private enum Event {
|
static let shared = FloatingButtonModel()
|
||||||
case appeared(String)
|
@Published var unreadBelow: Int = 0
|
||||||
case disappeared(String)
|
@Published var isNearBottom: Bool = true
|
||||||
case chatItemsChanged
|
@Published var date: Date?
|
||||||
}
|
@Published var isDateVisible: Bool = false
|
||||||
|
var totalUnread: Int = 0
|
||||||
|
var isReallyNearBottom: Bool = true
|
||||||
|
var hideDateWorkItem: DispatchWorkItem?
|
||||||
|
|
||||||
@Published var unreadChatItemCounts: UnreadChatItemCounts
|
func updateOnListChange(_ listState: ListState) {
|
||||||
|
let im = ItemsModel.shared
|
||||||
private let events = PassthroughSubject<Event, Never>()
|
let unreadBelow =
|
||||||
private var bag = Set<AnyCancellable>()
|
if let id = listState.bottomItemId,
|
||||||
|
let index = im.reversedChatItems.firstIndex(where: { $0.id == id })
|
||||||
init() {
|
{
|
||||||
unreadChatItemCounts = UnreadChatItemCounts(
|
im.reversedChatItems[..<index].reduce(into: 0) { unread, chatItem in
|
||||||
isNearBottom: true,
|
if chatItem.isRcvNew { unread += 1 }
|
||||||
isReallyNearBottom: true,
|
}
|
||||||
unreadBelow: 0
|
} else {
|
||||||
)
|
0
|
||||||
events
|
|
||||||
.receive(on: DispatchQueue.global(qos: .background))
|
|
||||||
.scan(Set<String>()) { itemsInView, event in
|
|
||||||
var updated = itemsInView
|
|
||||||
switch event {
|
|
||||||
case let .appeared(viewId): updated.insert(viewId)
|
|
||||||
case let .disappeared(viewId): updated.remove(viewId)
|
|
||||||
case .chatItemsChanged: ()
|
|
||||||
}
|
|
||||||
return updated
|
|
||||||
}
|
}
|
||||||
.map { ChatModel.shared.unreadChatItemCounts(itemsInView: $0) }
|
let date: Date? =
|
||||||
.removeDuplicates()
|
if let topItemDate = listState.topItemDate {
|
||||||
.throttle(for: .seconds(0.2), scheduler: DispatchQueue.main, latest: true)
|
Calendar.current.startOfDay(for: topItemDate)
|
||||||
.assign(to: \.unreadChatItemCounts, on: self)
|
} else {
|
||||||
.store(in: &bag)
|
nil
|
||||||
|
}
|
||||||
|
|
||||||
|
// set the counters and date indicator
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
guard let it = self else { return }
|
||||||
|
it.setDate(visibility: true)
|
||||||
|
it.unreadBelow = unreadBelow
|
||||||
|
it.date = date
|
||||||
|
it.isReallyNearBottom = listState.scrollOffset > 0 && listState.scrollOffset < 500
|
||||||
|
}
|
||||||
|
|
||||||
|
// set floating button indication mode
|
||||||
|
let nearBottom = listState.scrollOffset < 800
|
||||||
|
if nearBottom != self.isNearBottom {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) { [weak self] in
|
||||||
|
self?.isNearBottom = nearBottom
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
// hide Date indicator after 1 second of no scrolling
|
||||||
|
hideDateWorkItem?.cancel()
|
||||||
|
let workItem = DispatchWorkItem { [weak self] in
|
||||||
|
guard let it = self else { return }
|
||||||
|
it.setDate(visibility: false)
|
||||||
|
it.hideDateWorkItem = nil
|
||||||
|
}
|
||||||
|
DispatchQueue.main.async { [weak self] in
|
||||||
|
self?.hideDateWorkItem = workItem
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 1, execute: workItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func appeared(viewId: String) {
|
func resetDate() {
|
||||||
events.send(.appeared(viewId))
|
date = nil
|
||||||
|
isDateVisible = false
|
||||||
}
|
}
|
||||||
|
|
||||||
func disappeared(viewId: String) {
|
private func setDate(visibility isVisible: Bool) {
|
||||||
events.send(.disappeared(viewId))
|
if isVisible {
|
||||||
|
if !isNearBottom,
|
||||||
|
!isDateVisible,
|
||||||
|
let date, !Calendar.current.isDateInToday(date) {
|
||||||
|
withAnimation { self.isDateVisible = true }
|
||||||
|
}
|
||||||
|
} else if isDateVisible {
|
||||||
|
withAnimation { self.isDateVisible = false }
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
func chatItemsChanged() {
|
|
||||||
events.send(.chatItemsChanged)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func floatingButtons(counts: UnreadChatItemCounts) -> some View {
|
private struct FloatingButtons: View {
|
||||||
VStack {
|
let theme: AppTheme
|
||||||
let unreadAbove = chat.chatStats.unreadCount - counts.unreadBelow
|
let scrollModel: ReverseListScrollModel
|
||||||
if unreadAbove > 0 {
|
let chat: Chat
|
||||||
circleButton {
|
@ObservedObject var model = FloatingButtonModel.shared
|
||||||
unreadCountText(unreadAbove)
|
|
||||||
.font(.callout)
|
var body: some View {
|
||||||
.foregroundColor(theme.colors.primary)
|
ZStack(alignment: .top) {
|
||||||
|
if let date = model.date {
|
||||||
|
DateSeparator(date: date)
|
||||||
|
.padding(.vertical, 4).padding(.horizontal, 8)
|
||||||
|
.background(.thinMaterial)
|
||||||
|
.clipShape(Capsule())
|
||||||
|
.opacity(model.isDateVisible ? 1 : 0)
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
VStack {
|
||||||
scrollModel.scrollToNextPage()
|
let unreadAbove = model.totalUnread - model.unreadBelow
|
||||||
}
|
if unreadAbove > 0 {
|
||||||
.contextMenu {
|
circleButton {
|
||||||
Button {
|
unreadCountText(unreadAbove)
|
||||||
Task {
|
.font(.callout)
|
||||||
await markChatRead(chat)
|
.foregroundColor(theme.colors.primary)
|
||||||
}
|
}
|
||||||
} label: {
|
.onTapGesture {
|
||||||
Label("Mark read", systemImage: "checkmark")
|
scrollModel.scrollToNextPage()
|
||||||
|
}
|
||||||
|
.contextMenu {
|
||||||
|
Button {
|
||||||
|
Task {
|
||||||
|
await markChatRead(chat)
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
Label("Mark read", systemImage: "checkmark")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
if model.unreadBelow > 0 {
|
||||||
|
circleButton {
|
||||||
|
unreadCountText(model.unreadBelow)
|
||||||
|
.font(.callout)
|
||||||
|
.foregroundColor(theme.colors.primary)
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
scrollModel.scrollToBottom()
|
||||||
|
}
|
||||||
|
} else if !model.isNearBottom {
|
||||||
|
circleButton {
|
||||||
|
Image(systemName: "chevron.down")
|
||||||
|
.foregroundColor(theme.colors.primary)
|
||||||
|
}
|
||||||
|
.onTapGesture { scrollModel.scrollToBottom() }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.padding()
|
||||||
|
.frame(maxWidth: .infinity, alignment: .trailing)
|
||||||
}
|
}
|
||||||
Spacer()
|
.onDisappear(perform: model.resetDate)
|
||||||
if counts.unreadBelow > 0 {
|
}
|
||||||
circleButton {
|
|
||||||
unreadCountText(counts.unreadBelow)
|
private func circleButton<Content: View>(_ content: @escaping () -> Content) -> some View {
|
||||||
.font(.callout)
|
ZStack {
|
||||||
.foregroundColor(theme.colors.primary)
|
Circle()
|
||||||
}
|
.foregroundColor(Color(uiColor: .tertiarySystemGroupedBackground))
|
||||||
.onTapGesture {
|
.frame(width: 44, height: 44)
|
||||||
scrollModel.scrollToBottom()
|
content()
|
||||||
}
|
|
||||||
} else if !counts.isNearBottom {
|
|
||||||
circleButton {
|
|
||||||
Image(systemName: "chevron.down")
|
|
||||||
.foregroundColor(theme.colors.primary)
|
|
||||||
}
|
|
||||||
.onTapGesture { scrollModel.scrollToBottom() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func circleButton<Content: View>(_ content: @escaping () -> Content) -> some View {
|
private struct DateSeparator: View {
|
||||||
ZStack {
|
let date: Date
|
||||||
Circle()
|
|
||||||
.foregroundColor(Color(uiColor: .tertiarySystemGroupedBackground))
|
var body: some View {
|
||||||
.frame(width: 44, height: 44)
|
Text(String.localizedStringWithFormat(
|
||||||
content()
|
NSLocalizedString("%@, %@", comment: "format for date separator in chat"),
|
||||||
|
date.formatted(.dateTime.weekday(.abbreviated)),
|
||||||
|
date.formatted(.dateTime.day().month(.abbreviated))
|
||||||
|
))
|
||||||
|
.font(.callout)
|
||||||
|
.fontWeight(.medium)
|
||||||
|
.foregroundStyle(.secondary)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -568,8 +621,8 @@ struct ChatView: View {
|
|||||||
|
|
||||||
private func endCallButton(_ call: Call) -> some View {
|
private func endCallButton(_ call: Call) -> some View {
|
||||||
Button {
|
Button {
|
||||||
if let uuid = call.callkitUUID {
|
if CallController.useCallKit(), let callUUID = call.callUUID {
|
||||||
CallController.shared.endCall(callUUID: uuid)
|
CallController.shared.endCall(callUUID: callUUID)
|
||||||
} else {
|
} else {
|
||||||
CallController.shared.endCall(call: call) {}
|
CallController.shared.endCall(call: call) {}
|
||||||
}
|
}
|
||||||
@@ -696,6 +749,7 @@ struct ChatView: View {
|
|||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@Binding @ObservedObject var chat: Chat
|
@Binding @ObservedObject var chat: Chat
|
||||||
|
@ObservedObject var dummyModel: ChatItemDummyModel = .shared
|
||||||
let chatItem: ChatItem
|
let chatItem: ChatItem
|
||||||
let maxWidth: CGFloat
|
let maxWidth: CGFloat
|
||||||
@Binding var composeState: ComposeState
|
@Binding var composeState: ComposeState
|
||||||
@@ -709,38 +763,65 @@ struct ChatView: View {
|
|||||||
@State private var showChatItemInfoSheet: Bool = false
|
@State private var showChatItemInfoSheet: Bool = false
|
||||||
@State private var chatItemInfo: ChatItemInfo?
|
@State private var chatItemInfo: ChatItemInfo?
|
||||||
@State private var showForwardingSheet: Bool = false
|
@State private var showForwardingSheet: Bool = false
|
||||||
|
@State private var msgWidth: CGFloat = 0
|
||||||
|
|
||||||
@Binding var selectedChatItems: Set<Int64>?
|
@Binding var selectedChatItems: Set<Int64>?
|
||||||
|
|
||||||
@State private var allowMenu: Bool = true
|
@State private var allowMenu: Bool = true
|
||||||
|
@State private var markedRead = false
|
||||||
|
|
||||||
var revealed: Bool { chatItem == revealedChatItem }
|
var revealed: Bool { chatItem == revealedChatItem }
|
||||||
|
|
||||||
|
typealias ItemSeparation = (timestamp: Bool, largeGap: Bool, date: Date?)
|
||||||
|
|
||||||
|
func getItemSeparation(_ chatItem: ChatItem, at i: Int?) -> ItemSeparation {
|
||||||
|
let im = ItemsModel.shared
|
||||||
|
if let i, i > 0 && im.reversedChatItems.count >= i {
|
||||||
|
let nextItem = im.reversedChatItems[i - 1]
|
||||||
|
let largeGap = !nextItem.chatDir.sameDirection(chatItem.chatDir) || nextItem.meta.itemTs.timeIntervalSince(chatItem.meta.itemTs) > 60
|
||||||
|
return (
|
||||||
|
timestamp: largeGap || formatTimestampMeta(chatItem.meta.itemTs) != formatTimestampMeta(nextItem.meta.itemTs),
|
||||||
|
largeGap: largeGap,
|
||||||
|
date: Calendar.current.isDate(chatItem.meta.itemTs, inSameDayAs: nextItem.meta.itemTs) ? nil : nextItem.meta.itemTs
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
return (timestamp: true, largeGap: true, date: nil)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
let (currIndex, _) = m.getNextChatItem(chatItem)
|
let currIndex = m.getChatItemIndex(chatItem)
|
||||||
let ciCategory = chatItem.mergeCategory
|
let ciCategory = chatItem.mergeCategory
|
||||||
let (prevHidden, prevItem) = m.getPrevShownChatItem(currIndex, ciCategory)
|
let (prevHidden, prevItem) = m.getPrevShownChatItem(currIndex, ciCategory)
|
||||||
let range = itemsRange(currIndex, prevHidden)
|
let range = itemsRange(currIndex, prevHidden)
|
||||||
|
let timeSeparation = getItemSeparation(chatItem, at: currIndex)
|
||||||
let im = ItemsModel.shared
|
let im = ItemsModel.shared
|
||||||
Group {
|
Group {
|
||||||
if revealed, let range = range {
|
if revealed, let range = range {
|
||||||
let items = Array(zip(Array(range), im.reversedChatItems[range]))
|
let items = Array(zip(Array(range), im.reversedChatItems[range]))
|
||||||
ForEach(items.reversed(), id: \.1.viewId) { (i, ci) in
|
VStack(spacing: 0) {
|
||||||
let prev = i == prevHidden ? prevItem : im.reversedChatItems[i + 1]
|
ForEach(items.reversed(), id: \.1.viewId) { (i: Int, ci: ChatItem) in
|
||||||
chatItemView(ci, nil, prev)
|
let prev = i == prevHidden ? prevItem : im.reversedChatItems[i + 1]
|
||||||
.overlay {
|
chatItemView(ci, nil, prev, getItemSeparation(ci, at: i))
|
||||||
if let selected = selectedChatItems, ci.canBeDeletedForSelf {
|
.overlay {
|
||||||
Color.clear
|
if let selected = selectedChatItems, ci.canBeDeletedForSelf {
|
||||||
.contentShape(Rectangle())
|
Color.clear
|
||||||
.onTapGesture {
|
.contentShape(Rectangle())
|
||||||
let checked = selected.contains(ci.id)
|
.onTapGesture {
|
||||||
selectUnselectChatItem(select: !checked, ci)
|
let checked = selected.contains(ci.id)
|
||||||
|
selectUnselectChatItem(select: !checked, ci)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
chatItemView(chatItem, range, prevItem)
|
VStack(spacing: 0) {
|
||||||
|
chatItemView(chatItem, range, prevItem, timeSeparation)
|
||||||
|
if let date = timeSeparation.date {
|
||||||
|
DateSeparator(date: date).padding(8)
|
||||||
|
}
|
||||||
|
}
|
||||||
.overlay {
|
.overlay {
|
||||||
if let selected = selectedChatItems, chatItem.canBeDeletedForSelf {
|
if let selected = selectedChatItems, chatItem.canBeDeletedForSelf {
|
||||||
Color.clear
|
Color.clear
|
||||||
@@ -754,12 +835,16 @@ struct ChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onAppear {
|
.onAppear {
|
||||||
|
if markedRead {
|
||||||
|
return
|
||||||
|
} else {
|
||||||
|
markedRead = true
|
||||||
|
}
|
||||||
if let range {
|
if let range {
|
||||||
if let items = unreadItems(range) {
|
let itemIds = unreadItemIds(range)
|
||||||
|
if !itemIds.isEmpty {
|
||||||
waitToMarkRead {
|
waitToMarkRead {
|
||||||
for ci in items {
|
await apiMarkChatItemsRead(chat.chatInfo, itemIds)
|
||||||
await apiMarkChatItemRead(chat.chatInfo, ci)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else if chatItem.isRcvNew {
|
} else if chatItem.isRcvNew {
|
||||||
@@ -769,29 +854,75 @@ struct ChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unreadItems(_ range: ClosedRange<Int>) -> [ChatItem]? {
|
private func unreadItemIds(_ range: ClosedRange<Int>) -> [ChatItem.ID] {
|
||||||
let im = ItemsModel.shared
|
let im = ItemsModel.shared
|
||||||
let items = range.compactMap { i in
|
return range.compactMap { i in
|
||||||
if i >= 0 && i < im.reversedChatItems.count {
|
if i >= 0 && i < im.reversedChatItems.count {
|
||||||
let ci = im.reversedChatItems[i]
|
let ci = im.reversedChatItems[i]
|
||||||
return if ci.isRcvNew { ci } else { nil }
|
return if ci.isRcvNew { ci.id } else { nil }
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
return if items.isEmpty { nil } else { items }
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func waitToMarkRead(_ op: @Sendable @escaping () async -> Void) {
|
private func waitToMarkRead(_ op: @Sendable @escaping () async -> Void) {
|
||||||
DispatchQueue.main.asyncAfter(deadline: .now() + 0.6) {
|
Task {
|
||||||
|
_ = try? await Task.sleep(nanoseconds: 600_000000)
|
||||||
if m.chatId == chat.chatInfo.id {
|
if m.chatId == chat.chatInfo.id {
|
||||||
Task(operation: op)
|
await op()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
||||||
@ViewBuilder func chatItemView(_ ci: ChatItem, _ range: ClosedRange<Int>?, _ prevItem: ChatItem?) -> some View {
|
@available(iOS 16.0, *)
|
||||||
|
struct MemberLayout: Layout {
|
||||||
|
let spacing: Double
|
||||||
|
let msgWidth: Double
|
||||||
|
|
||||||
|
private func sizes(subviews: Subviews, proposal: ProposedViewSize) -> (CGSize, CGSize) {
|
||||||
|
assert(subviews.count == 2, "member layout must contain exactly two subviews")
|
||||||
|
let roleSize = subviews[1].sizeThatFits(proposal)
|
||||||
|
let memberSize = subviews[0].sizeThatFits(
|
||||||
|
ProposedViewSize(
|
||||||
|
width: (proposal.width ?? msgWidth) - roleSize.width,
|
||||||
|
height: proposal.height
|
||||||
|
)
|
||||||
|
)
|
||||||
|
return (memberSize, roleSize)
|
||||||
|
}
|
||||||
|
|
||||||
|
func sizeThatFits(proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) -> CGSize {
|
||||||
|
let (memberSize, roleSize) = sizes(subviews: subviews, proposal: proposal)
|
||||||
|
return CGSize(
|
||||||
|
width: min(
|
||||||
|
proposal.width ?? msgWidth,
|
||||||
|
max(msgWidth, roleSize.width + spacing + memberSize.width)
|
||||||
|
),
|
||||||
|
height: max(memberSize.height, roleSize.height)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
func placeSubviews(in bounds: CGRect, proposal: ProposedViewSize, subviews: Subviews, cache: inout Void) {
|
||||||
|
let (memberSize, roleSize) = sizes(subviews: subviews, proposal: proposal)
|
||||||
|
subviews[0].place(
|
||||||
|
at: CGPoint(x: bounds.minX, y: bounds.midY - memberSize.height / 2),
|
||||||
|
proposal: ProposedViewSize(memberSize)
|
||||||
|
)
|
||||||
|
subviews[1].place(
|
||||||
|
at: CGPoint(
|
||||||
|
x: bounds.minX + max(memberSize.width + spacing, msgWidth - roleSize.width),
|
||||||
|
y: bounds.midY - roleSize.height / 2
|
||||||
|
),
|
||||||
|
proposal: ProposedViewSize(roleSize)
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder func chatItemView(_ ci: ChatItem, _ range: ClosedRange<Int>?, _ prevItem: ChatItem?, _ itemSeparation: ItemSeparation) -> some View {
|
||||||
|
let bottomPadding: Double = itemSeparation.largeGap ? 10 : 2
|
||||||
if case let .groupRcv(member) = ci.chatDir,
|
if case let .groupRcv(member) = ci.chatDir,
|
||||||
case let .group(groupInfo) = chat.chatInfo {
|
case let .group(groupInfo) = chat.chatInfo {
|
||||||
let (prevMember, memCount): (GroupMember?, Int) =
|
let (prevMember, memCount): (GroupMember?, Int) =
|
||||||
@@ -803,24 +934,49 @@ struct ChatView: View {
|
|||||||
if prevItem == nil || showMemberImage(member, prevItem) || prevMember != nil {
|
if prevItem == nil || showMemberImage(member, prevItem) || prevMember != nil {
|
||||||
VStack(alignment: .leading, spacing: 4) {
|
VStack(alignment: .leading, spacing: 4) {
|
||||||
if ci.content.showMemberName {
|
if ci.content.showMemberName {
|
||||||
let t = if memCount == 1 && member.memberRole > .member {
|
Group {
|
||||||
Text(member.memberRole.text + " ").fontWeight(.semibold) + Text(member.displayName)
|
if memCount == 1 && member.memberRole > .member {
|
||||||
} else {
|
Group {
|
||||||
Text(memberNames(member, prevMember, memCount))
|
if #available(iOS 16.0, *) {
|
||||||
|
MemberLayout(spacing: 16, msgWidth: msgWidth) {
|
||||||
|
Text(member.chatViewName)
|
||||||
|
.lineLimit(1)
|
||||||
|
Text(member.memberRole.text)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.lineLimit(1)
|
||||||
|
.padding(.trailing, 8)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
HStack(spacing: 16) {
|
||||||
|
Text(member.chatViewName)
|
||||||
|
.lineLimit(1)
|
||||||
|
Text(member.memberRole.text)
|
||||||
|
.fontWeight(.semibold)
|
||||||
|
.lineLimit(1)
|
||||||
|
.layoutPriority(1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(
|
||||||
|
maxWidth: maxWidth,
|
||||||
|
alignment: chatItem.chatDir.sent ? .trailing : .leading
|
||||||
|
)
|
||||||
|
} else {
|
||||||
|
Text(memberNames(member, prevMember, memCount))
|
||||||
|
.lineLimit(2)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
t
|
|
||||||
.font(.caption)
|
.font(.caption)
|
||||||
.foregroundStyle(.secondary)
|
.foregroundStyle(.secondary)
|
||||||
.lineLimit(2)
|
|
||||||
.padding(.leading, memberImageSize + 14 + (selectedChatItems != nil && ci.canBeDeletedForSelf ? 12 + 24 : 0))
|
.padding(.leading, memberImageSize + 14 + (selectedChatItems != nil && ci.canBeDeletedForSelf ? 12 + 24 : 0))
|
||||||
.padding(.top, 7)
|
.padding(.top, 3) // this is in addition to message sequence gap
|
||||||
}
|
}
|
||||||
HStack(alignment: .center, spacing: 0) {
|
HStack(alignment: .center, spacing: 0) {
|
||||||
if selectedChatItems != nil && ci.canBeDeletedForSelf {
|
if selectedChatItems != nil && ci.canBeDeletedForSelf {
|
||||||
SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems)
|
SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems)
|
||||||
.padding(.trailing, 12)
|
.padding(.trailing, 12)
|
||||||
}
|
}
|
||||||
HStack(alignment: .top, spacing: 8) {
|
HStack(alignment: .top, spacing: 10) {
|
||||||
MemberProfileImage(member, size: memberImageSize, backgroundColor: theme.colors.background)
|
MemberProfileImage(member, size: memberImageSize, backgroundColor: theme.colors.background)
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if let member = m.getGroupMember(member.groupMemberId) {
|
if let member = m.getGroupMember(member.groupMemberId) {
|
||||||
@@ -833,11 +989,12 @@ struct ChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chatItemWithMenu(ci, range, maxWidth)
|
chatItemWithMenu(ci, range, maxWidth, itemSeparation)
|
||||||
|
.onPreferenceChange(DetermineWidth.Key.self) { msgWidth = $0 }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, bottomPadding)
|
||||||
.padding(.trailing)
|
.padding(.trailing)
|
||||||
.padding(.leading, 12)
|
.padding(.leading, 12)
|
||||||
} else {
|
} else {
|
||||||
@@ -846,11 +1003,11 @@ struct ChatView: View {
|
|||||||
SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems)
|
SelectedChatItem(ciId: ci.id, selectedChatItems: $selectedChatItems)
|
||||||
.padding(.leading, 12)
|
.padding(.leading, 12)
|
||||||
}
|
}
|
||||||
chatItemWithMenu(ci, range, maxWidth)
|
chatItemWithMenu(ci, range, maxWidth, itemSeparation)
|
||||||
.padding(.trailing)
|
.padding(.trailing)
|
||||||
.padding(.leading, memberImageSize + 8 + 12)
|
.padding(.leading, 10 + memberImageSize + 12)
|
||||||
}
|
}
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, bottomPadding)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
HStack(alignment: .center, spacing: 0) {
|
HStack(alignment: .center, spacing: 0) {
|
||||||
@@ -863,10 +1020,10 @@ struct ChatView: View {
|
|||||||
.padding(.leading)
|
.padding(.leading)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
chatItemWithMenu(ci, range, maxWidth)
|
chatItemWithMenu(ci, range, maxWidth, itemSeparation)
|
||||||
.padding(.horizontal)
|
.padding(.horizontal)
|
||||||
}
|
}
|
||||||
.padding(.bottom, 5)
|
.padding(.bottom, bottomPadding)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -881,7 +1038,7 @@ struct ChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder func chatItemWithMenu(_ ci: ChatItem, _ range: ClosedRange<Int>?, _ maxWidth: CGFloat) -> some View {
|
@ViewBuilder func chatItemWithMenu(_ ci: ChatItem, _ range: ClosedRange<Int>?, _ maxWidth: CGFloat, _ itemSeparation: ItemSeparation) -> some View {
|
||||||
let alignment: Alignment = ci.chatDir.sent ? .trailing : .leading
|
let alignment: Alignment = ci.chatDir.sent ? .trailing : .leading
|
||||||
VStack(alignment: alignment.horizontal, spacing: 3) {
|
VStack(alignment: alignment.horizontal, spacing: 3) {
|
||||||
ChatItemView(
|
ChatItemView(
|
||||||
@@ -891,7 +1048,8 @@ struct ChatView: View {
|
|||||||
revealed: .constant(revealed),
|
revealed: .constant(revealed),
|
||||||
allowMenu: $allowMenu
|
allowMenu: $allowMenu
|
||||||
)
|
)
|
||||||
.modifier(ChatItemClipped(ci))
|
.environment(\.showTimestamp, itemSeparation.timestamp)
|
||||||
|
.modifier(ChatItemClipped(ci, tailVisible: itemSeparation.largeGap))
|
||||||
.contextMenu { menu(ci, range, live: composeState.liveMessage != nil) }
|
.contextMenu { menu(ci, range, live: composeState.liveMessage != nil) }
|
||||||
.accessibilityLabel("")
|
.accessibilityLabel("")
|
||||||
if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 {
|
if ci.content.msgContent != nil && (ci.meta.itemDeleted == nil || revealed) && ci.reactions.count > 0 {
|
||||||
|
|||||||
@@ -18,7 +18,7 @@ struct ComposeImageView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
HStack(alignment: .center, spacing: 8) {
|
HStack(alignment: .center, spacing: 8) {
|
||||||
let imgs: [UIImage] = images.compactMap { image in
|
let imgs: [UIImage] = images.compactMap { image in
|
||||||
UIImage(base64Encoded: image)
|
imageFromBase64(image)
|
||||||
}
|
}
|
||||||
if imgs.count == 0 {
|
if imgs.count == 0 {
|
||||||
ProgressView()
|
ProgressView()
|
||||||
|
|||||||
@@ -40,7 +40,7 @@ struct ComposeLinkView: View {
|
|||||||
|
|
||||||
private func linkPreviewView(_ linkPreview: LinkPreview) -> some View {
|
private func linkPreviewView(_ linkPreview: LinkPreview) -> some View {
|
||||||
HStack(alignment: .center, spacing: 8) {
|
HStack(alignment: .center, spacing: 8) {
|
||||||
if let uiImage = UIImage(base64Encoded: linkPreview.image) {
|
if let uiImage = imageFromBase64(linkPreview.image) {
|
||||||
Image(uiImage: uiImage)
|
Image(uiImage: uiImage)
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
|
|||||||
@@ -751,6 +751,7 @@ struct ComposeView: View {
|
|||||||
case .linkPreview:
|
case .linkPreview:
|
||||||
sent = await send(checkLinkPreview(), quoted: quoted, live: live, ttl: ttl)
|
sent = await send(checkLinkPreview(), quoted: quoted, live: live, ttl: ttl)
|
||||||
case let .mediaPreviews(mediaPreviews: media):
|
case let .mediaPreviews(mediaPreviews: media):
|
||||||
|
// TODO batch send: batch media previews
|
||||||
let last = media.count - 1
|
let last = media.count - 1
|
||||||
if last >= 0 {
|
if last >= 0 {
|
||||||
for i in 0..<last {
|
for i in 0..<last {
|
||||||
@@ -887,22 +888,26 @@ struct ComposeView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func send(_ mc: MsgContent, quoted: Int64?, file: CryptoFile? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? {
|
func send(_ mc: MsgContent, quoted: Int64?, file: CryptoFile? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? {
|
||||||
if let chatItem = chat.chatInfo.chatType == .local
|
if let chatItems = chat.chatInfo.chatType == .local
|
||||||
? await apiCreateChatItem(noteFolderId: chat.chatInfo.apiId, file: file, msg: mc)
|
? await apiCreateChatItems(
|
||||||
: await apiSendMessage(
|
noteFolderId: chat.chatInfo.apiId,
|
||||||
|
composedMessages: [ComposedMessage(fileSource: file, msgContent: mc)]
|
||||||
|
)
|
||||||
|
: await apiSendMessages(
|
||||||
type: chat.chatInfo.chatType,
|
type: chat.chatInfo.chatType,
|
||||||
id: chat.chatInfo.apiId,
|
id: chat.chatInfo.apiId,
|
||||||
file: file,
|
|
||||||
quotedItemId: quoted,
|
|
||||||
msg: mc,
|
|
||||||
live: live,
|
live: live,
|
||||||
ttl: ttl
|
ttl: ttl,
|
||||||
|
composedMessages: [ComposedMessage(fileSource: file, quotedItemId: quoted, msgContent: mc)]
|
||||||
) {
|
) {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
chatModel.removeLiveDummy(animated: false)
|
chatModel.removeLiveDummy(animated: false)
|
||||||
chatModel.addChatItem(chat.chatInfo, chatItem)
|
for chatItem in chatItems {
|
||||||
|
chatModel.addChatItem(chat.chatInfo, chatItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return chatItem
|
// UI only supports sending one item at a time
|
||||||
|
return chatItems.first
|
||||||
}
|
}
|
||||||
if let file = file {
|
if let file = file {
|
||||||
removeFile(file.filePath)
|
removeFile(file.filePath)
|
||||||
@@ -911,18 +916,21 @@ struct ComposeView: View {
|
|||||||
}
|
}
|
||||||
|
|
||||||
func forwardItem(_ forwardedItem: ChatItem, _ fromChatInfo: ChatInfo, _ ttl: Int?) async -> ChatItem? {
|
func forwardItem(_ forwardedItem: ChatItem, _ fromChatInfo: ChatInfo, _ ttl: Int?) async -> ChatItem? {
|
||||||
if let chatItem = await apiForwardChatItem(
|
if let chatItems = await apiForwardChatItems(
|
||||||
toChatType: chat.chatInfo.chatType,
|
toChatType: chat.chatInfo.chatType,
|
||||||
toChatId: chat.chatInfo.apiId,
|
toChatId: chat.chatInfo.apiId,
|
||||||
fromChatType: fromChatInfo.chatType,
|
fromChatType: fromChatInfo.chatType,
|
||||||
fromChatId: fromChatInfo.apiId,
|
fromChatId: fromChatInfo.apiId,
|
||||||
itemId: forwardedItem.id,
|
itemIds: [forwardedItem.id],
|
||||||
ttl: ttl
|
ttl: ttl
|
||||||
) {
|
) {
|
||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
chatModel.addChatItem(chat.chatInfo, chatItem)
|
for chatItem in chatItems {
|
||||||
|
chatModel.addChatItem(chat.chatInfo, chatItem)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
return chatItem
|
// TODO batch send: forward multiple messages
|
||||||
|
return chatItems.first
|
||||||
}
|
}
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -8,15 +8,16 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
import Combine
|
import Combine
|
||||||
|
import SimpleXChat
|
||||||
|
|
||||||
/// A List, which displays it's items in reverse order - from bottom to top
|
/// A List, which displays it's items in reverse order - from bottom to top
|
||||||
struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIViewControllerRepresentable {
|
struct ReverseList<Content: View>: UIViewControllerRepresentable {
|
||||||
let items: Array<Item>
|
let items: Array<ChatItem>
|
||||||
|
|
||||||
@Binding var scrollState: ReverseListScrollModel<Item>.State
|
@Binding var scrollState: ReverseListScrollModel.State
|
||||||
|
|
||||||
/// Closure, that returns user interface for a given item
|
/// Closure, that returns user interface for a given item
|
||||||
let content: (Item) -> Content
|
let content: (ChatItem) -> Content
|
||||||
|
|
||||||
let loadPage: () -> Void
|
let loadPage: () -> Void
|
||||||
|
|
||||||
@@ -25,7 +26,9 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
|
|||||||
}
|
}
|
||||||
|
|
||||||
func updateUIViewController(_ controller: Controller, context: Context) {
|
func updateUIViewController(_ controller: Controller, context: Context) {
|
||||||
|
controller.representer = self
|
||||||
if case let .scrollingTo(destination) = scrollState, !items.isEmpty {
|
if case let .scrollingTo(destination) = scrollState, !items.isEmpty {
|
||||||
|
controller.view.layer.removeAllAnimations()
|
||||||
switch destination {
|
switch destination {
|
||||||
case .nextPage:
|
case .nextPage:
|
||||||
controller.scrollToNextPage()
|
controller.scrollToNextPage()
|
||||||
@@ -42,9 +45,10 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
|
|||||||
/// Controller, which hosts SwiftUI cells
|
/// Controller, which hosts SwiftUI cells
|
||||||
class Controller: UITableViewController {
|
class Controller: UITableViewController {
|
||||||
private enum Section { case main }
|
private enum Section { case main }
|
||||||
private let representer: ReverseList
|
var representer: ReverseList
|
||||||
private var dataSource: UITableViewDiffableDataSource<Section, Item>!
|
private var dataSource: UITableViewDiffableDataSource<Section, ChatItem>!
|
||||||
private var itemCount: Int = 0
|
private var itemCount: Int = 0
|
||||||
|
private let updateFloatingButtons = PassthroughSubject<Void, Never>()
|
||||||
private var bag = Set<AnyCancellable>()
|
private var bag = Set<AnyCancellable>()
|
||||||
|
|
||||||
init(representer: ReverseList) {
|
init(representer: ReverseList) {
|
||||||
@@ -71,7 +75,7 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
|
|||||||
}
|
}
|
||||||
|
|
||||||
// 3. Configure data source
|
// 3. Configure data source
|
||||||
self.dataSource = UITableViewDiffableDataSource<Section, Item>(
|
self.dataSource = UITableViewDiffableDataSource<Section, ChatItem>(
|
||||||
tableView: tableView
|
tableView: tableView
|
||||||
) { (tableView, indexPath, item) -> UITableViewCell? in
|
) { (tableView, indexPath, item) -> UITableViewCell? in
|
||||||
if indexPath.item > self.itemCount - 8, self.itemCount > 8 {
|
if indexPath.item > self.itemCount - 8, self.itemCount > 8 {
|
||||||
@@ -103,6 +107,15 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
|
|||||||
name: notificationName,
|
name: notificationName,
|
||||||
object: nil
|
object: nil
|
||||||
)
|
)
|
||||||
|
|
||||||
|
updateFloatingButtons
|
||||||
|
.throttle(for: 0.2, scheduler: DispatchQueue.global(qos: .background), latest: true)
|
||||||
|
.sink {
|
||||||
|
if let listState = DispatchQueue.main.sync(execute: { [weak self] in self?.getListState() }) {
|
||||||
|
ChatView.FloatingButtonModel.shared.updateOnListChange(listState)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.store(in: &bag)
|
||||||
}
|
}
|
||||||
|
|
||||||
@available(*, unavailable)
|
@available(*, unavailable)
|
||||||
@@ -171,8 +184,8 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
|
|||||||
Task { representer.scrollState = .atDestination }
|
Task { representer.scrollState = .atDestination }
|
||||||
}
|
}
|
||||||
|
|
||||||
func update(items: Array<Item>) {
|
func update(items: [ChatItem]) {
|
||||||
var snapshot = NSDiffableDataSourceSnapshot<Section, Item>()
|
var snapshot = NSDiffableDataSourceSnapshot<Section, ChatItem>()
|
||||||
snapshot.appendSections([.main])
|
snapshot.appendSections([.main])
|
||||||
snapshot.appendItems(items)
|
snapshot.appendItems(items)
|
||||||
dataSource.defaultRowAnimation = .none
|
dataSource.defaultRowAnimation = .none
|
||||||
@@ -180,7 +193,50 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
|
|||||||
snapshot,
|
snapshot,
|
||||||
animatingDifferences: itemCount != 0 && abs(items.count - itemCount) == 1
|
animatingDifferences: itemCount != 0 && abs(items.count - itemCount) == 1
|
||||||
)
|
)
|
||||||
|
// Sets content offset on initial load
|
||||||
|
if itemCount == 0 {
|
||||||
|
tableView.setContentOffset(
|
||||||
|
CGPoint(x: 0, y: -InvertedTableView.inset),
|
||||||
|
animated: false
|
||||||
|
)
|
||||||
|
}
|
||||||
itemCount = items.count
|
itemCount = items.count
|
||||||
|
updateFloatingButtons.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
override func scrollViewDidScroll(_ scrollView: UIScrollView) {
|
||||||
|
updateFloatingButtons.send()
|
||||||
|
}
|
||||||
|
|
||||||
|
func getListState() -> ListState? {
|
||||||
|
if let visibleRows = tableView.indexPathsForVisibleRows,
|
||||||
|
visibleRows.last?.item ?? 0 < representer.items.count {
|
||||||
|
let scrollOffset: Double = tableView.contentOffset.y + InvertedTableView.inset
|
||||||
|
let topItemDate: Date? =
|
||||||
|
if let lastVisible = visibleRows.last(where: { isVisible(indexPath: $0) }) {
|
||||||
|
representer.items[lastVisible.item].meta.itemTs
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
let bottomItemId: ChatItem.ID? =
|
||||||
|
if let firstVisible = visibleRows.first(where: { isVisible(indexPath: $0) }) {
|
||||||
|
representer.items[firstVisible.item].id
|
||||||
|
} else {
|
||||||
|
nil
|
||||||
|
}
|
||||||
|
return (scrollOffset: scrollOffset, topItemDate: topItemDate, bottomItemId: bottomItemId)
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
private func isVisible(indexPath: IndexPath) -> Bool {
|
||||||
|
if let relativeFrame = tableView.superview?.convert(
|
||||||
|
tableView.rectForRow(at: indexPath),
|
||||||
|
from: tableView
|
||||||
|
) {
|
||||||
|
relativeFrame.maxY > InvertedTableView.inset &&
|
||||||
|
relativeFrame.minY < tableView.frame.height - InvertedTableView.inset
|
||||||
|
} else { false }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -224,13 +280,19 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
typealias ListState = (
|
||||||
|
scrollOffset: Double,
|
||||||
|
topItemDate: Date?,
|
||||||
|
bottomItemId: ChatItem.ID?
|
||||||
|
)
|
||||||
|
|
||||||
/// Manages ``ReverseList`` scrolling
|
/// Manages ``ReverseList`` scrolling
|
||||||
class ReverseListScrollModel<Item: Identifiable>: ObservableObject {
|
class ReverseListScrollModel: ObservableObject {
|
||||||
/// Represents Scroll State of ``ReverseList``
|
/// Represents Scroll State of ``ReverseList``
|
||||||
enum State: Equatable {
|
enum State: Equatable {
|
||||||
enum Destination: Equatable {
|
enum Destination: Equatable {
|
||||||
case nextPage
|
case nextPage
|
||||||
case item(Item.ID)
|
case item(ChatItem.ID)
|
||||||
case bottom
|
case bottom
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -248,7 +310,7 @@ class ReverseListScrollModel<Item: Identifiable>: ObservableObject {
|
|||||||
state = .scrollingTo(.bottom)
|
state = .scrollingTo(.bottom)
|
||||||
}
|
}
|
||||||
|
|
||||||
func scrollToItem(id: Item.ID) {
|
func scrollToItem(id: ChatItem.ID) {
|
||||||
state = .scrollingTo(.item(id))
|
state = .scrollingTo(.item(id))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -9,6 +9,17 @@
|
|||||||
import SwiftUI
|
import SwiftUI
|
||||||
import SimpleXChat
|
import SimpleXChat
|
||||||
|
|
||||||
|
enum UserPickerSheet: Identifiable {
|
||||||
|
case address
|
||||||
|
case chatPreferences
|
||||||
|
case chatProfiles
|
||||||
|
case currentProfile
|
||||||
|
case useFromDesktop
|
||||||
|
case settings
|
||||||
|
|
||||||
|
var id: Self { self }
|
||||||
|
}
|
||||||
|
|
||||||
struct ChatListView: View {
|
struct ChatListView: View {
|
||||||
@EnvironmentObject var chatModel: ChatModel
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@@ -18,9 +29,9 @@ struct ChatListView: View {
|
|||||||
@State private var searchText = ""
|
@State private var searchText = ""
|
||||||
@State private var searchShowingSimplexLink = false
|
@State private var searchShowingSimplexLink = false
|
||||||
@State private var searchChatFilteredBySimplexLink: String? = nil
|
@State private var searchChatFilteredBySimplexLink: String? = nil
|
||||||
@State private var userPickerVisible = false
|
|
||||||
@State private var showConnectDesktop = false
|
|
||||||
@State private var scrollToSearchBar = false
|
@State private var scrollToSearchBar = false
|
||||||
|
@State private var activeUserPickerSheet: UserPickerSheet? = nil
|
||||||
|
@State private var userPickerShown: Bool = false
|
||||||
|
|
||||||
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
|
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
|
||||||
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
|
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
|
||||||
@@ -46,21 +57,44 @@ struct ChatListView: View {
|
|||||||
),
|
),
|
||||||
destination: chatView
|
destination: chatView
|
||||||
) { chatListView }
|
) { chatListView }
|
||||||
if userPickerVisible {
|
}
|
||||||
Rectangle().fill(.white.opacity(0.001)).onTapGesture {
|
.sheet(isPresented: $userPickerShown) {
|
||||||
withAnimation {
|
UserPicker(activeSheet: $activeUserPickerSheet)
|
||||||
userPickerVisible.toggle()
|
.sheet(item: $activeUserPickerSheet) { sheet in
|
||||||
|
if let currentUser = chatModel.currentUser {
|
||||||
|
switch sheet {
|
||||||
|
case .address:
|
||||||
|
NavigationView {
|
||||||
|
UserAddressView(shareViaProfile: currentUser.addressShared)
|
||||||
|
.navigationTitle("Public address")
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
.modifier(ThemedBackground(grouped: true))
|
||||||
|
}
|
||||||
|
case .chatProfiles:
|
||||||
|
NavigationView {
|
||||||
|
UserProfilesView()
|
||||||
|
}
|
||||||
|
case .currentProfile:
|
||||||
|
NavigationView {
|
||||||
|
UserProfile()
|
||||||
|
.navigationTitle("Your current profile")
|
||||||
|
.modifier(ThemedBackground())
|
||||||
|
}
|
||||||
|
case .chatPreferences:
|
||||||
|
NavigationView {
|
||||||
|
PreferencesView(profile: currentUser.profile, preferences: currentUser.fullPreferences, currentPreferences: currentUser.fullPreferences)
|
||||||
|
.navigationTitle("Your preferences")
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
.modifier(ThemedBackground(grouped: true))
|
||||||
|
}
|
||||||
|
case .useFromDesktop:
|
||||||
|
ConnectDesktopView(viaSettings: false)
|
||||||
|
case .settings:
|
||||||
|
SettingsView(showSettings: $showSettings)
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
|
||||||
UserPicker(
|
|
||||||
showSettings: $showSettings,
|
|
||||||
showConnectDesktop: $showConnectDesktop,
|
|
||||||
userPickerVisible: $userPickerVisible
|
|
||||||
)
|
|
||||||
}
|
|
||||||
.sheet(isPresented: $showConnectDesktop) {
|
|
||||||
ConnectDesktopView()
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -73,7 +107,7 @@ struct ChatListView: View {
|
|||||||
.navigationBarHidden(searchMode || oneHandUI)
|
.navigationBarHidden(searchMode || oneHandUI)
|
||||||
}
|
}
|
||||||
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
|
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
|
||||||
.onDisappear() { withAnimation { userPickerVisible = false } }
|
.onDisappear() { activeUserPickerSheet = nil }
|
||||||
.refreshable {
|
.refreshable {
|
||||||
AlertManager.shared.showAlert(Alert(
|
AlertManager.shared.showAlert(Alert(
|
||||||
title: Text("Reconnect servers?"),
|
title: Text("Reconnect servers?"),
|
||||||
@@ -164,7 +198,7 @@ struct ChatListView: View {
|
|||||||
let user = chatModel.currentUser ?? User.sampleData
|
let user = chatModel.currentUser ?? User.sampleData
|
||||||
ZStack(alignment: .topTrailing) {
|
ZStack(alignment: .topTrailing) {
|
||||||
ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel))
|
ProfileImage(imageStr: user.image, size: 32, color: Color(uiColor: .quaternaryLabel))
|
||||||
.padding(.trailing, 4)
|
.padding([.top, .trailing], 3)
|
||||||
let allRead = chatModel.users
|
let allRead = chatModel.users
|
||||||
.filter { u in !u.user.activeUser && !u.user.hidden }
|
.filter { u in !u.user.activeUser && !u.user.hidden }
|
||||||
.allSatisfy { u in u.unreadCount == 0 }
|
.allSatisfy { u in u.unreadCount == 0 }
|
||||||
@@ -173,13 +207,7 @@ struct ChatListView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onTapGesture {
|
.onTapGesture {
|
||||||
if chatModel.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 {
|
userPickerShown = true
|
||||||
withAnimation {
|
|
||||||
userPickerVisible.toggle()
|
|
||||||
}
|
|
||||||
} else {
|
|
||||||
showSettings = true
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -217,14 +245,31 @@ struct ChatListView: View {
|
|||||||
.listRowSeparator(.hidden)
|
.listRowSeparator(.hidden)
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
}
|
}
|
||||||
ForEach(cs, id: \.viewId) { chat in
|
if #available(iOS 16.0, *) {
|
||||||
ChatListNavLink(chat: chat)
|
ForEach(cs, id: \.viewId) { chat in
|
||||||
|
ChatListNavLink(chat: chat)
|
||||||
|
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
|
||||||
|
.padding(.trailing, -16)
|
||||||
|
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
|
||||||
|
.listRowBackground(Color.clear)
|
||||||
|
}
|
||||||
|
.offset(x: -8)
|
||||||
|
} else {
|
||||||
|
ForEach(cs, id: \.viewId) { chat in
|
||||||
|
VStack(spacing: .zero) {
|
||||||
|
Divider()
|
||||||
|
.padding(.leading, 16)
|
||||||
|
ChatListNavLink(chat: chat)
|
||||||
|
.padding(.horizontal, 8)
|
||||||
|
.padding(.vertical, 6)
|
||||||
|
}
|
||||||
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
|
.scaleEffect(x: 1, y: oneHandUI ? -1 : 1, anchor: .center)
|
||||||
.padding(.trailing, -16)
|
.listRowSeparator(.hidden)
|
||||||
|
.listRowInsets(EdgeInsets())
|
||||||
|
.background { theme.colors.background } // Hides default list selection colour
|
||||||
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
|
.disabled(chatModel.chatRunning != true || chatModel.deletedChats.contains(chat.chatInfo.id))
|
||||||
.listRowBackground(Color.clear)
|
}
|
||||||
}
|
}
|
||||||
.offset(x: -8)
|
|
||||||
}
|
}
|
||||||
.listStyle(.plain)
|
.listStyle(.plain)
|
||||||
.onChange(of: chatModel.chatId) { currentChatId in
|
.onChange(of: chatModel.chatId) { currentChatId in
|
||||||
@@ -252,7 +297,7 @@ struct ChatListView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func unreadBadge(_ text: Text? = Text(" "), size: CGFloat = 18) -> some View {
|
private func unreadBadge(size: CGFloat = 18) -> some View {
|
||||||
Circle()
|
Circle()
|
||||||
.frame(width: size, height: size)
|
.frame(width: size, height: size)
|
||||||
.foregroundColor(theme.colors.primary)
|
.foregroundColor(theme.colors.primary)
|
||||||
@@ -501,21 +546,21 @@ func chatStoppedIcon() -> some View {
|
|||||||
struct ChatListView_Previews: PreviewProvider {
|
struct ChatListView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let chatModel = ChatModel()
|
let chatModel = ChatModel()
|
||||||
chatModel.chats = [
|
chatModel.updateChats([
|
||||||
Chat(
|
ChatData(
|
||||||
chatInfo: ChatInfo.sampleData.direct,
|
chatInfo: ChatInfo.sampleData.direct,
|
||||||
chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")]
|
chatItems: [ChatItem.getSample(1, .directSnd, .now, "hello")]
|
||||||
),
|
),
|
||||||
Chat(
|
ChatData(
|
||||||
chatInfo: ChatInfo.sampleData.group,
|
chatInfo: ChatInfo.sampleData.group,
|
||||||
chatItems: [ChatItem.getSample(1, .directSnd, .now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")]
|
chatItems: [ChatItem.getSample(1, .directSnd, .now, "Lorem ipsum dolor sit amet, consectetur adipiscing elit, sed do eiusmod tempor incididunt ut labore et dolore magna aliqua. Ut enim ad minim veniam, quis nostrud exercitation ullamco laboris nisi ut aliquip ex ea commodo consequat. Duis aute irure dolor in reprehenderit in voluptate velit esse cillum dolore eu fugiat nulla pariatur. Excepteur sint occaecat cupidatat non proident, sunt in culpa qui officia deserunt mollit anim id est laborum.")]
|
||||||
),
|
),
|
||||||
Chat(
|
ChatData(
|
||||||
chatInfo: ChatInfo.sampleData.contactRequest,
|
chatInfo: ChatInfo.sampleData.contactRequest,
|
||||||
chatItems: []
|
chatItems: []
|
||||||
)
|
)
|
||||||
|
|
||||||
]
|
])
|
||||||
return Group {
|
return Group {
|
||||||
ChatListView(showSettings: Binding.constant(false))
|
ChatListView(showSettings: Binding.constant(false))
|
||||||
.environmentObject(chatModel)
|
.environmentObject(chatModel)
|
||||||
|
|||||||
@@ -302,7 +302,7 @@ struct ChatPreviewView: View {
|
|||||||
case let .link(_, preview):
|
case let .link(_, preview):
|
||||||
smallContentPreview(size: dynamicMediaSize) {
|
smallContentPreview(size: dynamicMediaSize) {
|
||||||
ZStack(alignment: .topTrailing) {
|
ZStack(alignment: .topTrailing) {
|
||||||
Image(uiImage: UIImage(base64Encoded: preview.image) ?? UIImage(systemName: "arrow.up.right")!)
|
Image(uiImage: imageFromBase64(preview.image) ?? UIImage(systemName: "arrow.up.right")!)
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fill)
|
.aspectRatio(contentMode: .fill)
|
||||||
.frame(width: dynamicMediaSize, height: dynamicMediaSize)
|
.frame(width: dynamicMediaSize, height: dynamicMediaSize)
|
||||||
@@ -323,13 +323,13 @@ struct ChatPreviewView: View {
|
|||||||
}
|
}
|
||||||
case let .image(_, image):
|
case let .image(_, image):
|
||||||
smallContentPreview(size: dynamicMediaSize) {
|
smallContentPreview(size: dynamicMediaSize) {
|
||||||
CIImageView(chatItem: ci, preview: UIImage(base64Encoded: image), maxWidth: dynamicMediaSize, smallView: true, showFullScreenImage: $showFullscreenGallery)
|
CIImageView(chatItem: ci, preview: imageFromBase64(image), maxWidth: dynamicMediaSize, smallView: true, showFullScreenImage: $showFullscreenGallery)
|
||||||
.environmentObject(ReverseListScrollModel<ChatItem>())
|
.environmentObject(ReverseListScrollModel())
|
||||||
}
|
}
|
||||||
case let .video(_,image, duration):
|
case let .video(_,image, duration):
|
||||||
smallContentPreview(size: dynamicMediaSize) {
|
smallContentPreview(size: dynamicMediaSize) {
|
||||||
CIVideoView(chatItem: ci, preview: UIImage(base64Encoded: image), duration: duration, maxWidth: dynamicMediaSize, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery)
|
CIVideoView(chatItem: ci, preview: imageFromBase64(image), duration: duration, maxWidth: dynamicMediaSize, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery)
|
||||||
.environmentObject(ReverseListScrollModel<ChatItem>())
|
.environmentObject(ReverseListScrollModel())
|
||||||
}
|
}
|
||||||
case let .voice(_, duration):
|
case let .voice(_, duration):
|
||||||
smallContentPreviewVoice(size: dynamicMediaSize) {
|
smallContentPreviewVoice(size: dynamicMediaSize) {
|
||||||
|
|||||||
@@ -8,176 +8,228 @@ import SimpleXChat
|
|||||||
|
|
||||||
struct UserPicker: View {
|
struct UserPicker: View {
|
||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@Environment(\.scenePhase) var scenePhase
|
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@Binding var showSettings: Bool
|
@Environment(\.dynamicTypeSize) private var userFont: DynamicTypeSize
|
||||||
@Binding var showConnectDesktop: Bool
|
@Environment(\.scenePhase) private var scenePhase: ScenePhase
|
||||||
@Binding var userPickerVisible: Bool
|
@Environment(\.colorScheme) private var colorScheme: ColorScheme
|
||||||
@State var scrollViewContentSize: CGSize = .zero
|
@Environment(\.dismiss) private var dismiss: DismissAction
|
||||||
@State var disableScrolling: Bool = true
|
@Binding var activeSheet: UserPickerSheet?
|
||||||
private let menuButtonHeight: CGFloat = 68
|
@State private var switchingProfile = false
|
||||||
@State var chatViewNameWidth: CGFloat = 0
|
|
||||||
|
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
if #available(iOS 16.0, *) {
|
||||||
Spacer().frame(height: 1)
|
let v = viewBody.presentationDetents([.height(420)])
|
||||||
VStack(spacing: 0) {
|
if #available(iOS 16.4, *) {
|
||||||
ScrollView {
|
v.scrollBounceBehavior(.basedOnSize)
|
||||||
ScrollViewReader { sp in
|
} else {
|
||||||
let users = m.users
|
v
|
||||||
.filter({ u in u.user.activeUser || !u.user.hidden })
|
|
||||||
.sorted { u, _ in u.user.activeUser }
|
|
||||||
VStack(spacing: 0) {
|
|
||||||
ForEach(users) { u in
|
|
||||||
userView(u)
|
|
||||||
Divider()
|
|
||||||
if u.user.activeUser { Divider() }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.overlay {
|
|
||||||
GeometryReader { geo -> Color in
|
|
||||||
DispatchQueue.main.async {
|
|
||||||
scrollViewContentSize = geo.size
|
|
||||||
let scenes = UIApplication.shared.connectedScenes
|
|
||||||
if let windowScene = scenes.first as? UIWindowScene {
|
|
||||||
let layoutFrame = windowScene.windows[0].safeAreaLayoutGuide.layoutFrame
|
|
||||||
disableScrolling = scrollViewContentSize.height + menuButtonHeight + 10 < layoutFrame.height
|
|
||||||
}
|
|
||||||
}
|
|
||||||
return Color.clear
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.onChange(of: userPickerVisible) { visible in
|
|
||||||
if visible, let u = users.first {
|
|
||||||
sp.scrollTo(u.id)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.simultaneousGesture(DragGesture(minimumDistance: disableScrolling ? 0 : 10000000))
|
|
||||||
.frame(maxHeight: scrollViewContentSize.height)
|
|
||||||
|
|
||||||
menuButton("Use from desktop", icon: "desktopcomputer") {
|
|
||||||
showConnectDesktop = true
|
|
||||||
withAnimation {
|
|
||||||
userPickerVisible.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
Divider()
|
|
||||||
menuButton("Settings", icon: "gearshape") {
|
|
||||||
showSettings = true
|
|
||||||
withAnimation {
|
|
||||||
userPickerVisible.toggle()
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.clipShape(RoundedRectangle(cornerRadius: 16))
|
|
||||||
.background(
|
|
||||||
Rectangle()
|
|
||||||
.fill(theme.colors.surface)
|
|
||||||
.cornerRadius(16)
|
|
||||||
.shadow(color: .black.opacity(0.12), radius: 24, x: 0, y: 0)
|
|
||||||
)
|
|
||||||
.onPreferenceChange(DetermineWidth.Key.self) { chatViewNameWidth = $0 }
|
|
||||||
.frame(maxWidth: chatViewNameWidth > 0 ? min(300, chatViewNameWidth + 130) : 300)
|
|
||||||
.padding(8)
|
|
||||||
.opacity(userPickerVisible ? 1.0 : 0.0)
|
|
||||||
.onAppear {
|
|
||||||
do {
|
|
||||||
// This check prevents the call of listUsers after the app is suspended, and the database is closed.
|
|
||||||
if case .active = scenePhase {
|
|
||||||
m.users = try listUsers()
|
|
||||||
}
|
|
||||||
} catch let error {
|
|
||||||
logger.error("Error loading users \(responseError(error))")
|
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
viewBody
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private var viewBody: some View {
|
||||||
|
let otherUsers = m.users.filter { u in !u.user.hidden && u.user.userId != m.currentUser?.userId }
|
||||||
|
return List {
|
||||||
|
Section(header: Text("You").foregroundColor(theme.colors.secondary)) {
|
||||||
|
if let user = m.currentUser {
|
||||||
|
openSheetOnTap(label: {
|
||||||
|
ZStack {
|
||||||
|
let v = ProfilePreview(profileOf: user)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.padding(.leading, -8)
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
v.padding(.vertical, 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}) {
|
||||||
|
activeSheet = .currentProfile
|
||||||
|
}
|
||||||
|
|
||||||
private func userView(_ u: UserInfo) -> some View {
|
openSheetOnTap(title: m.userAddress == nil ? "Create public address" : "Your public address", icon: "qrcode") {
|
||||||
let user = u.user
|
activeSheet = .address
|
||||||
return Button(action: {
|
}
|
||||||
if user.activeUser {
|
|
||||||
showSettings = true
|
openSheetOnTap(title: "Chat preferences", icon: "switch.2") {
|
||||||
withAnimation {
|
activeSheet = .chatPreferences
|
||||||
userPickerVisible.toggle()
|
}
|
||||||
}
|
}
|
||||||
} else {
|
}
|
||||||
|
|
||||||
|
Section {
|
||||||
|
if otherUsers.isEmpty {
|
||||||
|
openSheetOnTap(title: "Your chat profiles", icon: "person.crop.rectangle.stack") {
|
||||||
|
activeSheet = .chatProfiles
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
let v = userPickerRow(otherUsers, size: 44)
|
||||||
|
.padding(.leading, -11)
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
v
|
||||||
|
} else {
|
||||||
|
v.padding(.vertical, 4)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
openSheetOnTap(title: "Use from desktop", icon: "desktopcomputer") {
|
||||||
|
activeSheet = .useFromDesktop
|
||||||
|
}
|
||||||
|
|
||||||
|
ZStack(alignment: .trailing) {
|
||||||
|
openSheetOnTap(title: "Settings", icon: "gearshape") {
|
||||||
|
activeSheet = .settings
|
||||||
|
}
|
||||||
|
Label {} icon: {
|
||||||
|
Image(systemName: colorScheme == .light ? "sun.max" : "moon.fill")
|
||||||
|
.resizable()
|
||||||
|
.symbolRenderingMode(.monochrome)
|
||||||
|
.foregroundColor(theme.colors.secondary)
|
||||||
|
.frame(maxWidth: 20, maxHeight: 20)
|
||||||
|
}
|
||||||
|
.onTapGesture {
|
||||||
|
if (colorScheme == .light) {
|
||||||
|
ThemeManager.applyTheme(systemDarkThemeDefault.get())
|
||||||
|
} else {
|
||||||
|
ThemeManager.applyTheme(DefaultTheme.LIGHT.themeName)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onLongPressGesture {
|
||||||
|
ThemeManager.applyTheme(DefaultTheme.SYSTEM_THEME_NAME)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .topLeading)
|
||||||
|
.onAppear {
|
||||||
|
// This check prevents the call of listUsers after the app is suspended, and the database is closed.
|
||||||
|
if case .active = scenePhase {
|
||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
try await changeActiveUserAsync_(user.userId, viewPwd: nil)
|
let users = try await listUsersAsync()
|
||||||
await MainActor.run { userPickerVisible = false }
|
await MainActor.run { m.users = users }
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run {
|
logger.error("Error loading users \(responseError(error))")
|
||||||
AlertManager.shared.showAlertMsg(
|
|
||||||
title: "Error switching profile!",
|
|
||||||
message: "Error: \(responseError(error))"
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}, label: {
|
}
|
||||||
HStack(spacing: 0) {
|
.modifier(ThemedBackground(grouped: true))
|
||||||
ProfileImage(imageStr: user.image, size: 44, color: Color(uiColor: .tertiarySystemFill))
|
.disabled(switchingProfile)
|
||||||
.padding(.trailing, 12)
|
}
|
||||||
Text(user.chatViewName)
|
|
||||||
.fontWeight(user.activeUser ? .medium : .regular)
|
private func userPickerRow(_ users: [UserInfo], size: CGFloat) -> some View {
|
||||||
.foregroundColor(theme.colors.onBackground)
|
HStack(spacing: 6) {
|
||||||
.overlay(DetermineWidth())
|
let s = ScrollView(.horizontal) {
|
||||||
Spacer()
|
HStack(spacing: 27) {
|
||||||
if user.activeUser {
|
ForEach(users) { u in
|
||||||
Image(systemName: "checkmark")
|
if !u.user.hidden && u.user.userId != m.currentUser?.userId {
|
||||||
} else if u.unreadCount > 0 {
|
userView(u, size: size)
|
||||||
unreadCounter(u.unreadCount, color: user.showNtfs ? theme.colors.primary : theme.colors.secondary)
|
}
|
||||||
} else if !user.showNtfs {
|
}
|
||||||
Image(systemName: "speaker.slash")
|
}
|
||||||
|
.padding(.leading, 4)
|
||||||
|
.padding(.trailing, 22)
|
||||||
|
}
|
||||||
|
ZStack(alignment: .trailing) {
|
||||||
|
if #available(iOS 16.0, *) {
|
||||||
|
s.scrollIndicators(.hidden)
|
||||||
|
} else {
|
||||||
|
s
|
||||||
|
}
|
||||||
|
LinearGradient(
|
||||||
|
colors: [.clear, .black],
|
||||||
|
startPoint: .leading,
|
||||||
|
endPoint: .trailing
|
||||||
|
)
|
||||||
|
.frame(width: size, height: size + 3)
|
||||||
|
.blendMode(.destinationOut)
|
||||||
|
.allowsHitTesting(false)
|
||||||
|
}
|
||||||
|
.compositingGroup()
|
||||||
|
.padding(.top, -3) // to fit unread badge
|
||||||
|
Spacer()
|
||||||
|
Image(systemName: "chevron.right")
|
||||||
|
.foregroundColor(theme.colors.secondary)
|
||||||
|
.padding(.trailing, 4)
|
||||||
|
.onTapGesture {
|
||||||
|
activeSheet = .chatProfiles
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func userView(_ u: UserInfo, size: CGFloat) -> some View {
|
||||||
|
ZStack(alignment: .topTrailing) {
|
||||||
|
ProfileImage(imageStr: u.user.image, size: size, color: Color(uiColor: .tertiarySystemGroupedBackground))
|
||||||
|
.padding([.top, .trailing], 3)
|
||||||
|
if (u.unreadCount > 0) {
|
||||||
|
unreadBadge(u)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.frame(width: size)
|
||||||
|
.onTapGesture {
|
||||||
|
switchingProfile = true
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
try await changeActiveUserAsync_(u.user.userId, viewPwd: nil)
|
||||||
|
await MainActor.run {
|
||||||
|
switchingProfile = false
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
switchingProfile = false
|
||||||
|
AlertManager.shared.showAlertMsg(
|
||||||
|
title: "Error switching profile!",
|
||||||
|
message: "Error: \(responseError(error))"
|
||||||
|
)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(.trailing)
|
}
|
||||||
.padding([.leading, .vertical], 12)
|
|
||||||
})
|
|
||||||
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func menuButton(_ title: LocalizedStringKey, icon: String, action: @escaping () -> Void) -> some View {
|
private func openSheetOnTap(title: LocalizedStringKey, icon: String, action: @escaping () -> Void) -> some View {
|
||||||
Button(action: action) {
|
openSheetOnTap(label: {
|
||||||
HStack(spacing: 0) {
|
ZStack(alignment: .leading) {
|
||||||
Text(title)
|
Image(systemName: icon).frame(maxWidth: 24, maxHeight: 24, alignment: .center)
|
||||||
.overlay(DetermineWidth())
|
|
||||||
Spacer()
|
|
||||||
Image(systemName: icon)
|
|
||||||
.symbolRenderingMode(.monochrome)
|
.symbolRenderingMode(.monochrome)
|
||||||
.foregroundColor(theme.colors.secondary)
|
.foregroundColor(theme.colors.secondary)
|
||||||
|
Text(title)
|
||||||
|
.foregroundColor(.primary)
|
||||||
|
.padding(.leading, 36)
|
||||||
}
|
}
|
||||||
.padding(.horizontal)
|
}, action: action)
|
||||||
.padding(.vertical, 22)
|
}
|
||||||
.frame(height: menuButtonHeight)
|
|
||||||
}
|
private func openSheetOnTap<V: View>(label: () -> V, action: @escaping () -> Void) -> some View {
|
||||||
.buttonStyle(PressedButtonStyle(defaultColor: theme.colors.surface, pressedColor: Color(uiColor: .secondarySystemFill)))
|
Button(action: action, label: label)
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
|
.contentShape(Rectangle())
|
||||||
|
}
|
||||||
|
|
||||||
|
private func unreadBadge(_ u: UserInfo) -> some View {
|
||||||
|
let size = dynamicSize(userFont).chatInfoSize
|
||||||
|
return unreadCountText(u.unreadCount)
|
||||||
|
.font(userFont <= .xxxLarge ? .caption : .caption2)
|
||||||
|
.foregroundColor(.white)
|
||||||
|
.padding(.horizontal, dynamicSize(userFont).unreadPadding)
|
||||||
|
.frame(minWidth: size, minHeight: size)
|
||||||
|
.background(u.user.showNtfs ? theme.colors.primary : theme.colors.secondary)
|
||||||
|
.cornerRadius(dynamicSize(userFont).unreadCorner)
|
||||||
}
|
}
|
||||||
}
|
|
||||||
|
|
||||||
private func unreadCounter(_ unread: Int, color: Color) -> some View {
|
|
||||||
unreadCountText(unread)
|
|
||||||
.font(.caption)
|
|
||||||
.foregroundColor(.white)
|
|
||||||
.padding(.horizontal, 4)
|
|
||||||
.frame(minWidth: 18, minHeight: 18)
|
|
||||||
.background(color)
|
|
||||||
.cornerRadius(10)
|
|
||||||
}
|
}
|
||||||
|
|
||||||
struct UserPicker_Previews: PreviewProvider {
|
struct UserPicker_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
|
@State var activeSheet: UserPickerSheet?
|
||||||
|
|
||||||
let m = ChatModel()
|
let m = ChatModel()
|
||||||
m.users = [UserInfo.sampleData, UserInfo.sampleData]
|
m.users = [UserInfo.sampleData, UserInfo.sampleData]
|
||||||
return UserPicker(
|
return UserPicker(
|
||||||
showSettings: Binding.constant(false),
|
activeSheet: $activeSheet
|
||||||
showConnectDesktop: Binding.constant(false),
|
|
||||||
userPickerVisible: Binding.constant(true)
|
|
||||||
)
|
)
|
||||||
.environmentObject(m)
|
.environmentObject(m)
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -491,7 +491,7 @@ struct DatabaseView: View {
|
|||||||
appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory())
|
appFilesCountAndSize = directoryFileCountAndSize(getAppFilesDirectory())
|
||||||
do {
|
do {
|
||||||
let chats = try apiGetChats()
|
let chats = try apiGetChats()
|
||||||
m.updateChats(with: chats)
|
m.updateChats(chats)
|
||||||
} catch let error {
|
} catch let error {
|
||||||
logger.error("apiGetChats: cannot update chats \(responseError(error))")
|
logger.error("apiGetChats: cannot update chats \(responseError(error))")
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,50 +14,60 @@ import SimpleXChat
|
|||||||
/// Supports [Dynamic Type](https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically)
|
/// Supports [Dynamic Type](https://developer.apple.com/documentation/uikit/uifont/scaling_fonts_automatically)
|
||||||
/// by retaining pill shape, even when ``ChatItem``'s height is less that twice its corner radius
|
/// by retaining pill shape, even when ``ChatItem``'s height is less that twice its corner radius
|
||||||
struct ChatItemClipped: ViewModifier {
|
struct ChatItemClipped: ViewModifier {
|
||||||
struct ClipShape: Shape {
|
@AppStorage(DEFAULT_CHAT_ITEM_ROUNDNESS) private var roundness = defaultChatItemRoundness
|
||||||
let maxCornerRadius: Double
|
@AppStorage(DEFAULT_CHAT_ITEM_TAIL) private var tailEnabled = true
|
||||||
|
private let chatItem: (content: CIContent, chatDir: CIDirection)?
|
||||||
func path(in rect: CGRect) -> Path {
|
private let tailVisible: Bool
|
||||||
Path(
|
|
||||||
roundedRect: rect,
|
|
||||||
cornerRadius: min((rect.height / 2), maxCornerRadius),
|
|
||||||
style: .circular
|
|
||||||
)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
init() {
|
init() {
|
||||||
clipShape = ClipShape(
|
self.chatItem = nil
|
||||||
maxCornerRadius: 18
|
self.tailVisible = false
|
||||||
)
|
}
|
||||||
|
|
||||||
|
init(_ ci: ChatItem, tailVisible: Bool) {
|
||||||
|
self.chatItem = (ci.content, ci.chatDir)
|
||||||
|
self.tailVisible = tailVisible
|
||||||
}
|
}
|
||||||
|
|
||||||
init(_ chatItem: ChatItem) {
|
private func shapeStyle() -> ChatItemShape.Style {
|
||||||
clipShape = ClipShape(
|
if let ci = chatItem {
|
||||||
maxCornerRadius: {
|
switch ci.content {
|
||||||
switch chatItem.content {
|
case
|
||||||
case
|
.sndMsgContent,
|
||||||
.sndMsgContent,
|
|
||||||
.rcvMsgContent,
|
.rcvMsgContent,
|
||||||
.rcvDecryptionError,
|
.rcvDecryptionError,
|
||||||
.rcvGroupInvitation,
|
.sndDeleted,
|
||||||
.sndGroupInvitation,
|
|
||||||
.sndDeleted,
|
|
||||||
.rcvDeleted,
|
.rcvDeleted,
|
||||||
.rcvIntegrityError,
|
.rcvIntegrityError,
|
||||||
.sndModerated,
|
.sndModerated,
|
||||||
.rcvModerated,
|
.rcvModerated,
|
||||||
.rcvBlocked,
|
.rcvBlocked,
|
||||||
.invalidJSON: 18
|
.invalidJSON:
|
||||||
default: 8
|
let tail = if let mc = ci.content.msgContent, mc.isImageOrVideo && mc.text.isEmpty {
|
||||||
|
false
|
||||||
|
} else {
|
||||||
|
tailVisible
|
||||||
}
|
}
|
||||||
}()
|
return tailEnabled
|
||||||
)
|
? .bubble(
|
||||||
|
padding: ci.chatDir.sent ? .trailing : .leading,
|
||||||
|
tailVisible: tail
|
||||||
|
)
|
||||||
|
: .roundRect(radius: msgRectMaxRadius)
|
||||||
|
case .rcvGroupInvitation, .sndGroupInvitation:
|
||||||
|
return .roundRect(radius: msgRectMaxRadius)
|
||||||
|
default: return .roundRect(radius: 8)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
return .roundRect(radius: msgRectMaxRadius)
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private let clipShape: ClipShape
|
|
||||||
|
|
||||||
func body(content: Content) -> some View {
|
func body(content: Content) -> some View {
|
||||||
|
let clipShape = ChatItemShape(
|
||||||
|
roundness: roundness,
|
||||||
|
style: shapeStyle()
|
||||||
|
)
|
||||||
content
|
content
|
||||||
.contentShape(.dragPreview, clipShape)
|
.contentShape(.dragPreview, clipShape)
|
||||||
.contentShape(.contextMenuPreview, clipShape)
|
.contentShape(.contextMenuPreview, clipShape)
|
||||||
@@ -65,4 +75,106 @@ struct ChatItemClipped: ViewModifier {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
struct ChatTailPadding: ViewModifier {
|
||||||
|
func body(content: Content) -> some View {
|
||||||
|
content.padding(.horizontal, -msgTailWidth)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private let msgRectMaxRadius: Double = 18
|
||||||
|
private let msgBubbleMaxRadius: Double = msgRectMaxRadius * 1.2
|
||||||
|
private let msgTailWidth: Double = 9
|
||||||
|
private let msgTailMinHeight: Double = msgTailWidth * 1.254 // ~56deg
|
||||||
|
private let msgTailMaxHeight: Double = msgTailWidth * 1.732 // 60deg
|
||||||
|
|
||||||
|
struct ChatItemShape: Shape {
|
||||||
|
fileprivate enum Style {
|
||||||
|
case bubble(padding: HorizontalEdge, tailVisible: Bool)
|
||||||
|
case roundRect(radius: Double)
|
||||||
|
}
|
||||||
|
|
||||||
|
fileprivate let roundness: Double
|
||||||
|
fileprivate let style: Style
|
||||||
|
|
||||||
|
func path(in rect: CGRect) -> Path {
|
||||||
|
switch style {
|
||||||
|
case let .bubble(padding, tailVisible):
|
||||||
|
let w = rect.width
|
||||||
|
let h = rect.height
|
||||||
|
let rxMax = min(msgBubbleMaxRadius, w / 2)
|
||||||
|
let ryMax = min(msgBubbleMaxRadius, h / 2)
|
||||||
|
let rx = roundness * rxMax
|
||||||
|
let ry = roundness * ryMax
|
||||||
|
let tailHeight = min(msgTailMinHeight + roundness * (msgTailMaxHeight - msgTailMinHeight), h / 2)
|
||||||
|
var path = Path()
|
||||||
|
// top side
|
||||||
|
path.move(to: CGPoint(x: rx, y: 0))
|
||||||
|
path.addLine(to: CGPoint(x: w - rx, y: 0))
|
||||||
|
if roundness > 0 {
|
||||||
|
// top-right corner
|
||||||
|
path.addQuadCurve(to: CGPoint(x: w, y: ry), control: CGPoint(x: w, y: 0))
|
||||||
|
}
|
||||||
|
if rect.height > 2 * ry {
|
||||||
|
// right side
|
||||||
|
path.addLine(to: CGPoint(x: w, y: h - ry))
|
||||||
|
}
|
||||||
|
if roundness > 0 {
|
||||||
|
// bottom-right corner
|
||||||
|
path.addQuadCurve(to: CGPoint(x: w - rx, y: h), control: CGPoint(x: w, y: h))
|
||||||
|
}
|
||||||
|
// bottom side
|
||||||
|
if tailVisible {
|
||||||
|
path.addLine(to: CGPoint(x: -msgTailWidth, y: h))
|
||||||
|
if roundness > 0 {
|
||||||
|
// bottom-left tail
|
||||||
|
// distance of control point from touch point, calculated via ratios
|
||||||
|
let d = tailHeight - msgTailWidth * msgTailWidth / tailHeight
|
||||||
|
// tail control point
|
||||||
|
let tc = CGPoint(x: 0, y: h - tailHeight + d * sqrt(roundness))
|
||||||
|
// bottom-left tail curve
|
||||||
|
path.addQuadCurve(to: CGPoint(x: 0, y: h - tailHeight), control: tc)
|
||||||
|
} else {
|
||||||
|
path.addLine(to: CGPoint(x: 0, y: h - tailHeight))
|
||||||
|
}
|
||||||
|
if rect.height > ry + tailHeight {
|
||||||
|
// left side
|
||||||
|
path.addLine(to: CGPoint(x: 0, y: ry))
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
path.addLine(to: CGPoint(x: rx, y: h))
|
||||||
|
path.addQuadCurve(to: CGPoint(x: 0, y: h - ry), control: CGPoint(x: 0 , y: h))
|
||||||
|
if rect.height > 2 * ry {
|
||||||
|
// left side
|
||||||
|
path.addLine(to: CGPoint(x: 0, y: ry))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
if roundness > 0 {
|
||||||
|
// top-left corner
|
||||||
|
path.addQuadCurve(to: CGPoint(x: rx, y: 0), control: CGPoint(x: 0, y: 0))
|
||||||
|
}
|
||||||
|
path.closeSubpath()
|
||||||
|
return switch padding {
|
||||||
|
case .leading: path
|
||||||
|
case .trailing: path
|
||||||
|
.scale(x: -1, y: 1, anchor: .center)
|
||||||
|
.path(in: rect)
|
||||||
|
}
|
||||||
|
case let .roundRect(radius):
|
||||||
|
return Path(roundedRect: rect, cornerRadius: radius * roundness)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
var offset: Double? {
|
||||||
|
switch style {
|
||||||
|
case let .bubble(padding, isTailVisible):
|
||||||
|
if isTailVisible {
|
||||||
|
switch padding {
|
||||||
|
case .leading: -msgTailWidth
|
||||||
|
case .trailing: msgTailWidth
|
||||||
|
}
|
||||||
|
} else { 0 }
|
||||||
|
case .roundRect: 0
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
}
|
||||||
|
|||||||
@@ -100,7 +100,7 @@ extension PresetWallpaper {
|
|||||||
scale
|
scale
|
||||||
} else if let type = ChatModel.shared.currentUser?.uiThemes?.preferredMode(base.mode == DefaultThemeMode.dark)?.wallpaper?.toAppWallpaper().type, type.sameType(WallpaperType.preset(filename, nil)) {
|
} else if let type = ChatModel.shared.currentUser?.uiThemes?.preferredMode(base.mode == DefaultThemeMode.dark)?.wallpaper?.toAppWallpaper().type, type.sameType(WallpaperType.preset(filename, nil)) {
|
||||||
type.scale
|
type.scale
|
||||||
} else if let scale = themeOverridesDefault.get().first(where: { $0.wallpaper != nil && $0.wallpaper!.preset == filename })?.wallpaper?.scale {
|
} else if let scale = themeOverridesDefault.get().first(where: { $0.wallpaper != nil && $0.wallpaper!.preset == filename && $0.base == base })?.wallpaper?.scale {
|
||||||
scale
|
scale
|
||||||
} else {
|
} else {
|
||||||
Float(1.0)
|
Float(1.0)
|
||||||
|
|||||||
@@ -0,0 +1,21 @@
|
|||||||
|
//
|
||||||
|
// Test.swift
|
||||||
|
// SimpleX (iOS)
|
||||||
|
//
|
||||||
|
// Created by Levitating Pineapple on 31/08/2024.
|
||||||
|
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
import SwiftUI
|
||||||
|
|
||||||
|
extension View {
|
||||||
|
@ViewBuilder
|
||||||
|
func invertedForegroundStyle(enabled: Bool = true) -> some View {
|
||||||
|
if enabled {
|
||||||
|
foregroundStyle(Material.ultraThin)
|
||||||
|
.environment(\.colorScheme, .dark)
|
||||||
|
.grayscale(1)
|
||||||
|
.contrast(-20)
|
||||||
|
} else { self }
|
||||||
|
}
|
||||||
|
}
|
||||||
@@ -20,7 +20,7 @@ struct ProfileImage: View {
|
|||||||
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
|
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
if let uiImage = UIImage(base64Encoded: imageStr) {
|
if let uiImage = imageFromBase64(imageStr) {
|
||||||
clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius, blurred: blurred)
|
clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius, blurred: blurred)
|
||||||
} else {
|
} else {
|
||||||
let c = color.asAnotherColorFromSecondaryVariant(theme)
|
let c = color.asAnotherColorFromSecondaryVariant(theme)
|
||||||
|
|||||||
@@ -8,15 +8,47 @@
|
|||||||
|
|
||||||
import SwiftUI
|
import SwiftUI
|
||||||
|
|
||||||
func showShareSheet(items: [Any], completed: (() -> Void)? = nil) {
|
func getTopViewController() -> UIViewController? {
|
||||||
let keyWindowScene = UIApplication.shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene
|
let keyWindowScene = UIApplication.shared.connectedScenes.first { $0.activationState == .foregroundActive } as? UIWindowScene
|
||||||
if let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first,
|
if let keyWindow = keyWindowScene?.windows.filter(\.isKeyWindow).first,
|
||||||
let presentedViewController = keyWindow.rootViewController?.presentedViewController ?? keyWindow.rootViewController {
|
let rootViewController = keyWindow.rootViewController {
|
||||||
|
// Find the top-most presented view controller
|
||||||
|
var topController = rootViewController
|
||||||
|
while let presentedViewController = topController.presentedViewController {
|
||||||
|
topController = presentedViewController
|
||||||
|
}
|
||||||
|
return topController
|
||||||
|
}
|
||||||
|
return nil
|
||||||
|
}
|
||||||
|
|
||||||
|
func showShareSheet(items: [Any], completed: (() -> Void)? = nil) {
|
||||||
|
if let topController = getTopViewController() {
|
||||||
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
let activityViewController = UIActivityViewController(activityItems: items, applicationActivities: nil)
|
||||||
if let completed = completed {
|
if let completed = completed {
|
||||||
let handler: UIActivityViewController.CompletionWithItemsHandler = { _,_,_,_ in completed() }
|
activityViewController.completionWithItemsHandler = { _, _, _, _ in
|
||||||
activityViewController.completionWithItemsHandler = handler
|
completed()
|
||||||
}
|
}
|
||||||
presentedViewController.present(activityViewController, animated: true)
|
}
|
||||||
|
topController.present(activityViewController, animated: true)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
func showAlert(
|
||||||
|
title: String,
|
||||||
|
message: String? = nil,
|
||||||
|
buttonTitle: String,
|
||||||
|
buttonAction: @escaping () -> Void,
|
||||||
|
cancelButton: Bool
|
||||||
|
) -> Void {
|
||||||
|
if let topController = getTopViewController() {
|
||||||
|
let alert = UIAlertController(title: title, message: message, preferredStyle: .alert)
|
||||||
|
alert.addAction(UIAlertAction(title: buttonTitle, style: .default) { _ in
|
||||||
|
buttonAction()
|
||||||
|
})
|
||||||
|
if cancelButton {
|
||||||
|
alert.addAction(UIAlertAction(title: NSLocalizedString("Cancel", comment: "alert button"), style: .cancel))
|
||||||
|
}
|
||||||
|
topController.present(alert, animated: true)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -65,8 +65,7 @@ struct LocalAuthView: View {
|
|||||||
// Clear sensitive data on screen just in case app fails to hide its views while new database is created
|
// Clear sensitive data on screen just in case app fails to hide its views while new database is created
|
||||||
m.chatId = nil
|
m.chatId = nil
|
||||||
ItemsModel.shared.reversedChatItems = []
|
ItemsModel.shared.reversedChatItems = []
|
||||||
m.chats = []
|
m.updateChats([])
|
||||||
m.popChatCollector.clear()
|
|
||||||
m.users = []
|
m.users = []
|
||||||
_ = kcAppPassword.set(password)
|
_ = kcAppPassword.set(password)
|
||||||
_ = kcSelfDestructPassword.remove()
|
_ = kcSelfDestructPassword.remove()
|
||||||
|
|||||||
@@ -24,6 +24,7 @@ private enum MigrationFromState: Equatable {
|
|||||||
}
|
}
|
||||||
|
|
||||||
private enum MigrateFromDeviceViewAlert: Identifiable {
|
private enum MigrateFromDeviceViewAlert: Identifiable {
|
||||||
|
case finishMigration(_ fileId: Int64, _ ctrl: chat_ctrl)
|
||||||
case deleteChat(_ title: LocalizedStringKey = "Delete chat profile?", _ text: LocalizedStringKey = "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.")
|
case deleteChat(_ title: LocalizedStringKey = "Delete chat profile?", _ text: LocalizedStringKey = "This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.")
|
||||||
case startChat(_ title: LocalizedStringKey = "Start chat?", _ text: LocalizedStringKey = "Warning: starting chat on multiple devices is not supported and will cause message delivery failures")
|
case startChat(_ title: LocalizedStringKey = "Start chat?", _ text: LocalizedStringKey = "Warning: starting chat on multiple devices is not supported and will cause message delivery failures")
|
||||||
|
|
||||||
@@ -38,6 +39,7 @@ private enum MigrateFromDeviceViewAlert: Identifiable {
|
|||||||
|
|
||||||
var id: String {
|
var id: String {
|
||||||
switch self {
|
switch self {
|
||||||
|
case .finishMigration: return "finishMigration"
|
||||||
case let .deleteChat(title, text): return "\(title) \(text)"
|
case let .deleteChat(title, text): return "\(title) \(text)"
|
||||||
case let .startChat(title, text): return "\(title) \(text)"
|
case let .startChat(title, text): return "\(title) \(text)"
|
||||||
|
|
||||||
@@ -56,8 +58,6 @@ private enum MigrateFromDeviceViewAlert: Identifiable {
|
|||||||
struct MigrateFromDevice: View {
|
struct MigrateFromDevice: View {
|
||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@Environment(\.dismiss) var dismiss: DismissAction
|
|
||||||
@Binding var showSettings: Bool
|
|
||||||
@Binding var showProgressOnSettings: Bool
|
@Binding var showProgressOnSettings: Bool
|
||||||
@State private var migrationState: MigrationFromState = .chatStopInProgress
|
@State private var migrationState: MigrationFromState = .chatStopInProgress
|
||||||
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
||||||
@@ -106,9 +106,6 @@ struct MigrateFromDevice: View {
|
|||||||
finishedView(chatDeletion)
|
finishedView(chatDeletion)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.modifier(BackButton(label: "Back", disabled: $backDisabled) {
|
|
||||||
dismiss()
|
|
||||||
})
|
|
||||||
.onChange(of: migrationState) { state in
|
.onChange(of: migrationState) { state in
|
||||||
backDisabled = switch migrationState {
|
backDisabled = switch migrationState {
|
||||||
case .chatStopInProgress, .archiving, .linkShown, .finished: true
|
case .chatStopInProgress, .archiving, .linkShown, .finished: true
|
||||||
@@ -138,6 +135,15 @@ struct MigrateFromDevice: View {
|
|||||||
}
|
}
|
||||||
.alert(item: $alert) { alert in
|
.alert(item: $alert) { alert in
|
||||||
switch alert {
|
switch alert {
|
||||||
|
case let .finishMigration(fileId, ctrl):
|
||||||
|
return Alert(
|
||||||
|
title: Text("Remove archive?"),
|
||||||
|
message: Text("The uploaded database archive will be permanently removed from the servers."),
|
||||||
|
primaryButton: .destructive(Text("Continue")) {
|
||||||
|
finishMigration(fileId, ctrl)
|
||||||
|
},
|
||||||
|
secondaryButton: .cancel()
|
||||||
|
)
|
||||||
case let .startChat(title, text):
|
case let .startChat(title, text):
|
||||||
return Alert(
|
return Alert(
|
||||||
title: Text(title),
|
title: Text(title),
|
||||||
@@ -318,7 +324,7 @@ struct MigrateFromDevice: View {
|
|||||||
Text("Cancel migration").foregroundColor(.red)
|
Text("Cancel migration").foregroundColor(.red)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
Button(action: { finishMigration(fileId, ctrl) }) {
|
Button(action: { alert = .finishMigration(fileId, ctrl) }) {
|
||||||
settingsRow("checkmark", color: theme.colors.secondary) {
|
settingsRow("checkmark", color: theme.colors.secondary) {
|
||||||
Text("Finalize migration").foregroundColor(theme.colors.primary)
|
Text("Finalize migration").foregroundColor(theme.colors.primary)
|
||||||
}
|
}
|
||||||
@@ -590,7 +596,7 @@ struct MigrateFromDevice: View {
|
|||||||
} catch let error {
|
} catch let error {
|
||||||
fatalError("Error starting chat \(responseError(error))")
|
fatalError("Error starting chat \(responseError(error))")
|
||||||
}
|
}
|
||||||
showSettings = false
|
dismissAllSheets(animated: true)
|
||||||
}
|
}
|
||||||
} catch let error {
|
} catch let error {
|
||||||
alert = .error(title: "Error deleting database", error: responseError(error))
|
alert = .error(title: "Error deleting database", error: responseError(error))
|
||||||
@@ -613,9 +619,7 @@ struct MigrateFromDevice: View {
|
|||||||
}
|
}
|
||||||
// Hide settings anyway if chatDbStatus is not ok, probably passphrase needs to be entered
|
// Hide settings anyway if chatDbStatus is not ok, probably passphrase needs to be entered
|
||||||
if dismiss || m.chatDbStatus != .ok {
|
if dismiss || m.chatDbStatus != .ok {
|
||||||
await MainActor.run {
|
dismissAllSheets(animated: true)
|
||||||
showSettings = false
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -767,6 +771,6 @@ private class MigrationChatReceiver {
|
|||||||
|
|
||||||
struct MigrateFromDevice_Previews: PreviewProvider {
|
struct MigrateFromDevice_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
MigrateFromDevice(showSettings: Binding.constant(true), showProgressOnSettings: Binding.constant(false))
|
MigrateFromDevice(showProgressOnSettings: Binding.constant(false))
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -93,7 +93,6 @@ struct MigrateToDevice: View {
|
|||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@Environment(\.dismiss) var dismiss: DismissAction
|
@Environment(\.dismiss) var dismiss: DismissAction
|
||||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
|
||||||
@Binding var migrationState: MigrationToState?
|
@Binding var migrationState: MigrationToState?
|
||||||
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
@State private var useKeychain = storeDBPassphraseGroupDefault.get()
|
||||||
@State private var alert: MigrateToDeviceViewAlert?
|
@State private var alert: MigrateToDeviceViewAlert?
|
||||||
@@ -102,6 +101,7 @@ struct MigrateToDevice: View {
|
|||||||
// Prevent from hiding the view until migration is finished or app deleted
|
// Prevent from hiding the view until migration is finished or app deleted
|
||||||
@State private var backDisabled: Bool = false
|
@State private var backDisabled: Bool = false
|
||||||
@State private var showQRCodeScanner: Bool = true
|
@State private var showQRCodeScanner: Bool = true
|
||||||
|
@State private var pasteboardHasStrings = UIPasteboard.general.hasStrings
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack {
|
VStack {
|
||||||
@@ -197,10 +197,8 @@ struct MigrateToDevice: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
if developerTools {
|
Section(header: Text("Or paste archive link").foregroundColor(theme.colors.secondary)) {
|
||||||
Section(header: Text("Or paste archive link").foregroundColor(theme.colors.secondary)) {
|
pasteLinkView()
|
||||||
pasteLinkView()
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -218,7 +216,7 @@ struct MigrateToDevice: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Text("Tap to paste link")
|
Text("Tap to paste link")
|
||||||
}
|
}
|
||||||
.disabled(!ChatModel.shared.pasteboardHasStrings)
|
.disabled(!pasteboardHasStrings)
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -487,6 +485,9 @@ struct MigrateToDevice: View {
|
|||||||
do {
|
do {
|
||||||
if !hasChatCtrl() {
|
if !hasChatCtrl() {
|
||||||
chatInitControllerRemovingDatabases()
|
chatInitControllerRemovingDatabases()
|
||||||
|
} else if ChatModel.shared.chatRunning == true {
|
||||||
|
// cannot delete storage if chat is running
|
||||||
|
try await apiStopChat()
|
||||||
}
|
}
|
||||||
try await apiDeleteStorage()
|
try await apiDeleteStorage()
|
||||||
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
try? FileManager.default.createDirectory(at: getWallpaperDirectory(), withIntermediateDirectories: true)
|
||||||
@@ -556,11 +557,22 @@ struct MigrateToDevice: View {
|
|||||||
do {
|
do {
|
||||||
try? FileManager.default.removeItem(at: getMigrationTempFilesDirectory())
|
try? FileManager.default.removeItem(at: getMigrationTempFilesDirectory())
|
||||||
MigrationToDeviceState.save(nil)
|
MigrationToDeviceState.save(nil)
|
||||||
appSettings.importIntoApp()
|
try ObjC.catchException {
|
||||||
try SimpleX.startChat(refreshInvitations: true)
|
appSettings.importIntoApp()
|
||||||
AlertManager.shared.showAlertMsg(title: "Chat migrated!", message: "Finalize migration on another device.")
|
}
|
||||||
|
do {
|
||||||
|
try SimpleX.startChat(refreshInvitations: true)
|
||||||
|
AlertManager.shared.showAlertMsg(title: "Chat migrated!", message: "Finalize migration on another device.")
|
||||||
|
} catch let error {
|
||||||
|
AlertManager.shared.showAlert(Alert(title: Text("Error starting chat"), message: Text(responseError(error))))
|
||||||
|
}
|
||||||
} catch let error {
|
} catch let error {
|
||||||
AlertManager.shared.showAlert(Alert(title: Text("Error starting chat"), message: Text(responseError(error))))
|
logger.error("Error importing settings: \(error.localizedDescription)")
|
||||||
|
AlertManager.shared.showAlert(
|
||||||
|
Alert(
|
||||||
|
title: Text("Error migrating settings"),
|
||||||
|
message: Text ("Not all settings were migrated. Repeat migration if you need them.") + Text("\n\n") + Text(responseError(error)))
|
||||||
|
)
|
||||||
}
|
}
|
||||||
hideView()
|
hideView()
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -28,7 +28,9 @@ struct AddContactLearnMore: View {
|
|||||||
Text("If you can't meet in person, show QR code in a video call, or share the link.")
|
Text("If you can't meet in person, show QR code in a video call, or share the link.")
|
||||||
Text("Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).")
|
Text("Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).")
|
||||||
}
|
}
|
||||||
|
.frame(maxWidth: .infinity, alignment: .leading)
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
|
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||||
}
|
}
|
||||||
.modifier(ThemedBackground(grouped: true))
|
.modifier(ThemedBackground(grouped: true))
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -14,9 +14,10 @@ enum ContactType: Int {
|
|||||||
}
|
}
|
||||||
|
|
||||||
struct NewChatMenuButton: View {
|
struct NewChatMenuButton: View {
|
||||||
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
@State private var showNewChatSheet = false
|
@State private var showNewChatSheet = false
|
||||||
@State private var alert: SomeAlert? = nil
|
@State private var alert: SomeAlert? = nil
|
||||||
@State private var globalAlert: SomeAlert? = nil
|
@State private var pendingConnection: PendingContactConnection? = nil
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
Button {
|
Button {
|
||||||
@@ -28,22 +29,14 @@ struct NewChatMenuButton: View {
|
|||||||
.frame(width: 24, height: 24)
|
.frame(width: 24, height: 24)
|
||||||
}
|
}
|
||||||
.appSheet(isPresented: $showNewChatSheet) {
|
.appSheet(isPresented: $showNewChatSheet) {
|
||||||
NewChatSheet(alert: $alert)
|
NewChatSheet(pendingConnection: $pendingConnection)
|
||||||
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
|
.environment(\EnvironmentValues.refresh as! WritableKeyPath<EnvironmentValues, RefreshAction?>, nil)
|
||||||
.alert(item: $alert) { a in
|
.onDisappear {
|
||||||
return a.alert
|
alert = cleanupPendingConnection(chatModel: chatModel, contactConnection: pendingConnection)
|
||||||
|
pendingConnection = nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
// This is a workaround to show "Keep unused invitation" alert in both following cases:
|
.alert(item: $alert) { a in
|
||||||
// - on going back from NewChatView to NewChatSheet,
|
|
||||||
// - on dismissing NewChatMenuButton sheet while on NewChatView (skipping NewChatSheet)
|
|
||||||
.onChange(of: alert?.id) { a in
|
|
||||||
if !showNewChatSheet && alert != nil {
|
|
||||||
globalAlert = alert
|
|
||||||
alert = nil
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.alert(item: $globalAlert) { a in
|
|
||||||
return a.alert
|
return a.alert
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -60,7 +53,8 @@ struct NewChatSheet: View {
|
|||||||
@State private var searchText = ""
|
@State private var searchText = ""
|
||||||
@State private var searchShowingSimplexLink = false
|
@State private var searchShowingSimplexLink = false
|
||||||
@State private var searchChatFilteredBySimplexLink: String? = nil
|
@State private var searchChatFilteredBySimplexLink: String? = nil
|
||||||
@Binding var alert: SomeAlert?
|
@State private var alert: SomeAlert?
|
||||||
|
@Binding var pendingConnection: PendingContactConnection?
|
||||||
|
|
||||||
// Sheet height management
|
// Sheet height management
|
||||||
@State private var isAddContactActive = false
|
@State private var isAddContactActive = false
|
||||||
@@ -78,6 +72,9 @@ struct NewChatSheet: View {
|
|||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
.navigationBarHidden(searchMode)
|
.navigationBarHidden(searchMode)
|
||||||
.modifier(ThemedBackground(grouped: true))
|
.modifier(ThemedBackground(grouped: true))
|
||||||
|
.alert(item: $alert) { a in
|
||||||
|
return a.alert
|
||||||
|
}
|
||||||
}
|
}
|
||||||
if #available(iOS 16.0, *), oneHandUI {
|
if #available(iOS 16.0, *), oneHandUI {
|
||||||
let sheetHeight: CGFloat = showArchive ? 575 : 500
|
let sheetHeight: CGFloat = showArchive ? 575 : 500
|
||||||
@@ -112,7 +109,7 @@ struct NewChatSheet: View {
|
|||||||
if (searchText.isEmpty) {
|
if (searchText.isEmpty) {
|
||||||
Section {
|
Section {
|
||||||
NavigationLink(isActive: $isAddContactActive) {
|
NavigationLink(isActive: $isAddContactActive) {
|
||||||
NewChatView(selection: .invite, parentAlert: $alert)
|
NewChatView(selection: .invite, parentAlert: $alert, contactConnection: $pendingConnection)
|
||||||
.navigationTitle("New chat")
|
.navigationTitle("New chat")
|
||||||
.modifier(ThemedBackground(grouped: true))
|
.modifier(ThemedBackground(grouped: true))
|
||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
@@ -122,7 +119,7 @@ struct NewChatSheet: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
NavigationLink(isActive: $isScanPasteLinkActive) {
|
NavigationLink(isActive: $isScanPasteLinkActive) {
|
||||||
NewChatView(selection: .connect, showQRCodeScanner: true, parentAlert: $alert)
|
NewChatView(selection: .connect, showQRCodeScanner: true, parentAlert: $alert, contactConnection: $pendingConnection)
|
||||||
.navigationTitle("New chat")
|
.navigationTitle("New chat")
|
||||||
.modifier(ThemedBackground(grouped: true))
|
.modifier(ThemedBackground(grouped: true))
|
||||||
.navigationBarTitleDisplayMode(.large)
|
.navigationBarTitleDisplayMode(.large)
|
||||||
@@ -199,7 +196,7 @@ func chatContactType(chat: Chat) -> ContactType {
|
|||||||
case .contactRequest:
|
case .contactRequest:
|
||||||
return .request
|
return .request
|
||||||
case let .direct(contact):
|
case let .direct(contact):
|
||||||
if contact.activeConn == nil && contact.profile.contactLink != nil {
|
if contact.activeConn == nil && contact.profile.contactLink != nil && contact.active {
|
||||||
return .card
|
return .card
|
||||||
} else if contact.chatDeleted {
|
} else if contact.chatDeleted {
|
||||||
return .chatDeleted
|
return .chatDeleted
|
||||||
|
|||||||
@@ -45,18 +45,47 @@ enum NewChatOption: Identifiable {
|
|||||||
var id: Self { self }
|
var id: Self { self }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func cleanupPendingConnection(chatModel: ChatModel, contactConnection: PendingContactConnection?) -> SomeAlert? {
|
||||||
|
var alert: SomeAlert? = nil
|
||||||
|
|
||||||
|
if !(chatModel.showingInvitation?.connChatUsed ?? true),
|
||||||
|
let conn = contactConnection {
|
||||||
|
alert = SomeAlert(
|
||||||
|
alert: Alert(
|
||||||
|
title: Text("Keep unused invitation?"),
|
||||||
|
message: Text("You can view invitation link again in connection details."),
|
||||||
|
primaryButton: .default(Text("Keep")) {},
|
||||||
|
secondaryButton: .destructive(Text("Delete")) {
|
||||||
|
Task {
|
||||||
|
await deleteChat(Chat(
|
||||||
|
chatInfo: .contactConnection(contactConnection: conn),
|
||||||
|
chatItems: []
|
||||||
|
))
|
||||||
|
}
|
||||||
|
}
|
||||||
|
),
|
||||||
|
id: "keepUnusedInvitation"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
|
||||||
|
chatModel.showingInvitation = nil
|
||||||
|
|
||||||
|
return alert
|
||||||
|
}
|
||||||
|
|
||||||
struct NewChatView: View {
|
struct NewChatView: View {
|
||||||
@EnvironmentObject var m: ChatModel
|
@EnvironmentObject var m: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@State var selection: NewChatOption
|
@State var selection: NewChatOption
|
||||||
@State var showQRCodeScanner = false
|
@State var showQRCodeScanner = false
|
||||||
@State private var invitationUsed: Bool = false
|
@State private var invitationUsed: Bool = false
|
||||||
@State private var contactConnection: PendingContactConnection? = nil
|
|
||||||
@State private var connReqInvitation: String = ""
|
@State private var connReqInvitation: String = ""
|
||||||
@State private var creatingConnReq = false
|
@State private var creatingConnReq = false
|
||||||
|
@State var choosingProfile = false
|
||||||
@State private var pastedLink: String = ""
|
@State private var pastedLink: String = ""
|
||||||
@State private var alert: NewChatViewAlert?
|
@State private var alert: NewChatViewAlert?
|
||||||
@Binding var parentAlert: SomeAlert?
|
@Binding var parentAlert: SomeAlert?
|
||||||
|
@Binding var contactConnection: PendingContactConnection?
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
VStack(alignment: .leading) {
|
VStack(alignment: .leading) {
|
||||||
@@ -122,26 +151,10 @@ struct NewChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onDisappear {
|
.onDisappear {
|
||||||
if !(m.showingInvitation?.connChatUsed ?? true),
|
if !choosingProfile {
|
||||||
let conn = contactConnection {
|
parentAlert = cleanupPendingConnection(chatModel: m, contactConnection: contactConnection)
|
||||||
parentAlert = SomeAlert(
|
contactConnection = nil
|
||||||
alert: Alert(
|
|
||||||
title: Text("Keep unused invitation?"),
|
|
||||||
message: Text("You can view invitation link again in connection details."),
|
|
||||||
primaryButton: .default(Text("Keep")) {},
|
|
||||||
secondaryButton: .destructive(Text("Delete")) {
|
|
||||||
Task {
|
|
||||||
await deleteChat(Chat(
|
|
||||||
chatInfo: .contactConnection(contactConnection: conn),
|
|
||||||
chatItems: []
|
|
||||||
))
|
|
||||||
}
|
|
||||||
}
|
|
||||||
),
|
|
||||||
id: "keepUnusedInvitation"
|
|
||||||
)
|
|
||||||
}
|
}
|
||||||
m.showingInvitation = nil
|
|
||||||
}
|
}
|
||||||
.alert(item: $alert) { a in
|
.alert(item: $alert) { a in
|
||||||
switch(a) {
|
switch(a) {
|
||||||
@@ -159,7 +172,8 @@ struct NewChatView: View {
|
|||||||
InviteView(
|
InviteView(
|
||||||
invitationUsed: $invitationUsed,
|
invitationUsed: $invitationUsed,
|
||||||
contactConnection: $contactConnection,
|
contactConnection: $contactConnection,
|
||||||
connReqInvitation: connReqInvitation
|
connReqInvitation: $connReqInvitation,
|
||||||
|
choosingProfile: $choosingProfile
|
||||||
)
|
)
|
||||||
} else if creatingConnReq {
|
} else if creatingConnReq {
|
||||||
creatingLinkProgressView()
|
creatingLinkProgressView()
|
||||||
@@ -210,13 +224,24 @@ struct NewChatView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func incognitoProfileImage() -> some View {
|
||||||
|
Image(systemName: "theatermasks.fill")
|
||||||
|
.resizable()
|
||||||
|
.scaledToFit()
|
||||||
|
.frame(width: 30)
|
||||||
|
.foregroundColor(.indigo)
|
||||||
|
}
|
||||||
|
|
||||||
private struct InviteView: View {
|
private struct InviteView: View {
|
||||||
@EnvironmentObject var chatModel: ChatModel
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@Binding var invitationUsed: Bool
|
@Binding var invitationUsed: Bool
|
||||||
@Binding var contactConnection: PendingContactConnection?
|
@Binding var contactConnection: PendingContactConnection?
|
||||||
var connReqInvitation: String
|
@Binding var connReqInvitation: String
|
||||||
|
@Binding var choosingProfile: Bool
|
||||||
|
|
||||||
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
|
@AppStorage(GROUP_DEFAULT_INCOGNITO, store: groupDefaults) private var incognitoDefault = false
|
||||||
|
@State private var showSettings: Bool = false
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
@@ -226,28 +251,40 @@ private struct InviteView: View {
|
|||||||
.listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10))
|
.listRowInsets(EdgeInsets(top: 0, leading: 20, bottom: 0, trailing: 10))
|
||||||
|
|
||||||
qrCodeView()
|
qrCodeView()
|
||||||
|
if let selectedProfile = chatModel.currentUser {
|
||||||
Section {
|
Section {
|
||||||
IncognitoToggle(incognitoEnabled: $incognitoDefault)
|
NavigationLink {
|
||||||
} footer: {
|
ActiveProfilePicker(
|
||||||
sharedProfileInfo(incognitoDefault)
|
contactConnection: $contactConnection,
|
||||||
.foregroundColor(theme.colors.secondary)
|
connReqInvitation: $connReqInvitation,
|
||||||
|
incognitoEnabled: $incognitoDefault,
|
||||||
|
choosingProfile: $choosingProfile,
|
||||||
|
selectedProfile: selectedProfile
|
||||||
|
)
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
if incognitoDefault {
|
||||||
|
incognitoProfileImage()
|
||||||
|
Text("Incognito")
|
||||||
|
} else {
|
||||||
|
ProfileImage(imageStr: chatModel.currentUser?.image, size: 30)
|
||||||
|
Text(chatModel.currentUser?.chatViewName ?? "")
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} header: {
|
||||||
|
Text("Share profile").foregroundColor(theme.colors.secondary)
|
||||||
|
} footer: {
|
||||||
|
if incognitoDefault {
|
||||||
|
Text("A new random profile will be shared.")
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.onChange(of: incognitoDefault) { incognito in
|
.onChange(of: incognitoDefault) { incognito in
|
||||||
Task {
|
setInvitationUsed()
|
||||||
do {
|
}
|
||||||
if let contactConn = contactConnection,
|
.onChange(of: chatModel.currentUser) { u in
|
||||||
let conn = try await apiSetConnectionIncognito(connId: contactConn.pccConnId, incognito: incognito) {
|
|
||||||
await MainActor.run {
|
|
||||||
contactConnection = conn
|
|
||||||
chatModel.updateContactConnection(conn)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
} catch {
|
|
||||||
logger.error("apiSetConnectionIncognito error: \(responseError(error))")
|
|
||||||
}
|
|
||||||
}
|
|
||||||
setInvitationUsed()
|
setInvitationUsed()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -270,6 +307,7 @@ private struct InviteView: View {
|
|||||||
private func qrCodeView() -> some View {
|
private func qrCodeView() -> some View {
|
||||||
Section(header: Text("Or show this code").foregroundColor(theme.colors.secondary)) {
|
Section(header: Text("Or show this code").foregroundColor(theme.colors.secondary)) {
|
||||||
SimpleXLinkQRCode(uri: connReqInvitation, onShare: setInvitationUsed)
|
SimpleXLinkQRCode(uri: connReqInvitation, onShare: setInvitationUsed)
|
||||||
|
.id("simplex-qrcode-view-for-\(connReqInvitation)")
|
||||||
.padding()
|
.padding()
|
||||||
.background(
|
.background(
|
||||||
RoundedRectangle(cornerRadius: 12, style: .continuous)
|
RoundedRectangle(cornerRadius: 12, style: .continuous)
|
||||||
@@ -289,6 +327,257 @@ private struct InviteView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private enum ProfileSwitchStatus {
|
||||||
|
case switchingUser
|
||||||
|
case switchingIncognito
|
||||||
|
case idle
|
||||||
|
}
|
||||||
|
|
||||||
|
private struct ActiveProfilePicker: View {
|
||||||
|
@Environment(\.dismiss) var dismiss
|
||||||
|
@EnvironmentObject var chatModel: ChatModel
|
||||||
|
@EnvironmentObject var theme: AppTheme
|
||||||
|
@Binding var contactConnection: PendingContactConnection?
|
||||||
|
@Binding var connReqInvitation: String
|
||||||
|
@Binding var incognitoEnabled: Bool
|
||||||
|
@Binding var choosingProfile: Bool
|
||||||
|
@State private var alert: SomeAlert?
|
||||||
|
@State private var profileSwitchStatus: ProfileSwitchStatus = .idle
|
||||||
|
@State private var switchingProfileByTimeout = false
|
||||||
|
@State private var lastSwitchingProfileByTimeoutCall: Double?
|
||||||
|
@State private var profiles: [User] = []
|
||||||
|
@State private var searchTextOrPassword = ""
|
||||||
|
@State private var showIncognitoSheet = false
|
||||||
|
@State private var incognitoFirst: Bool = false
|
||||||
|
@State var selectedProfile: User
|
||||||
|
var trimmedSearchTextOrPassword: String { searchTextOrPassword.trimmingCharacters(in: .whitespaces)}
|
||||||
|
|
||||||
|
var body: some View {
|
||||||
|
viewBody()
|
||||||
|
.navigationTitle("Select chat profile")
|
||||||
|
.searchable(text: $searchTextOrPassword, placement: .navigationBarDrawer(displayMode: .always))
|
||||||
|
.autocorrectionDisabled(true)
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
.onAppear {
|
||||||
|
profiles = chatModel.users
|
||||||
|
.map { $0.user }
|
||||||
|
.sorted { u, _ in u.activeUser }
|
||||||
|
}
|
||||||
|
.onChange(of: incognitoEnabled) { incognito in
|
||||||
|
if profileSwitchStatus != .switchingIncognito {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
if let contactConn = contactConnection,
|
||||||
|
let conn = try await apiSetConnectionIncognito(connId: contactConn.pccConnId, incognito: incognito) {
|
||||||
|
await MainActor.run {
|
||||||
|
contactConnection = conn
|
||||||
|
chatModel.updateContactConnection(conn)
|
||||||
|
profileSwitchStatus = .idle
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
profileSwitchStatus = .idle
|
||||||
|
incognitoEnabled = !incognito
|
||||||
|
logger.error("apiSetConnectionIncognito error: \(responseError(error))")
|
||||||
|
let err = getErrorAlert(error, "Error changing to incognito!")
|
||||||
|
|
||||||
|
alert = SomeAlert(
|
||||||
|
alert: Alert(
|
||||||
|
title: Text(err.title),
|
||||||
|
message: Text(err.message ?? "Error: \(responseError(error))")
|
||||||
|
),
|
||||||
|
id: "setConnectionIncognitoError"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: profileSwitchStatus) { sp in
|
||||||
|
if sp != .idle {
|
||||||
|
DispatchQueue.main.asyncAfter(deadline: .now() + 0.5) {
|
||||||
|
switchingProfileByTimeout = profileSwitchStatus != .idle
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
switchingProfileByTimeout = false
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.onChange(of: selectedProfile) { profile in
|
||||||
|
if (profileSwitchStatus != .switchingUser) {
|
||||||
|
return
|
||||||
|
}
|
||||||
|
Task {
|
||||||
|
do {
|
||||||
|
if let contactConn = contactConnection,
|
||||||
|
let conn = try await apiChangeConnectionUser(connId: contactConn.pccConnId, userId: profile.userId) {
|
||||||
|
|
||||||
|
await MainActor.run {
|
||||||
|
contactConnection = conn
|
||||||
|
connReqInvitation = conn.connReqInv ?? ""
|
||||||
|
incognitoEnabled = false
|
||||||
|
chatModel.updateContactConnection(conn)
|
||||||
|
}
|
||||||
|
do {
|
||||||
|
try await changeActiveUserAsync_(profile.userId, viewPwd: profile.hidden ? trimmedSearchTextOrPassword : nil )
|
||||||
|
await MainActor.run {
|
||||||
|
profileSwitchStatus = .idle
|
||||||
|
dismiss()
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
profileSwitchStatus = .idle
|
||||||
|
alert = SomeAlert(
|
||||||
|
alert: Alert(
|
||||||
|
title: Text("Error switching profile"),
|
||||||
|
message: Text("Your connection was moved to \(profile.chatViewName) but an unexpected error occurred while redirecting you to the profile.")
|
||||||
|
),
|
||||||
|
id: "switchingProfileError"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
} catch {
|
||||||
|
await MainActor.run {
|
||||||
|
profileSwitchStatus = .idle
|
||||||
|
if let currentUser = chatModel.currentUser {
|
||||||
|
selectedProfile = currentUser
|
||||||
|
}
|
||||||
|
let err = getErrorAlert(error, "Error changing connection profile")
|
||||||
|
alert = SomeAlert(
|
||||||
|
alert: Alert(
|
||||||
|
title: Text(err.title),
|
||||||
|
message: Text(err.message ?? "Error: \(responseError(error))")
|
||||||
|
),
|
||||||
|
id: "changeConnectionUserError"
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.alert(item: $alert) { a in
|
||||||
|
a.alert
|
||||||
|
}
|
||||||
|
.onAppear {
|
||||||
|
incognitoFirst = incognitoEnabled
|
||||||
|
choosingProfile = true
|
||||||
|
}
|
||||||
|
.onDisappear {
|
||||||
|
choosingProfile = false
|
||||||
|
}
|
||||||
|
.sheet(isPresented: $showIncognitoSheet) {
|
||||||
|
IncognitoHelp()
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
|
||||||
|
@ViewBuilder private func viewBody() -> some View {
|
||||||
|
profilePicker()
|
||||||
|
.allowsHitTesting(!switchingProfileByTimeout)
|
||||||
|
.modifier(ThemedBackground(grouped: true))
|
||||||
|
.overlay {
|
||||||
|
if switchingProfileByTimeout {
|
||||||
|
ProgressView()
|
||||||
|
.scaleEffect(2)
|
||||||
|
.frame(maxWidth: .infinity, maxHeight: .infinity)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
private func filteredProfiles() -> [User] {
|
||||||
|
let s = trimmedSearchTextOrPassword
|
||||||
|
let lower = s.localizedLowercase
|
||||||
|
|
||||||
|
return profiles.filter { u in
|
||||||
|
if (u.activeUser || !u.hidden) && (s == "" || u.chatViewName.localizedLowercase.contains(lower)) {
|
||||||
|
return true
|
||||||
|
}
|
||||||
|
return correctPassword(u, s)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private func profilerPickerUserOption(_ user: User) -> some View {
|
||||||
|
Button {
|
||||||
|
if selectedProfile == user && incognitoEnabled {
|
||||||
|
incognitoEnabled = false
|
||||||
|
profileSwitchStatus = .switchingIncognito
|
||||||
|
} else if selectedProfile != user {
|
||||||
|
selectedProfile = user
|
||||||
|
profileSwitchStatus = .switchingUser
|
||||||
|
}
|
||||||
|
} label: {
|
||||||
|
HStack {
|
||||||
|
ProfileImage(imageStr: user.image, size: 30)
|
||||||
|
.padding(.trailing, 2)
|
||||||
|
Text(user.chatViewName)
|
||||||
|
.foregroundColor(theme.colors.onBackground)
|
||||||
|
.lineLimit(1)
|
||||||
|
Spacer()
|
||||||
|
if selectedProfile == user, !incognitoEnabled {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
.resizable().scaledToFit().frame(width: 16)
|
||||||
|
.foregroundColor(theme.colors.primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@ViewBuilder private func profilePicker() -> some View {
|
||||||
|
let incognitoOption = Button {
|
||||||
|
if !incognitoEnabled {
|
||||||
|
incognitoEnabled = true
|
||||||
|
profileSwitchStatus = .switchingIncognito
|
||||||
|
}
|
||||||
|
} label : {
|
||||||
|
HStack {
|
||||||
|
incognitoProfileImage()
|
||||||
|
Text("Incognito")
|
||||||
|
.foregroundColor(theme.colors.onBackground)
|
||||||
|
Image(systemName: "info.circle")
|
||||||
|
.foregroundColor(theme.colors.primary)
|
||||||
|
.font(.system(size: 14))
|
||||||
|
.onTapGesture {
|
||||||
|
showIncognitoSheet = true
|
||||||
|
}
|
||||||
|
Spacer()
|
||||||
|
if incognitoEnabled {
|
||||||
|
Image(systemName: "checkmark")
|
||||||
|
.resizable().scaledToFit().frame(width: 16)
|
||||||
|
.foregroundColor(theme.colors.primary)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
List {
|
||||||
|
let filteredProfiles = filteredProfiles()
|
||||||
|
let activeProfile = filteredProfiles.first { u in u.activeUser }
|
||||||
|
|
||||||
|
if let selectedProfile = activeProfile {
|
||||||
|
let otherProfiles = filteredProfiles.filter { u in u.userId != activeProfile?.userId }
|
||||||
|
|
||||||
|
if incognitoFirst {
|
||||||
|
incognitoOption
|
||||||
|
profilerPickerUserOption(selectedProfile)
|
||||||
|
} else {
|
||||||
|
profilerPickerUserOption(selectedProfile)
|
||||||
|
incognitoOption
|
||||||
|
}
|
||||||
|
|
||||||
|
ForEach(otherProfiles) { p in
|
||||||
|
profilerPickerUserOption(p)
|
||||||
|
}
|
||||||
|
} else {
|
||||||
|
incognitoOption
|
||||||
|
ForEach(filteredProfiles) { p in
|
||||||
|
profilerPickerUserOption(p)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
.opacity(switchingProfileByTimeout ? 0.4 : 1)
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
private struct ConnectView: View {
|
private struct ConnectView: View {
|
||||||
@Environment(\.dismiss) var dismiss: DismissAction
|
@Environment(\.dismiss) var dismiss: DismissAction
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@@ -296,6 +585,7 @@ private struct ConnectView: View {
|
|||||||
@Binding var pastedLink: String
|
@Binding var pastedLink: String
|
||||||
@Binding var alert: NewChatViewAlert?
|
@Binding var alert: NewChatViewAlert?
|
||||||
@State private var sheet: PlanAndConnectActionSheet?
|
@State private var sheet: PlanAndConnectActionSheet?
|
||||||
|
@State private var pasteboardHasStrings = UIPasteboard.general.hasStrings
|
||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
List {
|
List {
|
||||||
@@ -332,7 +622,7 @@ private struct ConnectView: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Text("Tap to paste link")
|
Text("Tap to paste link")
|
||||||
}
|
}
|
||||||
.disabled(!ChatModel.shared.pasteboardHasStrings)
|
.disabled(!pasteboardHasStrings)
|
||||||
.frame(maxWidth: .infinity, alignment: .center)
|
.frame(maxWidth: .infinity, alignment: .center)
|
||||||
} else {
|
} else {
|
||||||
linkTextView(pastedLink)
|
linkTextView(pastedLink)
|
||||||
@@ -975,10 +1265,12 @@ func connReqSentAlert(_ type: ConnReqType) -> Alert {
|
|||||||
struct NewChatView_Previews: PreviewProvider {
|
struct NewChatView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
@State var parentAlert: SomeAlert?
|
@State var parentAlert: SomeAlert?
|
||||||
|
@State var contactConnection: PendingContactConnection? = nil
|
||||||
|
|
||||||
NewChatView(
|
NewChatView(
|
||||||
selection: .invite,
|
selection: .invite,
|
||||||
parentAlert: $parentAlert
|
parentAlert: $parentAlert,
|
||||||
|
contactConnection: $contactConnection
|
||||||
)
|
)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -59,13 +59,6 @@ struct ConnectDesktopView: View {
|
|||||||
var body: some View {
|
var body: some View {
|
||||||
if viaSettings {
|
if viaSettings {
|
||||||
viewBody
|
viewBody
|
||||||
.modifier(BackButton(label: "Back", disabled: Binding.constant(false)) {
|
|
||||||
if m.activeRemoteCtrl {
|
|
||||||
alert = .disconnectDesktop(action: .back)
|
|
||||||
} else {
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
})
|
|
||||||
} else {
|
} else {
|
||||||
NavigationView {
|
NavigationView {
|
||||||
viewBody
|
viewBody
|
||||||
|
|||||||
@@ -160,7 +160,7 @@ struct TerminalView_Previews: PreviewProvider {
|
|||||||
let chatModel = ChatModel()
|
let chatModel = ChatModel()
|
||||||
chatModel.terminalItems = [
|
chatModel.terminalItems = [
|
||||||
.resp(.now, ChatResponse.response(type: "contactSubscribed", json: "{}")),
|
.resp(.now, ChatResponse.response(type: "contactSubscribed", json: "{}")),
|
||||||
.resp(.now, ChatResponse.response(type: "newChatItem", json: "{}"))
|
.resp(.now, ChatResponse.response(type: "newChatItems", json: "{}"))
|
||||||
]
|
]
|
||||||
return NavigationView {
|
return NavigationView {
|
||||||
TerminalView()
|
TerminalView()
|
||||||
|
|||||||
@@ -52,10 +52,10 @@ extension AppSettings {
|
|||||||
profileImageCornerRadiusGroupDefault.set(val)
|
profileImageCornerRadiusGroupDefault.set(val)
|
||||||
def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
|
def.setValue(val, forKey: DEFAULT_PROFILE_IMAGE_CORNER_RADIUS)
|
||||||
}
|
}
|
||||||
if let val = uiColorScheme { def.setValue(val, forKey: DEFAULT_CURRENT_THEME) }
|
if let val = uiColorScheme { currentThemeDefault.set(val) }
|
||||||
if let val = uiDarkColorScheme { def.setValue(val, forKey: DEFAULT_SYSTEM_DARK_THEME) }
|
if let val = uiDarkColorScheme { systemDarkThemeDefault.set(val) }
|
||||||
if let val = uiCurrentThemeIds { def.setValue(val, forKey: DEFAULT_CURRENT_THEME_IDS) }
|
if let val = uiCurrentThemeIds { currentThemeIdsDefault.set(val) }
|
||||||
if let val = uiThemes { def.setValue(val.skipDuplicates(), forKey: DEFAULT_THEME_OVERRIDES) }
|
if let val = uiThemes { themeOverridesDefault.set(val.skipDuplicates()) }
|
||||||
if let val = oneHandUI { groupDefaults.setValue(val, forKey: GROUP_DEFAULT_ONE_HAND_UI) }
|
if let val = oneHandUI { groupDefaults.setValue(val, forKey: GROUP_DEFAULT_ONE_HAND_UI) }
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -33,6 +33,8 @@ struct AppearanceSettings: View {
|
|||||||
}()
|
}()
|
||||||
@State private var darkModeTheme: String = UserDefaults.standard.string(forKey: DEFAULT_SYSTEM_DARK_THEME) ?? DefaultTheme.DARK.themeName
|
@State private var darkModeTheme: String = UserDefaults.standard.string(forKey: DEFAULT_SYSTEM_DARK_THEME) ?? DefaultTheme.DARK.themeName
|
||||||
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileImageCornerRadius = defaultProfileImageCorner
|
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var profileImageCornerRadius = defaultProfileImageCorner
|
||||||
|
@AppStorage(DEFAULT_CHAT_ITEM_ROUNDNESS) private var chatItemRoundness = defaultChatItemRoundness
|
||||||
|
@AppStorage(DEFAULT_CHAT_ITEM_TAIL) private var chatItemTail = true
|
||||||
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
|
@AppStorage(GROUP_DEFAULT_ONE_HAND_UI, store: groupDefaults) private var oneHandUI = true
|
||||||
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
|
@AppStorage(DEFAULT_TOOLBAR_MATERIAL) private var toolbarMaterial = ToolbarMaterial.defaultMaterial
|
||||||
|
|
||||||
@@ -179,6 +181,14 @@ struct AppearanceSettings: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section(header: Text("Message shape").foregroundColor(theme.colors.secondary)) {
|
||||||
|
HStack {
|
||||||
|
Text("Corner")
|
||||||
|
Slider(value: $chatItemRoundness, in: 0...1, step: 0.05)
|
||||||
|
}
|
||||||
|
Toggle("Tail", isOn: $chatItemTail)
|
||||||
|
}
|
||||||
|
|
||||||
Section(header: Text("Profile images").foregroundColor(theme.colors.secondary)) {
|
Section(header: Text("Profile images").foregroundColor(theme.colors.secondary)) {
|
||||||
HStack(spacing: 16) {
|
HStack(spacing: 16) {
|
||||||
if let img = m.currentUser?.image, img != "" {
|
if let img = m.currentUser?.image, img != "" {
|
||||||
@@ -358,20 +368,21 @@ struct ChatThemePreview: View {
|
|||||||
let bob = ChatItem.getSample(2, CIDirection.directSnd, Date.now, NSLocalizedString("Good morning!", comment: "message preview"), quotedItem: CIQuote.getSample(alice.id, alice.meta.itemTs, alice.content.text, chatDir: alice.chatDir))
|
let bob = ChatItem.getSample(2, CIDirection.directSnd, Date.now, NSLocalizedString("Good morning!", comment: "message preview"), quotedItem: CIQuote.getSample(alice.id, alice.meta.itemTs, alice.content.text, chatDir: alice.chatDir))
|
||||||
HStack {
|
HStack {
|
||||||
ChatItemView(chat: Chat.sampleData, chatItem: alice, revealed: Binding.constant(false))
|
ChatItemView(chat: Chat.sampleData, chatItem: alice, revealed: Binding.constant(false))
|
||||||
.modifier(ChatItemClipped())
|
.modifier(ChatItemClipped(alice, tailVisible: true))
|
||||||
Spacer()
|
Spacer()
|
||||||
}
|
}
|
||||||
HStack {
|
HStack {
|
||||||
Spacer()
|
Spacer()
|
||||||
ChatItemView(chat: Chat.sampleData, chatItem: bob, revealed: Binding.constant(false))
|
ChatItemView(chat: Chat.sampleData, chatItem: bob, revealed: Binding.constant(false))
|
||||||
.modifier(ChatItemClipped())
|
.modifier(ChatItemClipped(bob, tailVisible: true))
|
||||||
.frame(alignment: .trailing)
|
.frame(alignment: .trailing)
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
Rectangle().fill(.clear)
|
Rectangle().fill(.clear)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
.padding(10)
|
.padding(.vertical, 10)
|
||||||
|
.padding(.horizontal, 16)
|
||||||
.frame(maxWidth: .infinity)
|
.frame(maxWidth: .infinity)
|
||||||
|
|
||||||
if let wallpaperType, let wallpaperImage = wallpaperType.image, let backgroundColor, let tintColor {
|
if let wallpaperType, let wallpaperImage = wallpaperType.image, let backgroundColor, let tintColor {
|
||||||
|
|||||||
@@ -26,6 +26,7 @@ struct IncognitoHelp: View {
|
|||||||
Text("Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).")
|
Text("Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).")
|
||||||
}
|
}
|
||||||
.listRowBackground(Color.clear)
|
.listRowBackground(Color.clear)
|
||||||
|
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
|
||||||
}
|
}
|
||||||
.modifier(ThemedBackground())
|
.modifier(ThemedBackground())
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -32,6 +32,17 @@ struct PreferencesView: View {
|
|||||||
.disabled(currentPreferences == preferences)
|
.disabled(currentPreferences == preferences)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
.onDisappear {
|
||||||
|
if currentPreferences != preferences {
|
||||||
|
showAlert(
|
||||||
|
title: NSLocalizedString("Your chat preferences", comment: "alert title"),
|
||||||
|
message: NSLocalizedString("Chat preferences were changed.", comment: "alert message"),
|
||||||
|
buttonTitle: NSLocalizedString("Save", comment: "alert button"),
|
||||||
|
buttonAction: savePreferences,
|
||||||
|
cancelButton: true
|
||||||
|
)
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private func featureSection(_ feature: ChatFeature, _ allowFeature: Binding<FeatureAllowed>) -> some View {
|
private func featureSection(_ feature: ChatFeature, _ allowFeature: Binding<FeatureAllowed>) -> some View {
|
||||||
|
|||||||
@@ -47,6 +47,8 @@ let DEFAULT_ACCENT_COLOR_GREEN = "accentColorGreen" // deprecated, only used for
|
|||||||
let DEFAULT_ACCENT_COLOR_BLUE = "accentColorBlue" // deprecated, only used for migration
|
let DEFAULT_ACCENT_COLOR_BLUE = "accentColorBlue" // deprecated, only used for migration
|
||||||
let DEFAULT_USER_INTERFACE_STYLE = "userInterfaceStyle" // deprecated, only used for migration
|
let DEFAULT_USER_INTERFACE_STYLE = "userInterfaceStyle" // deprecated, only used for migration
|
||||||
let DEFAULT_PROFILE_IMAGE_CORNER_RADIUS = "profileImageCornerRadius"
|
let DEFAULT_PROFILE_IMAGE_CORNER_RADIUS = "profileImageCornerRadius"
|
||||||
|
let DEFAULT_CHAT_ITEM_ROUNDNESS = "chatItemRoundness"
|
||||||
|
let DEFAULT_CHAT_ITEM_TAIL = "chatItemTail"
|
||||||
let DEFAULT_ONE_HAND_UI_CARD_SHOWN = "oneHandUICardShown"
|
let DEFAULT_ONE_HAND_UI_CARD_SHOWN = "oneHandUICardShown"
|
||||||
let DEFAULT_TOOLBAR_MATERIAL = "toolbarMaterial"
|
let DEFAULT_TOOLBAR_MATERIAL = "toolbarMaterial"
|
||||||
let DEFAULT_CONNECT_VIA_LINK_TAB = "connectViaLinkTab"
|
let DEFAULT_CONNECT_VIA_LINK_TAB = "connectViaLinkTab"
|
||||||
@@ -75,6 +77,8 @@ let DEFAULT_THEME_OVERRIDES = "themeOverrides"
|
|||||||
|
|
||||||
let ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN = "androidCallOnLockScreen"
|
let ANDROID_DEFAULT_CALL_ON_LOCK_SCREEN = "androidCallOnLockScreen"
|
||||||
|
|
||||||
|
let defaultChatItemRoundness: Double = 0.75
|
||||||
|
|
||||||
let appDefaults: [String: Any] = [
|
let appDefaults: [String: Any] = [
|
||||||
DEFAULT_SHOW_LA_NOTICE: false,
|
DEFAULT_SHOW_LA_NOTICE: false,
|
||||||
DEFAULT_LA_NOTICE_SHOWN: false,
|
DEFAULT_LA_NOTICE_SHOWN: false,
|
||||||
@@ -98,6 +102,8 @@ let appDefaults: [String: Any] = [
|
|||||||
DEFAULT_DEVELOPER_TOOLS: false,
|
DEFAULT_DEVELOPER_TOOLS: false,
|
||||||
DEFAULT_ENCRYPTION_STARTED: false,
|
DEFAULT_ENCRYPTION_STARTED: false,
|
||||||
DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner,
|
DEFAULT_PROFILE_IMAGE_CORNER_RADIUS: defaultProfileImageCorner,
|
||||||
|
DEFAULT_CHAT_ITEM_ROUNDNESS: defaultChatItemRoundness,
|
||||||
|
DEFAULT_CHAT_ITEM_TAIL: true,
|
||||||
DEFAULT_ONE_HAND_UI_CARD_SHOWN: false,
|
DEFAULT_ONE_HAND_UI_CARD_SHOWN: false,
|
||||||
DEFAULT_TOOLBAR_MATERIAL: ToolbarMaterial.defaultMaterial,
|
DEFAULT_TOOLBAR_MATERIAL: ToolbarMaterial.defaultMaterial,
|
||||||
DEFAULT_CONNECT_VIA_LINK_TAB: ConnectViaLinkTab.scan.rawValue,
|
DEFAULT_CONNECT_VIA_LINK_TAB: ConnectViaLinkTab.scan.rawValue,
|
||||||
@@ -256,7 +262,9 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
settingsView()
|
NavigationView {
|
||||||
|
settingsView()
|
||||||
|
}
|
||||||
if showProgress {
|
if showProgress {
|
||||||
progressView()
|
progressView()
|
||||||
}
|
}
|
||||||
@@ -268,63 +276,7 @@ struct SettingsView: View {
|
|||||||
|
|
||||||
@ViewBuilder func settingsView() -> some View {
|
@ViewBuilder func settingsView() -> some View {
|
||||||
let user = chatModel.currentUser
|
let user = chatModel.currentUser
|
||||||
NavigationView {
|
|
||||||
List {
|
List {
|
||||||
Section(header: Text("You").foregroundColor(theme.colors.secondary)) {
|
|
||||||
if let user = user {
|
|
||||||
NavigationLink {
|
|
||||||
UserProfile()
|
|
||||||
.navigationTitle("Your current profile")
|
|
||||||
.modifier(ThemedBackground())
|
|
||||||
} label: {
|
|
||||||
ProfilePreview(profileOf: user)
|
|
||||||
.padding(.leading, -8)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink {
|
|
||||||
UserProfilesView(showSettings: $showSettings)
|
|
||||||
} label: {
|
|
||||||
settingsRow("person.crop.rectangle.stack", color: theme.colors.secondary) { Text("Your chat profiles") }
|
|
||||||
}
|
|
||||||
|
|
||||||
|
|
||||||
if let user = user {
|
|
||||||
NavigationLink {
|
|
||||||
UserAddressView(shareViaProfile: user.addressShared)
|
|
||||||
.navigationTitle("SimpleX address")
|
|
||||||
.modifier(ThemedBackground(grouped: true))
|
|
||||||
.navigationBarTitleDisplayMode(.large)
|
|
||||||
} label: {
|
|
||||||
settingsRow("qrcode", color: theme.colors.secondary) { Text("Your SimpleX address") }
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink {
|
|
||||||
PreferencesView(profile: user.profile, preferences: user.fullPreferences, currentPreferences: user.fullPreferences)
|
|
||||||
.navigationTitle("Your preferences")
|
|
||||||
.modifier(ThemedBackground(grouped: true))
|
|
||||||
} label: {
|
|
||||||
settingsRow("switch.2", color: theme.colors.secondary) { Text("Chat preferences") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink {
|
|
||||||
ConnectDesktopView(viaSettings: true)
|
|
||||||
} label: {
|
|
||||||
settingsRow("desktopcomputer", color: theme.colors.secondary) { Text("Use from desktop") }
|
|
||||||
}
|
|
||||||
|
|
||||||
NavigationLink {
|
|
||||||
MigrateFromDevice(showSettings: $showSettings, showProgressOnSettings: $showProgress)
|
|
||||||
.navigationTitle("Migrate device")
|
|
||||||
.modifier(ThemedBackground(grouped: true))
|
|
||||||
.navigationBarTitleDisplayMode(.large)
|
|
||||||
} label: {
|
|
||||||
settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") }
|
|
||||||
}
|
|
||||||
}
|
|
||||||
.disabled(chatModel.chatRunning != true)
|
|
||||||
|
|
||||||
Section(header: Text("Settings").foregroundColor(theme.colors.secondary)) {
|
Section(header: Text("Settings").foregroundColor(theme.colors.secondary)) {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
NotificationsView()
|
NotificationsView()
|
||||||
@@ -375,10 +327,20 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
.disabled(chatModel.chatRunning != true)
|
.disabled(chatModel.chatRunning != true)
|
||||||
}
|
}
|
||||||
|
|
||||||
chatDatabaseRow()
|
|
||||||
}
|
}
|
||||||
|
|
||||||
|
Section(header: Text("Chat database").foregroundColor(theme.colors.secondary)) {
|
||||||
|
chatDatabaseRow()
|
||||||
|
NavigationLink {
|
||||||
|
MigrateFromDevice(showProgressOnSettings: $showProgress)
|
||||||
|
.navigationTitle("Migrate device")
|
||||||
|
.modifier(ThemedBackground(grouped: true))
|
||||||
|
.navigationBarTitleDisplayMode(.large)
|
||||||
|
} label: {
|
||||||
|
settingsRow("tray.and.arrow.up", color: theme.colors.secondary) { Text("Migrate to another device") }
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
Section(header: Text("Help").foregroundColor(theme.colors.secondary)) {
|
Section(header: Text("Help").foregroundColor(theme.colors.secondary)) {
|
||||||
if let user = user {
|
if let user = user {
|
||||||
NavigationLink {
|
NavigationLink {
|
||||||
@@ -456,11 +418,10 @@ struct SettingsView: View {
|
|||||||
}
|
}
|
||||||
.navigationTitle("Your settings")
|
.navigationTitle("Your settings")
|
||||||
.modifier(ThemedBackground(grouped: true))
|
.modifier(ThemedBackground(grouped: true))
|
||||||
}
|
.onDisappear {
|
||||||
.onDisappear {
|
chatModel.showingTerminal = false
|
||||||
chatModel.showingTerminal = false
|
chatModel.terminalItems = []
|
||||||
chatModel.terminalItems = []
|
}
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
private func chatDatabaseRow() -> some View {
|
private func chatDatabaseRow() -> some View {
|
||||||
@@ -543,17 +504,18 @@ struct ProfilePreview: View {
|
|||||||
HStack {
|
HStack {
|
||||||
ProfileImage(imageStr: profileOf.image, size: 44, color: color)
|
ProfileImage(imageStr: profileOf.image, size: 44, color: color)
|
||||||
.padding(.trailing, 6)
|
.padding(.trailing, 6)
|
||||||
.padding(.vertical, 6)
|
profileName().lineLimit(1)
|
||||||
VStack(alignment: .leading) {
|
|
||||||
Text(profileOf.displayName)
|
|
||||||
.fontWeight(.bold)
|
|
||||||
.font(.title2)
|
|
||||||
if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName {
|
|
||||||
Text(profileOf.fullName)
|
|
||||||
}
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private func profileName() -> Text {
|
||||||
|
var t = Text(profileOf.displayName).fontWeight(.semibold).font(.title2)
|
||||||
|
if profileOf.fullName != "" && profileOf.fullName != profileOf.displayName {
|
||||||
|
t = t + Text(" (" + profileOf.fullName + ")")
|
||||||
|
// .font(.callout)
|
||||||
|
}
|
||||||
|
return t
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct SettingsView_Previews: PreviewProvider {
|
struct SettingsView_Previews: PreviewProvider {
|
||||||
|
|||||||
@@ -14,7 +14,6 @@ struct UserAddressView: View {
|
|||||||
@Environment(\.dismiss) var dismiss: DismissAction
|
@Environment(\.dismiss) var dismiss: DismissAction
|
||||||
@EnvironmentObject private var chatModel: ChatModel
|
@EnvironmentObject private var chatModel: ChatModel
|
||||||
@EnvironmentObject var theme: AppTheme
|
@EnvironmentObject var theme: AppTheme
|
||||||
@State var viaCreateLinkView = false
|
|
||||||
@State var shareViaProfile = false
|
@State var shareViaProfile = false
|
||||||
@State private var aas = AutoAcceptState()
|
@State private var aas = AutoAcceptState()
|
||||||
@State private var savedAAS = AutoAcceptState()
|
@State private var savedAAS = AutoAcceptState()
|
||||||
@@ -22,7 +21,6 @@ struct UserAddressView: View {
|
|||||||
@State private var showMailView = false
|
@State private var showMailView = false
|
||||||
@State private var mailViewResult: Result<MFMailComposeResult, Error>? = nil
|
@State private var mailViewResult: Result<MFMailComposeResult, Error>? = nil
|
||||||
@State private var alert: UserAddressAlert?
|
@State private var alert: UserAddressAlert?
|
||||||
@State private var showSaveDialogue = false
|
|
||||||
@State private var progressIndicator = false
|
@State private var progressIndicator = false
|
||||||
@FocusState private var keyboardVisible: Bool
|
@FocusState private var keyboardVisible: Bool
|
||||||
|
|
||||||
@@ -44,26 +42,19 @@ struct UserAddressView: View {
|
|||||||
|
|
||||||
var body: some View {
|
var body: some View {
|
||||||
ZStack {
|
ZStack {
|
||||||
if viaCreateLinkView {
|
userAddressScrollView()
|
||||||
userAddressScrollView()
|
.onDisappear {
|
||||||
} else {
|
if savedAAS != aas {
|
||||||
userAddressScrollView()
|
showAlert(
|
||||||
.modifier(BackButton(disabled: Binding.constant(false)) {
|
title: NSLocalizedString("Auto-accept settings", comment: "alert title"),
|
||||||
if savedAAS == aas {
|
message: NSLocalizedString("Settings were changed.", comment: "alert message"),
|
||||||
dismiss()
|
buttonTitle: NSLocalizedString("Save", comment: "alert button"),
|
||||||
} else {
|
buttonAction: saveAAS,
|
||||||
keyboardVisible = false
|
cancelButton: true
|
||||||
showSaveDialogue = true
|
)
|
||||||
}
|
|
||||||
})
|
|
||||||
.confirmationDialog("Save settings?", isPresented: $showSaveDialogue) {
|
|
||||||
Button("Save auto-accept settings") {
|
|
||||||
saveAAS()
|
|
||||||
dismiss()
|
|
||||||
}
|
|
||||||
Button("Exit without saving") { dismiss() }
|
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
if progressIndicator {
|
if progressIndicator {
|
||||||
ZStack {
|
ZStack {
|
||||||
if chatModel.userAddress != nil {
|
if chatModel.userAddress != nil {
|
||||||
@@ -238,7 +229,7 @@ struct UserAddressView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
Label("Create SimpleX address", systemImage: "qrcode")
|
Label("Create public address", systemImage: "qrcode")
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -342,7 +333,7 @@ struct UserAddressView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
private struct AutoAcceptState: Equatable {
|
private struct AutoAcceptState: Equatable {
|
||||||
var enable = false
|
var enable = false
|
||||||
var incognito = false
|
var incognito = false
|
||||||
@@ -447,6 +438,8 @@ struct UserAddressView_Previews: PreviewProvider {
|
|||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
let chatModel = ChatModel()
|
let chatModel = ChatModel()
|
||||||
chatModel.userAddress = UserContactLink(connReqContact: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D")
|
chatModel.userAddress = UserContactLink(connReqContact: "https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D")
|
||||||
|
|
||||||
|
|
||||||
return Group {
|
return Group {
|
||||||
UserAddressView()
|
UserAddressView()
|
||||||
.environmentObject(chatModel)
|
.environmentObject(chatModel)
|
||||||
|
|||||||
@@ -9,7 +9,6 @@ import SimpleXChat
|
|||||||
struct UserProfilesView: View {
|
struct UserProfilesView: View {
|
||||||
@EnvironmentObject private var m: ChatModel
|
@EnvironmentObject private var m: ChatModel
|
||||||
@EnvironmentObject private var theme: AppTheme
|
@EnvironmentObject private var theme: AppTheme
|
||||||
@Binding var showSettings: Bool
|
|
||||||
@Environment(\.editMode) private var editMode
|
@Environment(\.editMode) private var editMode
|
||||||
@AppStorage(DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE) private var showHiddenProfilesNotice = true
|
@AppStorage(DEFAULT_SHOW_HIDDEN_PROFILES_NOTICE) private var showHiddenProfilesNotice = true
|
||||||
@AppStorage(DEFAULT_SHOW_MUTE_PROFILE_ALERT) private var showMuteProfileAlert = true
|
@AppStorage(DEFAULT_SHOW_MUTE_PROFILE_ALERT) private var showMuteProfileAlert = true
|
||||||
@@ -96,8 +95,7 @@ struct UserProfilesView: View {
|
|||||||
} label: {
|
} label: {
|
||||||
Label("Add profile", systemImage: "plus")
|
Label("Add profile", systemImage: "plus")
|
||||||
}
|
}
|
||||||
.frame(height: 44)
|
.frame(height: 38)
|
||||||
.padding(.vertical, 4)
|
|
||||||
}
|
}
|
||||||
} footer: {
|
} footer: {
|
||||||
Text("Tap to activate profile.")
|
Text("Tap to activate profile.")
|
||||||
@@ -285,7 +283,7 @@ struct UserProfilesView: View {
|
|||||||
await MainActor.run {
|
await MainActor.run {
|
||||||
onboardingStageDefault.set(.step1_SimpleXInfo)
|
onboardingStageDefault.set(.step1_SimpleXInfo)
|
||||||
m.onboardingStage = .step1_SimpleXInfo
|
m.onboardingStage = .step1_SimpleXInfo
|
||||||
showSettings = false
|
dismissAllSheets()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} else {
|
} else {
|
||||||
@@ -308,14 +306,14 @@ struct UserProfilesView: View {
|
|||||||
Task {
|
Task {
|
||||||
do {
|
do {
|
||||||
try await changeActiveUserAsync_(user.userId, viewPwd: userViewPassword(user))
|
try await changeActiveUserAsync_(user.userId, viewPwd: userViewPassword(user))
|
||||||
|
dismissAllSheets()
|
||||||
} catch {
|
} catch {
|
||||||
await MainActor.run { alert = .activateUserError(error: responseError(error)) }
|
await MainActor.run { alert = .activateUserError(error: responseError(error)) }
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} label: {
|
} label: {
|
||||||
HStack {
|
HStack {
|
||||||
ProfileImage(imageStr: user.image, size: 44)
|
ProfileImage(imageStr: user.image, size: 38)
|
||||||
.padding(.vertical, 4)
|
|
||||||
.padding(.trailing, 12)
|
.padding(.trailing, 12)
|
||||||
Text(user.chatViewName)
|
Text(user.chatViewName)
|
||||||
Spacer()
|
Spacer()
|
||||||
@@ -406,8 +404,15 @@ public func chatPasswordHash(_ pwd: String, _ salt: String) -> String {
|
|||||||
return hash
|
return hash
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func correctPassword(_ user: User, _ pwd: String) -> Bool {
|
||||||
|
if let ph = user.viewPwdHash {
|
||||||
|
return pwd != "" && chatPasswordHash(pwd, ph.salt) == ph.hash
|
||||||
|
}
|
||||||
|
return false
|
||||||
|
}
|
||||||
|
|
||||||
struct UserProfilesView_Previews: PreviewProvider {
|
struct UserProfilesView_Previews: PreviewProvider {
|
||||||
static var previews: some View {
|
static var previews: some View {
|
||||||
UserProfilesView(showSettings: Binding.constant(true))
|
UserProfilesView()
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ иска да се свърже!</target>
|
<target>%@ иска да се свърже!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ и %lld членове</target>
|
<target>%@, %@ и %lld членове</target>
|
||||||
@@ -1685,6 +1689,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Версия на ядрото: v%@</target>
|
<target>Версия на ядрото: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Поправи име на %@?</target>
|
<target>Поправи име на %@?</target>
|
||||||
@@ -2645,6 +2653,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Грешка при промяна на адреса</target>
|
<target>Грешка при промяна на адреса</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Грешка при промяна на ролята</target>
|
<target>Грешка при промяна на ролята</target>
|
||||||
@@ -2655,6 +2667,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Грешка при промяна на настройката</target>
|
<target>Грешка при промяна на настройката</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2870,6 +2886,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Грешка при спиране на чата</target>
|
<target>Грешка при спиране на чата</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Грешка при смяна на профил!</target>
|
<target>Грешка при смяна на профил!</target>
|
||||||
@@ -4069,6 +4089,10 @@ This is your link for group %@!</source>
|
|||||||
<source>Message servers</source>
|
<source>Message servers</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>Източникът на съобщението остава скрит.</target>
|
<target>Източникът на съобщението остава скрит.</target>
|
||||||
@@ -5562,6 +5586,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Избери</target>
|
<target>Избери</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5911,6 +5939,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Сподели линк</target>
|
<target>Сподели линк</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Сподели този еднократен линк за връзка</target>
|
<target>Сподели този еднократен линк за връзка</target>
|
||||||
@@ -6235,6 +6267,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Направи снимка</target>
|
<target>Направи снимка</target>
|
||||||
@@ -7480,6 +7516,10 @@ Repeat connection request?</source>
|
|||||||
<target>Вашите чат профили</target>
|
<target>Вашите чат профили</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@).</target>
|
<target>Вашият контакт изпрати файл, който е по-голям от поддържания в момента максимален размер (%@).</target>
|
||||||
|
|||||||
@@ -135,6 +135,10 @@
|
|||||||
<target>%@ se chce připojit!</target>
|
<target>%@ se chce připojit!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -1623,6 +1627,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Verze jádra: v%@</target>
|
<target>Verze jádra: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2552,6 +2560,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Chuba změny adresy</target>
|
<target>Chuba změny adresy</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Chyba při změně role</target>
|
<target>Chyba při změně role</target>
|
||||||
@@ -2562,6 +2574,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Chyba změny nastavení</target>
|
<target>Chyba změny nastavení</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2772,6 +2788,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Chyba při zastavení chatu</target>
|
<target>Chyba při zastavení chatu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Chyba při přepínání profilu!</target>
|
<target>Chyba při přepínání profilu!</target>
|
||||||
@@ -3928,6 +3948,10 @@ This is your link for group %@!</source>
|
|||||||
<source>Message servers</source>
|
<source>Message servers</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5363,6 +5387,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Vybrat</target>
|
<target>Vybrat</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5708,6 +5736,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Sdílet odkaz</target>
|
<target>Sdílet odkaz</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -6024,6 +6056,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Vyfotit</target>
|
<target>Vyfotit</target>
|
||||||
@@ -7209,6 +7245,10 @@ Repeat connection request?</source>
|
|||||||
<target>Vaše chat profily</target>
|
<target>Vaše chat profily</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Kontakt odeslal soubor, který je větší než aktuálně podporovaná maximální velikost (%@).</target>
|
<target>Kontakt odeslal soubor, který je větší než aktuálně podporovaná maximální velikost (%@).</target>
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ will sich mit Ihnen verbinden!</target>
|
<target>%@ will sich mit Ihnen verbinden!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ und %lld Mitglieder</target>
|
<target>%@, %@ und %lld Mitglieder</target>
|
||||||
@@ -1740,6 +1744,10 @@ Das ist Ihr eigener Einmal-Link!</target>
|
|||||||
<target>Core Version: v%@</target>
|
<target>Core Version: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Richtiger Name für %@?</target>
|
<target>Richtiger Name für %@?</target>
|
||||||
@@ -2724,6 +2732,10 @@ Das ist Ihr eigener Einmal-Link!</target>
|
|||||||
<target>Fehler beim Wechseln der Empfängeradresse</target>
|
<target>Fehler beim Wechseln der Empfängeradresse</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Fehler beim Ändern der Rolle</target>
|
<target>Fehler beim Ändern der Rolle</target>
|
||||||
@@ -2734,6 +2746,10 @@ Das ist Ihr eigener Einmal-Link!</target>
|
|||||||
<target>Fehler beim Ändern der Einstellung</target>
|
<target>Fehler beim Ändern der Einstellung</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<target>Fehler beim Verbinden mit dem Weiterleitungsserver %@. Bitte versuchen Sie es später erneut.</target>
|
<target>Fehler beim Verbinden mit dem Weiterleitungsserver %@. Bitte versuchen Sie es später erneut.</target>
|
||||||
@@ -2954,6 +2970,10 @@ Das ist Ihr eigener Einmal-Link!</target>
|
|||||||
<target>Fehler beim Beenden des Chats</target>
|
<target>Fehler beim Beenden des Chats</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Fehler beim Umschalten des Profils!</target>
|
<target>Fehler beim Umschalten des Profils!</target>
|
||||||
@@ -4184,6 +4204,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
|||||||
<target>Nachrichten-Server</target>
|
<target>Nachrichten-Server</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>Die Nachrichtenquelle bleibt privat.</target>
|
<target>Die Nachrichtenquelle bleibt privat.</target>
|
||||||
@@ -5729,6 +5753,10 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen.</target>
|
|||||||
<target>Auswählen</target>
|
<target>Auswählen</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<target>%lld ausgewählt</target>
|
<target>%lld ausgewählt</target>
|
||||||
@@ -6099,6 +6127,10 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen.</target>
|
|||||||
<target>Link teilen</target>
|
<target>Link teilen</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Teilen Sie diesen Einmal-Einladungslink</target>
|
<target>Teilen Sie diesen Einmal-Einladungslink</target>
|
||||||
@@ -6439,6 +6471,10 @@ Aktivieren Sie es in den *Netzwerk & Server* Einstellungen.</target>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Machen Sie ein Foto</target>
|
<target>Machen Sie ein Foto</target>
|
||||||
@@ -7719,6 +7755,10 @@ Verbindungsanfrage wiederholen?</target>
|
|||||||
<target>Ihre Chat-Profile</target>
|
<target>Ihre Chat-Profile</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Ihr Kontakt hat eine Datei gesendet, die größer ist als die derzeit unterstützte maximale Größe (%@).</target>
|
<target>Ihr Kontakt hat eine Datei gesendet, die größer ist als die derzeit unterstützte maximale Größe (%@).</target>
|
||||||
|
|||||||
@@ -137,6 +137,11 @@
|
|||||||
<target>%@ wants to connect!</target>
|
<target>%@ wants to connect!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<target>%1$@, %2$@</target>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ and %lld members</target>
|
<target>%@, %@ and %lld members</target>
|
||||||
@@ -1740,6 +1745,11 @@ This is your own one-time link!</target>
|
|||||||
<target>Core version: v%@</target>
|
<target>Core version: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<target>Corner</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Correct name to %@?</target>
|
<target>Correct name to %@?</target>
|
||||||
@@ -2724,6 +2734,11 @@ This is your own one-time link!</target>
|
|||||||
<target>Error changing address</target>
|
<target>Error changing address</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<target>Error changing connection profile</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Error changing role</target>
|
<target>Error changing role</target>
|
||||||
@@ -2734,6 +2749,11 @@ This is your own one-time link!</target>
|
|||||||
<target>Error changing setting</target>
|
<target>Error changing setting</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<target>Error changing to incognito!</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<target>Error connecting to forwarding server %@. Please try later.</target>
|
<target>Error connecting to forwarding server %@. Please try later.</target>
|
||||||
@@ -2954,6 +2974,11 @@ This is your own one-time link!</target>
|
|||||||
<target>Error stopping chat</target>
|
<target>Error stopping chat</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<target>Error switching profile</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Error switching profile!</target>
|
<target>Error switching profile!</target>
|
||||||
@@ -4184,6 +4209,11 @@ This is your link for group %@!</target>
|
|||||||
<target>Message servers</target>
|
<target>Message servers</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<target>Message shape</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>Message source remains private.</target>
|
<target>Message source remains private.</target>
|
||||||
@@ -5729,6 +5759,11 @@ Enable in *Network & servers* settings.</target>
|
|||||||
<target>Select</target>
|
<target>Select</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<target>Select chat profile</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<target>Selected %lld</target>
|
<target>Selected %lld</target>
|
||||||
@@ -6099,6 +6134,11 @@ Enable in *Network & servers* settings.</target>
|
|||||||
<target>Share link</target>
|
<target>Share link</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<target>Share profile</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Share this 1-time invite link</target>
|
<target>Share this 1-time invite link</target>
|
||||||
@@ -6439,6 +6479,11 @@ Enable in *Network & servers* settings.</target>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<target>Tail</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Take picture</target>
|
<target>Take picture</target>
|
||||||
@@ -7719,6 +7764,11 @@ Repeat connection request?</target>
|
|||||||
<target>Your chat profiles</target>
|
<target>Your chat profiles</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<target>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Your contact sent a file that is larger than currently supported maximum size (%@).</target>
|
<target>Your contact sent a file that is larger than currently supported maximum size (%@).</target>
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>¡ %@ quiere contactar!</target>
|
<target>¡ %@ quiere contactar!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ y %lld miembro(s) más</target>
|
<target>%@, %@ y %lld miembro(s) más</target>
|
||||||
@@ -802,7 +806,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||||
<source>Allow to send SimpleX links.</source>
|
<source>Allow to send SimpleX links.</source>
|
||||||
<target>Permitir enviar enlaces SimpleX.</target>
|
<target>Se permite enviar enlaces SimpleX.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||||
@@ -1740,6 +1744,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Versión Core: v%@</target>
|
<target>Versión Core: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>¿Corregir el nombre a %@?</target>
|
<target>¿Corregir el nombre a %@?</target>
|
||||||
@@ -2406,7 +2414,7 @@ This is your own one-time link!</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Do not send history to new members." xml:space="preserve">
|
<trans-unit id="Do not send history to new members." xml:space="preserve">
|
||||||
<source>Do not send history to new members.</source>
|
<source>Do not send history to new members.</source>
|
||||||
<target>No enviar historial a miembros nuevos.</target>
|
<target>No se envía el historial a los miembros nuevos.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Don't create address" xml:space="preserve">
|
<trans-unit id="Don't create address" xml:space="preserve">
|
||||||
@@ -2724,6 +2732,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Error al cambiar servidor</target>
|
<target>Error al cambiar servidor</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Error al cambiar rol</target>
|
<target>Error al cambiar rol</target>
|
||||||
@@ -2734,6 +2746,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Error cambiando configuración</target>
|
<target>Error cambiando configuración</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<target>Error al conectar con el servidor de reenvío %@. Por favor, inténtalo más tarde.</target>
|
<target>Error al conectar con el servidor de reenvío %@. Por favor, inténtalo más tarde.</target>
|
||||||
@@ -2954,6 +2970,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Error al parar SimpleX</target>
|
<target>Error al parar SimpleX</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>¡Error al cambiar perfil!</target>
|
<target>¡Error al cambiar perfil!</target>
|
||||||
@@ -4184,6 +4204,10 @@ This is your link for group %@!</source>
|
|||||||
<target>Servidores de mensajes</target>
|
<target>Servidores de mensajes</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>El autor del mensaje se mantiene privado.</target>
|
<target>El autor del mensaje se mantiene privado.</target>
|
||||||
@@ -5068,7 +5092,7 @@ Error: %@</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||||
<source>Prohibit sending SimpleX links.</source>
|
<source>Prohibit sending SimpleX links.</source>
|
||||||
<target>No permitir el envío de enlaces SimpleX.</target>
|
<target>No se permite enviar enlaces SimpleX.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||||
@@ -5729,6 +5753,10 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
|
|||||||
<target>Seleccionar</target>
|
<target>Seleccionar</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<target>Seleccionados %lld</target>
|
<target>Seleccionados %lld</target>
|
||||||
@@ -5841,7 +5869,7 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Send up to 100 last messages to new members." xml:space="preserve">
|
<trans-unit id="Send up to 100 last messages to new members." xml:space="preserve">
|
||||||
<source>Send up to 100 last messages to new members.</source>
|
<source>Send up to 100 last messages to new members.</source>
|
||||||
<target>Enviar hasta 100 últimos mensajes a los miembros nuevos.</target>
|
<target>Se envían hasta 100 mensajes más recientes a los miembros nuevos.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Sender cancelled file transfer." xml:space="preserve">
|
<trans-unit id="Sender cancelled file transfer." xml:space="preserve">
|
||||||
@@ -6099,6 +6127,10 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
|
|||||||
<target>Compartir enlace</target>
|
<target>Compartir enlace</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Comparte este enlace de un solo uso</target>
|
<target>Comparte este enlace de un solo uso</target>
|
||||||
@@ -6439,6 +6471,10 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Tomar foto</target>
|
<target>Tomar foto</target>
|
||||||
@@ -7449,7 +7485,7 @@ Repeat join request?</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
|
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
|
||||||
<source>You can change it in Appearance settings.</source>
|
<source>You can change it in Appearance settings.</source>
|
||||||
<target>Puede cambiarlo desde el menú Apariencia.</target>
|
<target>Puedes cambiar la posición de la barra desde el menú Apariencia.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can create it later" xml:space="preserve">
|
<trans-unit id="You can create it later" xml:space="preserve">
|
||||||
@@ -7719,6 +7755,10 @@ Repeat connection request?</source>
|
|||||||
<target>Mis perfiles</target>
|
<target>Mis perfiles</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>El contacto ha enviado un archivo mayor al máximo admitido (%@).</target>
|
<target>El contacto ha enviado un archivo mayor al máximo admitido (%@).</target>
|
||||||
|
|||||||
@@ -133,6 +133,10 @@
|
|||||||
<target>%@ haluaa muodostaa yhteyden!</target>
|
<target>%@ haluaa muodostaa yhteyden!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -1616,6 +1620,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Ydinversio: v%@</target>
|
<target>Ydinversio: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2544,6 +2552,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Virhe osoitteenvaihdossa</target>
|
<target>Virhe osoitteenvaihdossa</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Virhe roolin vaihdossa</target>
|
<target>Virhe roolin vaihdossa</target>
|
||||||
@@ -2554,6 +2566,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Virhe asetuksen muuttamisessa</target>
|
<target>Virhe asetuksen muuttamisessa</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2762,6 +2778,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Virhe keskustelun lopettamisessa</target>
|
<target>Virhe keskustelun lopettamisessa</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Virhe profiilin vaihdossa!</target>
|
<target>Virhe profiilin vaihdossa!</target>
|
||||||
@@ -3918,6 +3938,10 @@ This is your link for group %@!</source>
|
|||||||
<source>Message servers</source>
|
<source>Message servers</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5351,6 +5375,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Valitse</target>
|
<target>Valitse</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5695,6 +5723,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Jaa linkki</target>
|
<target>Jaa linkki</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -6010,6 +6042,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Ota kuva</target>
|
<target>Ota kuva</target>
|
||||||
@@ -7194,6 +7230,10 @@ Repeat connection request?</source>
|
|||||||
<target>Keskusteluprofiilisi</target>
|
<target>Keskusteluprofiilisi</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Yhteyshenkilösi lähetti tiedoston, joka on suurempi kuin tällä hetkellä tuettu enimmäiskoko (%@).</target>
|
<target>Yhteyshenkilösi lähetti tiedoston, joka on suurempi kuin tällä hetkellä tuettu enimmäiskoko (%@).</target>
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ veut se connecter !</target>
|
<target>%@ veut se connecter !</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ et %lld membres</target>
|
<target>%@, %@ et %lld membres</target>
|
||||||
@@ -1740,6 +1744,10 @@ Il s'agit de votre propre lien unique !</target>
|
|||||||
<target>Version du cœur : v%@</target>
|
<target>Version du cœur : v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Corriger le nom pour %@ ?</target>
|
<target>Corriger le nom pour %@ ?</target>
|
||||||
@@ -2724,6 +2732,10 @@ Il s'agit de votre propre lien unique !</target>
|
|||||||
<target>Erreur de changement d'adresse</target>
|
<target>Erreur de changement d'adresse</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Erreur lors du changement de rôle</target>
|
<target>Erreur lors du changement de rôle</target>
|
||||||
@@ -2734,6 +2746,10 @@ Il s'agit de votre propre lien unique !</target>
|
|||||||
<target>Erreur de changement de paramètre</target>
|
<target>Erreur de changement de paramètre</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<target>Erreur de connexion au serveur de redirection %@. Veuillez réessayer plus tard.</target>
|
<target>Erreur de connexion au serveur de redirection %@. Veuillez réessayer plus tard.</target>
|
||||||
@@ -2954,6 +2970,10 @@ Il s'agit de votre propre lien unique !</target>
|
|||||||
<target>Erreur lors de l'arrêt du chat</target>
|
<target>Erreur lors de l'arrêt du chat</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Erreur lors du changement de profil !</target>
|
<target>Erreur lors du changement de profil !</target>
|
||||||
@@ -4184,6 +4204,10 @@ Voici votre lien pour le groupe %@ !</target>
|
|||||||
<target>Serveurs de messages</target>
|
<target>Serveurs de messages</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>La source du message reste privée.</target>
|
<target>La source du message reste privée.</target>
|
||||||
@@ -5729,6 +5753,10 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
|
|||||||
<target>Choisir</target>
|
<target>Choisir</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<target>%lld sélectionné(s)</target>
|
<target>%lld sélectionné(s)</target>
|
||||||
@@ -6099,6 +6127,10 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
|
|||||||
<target>Partager le lien</target>
|
<target>Partager le lien</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Partager ce lien d'invitation unique</target>
|
<target>Partager ce lien d'invitation unique</target>
|
||||||
@@ -6439,6 +6471,10 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Prendre une photo</target>
|
<target>Prendre une photo</target>
|
||||||
@@ -7719,6 +7755,10 @@ Répéter la demande de connexion ?</target>
|
|||||||
<target>Vos profils de chat</target>
|
<target>Vos profils de chat</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Votre contact a envoyé un fichier plus grand que la taille maximale supportée actuellement(%@).</target>
|
<target>Votre contact a envoyé un fichier plus grand que la taille maximale supportée actuellement(%@).</target>
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ kapcsolódni szeretne!</target>
|
<target>%@ kapcsolódni szeretne!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ és további %lld tag</target>
|
<target>%@, %@ és további %lld tag</target>
|
||||||
@@ -1042,7 +1046,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Bad message hash" xml:space="preserve">
|
<trans-unit id="Bad message hash" xml:space="preserve">
|
||||||
<source>Bad message hash</source>
|
<source>Bad message hash</source>
|
||||||
<target>Téves üzenet hash</target>
|
<target>Hibás az üzenet ellenőrzőösszege</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Better groups" xml:space="preserve">
|
<trans-unit id="Better groups" xml:space="preserve">
|
||||||
@@ -1740,6 +1744,10 @@ Ez az egyszer használatos hivatkozása!</target>
|
|||||||
<target>Alapverziószám: v%@</target>
|
<target>Alapverziószám: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Név javítása erre: %@?</target>
|
<target>Név javítása erre: %@?</target>
|
||||||
@@ -2131,7 +2139,7 @@ Ez az egyszer használatos hivatkozása!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete member message?" xml:space="preserve">
|
<trans-unit id="Delete member message?" xml:space="preserve">
|
||||||
<source>Delete member message?</source>
|
<source>Delete member message?</source>
|
||||||
<target>Csoporttag üzenet törlése?</target>
|
<target>Csoporttag üzenetének törlése?</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete message?" xml:space="preserve">
|
<trans-unit id="Delete message?" xml:space="preserve">
|
||||||
@@ -2221,7 +2229,7 @@ Ez az egyszer használatos hivatkozása!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delivery receipts!" xml:space="preserve">
|
<trans-unit id="Delivery receipts!" xml:space="preserve">
|
||||||
<source>Delivery receipts!</source>
|
<source>Delivery receipts!</source>
|
||||||
<target>Kézbesítési igazolások!</target>
|
<target>Üzenet kézbesítési jelentések!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Description" xml:space="preserve">
|
<trans-unit id="Description" xml:space="preserve">
|
||||||
@@ -2681,12 +2689,12 @@ Ez az egyszer használatos hivatkozása!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Enter welcome message…" xml:space="preserve">
|
<trans-unit id="Enter welcome message…" xml:space="preserve">
|
||||||
<source>Enter welcome message…</source>
|
<source>Enter welcome message…</source>
|
||||||
<target>Üdvözlő üzenetet megadása…</target>
|
<target>Üdvözlő üzenet megadása…</target>
|
||||||
<note>placeholder</note>
|
<note>placeholder</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Enter welcome message… (optional)" xml:space="preserve">
|
<trans-unit id="Enter welcome message… (optional)" xml:space="preserve">
|
||||||
<source>Enter welcome message… (optional)</source>
|
<source>Enter welcome message… (optional)</source>
|
||||||
<target>Üdvözlő üzenetet megadása… (opcionális)</target>
|
<target>Üdvözlő üzenet megadása… (opcionális)</target>
|
||||||
<note>placeholder</note>
|
<note>placeholder</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Enter your name…" xml:space="preserve">
|
<trans-unit id="Enter your name…" xml:space="preserve">
|
||||||
@@ -2724,6 +2732,10 @@ Ez az egyszer használatos hivatkozása!</target>
|
|||||||
<target>Hiba a cím megváltoztatásakor</target>
|
<target>Hiba a cím megváltoztatásakor</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Hiba a szerepkör megváltoztatásakor</target>
|
<target>Hiba a szerepkör megváltoztatásakor</target>
|
||||||
@@ -2734,6 +2746,10 @@ Ez az egyszer használatos hivatkozása!</target>
|
|||||||
<target>Hiba a beállítás megváltoztatásakor</target>
|
<target>Hiba a beállítás megváltoztatásakor</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<target>Hiba a(z) %@ továbbító kiszolgálóhoz való kapcsolódáskor. Próbálja meg később.</target>
|
<target>Hiba a(z) %@ továbbító kiszolgálóhoz való kapcsolódáskor. Próbálja meg később.</target>
|
||||||
@@ -2954,6 +2970,10 @@ Ez az egyszer használatos hivatkozása!</target>
|
|||||||
<target>Hiba a csevegés megállításakor</target>
|
<target>Hiba a csevegés megállításakor</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Hiba a profil váltásakor!</target>
|
<target>Hiba a profil váltásakor!</target>
|
||||||
@@ -4131,7 +4151,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Message delivery receipts!" xml:space="preserve">
|
<trans-unit id="Message delivery receipts!" xml:space="preserve">
|
||||||
<source>Message delivery receipts!</source>
|
<source>Message delivery receipts!</source>
|
||||||
<target>Üzenetkézbesítési bizonylatok!</target>
|
<target>Üzenet kézbesítési jelentések!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Message delivery warning" xml:space="preserve">
|
<trans-unit id="Message delivery warning" xml:space="preserve">
|
||||||
@@ -4176,7 +4196,7 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Message reception" xml:space="preserve">
|
<trans-unit id="Message reception" xml:space="preserve">
|
||||||
<source>Message reception</source>
|
<source>Message reception</source>
|
||||||
<target>Üzenetjelentés</target>
|
<target>Üzenet kézbesítési jelentés</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Message servers" xml:space="preserve">
|
<trans-unit id="Message servers" xml:space="preserve">
|
||||||
@@ -4184,6 +4204,10 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
|
|||||||
<target>Üzenetkiszolgálók</target>
|
<target>Üzenetkiszolgálók</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>Az üzenet forrása titokban marad.</target>
|
<target>Az üzenet forrása titokban marad.</target>
|
||||||
@@ -5729,6 +5753,10 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben.</target>
|
|||||||
<target>Választás</target>
|
<target>Választás</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<target>%lld kiválasztva</target>
|
<target>%lld kiválasztva</target>
|
||||||
@@ -6099,6 +6127,10 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben.</target>
|
|||||||
<target>Hivatkozás megosztása</target>
|
<target>Hivatkozás megosztása</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Egyszer használatos meghívó hivatkozás megosztása</target>
|
<target>Egyszer használatos meghívó hivatkozás megosztása</target>
|
||||||
@@ -6439,6 +6471,10 @@ Engedélyezze a Beállítások / Hálózat és kiszolgálók menüben.</target>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Kép készítése</target>
|
<target>Kép készítése</target>
|
||||||
@@ -6573,7 +6609,7 @@ Ez valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="The hash of the previous message is different." xml:space="preserve">
|
<trans-unit id="The hash of the previous message is different." xml:space="preserve">
|
||||||
<source>The hash of the previous message is different.</source>
|
<source>The hash of the previous message is different.</source>
|
||||||
<target>Az előző üzenet hash-e más.</target>
|
<target>Az előző üzenet ellenőrzőösszege különbözik.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="The message will be deleted for all members." xml:space="preserve">
|
<trans-unit id="The message will be deleted for all members." xml:space="preserve">
|
||||||
@@ -7719,6 +7755,10 @@ Kapcsolódási kérés megismétlése?</target>
|
|||||||
<target>Csevegési profilok</target>
|
<target>Csevegési profilok</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Ismerőse olyan fájlt küldött, amely meghaladja a jelenleg támogatott maximális méretet (%@).</target>
|
<target>Ismerőse olyan fájlt küldött, amely meghaladja a jelenleg támogatott maximális méretet (%@).</target>
|
||||||
@@ -7888,7 +7928,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="bad message hash" xml:space="preserve">
|
<trans-unit id="bad message hash" xml:space="preserve">
|
||||||
<source>bad message hash</source>
|
<source>bad message hash</source>
|
||||||
<target>téves üzenet hash</target>
|
<target>hibás az üzenet ellenőrzőösszege</target>
|
||||||
<note>integrity error chat item</note>
|
<note>integrity error chat item</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="blocked" xml:space="preserve">
|
<trans-unit id="blocked" xml:space="preserve">
|
||||||
@@ -8490,7 +8530,7 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="removed %@" xml:space="preserve">
|
<trans-unit id="removed %@" xml:space="preserve">
|
||||||
<source>removed %@</source>
|
<source>removed %@</source>
|
||||||
<target>%@ eltávolítva</target>
|
<target>eltávolította őt: %@</target>
|
||||||
<note>rcv group event chat item</note>
|
<note>rcv group event chat item</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="removed contact address" xml:space="preserve">
|
<trans-unit id="removed contact address" xml:space="preserve">
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ si vuole connettere!</target>
|
<target>%@ si vuole connettere!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ e %lld membri</target>
|
<target>%@, %@ e %lld membri</target>
|
||||||
@@ -1617,7 +1621,7 @@ Questo è il tuo link una tantum!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connection and servers status." xml:space="preserve">
|
<trans-unit id="Connection and servers status." xml:space="preserve">
|
||||||
<source>Connection and servers status.</source>
|
<source>Connection and servers status.</source>
|
||||||
<target>Stato di connessione e server.</target>
|
<target>Stato della connessione e dei server.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connection error" xml:space="preserve">
|
<trans-unit id="Connection error" xml:space="preserve">
|
||||||
@@ -1740,6 +1744,10 @@ Questo è il tuo link una tantum!</target>
|
|||||||
<target>Versione core: v%@</target>
|
<target>Versione core: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Correggere il nome a %@?</target>
|
<target>Correggere il nome a %@?</target>
|
||||||
@@ -2724,6 +2732,10 @@ Questo è il tuo link una tantum!</target>
|
|||||||
<target>Errore nella modifica dell'indirizzo</target>
|
<target>Errore nella modifica dell'indirizzo</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Errore nel cambio di ruolo</target>
|
<target>Errore nel cambio di ruolo</target>
|
||||||
@@ -2734,6 +2746,10 @@ Questo è il tuo link una tantum!</target>
|
|||||||
<target>Errore nella modifica dell'impostazione</target>
|
<target>Errore nella modifica dell'impostazione</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<target>Errore di connessione al server di inoltro %@. Riprova più tardi.</target>
|
<target>Errore di connessione al server di inoltro %@. Riprova più tardi.</target>
|
||||||
@@ -2954,6 +2970,10 @@ Questo è il tuo link una tantum!</target>
|
|||||||
<target>Errore nell'interruzione della chat</target>
|
<target>Errore nell'interruzione della chat</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Errore nel cambio di profilo!</target>
|
<target>Errore nel cambio di profilo!</target>
|
||||||
@@ -4184,6 +4204,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
|||||||
<target>Server dei messaggi</target>
|
<target>Server dei messaggi</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>La fonte del messaggio resta privata.</target>
|
<target>La fonte del messaggio resta privata.</target>
|
||||||
@@ -5729,6 +5753,10 @@ Attivalo nelle impostazioni *Rete e server*.</target>
|
|||||||
<target>Seleziona</target>
|
<target>Seleziona</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<target>%lld selezionato</target>
|
<target>%lld selezionato</target>
|
||||||
@@ -6099,6 +6127,10 @@ Attivalo nelle impostazioni *Rete e server*.</target>
|
|||||||
<target>Condividi link</target>
|
<target>Condividi link</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Condividi questo link di invito una tantum</target>
|
<target>Condividi questo link di invito una tantum</target>
|
||||||
@@ -6439,6 +6471,10 @@ Attivalo nelle impostazioni *Rete e server*.</target>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Scatta foto</target>
|
<target>Scatta foto</target>
|
||||||
@@ -7719,6 +7755,10 @@ Ripetere la richiesta di connessione?</target>
|
|||||||
<target>I tuoi profili di chat</target>
|
<target>I tuoi profili di chat</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Il tuo contatto ha inviato un file più grande della dimensione massima attualmente supportata (%@).</target>
|
<target>Il tuo contatto ha inviato un file più grande della dimensione massima attualmente supportata (%@).</target>
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ が接続を希望しています!</target>
|
<target>%@ が接続を希望しています!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@や%@など%lld人のメンバー</target>
|
<target>%@や%@など%lld人のメンバー</target>
|
||||||
@@ -1640,6 +1644,10 @@ This is your own one-time link!</source>
|
|||||||
<target>コアのバージョン: v%@</target>
|
<target>コアのバージョン: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2569,6 +2577,10 @@ This is your own one-time link!</source>
|
|||||||
<target>アドレス変更にエラー発生</target>
|
<target>アドレス変更にエラー発生</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>役割変更にエラー発生</target>
|
<target>役割変更にエラー発生</target>
|
||||||
@@ -2579,6 +2591,10 @@ This is your own one-time link!</source>
|
|||||||
<target>設定変更にエラー発生</target>
|
<target>設定変更にエラー発生</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2787,6 +2803,10 @@ This is your own one-time link!</source>
|
|||||||
<target>チャット停止にエラー発生</target>
|
<target>チャット停止にエラー発生</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>プロフィール切り替えにエラー発生!</target>
|
<target>プロフィール切り替えにエラー発生!</target>
|
||||||
@@ -3942,6 +3962,10 @@ This is your link for group %@!</source>
|
|||||||
<source>Message servers</source>
|
<source>Message servers</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5376,6 +5400,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>選択</target>
|
<target>選択</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5713,6 +5741,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>リンクを送る</target>
|
<target>リンクを送る</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -6029,6 +6061,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>写真を撮影</target>
|
<target>写真を撮影</target>
|
||||||
@@ -7212,6 +7248,10 @@ Repeat connection request?</source>
|
|||||||
<target>あなたのチャットプロフィール</target>
|
<target>あなたのチャットプロフィール</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>連絡先が現在サポートされている最大サイズ (%@) より大きいファイルを送信しました。</target>
|
<target>連絡先が現在サポートされている最大サイズ (%@) より大きいファイルを送信しました。</target>
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ wil verbinding maken!</target>
|
<target>%@ wil verbinding maken!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ en %lld leden</target>
|
<target>%@, %@ en %lld leden</target>
|
||||||
@@ -1740,6 +1744,10 @@ Dit is uw eigen eenmalige link!</target>
|
|||||||
<target>Core versie: v% @</target>
|
<target>Core versie: v% @</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Juiste naam voor %@?</target>
|
<target>Juiste naam voor %@?</target>
|
||||||
@@ -2724,6 +2732,10 @@ Dit is uw eigen eenmalige link!</target>
|
|||||||
<target>Fout bij wijzigen van adres</target>
|
<target>Fout bij wijzigen van adres</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Fout bij wisselen van rol</target>
|
<target>Fout bij wisselen van rol</target>
|
||||||
@@ -2734,6 +2746,10 @@ Dit is uw eigen eenmalige link!</target>
|
|||||||
<target>Fout bij wijzigen van instelling</target>
|
<target>Fout bij wijzigen van instelling</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<target>Fout bij het verbinden met doorstuurserver %@. Probeer het later opnieuw.</target>
|
<target>Fout bij het verbinden met doorstuurserver %@. Probeer het later opnieuw.</target>
|
||||||
@@ -2954,6 +2970,10 @@ Dit is uw eigen eenmalige link!</target>
|
|||||||
<target>Fout bij het stoppen van de chat</target>
|
<target>Fout bij het stoppen van de chat</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Fout bij wisselen van profiel!</target>
|
<target>Fout bij wisselen van profiel!</target>
|
||||||
@@ -3906,7 +3926,7 @@ Dit is jouw link voor groep %@!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Keep conversation" xml:space="preserve">
|
<trans-unit id="Keep conversation" xml:space="preserve">
|
||||||
<source>Keep conversation</source>
|
<source>Keep conversation</source>
|
||||||
<target>Blijf in gesprek</target>
|
<target>Behoud het gesprek</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||||
@@ -4184,6 +4204,10 @@ Dit is jouw link voor groep %@!</target>
|
|||||||
<target>Berichtservers</target>
|
<target>Berichtservers</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>Berichtbron blijft privé.</target>
|
<target>Berichtbron blijft privé.</target>
|
||||||
@@ -5729,6 +5753,10 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
|
|||||||
<target>Selecteer</target>
|
<target>Selecteer</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<target>%lld geselecteerd</target>
|
<target>%lld geselecteerd</target>
|
||||||
@@ -6099,6 +6127,10 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
|
|||||||
<target>Deel link</target>
|
<target>Deel link</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Deel deze eenmalige uitnodigingslink</target>
|
<target>Deel deze eenmalige uitnodigingslink</target>
|
||||||
@@ -6439,6 +6471,10 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Foto nemen</target>
|
<target>Foto nemen</target>
|
||||||
@@ -7719,6 +7755,10 @@ Verbindingsverzoek herhalen?</target>
|
|||||||
<target>Uw chat profielen</target>
|
<target>Uw chat profielen</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Uw contact heeft een bestand verzonden dat groter is dan de momenteel ondersteunde maximale grootte (%@).</target>
|
<target>Uw contact heeft een bestand verzonden dat groter is dan de momenteel ondersteunde maximale grootte (%@).</target>
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ chce się połączyć!</target>
|
<target>%@ chce się połączyć!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ i %lld członków</target>
|
<target>%@, %@ i %lld członków</target>
|
||||||
@@ -752,6 +756,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Allow calls?" xml:space="preserve">
|
<trans-unit id="Allow calls?" xml:space="preserve">
|
||||||
<source>Allow calls?</source>
|
<source>Allow calls?</source>
|
||||||
|
<target>Zezwolić na połączenia?</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
|
<trans-unit id="Allow disappearing messages only if your contact allows it to you." xml:space="preserve">
|
||||||
@@ -791,6 +796,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Allow sharing" xml:space="preserve">
|
<trans-unit id="Allow sharing" xml:space="preserve">
|
||||||
<source>Allow sharing</source>
|
<source>Allow sharing</source>
|
||||||
|
<target>Zezwól na udostępnianie</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Allow to irreversibly delete sent messages. (24 hours)" xml:space="preserve">
|
<trans-unit id="Allow to irreversibly delete sent messages. (24 hours)" xml:space="preserve">
|
||||||
@@ -945,10 +951,12 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
|
<trans-unit id="Archive contacts to chat later." xml:space="preserve">
|
||||||
<source>Archive contacts to chat later.</source>
|
<source>Archive contacts to chat later.</source>
|
||||||
|
<target>Archiwizuj kontakty aby porozmawiać później.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Archived contacts" xml:space="preserve">
|
<trans-unit id="Archived contacts" xml:space="preserve">
|
||||||
<source>Archived contacts</source>
|
<source>Archived contacts</source>
|
||||||
|
<target>Zarchiwizowane kontakty</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Archiving database" xml:space="preserve">
|
<trans-unit id="Archiving database" xml:space="preserve">
|
||||||
@@ -1053,6 +1061,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Better networking" xml:space="preserve">
|
<trans-unit id="Better networking" xml:space="preserve">
|
||||||
<source>Better networking</source>
|
<source>Better networking</source>
|
||||||
|
<target>Lepsze sieciowanie</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Black" xml:space="preserve">
|
<trans-unit id="Black" xml:space="preserve">
|
||||||
@@ -1097,10 +1106,12 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Blur for better privacy." xml:space="preserve">
|
<trans-unit id="Blur for better privacy." xml:space="preserve">
|
||||||
<source>Blur for better privacy.</source>
|
<source>Blur for better privacy.</source>
|
||||||
|
<target>Rozmycie dla lepszej prywatności.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Blur media" xml:space="preserve">
|
<trans-unit id="Blur media" xml:space="preserve">
|
||||||
<source>Blur media</source>
|
<source>Blur media</source>
|
||||||
|
<target>Rozmycie mediów</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Both you and your contact can add message reactions." xml:space="preserve">
|
<trans-unit id="Both you and your contact can add message reactions." xml:space="preserve">
|
||||||
@@ -1150,6 +1161,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Calls prohibited!" xml:space="preserve">
|
<trans-unit id="Calls prohibited!" xml:space="preserve">
|
||||||
<source>Calls prohibited!</source>
|
<source>Calls prohibited!</source>
|
||||||
|
<target>Połączenia zakazane!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Camera not available" xml:space="preserve">
|
<trans-unit id="Camera not available" xml:space="preserve">
|
||||||
@@ -1159,10 +1171,12 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Can't call contact" xml:space="preserve">
|
<trans-unit id="Can't call contact" xml:space="preserve">
|
||||||
<source>Can't call contact</source>
|
<source>Can't call contact</source>
|
||||||
|
<target>Nie można zadzwonić do kontaktu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Can't call member" xml:space="preserve">
|
<trans-unit id="Can't call member" xml:space="preserve">
|
||||||
<source>Can't call member</source>
|
<source>Can't call member</source>
|
||||||
|
<target>Nie można zadzwonić do członka</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Can't invite contact!" xml:space="preserve">
|
<trans-unit id="Can't invite contact!" xml:space="preserve">
|
||||||
@@ -1177,6 +1191,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Can't message member" xml:space="preserve">
|
<trans-unit id="Can't message member" xml:space="preserve">
|
||||||
<source>Can't message member</source>
|
<source>Can't message member</source>
|
||||||
|
<target>Nie można wysłać wiadomości do członka</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Cancel" xml:space="preserve">
|
<trans-unit id="Cancel" xml:space="preserve">
|
||||||
@@ -1292,6 +1307,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Chat database exported" xml:space="preserve">
|
<trans-unit id="Chat database exported" xml:space="preserve">
|
||||||
<source>Chat database exported</source>
|
<source>Chat database exported</source>
|
||||||
|
<target>Wyeksportowano bazę danych czatu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Chat database imported" xml:space="preserve">
|
<trans-unit id="Chat database imported" xml:space="preserve">
|
||||||
@@ -1316,6 +1332,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Chat list" xml:space="preserve">
|
<trans-unit id="Chat list" xml:space="preserve">
|
||||||
<source>Chat list</source>
|
<source>Chat list</source>
|
||||||
|
<target>Lista czatów</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Chat migrated!" xml:space="preserve">
|
<trans-unit id="Chat migrated!" xml:space="preserve">
|
||||||
@@ -1405,6 +1422,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Color chats with the new themes." xml:space="preserve">
|
<trans-unit id="Color chats with the new themes." xml:space="preserve">
|
||||||
<source>Color chats with the new themes.</source>
|
<source>Color chats with the new themes.</source>
|
||||||
|
<target>Koloruj czaty z nowymi motywami.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Color mode" xml:space="preserve">
|
<trans-unit id="Color mode" xml:space="preserve">
|
||||||
@@ -1449,6 +1467,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
|
<trans-unit id="Confirm contact deletion?" xml:space="preserve">
|
||||||
<source>Confirm contact deletion?</source>
|
<source>Confirm contact deletion?</source>
|
||||||
|
<target>Potwierdzić usunięcie kontaktu?</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Confirm database upgrades" xml:space="preserve">
|
<trans-unit id="Confirm database upgrades" xml:space="preserve">
|
||||||
@@ -1508,6 +1527,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connect to your friends faster." xml:space="preserve">
|
<trans-unit id="Connect to your friends faster." xml:space="preserve">
|
||||||
<source>Connect to your friends faster.</source>
|
<source>Connect to your friends faster.</source>
|
||||||
|
<target>Szybciej łącz się ze znajomymi.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connect to yourself?" xml:space="preserve">
|
<trans-unit id="Connect to yourself?" xml:space="preserve">
|
||||||
@@ -1586,6 +1606,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
|
<trans-unit id="Connecting to contact, please wait or check later!" xml:space="preserve">
|
||||||
<source>Connecting to contact, please wait or check later!</source>
|
<source>Connecting to contact, please wait or check later!</source>
|
||||||
|
<target>Łączenie z kontaktem, poczekaj lub sprawdź później!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connecting to desktop" xml:space="preserve">
|
<trans-unit id="Connecting to desktop" xml:space="preserve">
|
||||||
@@ -1600,6 +1621,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connection and servers status." xml:space="preserve">
|
<trans-unit id="Connection and servers status." xml:space="preserve">
|
||||||
<source>Connection and servers status.</source>
|
<source>Connection and servers status.</source>
|
||||||
|
<target>Stan połączenia i serwerów.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connection error" xml:space="preserve">
|
<trans-unit id="Connection error" xml:space="preserve">
|
||||||
@@ -1614,6 +1636,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connection notifications" xml:space="preserve">
|
<trans-unit id="Connection notifications" xml:space="preserve">
|
||||||
<source>Connection notifications</source>
|
<source>Connection notifications</source>
|
||||||
|
<target>Powiadomienia o połączeniu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Connection request sent!" xml:space="preserve">
|
<trans-unit id="Connection request sent!" xml:space="preserve">
|
||||||
@@ -1653,6 +1676,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Contact deleted!" xml:space="preserve">
|
<trans-unit id="Contact deleted!" xml:space="preserve">
|
||||||
<source>Contact deleted!</source>
|
<source>Contact deleted!</source>
|
||||||
|
<target>Kontakt usunięty!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Contact hidden:" xml:space="preserve">
|
<trans-unit id="Contact hidden:" xml:space="preserve">
|
||||||
@@ -1667,6 +1691,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Contact is deleted." xml:space="preserve">
|
<trans-unit id="Contact is deleted." xml:space="preserve">
|
||||||
<source>Contact is deleted.</source>
|
<source>Contact is deleted.</source>
|
||||||
|
<target>Kontakt jest usunięty.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Contact name" xml:space="preserve">
|
<trans-unit id="Contact name" xml:space="preserve">
|
||||||
@@ -1681,6 +1706,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
|
<trans-unit id="Contact will be deleted - this cannot be undone!" xml:space="preserve">
|
||||||
<source>Contact will be deleted - this cannot be undone!</source>
|
<source>Contact will be deleted - this cannot be undone!</source>
|
||||||
|
<target>Kontakt zostanie usunięty – nie można tego cofnąć!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Contacts" xml:space="preserve">
|
<trans-unit id="Contacts" xml:space="preserve">
|
||||||
@@ -1700,6 +1726,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Conversation deleted!" xml:space="preserve">
|
<trans-unit id="Conversation deleted!" xml:space="preserve">
|
||||||
<source>Conversation deleted!</source>
|
<source>Conversation deleted!</source>
|
||||||
|
<target>Rozmowa usunięta!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Copy" xml:space="preserve">
|
<trans-unit id="Copy" xml:space="preserve">
|
||||||
@@ -1717,6 +1744,10 @@ To jest twój jednorazowy link!</target>
|
|||||||
<target>Wersja rdzenia: v%@</target>
|
<target>Wersja rdzenia: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Poprawić imię na %@?</target>
|
<target>Poprawić imię na %@?</target>
|
||||||
@@ -1978,6 +2009,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
|
<trans-unit id="Delete %lld messages of members?" xml:space="preserve">
|
||||||
<source>Delete %lld messages of members?</source>
|
<source>Delete %lld messages of members?</source>
|
||||||
|
<target>Usunąć %lld wiadomości członków?</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete %lld messages?" xml:space="preserve">
|
<trans-unit id="Delete %lld messages?" xml:space="preserve">
|
||||||
@@ -2042,6 +2074,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete contact?" xml:space="preserve">
|
<trans-unit id="Delete contact?" xml:space="preserve">
|
||||||
<source>Delete contact?</source>
|
<source>Delete contact?</source>
|
||||||
|
<target>Usunąć kontakt?</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete database" xml:space="preserve">
|
<trans-unit id="Delete database" xml:space="preserve">
|
||||||
@@ -2151,6 +2184,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
|
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
|
||||||
<source>Delete up to 20 messages at once.</source>
|
<source>Delete up to 20 messages at once.</source>
|
||||||
|
<target>Usuń do 20 wiadomości na raz.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete user profile?" xml:space="preserve">
|
<trans-unit id="Delete user profile?" xml:space="preserve">
|
||||||
@@ -2160,6 +2194,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete without notification" xml:space="preserve">
|
<trans-unit id="Delete without notification" xml:space="preserve">
|
||||||
<source>Delete without notification</source>
|
<source>Delete without notification</source>
|
||||||
|
<target>Usuń bez powiadomienia</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Deleted" xml:space="preserve">
|
<trans-unit id="Deleted" xml:space="preserve">
|
||||||
@@ -2219,6 +2254,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Destination server address of %@ is incompatible with forwarding server %@ settings." xml:space="preserve">
|
<trans-unit id="Destination server address of %@ is incompatible with forwarding server %@ settings." xml:space="preserve">
|
||||||
<source>Destination server address of %@ is incompatible with forwarding server %@ settings.</source>
|
<source>Destination server address of %@ is incompatible with forwarding server %@ settings.</source>
|
||||||
|
<target>Adres serwera docelowego %@ jest niekompatybilny z ustawieniami serwera przekazującego %@.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Destination server error: %@" xml:space="preserve">
|
<trans-unit id="Destination server error: %@" xml:space="preserve">
|
||||||
@@ -2228,6 +2264,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Destination server version of %@ is incompatible with forwarding server %@." xml:space="preserve">
|
<trans-unit id="Destination server version of %@ is incompatible with forwarding server %@." xml:space="preserve">
|
||||||
<source>Destination server version of %@ is incompatible with forwarding server %@.</source>
|
<source>Destination server version of %@ is incompatible with forwarding server %@.</source>
|
||||||
|
<target>Wersja serwera docelowego %@ jest niekompatybilna z serwerem przekierowującym %@.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Detailed statistics" xml:space="preserve">
|
<trans-unit id="Detailed statistics" xml:space="preserve">
|
||||||
@@ -2247,6 +2284,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Developer options" xml:space="preserve">
|
<trans-unit id="Developer options" xml:space="preserve">
|
||||||
<source>Developer options</source>
|
<source>Developer options</source>
|
||||||
|
<target>Opcje deweloperskie</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Developer tools" xml:space="preserve">
|
<trans-unit id="Developer tools" xml:space="preserve">
|
||||||
@@ -2301,6 +2339,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Disabled" xml:space="preserve">
|
<trans-unit id="Disabled" xml:space="preserve">
|
||||||
<source>Disabled</source>
|
<source>Disabled</source>
|
||||||
|
<target>Wyłączony</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Disappearing message" xml:space="preserve">
|
<trans-unit id="Disappearing message" xml:space="preserve">
|
||||||
@@ -2530,6 +2569,7 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Enabled" xml:space="preserve">
|
<trans-unit id="Enabled" xml:space="preserve">
|
||||||
<source>Enabled</source>
|
<source>Enabled</source>
|
||||||
|
<target>Włączony</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Enabled for" xml:space="preserve">
|
<trans-unit id="Enabled for" xml:space="preserve">
|
||||||
@@ -2692,6 +2732,10 @@ To jest twój jednorazowy link!</target>
|
|||||||
<target>Błąd zmiany adresu</target>
|
<target>Błąd zmiany adresu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Błąd zmiany roli</target>
|
<target>Błąd zmiany roli</target>
|
||||||
@@ -2702,8 +2746,13 @@ To jest twój jednorazowy link!</target>
|
|||||||
<target>Błąd zmiany ustawienia</target>
|
<target>Błąd zmiany ustawienia</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
|
<target>Błąd połączenia z serwerem przekierowania %@. Spróbuj ponownie później.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Error creating address" xml:space="preserve">
|
<trans-unit id="Error creating address" xml:space="preserve">
|
||||||
@@ -2921,6 +2970,10 @@ To jest twój jednorazowy link!</target>
|
|||||||
<target>Błąd zatrzymania czatu</target>
|
<target>Błąd zatrzymania czatu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Błąd przełączania profilu!</target>
|
<target>Błąd przełączania profilu!</target>
|
||||||
@@ -3209,14 +3262,17 @@ To jest twój jednorazowy link!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Forwarding server %@ failed to connect to destination server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Forwarding server %@ failed to connect to destination server %@. Please try later." xml:space="preserve">
|
||||||
<source>Forwarding server %@ failed to connect to destination server %@. Please try later.</source>
|
<source>Forwarding server %@ failed to connect to destination server %@. Please try later.</source>
|
||||||
|
<target>Serwer przekazujący %@ nie mógł połączyć się z serwerem docelowym %@. Spróbuj ponownie później.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Forwarding server address is incompatible with network settings: %@." xml:space="preserve">
|
<trans-unit id="Forwarding server address is incompatible with network settings: %@." xml:space="preserve">
|
||||||
<source>Forwarding server address is incompatible with network settings: %@.</source>
|
<source>Forwarding server address is incompatible with network settings: %@.</source>
|
||||||
|
<target>Adres serwera przekierowującego jest niekompatybilny z ustawieniami sieciowymi: %@.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Forwarding server version is incompatible with network settings: %@." xml:space="preserve">
|
<trans-unit id="Forwarding server version is incompatible with network settings: %@." xml:space="preserve">
|
||||||
<source>Forwarding server version is incompatible with network settings: %@.</source>
|
<source>Forwarding server version is incompatible with network settings: %@.</source>
|
||||||
|
<target>Wersja serwera przekierowującego jest niekompatybilna z ustawieniami sieciowymi: %@.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Forwarding server: %@ Destination server error: %@" xml:space="preserve">
|
<trans-unit id="Forwarding server: %@ Destination server error: %@" xml:space="preserve">
|
||||||
@@ -3803,6 +3859,7 @@ Błąd: %2$@</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
|
<trans-unit id="It protects your IP address and connections." xml:space="preserve">
|
||||||
<source>It protects your IP address and connections.</source>
|
<source>It protects your IP address and connections.</source>
|
||||||
|
<target>Chroni Twój adres IP i połączenia.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
|
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
|
||||||
@@ -3869,6 +3926,7 @@ To jest twój link do grupy %@!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Keep conversation" xml:space="preserve">
|
<trans-unit id="Keep conversation" xml:space="preserve">
|
||||||
<source>Keep conversation</source>
|
<source>Keep conversation</source>
|
||||||
|
<target>Zachowaj rozmowę</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
<trans-unit id="Keep the app open to use it from desktop" xml:space="preserve">
|
||||||
@@ -4048,10 +4106,12 @@ To jest twój link do grupy %@!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Media & file servers" xml:space="preserve">
|
<trans-unit id="Media & file servers" xml:space="preserve">
|
||||||
<source>Media & file servers</source>
|
<source>Media & file servers</source>
|
||||||
|
<target>Serwery mediów i plików</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Medium" xml:space="preserve">
|
<trans-unit id="Medium" xml:space="preserve">
|
||||||
<source>Medium</source>
|
<source>Medium</source>
|
||||||
|
<target>Średni</target>
|
||||||
<note>blur media</note>
|
<note>blur media</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Member" xml:space="preserve">
|
<trans-unit id="Member" xml:space="preserve">
|
||||||
@@ -4141,6 +4201,11 @@ To jest twój link do grupy %@!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Message servers" xml:space="preserve">
|
<trans-unit id="Message servers" xml:space="preserve">
|
||||||
<source>Message servers</source>
|
<source>Message servers</source>
|
||||||
|
<target>Serwery wiadomości</target>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
@@ -4355,6 +4420,7 @@ To jest twój link do grupy %@!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="New chat experience 🎉" xml:space="preserve">
|
<trans-unit id="New chat experience 🎉" xml:space="preserve">
|
||||||
<source>New chat experience 🎉</source>
|
<source>New chat experience 🎉</source>
|
||||||
|
<target>Nowe możliwości czatu 🎉</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="New contact request" xml:space="preserve">
|
<trans-unit id="New contact request" xml:space="preserve">
|
||||||
@@ -4389,6 +4455,7 @@ To jest twój link do grupy %@!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="New media options" xml:space="preserve">
|
<trans-unit id="New media options" xml:space="preserve">
|
||||||
<source>New media options</source>
|
<source>New media options</source>
|
||||||
|
<target>Nowe opcje mediów</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="New member role" xml:space="preserve">
|
<trans-unit id="New member role" xml:space="preserve">
|
||||||
@@ -4483,6 +4550,7 @@ To jest twój link do grupy %@!</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Nothing selected" xml:space="preserve">
|
<trans-unit id="Nothing selected" xml:space="preserve">
|
||||||
<source>Nothing selected</source>
|
<source>Nothing selected</source>
|
||||||
|
<target>Nic nie jest zaznaczone</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Notifications" xml:space="preserve">
|
<trans-unit id="Notifications" xml:space="preserve">
|
||||||
@@ -4560,6 +4628,7 @@ Wymaga włączenia VPN.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Only delete conversation" xml:space="preserve">
|
<trans-unit id="Only delete conversation" xml:space="preserve">
|
||||||
<source>Only delete conversation</source>
|
<source>Only delete conversation</source>
|
||||||
|
<target>Usuń tylko rozmowę</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
|
<trans-unit id="Only group owners can change group preferences." xml:space="preserve">
|
||||||
@@ -4799,10 +4868,12 @@ Wymaga włączenia VPN.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Play from the chat list." xml:space="preserve">
|
<trans-unit id="Play from the chat list." xml:space="preserve">
|
||||||
<source>Play from the chat list.</source>
|
<source>Play from the chat list.</source>
|
||||||
|
<target>Odtwórz z listy czatów.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
|
<trans-unit id="Please ask your contact to enable calls." xml:space="preserve">
|
||||||
<source>Please ask your contact to enable calls.</source>
|
<source>Please ask your contact to enable calls.</source>
|
||||||
|
<target>Poproś kontakt o włącznie połączeń.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
|
<trans-unit id="Please ask your contact to enable sending voice messages." xml:space="preserve">
|
||||||
@@ -5108,6 +5179,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
|
<trans-unit id="Reachable chat toolbar" xml:space="preserve">
|
||||||
<source>Reachable chat toolbar</source>
|
<source>Reachable chat toolbar</source>
|
||||||
|
<target>Osiągalny pasek narzędzi czatu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="React…" xml:space="preserve">
|
<trans-unit id="React…" xml:space="preserve">
|
||||||
@@ -5383,6 +5455,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Reset all hints" xml:space="preserve">
|
<trans-unit id="Reset all hints" xml:space="preserve">
|
||||||
<source>Reset all hints</source>
|
<source>Reset all hints</source>
|
||||||
|
<target>Zresetuj wszystkie wskazówki</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Reset all statistics" xml:space="preserve">
|
<trans-unit id="Reset all statistics" xml:space="preserve">
|
||||||
@@ -5517,6 +5590,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Save and reconnect" xml:space="preserve">
|
<trans-unit id="Save and reconnect" xml:space="preserve">
|
||||||
<source>Save and reconnect</source>
|
<source>Save and reconnect</source>
|
||||||
|
<target>Zapisz i połącz ponownie</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Save and update group profile" xml:space="preserve">
|
<trans-unit id="Save and update group profile" xml:space="preserve">
|
||||||
@@ -5679,8 +5753,13 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
<target>Wybierz</target>
|
<target>Wybierz</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
|
<target>Zaznaczono %lld</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Selected chat preferences prohibit this message." xml:space="preserve">
|
<trans-unit id="Selected chat preferences prohibit this message." xml:space="preserve">
|
||||||
@@ -5750,6 +5829,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Send message to enable calls." xml:space="preserve">
|
<trans-unit id="Send message to enable calls." xml:space="preserve">
|
||||||
<source>Send message to enable calls.</source>
|
<source>Send message to enable calls.</source>
|
||||||
|
<target>Wyślij wiadomość aby włączyć połączenia.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
|
<trans-unit id="Send messages directly when IP address is protected and your or destination server does not support private routing." xml:space="preserve">
|
||||||
@@ -6039,6 +6119,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Share from other apps." xml:space="preserve">
|
<trans-unit id="Share from other apps." xml:space="preserve">
|
||||||
<source>Share from other apps.</source>
|
<source>Share from other apps.</source>
|
||||||
|
<target>Udostępnij z innych aplikacji.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Share link" xml:space="preserve">
|
<trans-unit id="Share link" xml:space="preserve">
|
||||||
@@ -6046,6 +6127,10 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
<target>Udostępnij link</target>
|
<target>Udostępnij link</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Udostępnij ten jednorazowy link</target>
|
<target>Udostępnij ten jednorazowy link</target>
|
||||||
@@ -6053,6 +6138,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Share to SimpleX" xml:space="preserve">
|
<trans-unit id="Share to SimpleX" xml:space="preserve">
|
||||||
<source>Share to SimpleX</source>
|
<source>Share to SimpleX</source>
|
||||||
|
<target>Udostępnij do SimpleX</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Share with contacts" xml:space="preserve">
|
<trans-unit id="Share with contacts" xml:space="preserve">
|
||||||
@@ -6207,10 +6293,12 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Soft" xml:space="preserve">
|
<trans-unit id="Soft" xml:space="preserve">
|
||||||
<source>Soft</source>
|
<source>Soft</source>
|
||||||
|
<target>Łagodny</target>
|
||||||
<note>blur media</note>
|
<note>blur media</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
|
<trans-unit id="Some file(s) were not exported:" xml:space="preserve">
|
||||||
<source>Some file(s) were not exported:</source>
|
<source>Some file(s) were not exported:</source>
|
||||||
|
<target>Niektóre plik(i) nie zostały wyeksportowane:</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
|
<trans-unit id="Some non-fatal errors occurred during import - you may see Chat console for more details." xml:space="preserve">
|
||||||
@@ -6220,6 +6308,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
|
<trans-unit id="Some non-fatal errors occurred during import:" xml:space="preserve">
|
||||||
<source>Some non-fatal errors occurred during import:</source>
|
<source>Some non-fatal errors occurred during import:</source>
|
||||||
|
<target>Podczas importu wystąpiły niekrytyczne błędy:</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Somebody" xml:space="preserve">
|
<trans-unit id="Somebody" xml:space="preserve">
|
||||||
@@ -6319,6 +6408,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Strong" xml:space="preserve">
|
<trans-unit id="Strong" xml:space="preserve">
|
||||||
<source>Strong</source>
|
<source>Strong</source>
|
||||||
|
<target>Silne</target>
|
||||||
<note>blur media</note>
|
<note>blur media</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Submit" xml:space="preserve">
|
<trans-unit id="Submit" xml:space="preserve">
|
||||||
@@ -6358,6 +6448,7 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="TCP connection" xml:space="preserve">
|
<trans-unit id="TCP connection" xml:space="preserve">
|
||||||
<source>TCP connection</source>
|
<source>TCP connection</source>
|
||||||
|
<target>Połączenie TCP</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="TCP connection timeout" xml:space="preserve">
|
<trans-unit id="TCP connection timeout" xml:space="preserve">
|
||||||
@@ -6380,6 +6471,10 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Zrób zdjęcie</target>
|
<target>Zrób zdjęcie</target>
|
||||||
@@ -6529,10 +6624,12 @@ Może się to zdarzyć z powodu jakiegoś błędu lub gdy połączenie jest skom
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="The messages will be deleted for all members." xml:space="preserve">
|
<trans-unit id="The messages will be deleted for all members." xml:space="preserve">
|
||||||
<source>The messages will be deleted for all members.</source>
|
<source>The messages will be deleted for all members.</source>
|
||||||
|
<target>Wiadomości zostaną usunięte dla wszystkich członków.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="The messages will be marked as moderated for all members." xml:space="preserve">
|
<trans-unit id="The messages will be marked as moderated for all members." xml:space="preserve">
|
||||||
<source>The messages will be marked as moderated for all members.</source>
|
<source>The messages will be marked as moderated for all members.</source>
|
||||||
|
<target>Wiadomości zostaną oznaczone jako moderowane dla wszystkich członków.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="The next generation of private messaging" xml:space="preserve">
|
<trans-unit id="The next generation of private messaging" xml:space="preserve">
|
||||||
@@ -6719,6 +6816,7 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania.</ta
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Toggle chat list:" xml:space="preserve">
|
<trans-unit id="Toggle chat list:" xml:space="preserve">
|
||||||
<source>Toggle chat list:</source>
|
<source>Toggle chat list:</source>
|
||||||
|
<target>Przełącz listę czatów:</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
|
<trans-unit id="Toggle incognito when connecting." xml:space="preserve">
|
||||||
@@ -6728,6 +6826,7 @@ Przed włączeniem tej funkcji zostanie wyświetlony monit uwierzytelniania.</ta
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Toolbar opacity" xml:space="preserve">
|
<trans-unit id="Toolbar opacity" xml:space="preserve">
|
||||||
<source>Toolbar opacity</source>
|
<source>Toolbar opacity</source>
|
||||||
|
<target>Nieprzezroczystość paska narzędzi</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Total" xml:space="preserve">
|
<trans-unit id="Total" xml:space="preserve">
|
||||||
@@ -6914,6 +7013,7 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Update settings?" xml:space="preserve">
|
<trans-unit id="Update settings?" xml:space="preserve">
|
||||||
<source>Update settings?</source>
|
<source>Update settings?</source>
|
||||||
|
<target>Zaktualizować ustawienia?</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
|
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
|
||||||
@@ -7023,6 +7123,7 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Use the app with one hand." xml:space="preserve">
|
<trans-unit id="Use the app with one hand." xml:space="preserve">
|
||||||
<source>Use the app with one hand.</source>
|
<source>Use the app with one hand.</source>
|
||||||
|
<target>Korzystaj z aplikacji jedną ręką.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="User profile" xml:space="preserve">
|
<trans-unit id="User profile" xml:space="preserve">
|
||||||
@@ -7384,6 +7485,7 @@ Powtórzyć prośbę dołączenia?</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
|
<trans-unit id="You can change it in Appearance settings." xml:space="preserve">
|
||||||
<source>You can change it in Appearance settings.</source>
|
<source>You can change it in Appearance settings.</source>
|
||||||
|
<target>Możesz to zmienić w ustawieniach wyglądu.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can create it later" xml:space="preserve">
|
<trans-unit id="You can create it later" xml:space="preserve">
|
||||||
@@ -7423,6 +7525,7 @@ Powtórzyć prośbę dołączenia?</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
|
<trans-unit id="You can send messages to %@ from Archived contacts." xml:space="preserve">
|
||||||
<source>You can send messages to %@ from Archived contacts.</source>
|
<source>You can send messages to %@ from Archived contacts.</source>
|
||||||
|
<target>Możesz wysyłać wiadomości do %@ ze zarchiwizowanych kontaktów.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
|
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
|
||||||
@@ -7452,6 +7555,7 @@ Powtórzyć prośbę dołączenia?</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
|
<trans-unit id="You can still view conversation with %@ in the list of chats." xml:space="preserve">
|
||||||
<source>You can still view conversation with %@ in the list of chats.</source>
|
<source>You can still view conversation with %@ in the list of chats.</source>
|
||||||
|
<target>Nadal możesz przeglądać rozmowę z %@ na liście czatów.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
|
<trans-unit id="You can turn on SimpleX Lock via Settings." xml:space="preserve">
|
||||||
@@ -7518,10 +7622,12 @@ Powtórzyć prośbę połączenia?</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You may migrate the exported database." xml:space="preserve">
|
<trans-unit id="You may migrate the exported database." xml:space="preserve">
|
||||||
<source>You may migrate the exported database.</source>
|
<source>You may migrate the exported database.</source>
|
||||||
|
<target>Możesz zmigrować wyeksportowaną bazy danych.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You may save the exported archive." xml:space="preserve">
|
<trans-unit id="You may save the exported archive." xml:space="preserve">
|
||||||
<source>You may save the exported archive.</source>
|
<source>You may save the exported archive.</source>
|
||||||
|
<target>Możesz zapisać wyeksportowane archiwum.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." xml:space="preserve">
|
<trans-unit id="You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." xml:space="preserve">
|
||||||
@@ -7531,6 +7637,7 @@ Powtórzyć prośbę połączenia?</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
|
<trans-unit id="You need to allow your contact to call to be able to call them." xml:space="preserve">
|
||||||
<source>You need to allow your contact to call to be able to call them.</source>
|
<source>You need to allow your contact to call to be able to call them.</source>
|
||||||
|
<target>Aby móc dzwonić, musisz zezwolić kontaktowi na połączenia.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
|
<trans-unit id="You need to allow your contact to send voice messages to be able to send them." xml:space="preserve">
|
||||||
@@ -7648,6 +7755,10 @@ Powtórzyć prośbę połączenia?</target>
|
|||||||
<target>Twoje profile czatu</target>
|
<target>Twoje profile czatu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Twój kontakt wysłał plik, który jest większy niż obecnie obsługiwany maksymalny rozmiar (%@).</target>
|
<target>Twój kontakt wysłał plik, który jest większy niż obecnie obsługiwany maksymalny rozmiar (%@).</target>
|
||||||
@@ -7842,6 +7953,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="call" xml:space="preserve">
|
<trans-unit id="call" xml:space="preserve">
|
||||||
<source>call</source>
|
<source>call</source>
|
||||||
|
<target>zadzwoń</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="call error" xml:space="preserve">
|
<trans-unit id="call error" xml:space="preserve">
|
||||||
@@ -8216,6 +8328,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="invite" xml:space="preserve">
|
<trans-unit id="invite" xml:space="preserve">
|
||||||
<source>invite</source>
|
<source>invite</source>
|
||||||
|
<target>zaproś</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="invited" xml:space="preserve">
|
<trans-unit id="invited" xml:space="preserve">
|
||||||
@@ -8275,6 +8388,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="message" xml:space="preserve">
|
<trans-unit id="message" xml:space="preserve">
|
||||||
<source>message</source>
|
<source>message</source>
|
||||||
|
<target>wiadomość</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="message received" xml:space="preserve">
|
<trans-unit id="message received" xml:space="preserve">
|
||||||
@@ -8309,6 +8423,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="mute" xml:space="preserve">
|
<trans-unit id="mute" xml:space="preserve">
|
||||||
<source>mute</source>
|
<source>mute</source>
|
||||||
|
<target>wycisz</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="never" xml:space="preserve">
|
<trans-unit id="never" xml:space="preserve">
|
||||||
@@ -8445,6 +8560,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="search" xml:space="preserve">
|
<trans-unit id="search" xml:space="preserve">
|
||||||
<source>search</source>
|
<source>search</source>
|
||||||
|
<target>szukaj</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="sec" xml:space="preserve">
|
<trans-unit id="sec" xml:space="preserve">
|
||||||
@@ -8533,6 +8649,7 @@ ostatnia otrzymana wiadomość: %2$@</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="unmute" xml:space="preserve">
|
<trans-unit id="unmute" xml:space="preserve">
|
||||||
<source>unmute</source>
|
<source>unmute</source>
|
||||||
|
<target>wyłącz wyciszenie</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="unprotected" xml:space="preserve">
|
<trans-unit id="unprotected" xml:space="preserve">
|
||||||
@@ -8582,6 +8699,7 @@ ostatnia otrzymana wiadomość: %2$@</target>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="video" xml:space="preserve">
|
<trans-unit id="video" xml:space="preserve">
|
||||||
<source>video</source>
|
<source>video</source>
|
||||||
|
<target>wideo</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
|
<trans-unit id="video call (not e2e encrypted)" xml:space="preserve">
|
||||||
@@ -8762,14 +8880,17 @@ ostatnia otrzymana wiadomość: %2$@</target>
|
|||||||
<body>
|
<body>
|
||||||
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
<trans-unit id="CFBundleDisplayName" xml:space="preserve">
|
||||||
<source>SimpleX SE</source>
|
<source>SimpleX SE</source>
|
||||||
|
<target>SimpleX SE</target>
|
||||||
<note>Bundle display name</note>
|
<note>Bundle display name</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="CFBundleName" xml:space="preserve">
|
<trans-unit id="CFBundleName" xml:space="preserve">
|
||||||
<source>SimpleX SE</source>
|
<source>SimpleX SE</source>
|
||||||
|
<target>SimpleX SE</target>
|
||||||
<note>Bundle name</note>
|
<note>Bundle name</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="NSHumanReadableCopyright" xml:space="preserve">
|
<trans-unit id="NSHumanReadableCopyright" xml:space="preserve">
|
||||||
<source>Copyright © 2024 SimpleX Chat. All rights reserved.</source>
|
<source>Copyright © 2024 SimpleX Chat. All rights reserved.</source>
|
||||||
|
<target>Copyright © 2024 SimpleX Chat. Wszelkie prawa zastrzeżone.</target>
|
||||||
<note>Copyright (human-readable)</note>
|
<note>Copyright (human-readable)</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
@@ -8781,150 +8902,187 @@ ostatnia otrzymana wiadomość: %2$@</target>
|
|||||||
<body>
|
<body>
|
||||||
<trans-unit id="%@" xml:space="preserve">
|
<trans-unit id="%@" xml:space="preserve">
|
||||||
<source>%@</source>
|
<source>%@</source>
|
||||||
|
<target>%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="App is locked!" xml:space="preserve">
|
<trans-unit id="App is locked!" xml:space="preserve">
|
||||||
<source>App is locked!</source>
|
<source>App is locked!</source>
|
||||||
|
<target>Aplikacja zablokowana!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Cancel" xml:space="preserve">
|
<trans-unit id="Cancel" xml:space="preserve">
|
||||||
<source>Cancel</source>
|
<source>Cancel</source>
|
||||||
|
<target>Anuluj</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Cannot access keychain to save database password" xml:space="preserve">
|
<trans-unit id="Cannot access keychain to save database password" xml:space="preserve">
|
||||||
<source>Cannot access keychain to save database password</source>
|
<source>Cannot access keychain to save database password</source>
|
||||||
|
<target>Nie można uzyskać dostępu do pęku kluczy aby zapisać hasło do bazy danych</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Cannot forward message" xml:space="preserve">
|
<trans-unit id="Cannot forward message" xml:space="preserve">
|
||||||
<source>Cannot forward message</source>
|
<source>Cannot forward message</source>
|
||||||
|
<target>Nie można przekazać wiadomości</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Comment" xml:space="preserve">
|
<trans-unit id="Comment" xml:space="preserve">
|
||||||
<source>Comment</source>
|
<source>Comment</source>
|
||||||
|
<target>Komentarz</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
|
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
|
||||||
<source>Currently maximum supported file size is %@.</source>
|
<source>Currently maximum supported file size is %@.</source>
|
||||||
|
<target>Obecnie maksymalny obsługiwany rozmiar pliku to %@.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Database downgrade required" xml:space="preserve">
|
<trans-unit id="Database downgrade required" xml:space="preserve">
|
||||||
<source>Database downgrade required</source>
|
<source>Database downgrade required</source>
|
||||||
|
<target>Wymagane obniżenie wersji bazy danych</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Database encrypted!" xml:space="preserve">
|
<trans-unit id="Database encrypted!" xml:space="preserve">
|
||||||
<source>Database encrypted!</source>
|
<source>Database encrypted!</source>
|
||||||
|
<target>Baza danych zaszyfrowana!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Database error" xml:space="preserve">
|
<trans-unit id="Database error" xml:space="preserve">
|
||||||
<source>Database error</source>
|
<source>Database error</source>
|
||||||
|
<target>Błąd bazy danych</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Database passphrase is different from saved in the keychain." xml:space="preserve">
|
<trans-unit id="Database passphrase is different from saved in the keychain." xml:space="preserve">
|
||||||
<source>Database passphrase is different from saved in the keychain.</source>
|
<source>Database passphrase is different from saved in the keychain.</source>
|
||||||
|
<target>Hasło bazy danych jest inne niż zapisane w pęku kluczy.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Database passphrase is required to open chat." xml:space="preserve">
|
<trans-unit id="Database passphrase is required to open chat." xml:space="preserve">
|
||||||
<source>Database passphrase is required to open chat.</source>
|
<source>Database passphrase is required to open chat.</source>
|
||||||
|
<target>Hasło do bazy danych jest wymagane do otwarcia czatu.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Database upgrade required" xml:space="preserve">
|
<trans-unit id="Database upgrade required" xml:space="preserve">
|
||||||
<source>Database upgrade required</source>
|
<source>Database upgrade required</source>
|
||||||
|
<target>Wymagana aktualizacja bazy danych</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Error preparing file" xml:space="preserve">
|
<trans-unit id="Error preparing file" xml:space="preserve">
|
||||||
<source>Error preparing file</source>
|
<source>Error preparing file</source>
|
||||||
|
<target>Błąd przygotowania pliku</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Error preparing message" xml:space="preserve">
|
<trans-unit id="Error preparing message" xml:space="preserve">
|
||||||
<source>Error preparing message</source>
|
<source>Error preparing message</source>
|
||||||
|
<target>Błąd przygotowania wiadomości</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Error: %@" xml:space="preserve">
|
<trans-unit id="Error: %@" xml:space="preserve">
|
||||||
<source>Error: %@</source>
|
<source>Error: %@</source>
|
||||||
|
<target>Błąd: %@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="File error" xml:space="preserve">
|
<trans-unit id="File error" xml:space="preserve">
|
||||||
<source>File error</source>
|
<source>File error</source>
|
||||||
|
<target>Błąd pliku</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Incompatible database version" xml:space="preserve">
|
<trans-unit id="Incompatible database version" xml:space="preserve">
|
||||||
<source>Incompatible database version</source>
|
<source>Incompatible database version</source>
|
||||||
|
<target>Niekompatybilna wersja bazy danych</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
<trans-unit id="Invalid migration confirmation" xml:space="preserve">
|
||||||
<source>Invalid migration confirmation</source>
|
<source>Invalid migration confirmation</source>
|
||||||
|
<target>Nieprawidłowe potwierdzenie migracji</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Keychain error" xml:space="preserve">
|
<trans-unit id="Keychain error" xml:space="preserve">
|
||||||
<source>Keychain error</source>
|
<source>Keychain error</source>
|
||||||
|
<target>Błąd pęku kluczy</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Large file!" xml:space="preserve">
|
<trans-unit id="Large file!" xml:space="preserve">
|
||||||
<source>Large file!</source>
|
<source>Large file!</source>
|
||||||
|
<target>Duży plik!</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="No active profile" xml:space="preserve">
|
<trans-unit id="No active profile" xml:space="preserve">
|
||||||
<source>No active profile</source>
|
<source>No active profile</source>
|
||||||
|
<target>Brak aktywnego profilu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Ok" xml:space="preserve">
|
<trans-unit id="Ok" xml:space="preserve">
|
||||||
<source>Ok</source>
|
<source>Ok</source>
|
||||||
|
<target>Ok</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Open the app to downgrade the database." xml:space="preserve">
|
<trans-unit id="Open the app to downgrade the database." xml:space="preserve">
|
||||||
<source>Open the app to downgrade the database.</source>
|
<source>Open the app to downgrade the database.</source>
|
||||||
|
<target>Otwórz aplikację aby obniżyć wersję bazy danych.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Open the app to upgrade the database." xml:space="preserve">
|
<trans-unit id="Open the app to upgrade the database." xml:space="preserve">
|
||||||
<source>Open the app to upgrade the database.</source>
|
<source>Open the app to upgrade the database.</source>
|
||||||
|
<target>Otwórz aplikację aby zaktualizować bazę danych.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Passphrase" xml:space="preserve">
|
<trans-unit id="Passphrase" xml:space="preserve">
|
||||||
<source>Passphrase</source>
|
<source>Passphrase</source>
|
||||||
|
<target>Hasło</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Please create a profile in the SimpleX app" xml:space="preserve">
|
<trans-unit id="Please create a profile in the SimpleX app" xml:space="preserve">
|
||||||
<source>Please create a profile in the SimpleX app</source>
|
<source>Please create a profile in the SimpleX app</source>
|
||||||
|
<target>Proszę utworzyć profil w aplikacji SimpleX</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Selected chat preferences prohibit this message." xml:space="preserve">
|
<trans-unit id="Selected chat preferences prohibit this message." xml:space="preserve">
|
||||||
<source>Selected chat preferences prohibit this message.</source>
|
<source>Selected chat preferences prohibit this message.</source>
|
||||||
|
<target>Wybrane preferencje czatu zabraniają tej wiadomości.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
|
<trans-unit id="Sending a message takes longer than expected." xml:space="preserve">
|
||||||
<source>Sending a message takes longer than expected.</source>
|
<source>Sending a message takes longer than expected.</source>
|
||||||
|
<target>Wysłanie wiadomości trwa dłużej niż oczekiwano.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Sending message…" xml:space="preserve">
|
<trans-unit id="Sending message…" xml:space="preserve">
|
||||||
<source>Sending message…</source>
|
<source>Sending message…</source>
|
||||||
|
<target>Wysyłanie wiadomości…</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Share" xml:space="preserve">
|
<trans-unit id="Share" xml:space="preserve">
|
||||||
<source>Share</source>
|
<source>Share</source>
|
||||||
|
<target>Udostępnij</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Slow network?" xml:space="preserve">
|
<trans-unit id="Slow network?" xml:space="preserve">
|
||||||
<source>Slow network?</source>
|
<source>Slow network?</source>
|
||||||
|
<target>Wolna sieć?</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Unknown database error: %@" xml:space="preserve">
|
<trans-unit id="Unknown database error: %@" xml:space="preserve">
|
||||||
<source>Unknown database error: %@</source>
|
<source>Unknown database error: %@</source>
|
||||||
|
<target>Nieznany błąd bazy danych: %@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Unsupported format" xml:space="preserve">
|
<trans-unit id="Unsupported format" xml:space="preserve">
|
||||||
<source>Unsupported format</source>
|
<source>Unsupported format</source>
|
||||||
|
<target>Niewspierany format</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Wait" xml:space="preserve">
|
<trans-unit id="Wait" xml:space="preserve">
|
||||||
<source>Wait</source>
|
<source>Wait</source>
|
||||||
|
<target>Czekaj</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Wrong database passphrase" xml:space="preserve">
|
<trans-unit id="Wrong database passphrase" xml:space="preserve">
|
||||||
<source>Wrong database passphrase</source>
|
<source>Wrong database passphrase</source>
|
||||||
|
<target>Nieprawidłowe hasło bazy danych</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="You can allow sharing in Privacy & Security / SimpleX Lock settings." xml:space="preserve">
|
<trans-unit id="You can allow sharing in Privacy & Security / SimpleX Lock settings." xml:space="preserve">
|
||||||
<source>You can allow sharing in Privacy & Security / SimpleX Lock settings.</source>
|
<source>You can allow sharing in Privacy & Security / SimpleX Lock settings.</source>
|
||||||
|
<target>Możesz zezwolić na udostępnianie w ustawieniach Prywatność i bezpieczeństwo / Blokada SimpleX.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
|
|||||||
@@ -5234,6 +5234,274 @@ Isso pode acontecer por causa de algum bug ou quando a conexão está comprometi
|
|||||||
<target state="translated">%1$@ em %2$@:</target>
|
<target state="translated">%1$@ em %2$@:</target>
|
||||||
<note>copied message info, <sender> at <time></note>
|
<note>copied message info, <sender> at <time></note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow your contacts to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow your contacts to irreversibly delete sent messages. (24 hours)</source>
|
||||||
|
<target state="translated">Permitir que seus contatos deletem mensagens enviadas de maneira irreversível. (24 horas)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%@ downloaded" xml:space="preserve" approved="no">
|
||||||
|
<source>%@ downloaded</source>
|
||||||
|
<target state="translated">baixado</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%@ uploaded" xml:space="preserve" approved="no">
|
||||||
|
<source>%@ uploaded</source>
|
||||||
|
<target state="translated">transferido</target>
|
||||||
|
</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">Um novo perfil aleatório será compartilhado.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Camera not available" xml:space="preserve" approved="no">
|
||||||
|
<source>Camera not available</source>
|
||||||
|
<target state="translated">Câmera indisponível</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Admins can block a member for all." xml:space="preserve" approved="no">
|
||||||
|
<source>Admins can block a member for all.</source>
|
||||||
|
<target state="translated">Administradores podem bloquear um membro para todos.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow to irreversibly delete sent messages. (24 hours)</source>
|
||||||
|
<target state="translated">Permitir que mensagens enviadas sejam deletadas de maneira irreversível. (24 horas)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Apply" xml:space="preserve" approved="no">
|
||||||
|
<source>Apply</source>
|
||||||
|
<target state="translated">Aplicar</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Accent" xml:space="preserve" approved="no">
|
||||||
|
<source>Accent</source>
|
||||||
|
<target state="translated">Esquema</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Accept connection request?" xml:space="preserve" approved="no">
|
||||||
|
<source>Accept connection request?</source>
|
||||||
|
<target state="translated">Aceitar solicitação de conexão?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Active connections" xml:space="preserve" approved="no">
|
||||||
|
<source>Active connections</source>
|
||||||
|
<target state="translated">Conexões ativas</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Add contact" xml:space="preserve" approved="no">
|
||||||
|
<source>Add contact</source>
|
||||||
|
<target state="translated">Adicionar contato</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Additional accent" xml:space="preserve" approved="no">
|
||||||
|
<source>Additional accent</source>
|
||||||
|
<target state="translated">Esquema adicional</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="All new messages from %@ will be hidden!" xml:space="preserve" approved="no">
|
||||||
|
<source>All new messages from %@ will be hidden!</source>
|
||||||
|
<target state="translated">Todas as novas mensagens de %@ serão ocultas!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="All profiles" xml:space="preserve" approved="no">
|
||||||
|
<source>All profiles</source>
|
||||||
|
<target state="translated">Todos perfis</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow calls?" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow calls?</source>
|
||||||
|
<target state="translated">Permitir chamadas?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Archive contacts to chat later." xml:space="preserve" approved="no">
|
||||||
|
<source>Archive contacts to chat later.</source>
|
||||||
|
<target state="translated">Arquivar contatos para conversar depois.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Blur media" xml:space="preserve" approved="no">
|
||||||
|
<source>Blur media</source>
|
||||||
|
<target state="translated">Censurar mídia</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Calls prohibited!" xml:space="preserve" approved="no">
|
||||||
|
<source>Calls prohibited!</source>
|
||||||
|
<target state="translated">Chamadas proibidas!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Can't call contact" xml:space="preserve" approved="no">
|
||||||
|
<source>Can't call contact</source>
|
||||||
|
<target state="translated">Não foi possível ligar para o contato</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%lld messages marked deleted" xml:space="preserve" approved="no">
|
||||||
|
<source>%lld messages marked deleted</source>
|
||||||
|
<target state="translated">mensagens deletadas</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="0 sec" xml:space="preserve" approved="no">
|
||||||
|
<source>0 sec</source>
|
||||||
|
<target state="translated">0 seg</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%lld messages blocked" xml:space="preserve" approved="no">
|
||||||
|
<source>%lld messages blocked</source>
|
||||||
|
<target state="translated">mensagens bloqueadas</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%lld messages blocked by admin" xml:space="preserve" approved="no">
|
||||||
|
<source>%lld messages blocked by admin</source>
|
||||||
|
<target state="translated">mensagens bloqueadas pelo administrador</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection." xml:space="preserve" approved="no">
|
||||||
|
<source>**Please note**: using the same database on two devices will break the decryption of messages from your connections, as a security protection.</source>
|
||||||
|
<target state="translated">**Nota**: usar o mesmo banco de dados em dois dispositivos irá quebrar a desencriptação das mensagens de suas conexões como uma medida de segurança.</target>
|
||||||
|
</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">- entrega de mensagens mais estável.
|
||||||
|
- grupos melhorados.
|
||||||
|
- e muito mais!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="All messages will be deleted - this cannot be undone!" xml:space="preserve" approved="no">
|
||||||
|
<source>All messages will be deleted - this cannot be undone!</source>
|
||||||
|
<target state="translated">Todas as mensagens serão deletadas - isto não pode ser desfeito!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow to send files and media." xml:space="preserve" approved="no">
|
||||||
|
<source>Allow to send files and media.</source>
|
||||||
|
<target state="translated">Permitir o envio de arquivos e mídia.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow to send SimpleX links." xml:space="preserve" approved="no">
|
||||||
|
<source>Allow to send SimpleX links.</source>
|
||||||
|
<target state="translated">Permitir envio de links SimpleX.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block for all" xml:space="preserve" approved="no">
|
||||||
|
<source>Block for all</source>
|
||||||
|
<target state="translated">Bloquear para todos</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block member" xml:space="preserve" approved="no">
|
||||||
|
<source>Block member</source>
|
||||||
|
<target state="translated">Bloquear membro</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Blocked by admin" xml:space="preserve" approved="no">
|
||||||
|
<source>Blocked by admin</source>
|
||||||
|
<target state="translated">Bloqueado por um administrador</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block group members" xml:space="preserve" approved="no">
|
||||||
|
<source>Block group members</source>
|
||||||
|
<target state="translated">Bloquear membros de grupo</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block member for all?" xml:space="preserve" approved="no">
|
||||||
|
<source>Block member for all?</source>
|
||||||
|
<target state="translated">Bloquear membro para todos?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block member?" xml:space="preserve" approved="no">
|
||||||
|
<source>Block member?</source>
|
||||||
|
<target state="translated">Bloquear membro?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Both you and your contact can irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
|
||||||
|
<source>Both you and your contact can irreversibly delete sent messages. (24 hours)</source>
|
||||||
|
<target state="translated">Você e seu contato podem apagar mensagens enviadas de maneira irreversível. (24 horas)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Can't call member" xml:space="preserve" approved="no">
|
||||||
|
<source>Can't call member</source>
|
||||||
|
<target state="translated">Não foi possível ligar para este membro</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Can't message member" xml:space="preserve" approved="no">
|
||||||
|
<source>Can't message member</source>
|
||||||
|
<target state="translated">Não foi possível enviar mensagem para este membro</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Cancel migration" xml:space="preserve" approved="no">
|
||||||
|
<source>Cancel migration</source>
|
||||||
|
<target state="translated">Cancelar migração</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Abort" xml:space="preserve" approved="no">
|
||||||
|
<source>Abort</source>
|
||||||
|
<target state="translated">Abortar</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Abort changing address" xml:space="preserve" approved="no">
|
||||||
|
<source>Abort changing address</source>
|
||||||
|
<target state="translated">Abortar troca de endereço</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Abort changing address?" xml:space="preserve" approved="no">
|
||||||
|
<source>Abort changing address?</source>
|
||||||
|
<target state="translated">Abortar troca de endereço?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - and more!" xml:space="preserve" approved="no">
|
||||||
|
<source>- optionally notify deleted contacts.
|
||||||
|
- profile names with spaces.
|
||||||
|
- and more!</source>
|
||||||
|
<target state="translated">- notificar contatos apagados de maneira opcional.
|
||||||
|
- nome de perfil com espaços.
|
||||||
|
- e muito mais!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow sharing" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow sharing</source>
|
||||||
|
<target state="translated">Permitir compartilhamento</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block" xml:space="preserve" approved="no">
|
||||||
|
<source>Block</source>
|
||||||
|
<target state="translated">Bloquear</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Additional accent 2" xml:space="preserve" approved="no">
|
||||||
|
<source>Additional accent 2</source>
|
||||||
|
<target state="translated">Esquema adicional 2</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Address change will be aborted. Old receiving address will be used." xml:space="preserve" approved="no">
|
||||||
|
<source>Address change will be aborted. Old receiving address will be used.</source>
|
||||||
|
<target state="translated">Alteração de endereço será abortada. O endereço antigo será utilizado.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Advanced settings" xml:space="preserve" approved="no">
|
||||||
|
<source>Advanced settings</source>
|
||||||
|
<target state="translated">Configurações avançadas</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="All data is private to your device." xml:space="preserve" approved="no">
|
||||||
|
<source>All data is private to your device.</source>
|
||||||
|
<target state="translated">Toda informação é privada em seu dispositivo.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." xml:space="preserve" approved="no">
|
||||||
|
<source>All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays.</source>
|
||||||
|
<target state="translated">Todos os seus contatos, conversas e arquivos serão encriptados e enviados em pedaços para nós XFTP.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
|
||||||
|
<target state="translated">Permitir deletar mensagens de maneira irreversível apenas se seu contato permitir para você. (24 horas)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Already connecting!" xml:space="preserve" approved="no">
|
||||||
|
<source>Already connecting!</source>
|
||||||
|
<target state="translated">Já está conectando!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Already joining the group!" xml:space="preserve" approved="no">
|
||||||
|
<source>Already joining the group!</source>
|
||||||
|
<target state="translated">Já está entrando no grupo!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Always use private routing." xml:space="preserve" approved="no">
|
||||||
|
<source>Always use private routing.</source>
|
||||||
|
<target state="translated">Sempre use rotas privadas.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Apply to" xml:space="preserve" approved="no">
|
||||||
|
<source>Apply to</source>
|
||||||
|
<target state="translated">Aplicar em</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Archiving database" xml:space="preserve" approved="no">
|
||||||
|
<source>Archiving database</source>
|
||||||
|
<target state="translated">Arquivando banco de dados</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Black" xml:space="preserve" approved="no">
|
||||||
|
<source>Black</source>
|
||||||
|
<target state="translated">Preto</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Cannot forward message" xml:space="preserve" approved="no">
|
||||||
|
<source>Cannot forward message</source>
|
||||||
|
<target state="translated">Não é possível encaminhar mensagem</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="(new)" xml:space="preserve" approved="no">
|
||||||
|
<source>(new)</source>
|
||||||
|
<target state="translated">(novo)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="(this device v%@)" xml:space="preserve" approved="no">
|
||||||
|
<source>(this device v%@)</source>
|
||||||
|
<target state="translated">este dispositivo</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="**Add contact**: to create a new invitation link, or connect via a link you received." xml:space="preserve" approved="no">
|
||||||
|
<source>**Add contact**: to create a new invitation link, or connect via a link you received.</source>
|
||||||
|
<target state="translated">**Adicionar contato**: criar um novo link de convite ou conectar via um link que você recebeu.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="**Create group**: to create a new group." xml:space="preserve" approved="no">
|
||||||
|
<source>**Create group**: to create a new group.</source>
|
||||||
|
<target state="translated">**Criar grupo**: criar um novo grupo.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="**Warning**: the archive will be removed." xml:space="preserve" approved="no">
|
||||||
|
<source>**Warning**: the archive will be removed.</source>
|
||||||
|
<target state="translated">**Aviso**: o arquivo será removido.</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="A few more things" xml:space="preserve" approved="no">
|
||||||
|
<source>A few more things</source>
|
||||||
|
<target state="translated">E mais algumas coisas</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Archived contacts" xml:space="preserve" approved="no">
|
||||||
|
<source>Archived contacts</source>
|
||||||
|
<target state="translated">Contatos arquivados</target>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="pt-BR" datatype="plaintext">
|
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="pt-BR" datatype="plaintext">
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ хочет соединиться!</target>
|
<target>%@ хочет соединиться!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ и %lld членов группы</target>
|
<target>%@, %@ и %lld членов группы</target>
|
||||||
@@ -1418,7 +1422,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Color chats with the new themes." xml:space="preserve">
|
<trans-unit id="Color chats with the new themes." xml:space="preserve">
|
||||||
<source>Color chats with the new themes.</source>
|
<source>Color chats with the new themes.</source>
|
||||||
<target>Добавьте цвета к чатам в настройках тем.</target>
|
<target>Добавьте цвета к чатам в настройках.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Color mode" xml:space="preserve">
|
<trans-unit id="Color mode" xml:space="preserve">
|
||||||
@@ -1740,6 +1744,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Версия ядра: v%@</target>
|
<target>Версия ядра: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>Исправить имя на %@?</target>
|
<target>Исправить имя на %@?</target>
|
||||||
@@ -2176,7 +2184,7 @@ This is your own one-time link!</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
|
<trans-unit id="Delete up to 20 messages at once." xml:space="preserve">
|
||||||
<source>Delete up to 20 messages at once.</source>
|
<source>Delete up to 20 messages at once.</source>
|
||||||
<target>Удаляйте до 20 сообщений одновременно.</target>
|
<target>Удаляйте до 20 сообщений за раз.</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Delete user profile?" xml:space="preserve">
|
<trans-unit id="Delete user profile?" xml:space="preserve">
|
||||||
@@ -2724,6 +2732,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Ошибка при изменении адреса</target>
|
<target>Ошибка при изменении адреса</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Ошибка при изменении роли</target>
|
<target>Ошибка при изменении роли</target>
|
||||||
@@ -2734,6 +2746,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Ошибка при изменении настройки</target>
|
<target>Ошибка при изменении настройки</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<target>Ошибка подключения к пересылающему серверу %@. Попробуйте позже.</target>
|
<target>Ошибка подключения к пересылающему серверу %@. Попробуйте позже.</target>
|
||||||
@@ -2954,6 +2970,10 @@ This is your own one-time link!</source>
|
|||||||
<target>Ошибка при остановке чата</target>
|
<target>Ошибка при остановке чата</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Ошибка выбора профиля!</target>
|
<target>Ошибка выбора профиля!</target>
|
||||||
@@ -4184,6 +4204,10 @@ This is your link for group %@!</source>
|
|||||||
<target>Серверы сообщений</target>
|
<target>Серверы сообщений</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>Источник сообщения остаётся конфиденциальным.</target>
|
<target>Источник сообщения остаётся конфиденциальным.</target>
|
||||||
@@ -4988,7 +5012,7 @@ Error: %@</source>
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
<trans-unit id="Private message routing 🚀" xml:space="preserve">
|
||||||
<source>Private message routing 🚀</source>
|
<source>Private message routing 🚀</source>
|
||||||
<target>Конфиденциальная доставка сообщений 🚀</target>
|
<target>Конфиденциальная доставка 🚀</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Private notes" xml:space="preserve">
|
<trans-unit id="Private notes" xml:space="preserve">
|
||||||
@@ -5729,6 +5753,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Выбрать</target>
|
<target>Выбрать</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<target>Выбрано %lld</target>
|
<target>Выбрано %lld</target>
|
||||||
@@ -6099,6 +6127,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Поделиться ссылкой</target>
|
<target>Поделиться ссылкой</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Поделиться одноразовой ссылкой-приглашением</target>
|
<target>Поделиться одноразовой ссылкой-приглашением</target>
|
||||||
@@ -6439,6 +6471,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Сделать фото</target>
|
<target>Сделать фото</target>
|
||||||
@@ -7719,6 +7755,10 @@ Repeat connection request?</source>
|
|||||||
<target>Ваши профили чата</target>
|
<target>Ваши профили чата</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Ваш контакт отправил файл, размер которого превышает максимальный размер (%@).</target>
|
<target>Ваш контакт отправил файл, размер которого превышает максимальный размер (%@).</target>
|
||||||
|
|||||||
@@ -129,6 +129,10 @@
|
|||||||
<target>%@ อยากเชื่อมต่อ!</target>
|
<target>%@ อยากเชื่อมต่อ!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -1606,6 +1610,10 @@ This is your own one-time link!</source>
|
|||||||
<target>รุ่นหลัก: v%@</target>
|
<target>รุ่นหลัก: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2530,6 +2538,10 @@ This is your own one-time link!</source>
|
|||||||
<target>เกิดข้อผิดพลาดในการเปลี่ยนที่อยู่</target>
|
<target>เกิดข้อผิดพลาดในการเปลี่ยนที่อยู่</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>เกิดข้อผิดพลาดในการเปลี่ยนบทบาท</target>
|
<target>เกิดข้อผิดพลาดในการเปลี่ยนบทบาท</target>
|
||||||
@@ -2540,6 +2552,10 @@ This is your own one-time link!</source>
|
|||||||
<target>เกิดข้อผิดพลาดในการเปลี่ยนการตั้งค่า</target>
|
<target>เกิดข้อผิดพลาดในการเปลี่ยนการตั้งค่า</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2747,6 +2763,10 @@ This is your own one-time link!</source>
|
|||||||
<target>เกิดข้อผิดพลาดในการหยุดแชท</target>
|
<target>เกิดข้อผิดพลาดในการหยุดแชท</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>เกิดข้อผิดพลาดในการเปลี่ยนโปรไฟล์!</target>
|
<target>เกิดข้อผิดพลาดในการเปลี่ยนโปรไฟล์!</target>
|
||||||
@@ -3901,6 +3921,10 @@ This is your link for group %@!</source>
|
|||||||
<source>Message servers</source>
|
<source>Message servers</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5328,6 +5352,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>เลือก</target>
|
<target>เลือก</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5670,6 +5698,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>แชร์ลิงก์</target>
|
<target>แชร์ลิงก์</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5983,6 +6015,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>ถ่ายภาพ</target>
|
<target>ถ่ายภาพ</target>
|
||||||
@@ -7163,6 +7199,10 @@ Repeat connection request?</source>
|
|||||||
<target>โปรไฟล์แชทของคุณ</target>
|
<target>โปรไฟล์แชทของคุณ</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>ผู้ติดต่อของคุณส่งไฟล์ที่ใหญ่กว่าขนาดสูงสุดที่รองรับในปัจจุบัน (%@)</target>
|
<target>ผู้ติดต่อของคุณส่งไฟล์ที่ใหญ่กว่าขนาดสูงสุดที่รองรับในปัจจุบัน (%@)</target>
|
||||||
|
|||||||
@@ -137,6 +137,10 @@
|
|||||||
<target>%@ bağlanmak istiyor!</target>
|
<target>%@ bağlanmak istiyor!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ ve %lld üyeleri</target>
|
<target>%@, %@ ve %lld üyeleri</target>
|
||||||
@@ -1689,6 +1693,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
|
|||||||
<target>Çekirdek sürümü: v%@</target>
|
<target>Çekirdek sürümü: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<target>İsim %@ olarak düzeltilsin mi?</target>
|
<target>İsim %@ olarak düzeltilsin mi?</target>
|
||||||
@@ -2653,6 +2661,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
|
|||||||
<target>Adres değiştirilirken hata oluştu</target>
|
<target>Adres değiştirilirken hata oluştu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>Rol değiştirilirken hata oluştu</target>
|
<target>Rol değiştirilirken hata oluştu</target>
|
||||||
@@ -2663,6 +2675,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
|
|||||||
<target>Ayar değiştirilirken hata oluştu</target>
|
<target>Ayar değiştirilirken hata oluştu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2878,6 +2894,10 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
|
|||||||
<target>Sohbet durdurulurken hata oluştu</target>
|
<target>Sohbet durdurulurken hata oluştu</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>Profil değiştirilirken hata oluştu!</target>
|
<target>Profil değiştirilirken hata oluştu!</target>
|
||||||
@@ -4084,6 +4104,10 @@ Bu senin grup için bağlantın %@!</target>
|
|||||||
<source>Message servers</source>
|
<source>Message servers</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>Mesaj kaynağı gizli kalır.</target>
|
<target>Mesaj kaynağı gizli kalır.</target>
|
||||||
@@ -5585,6 +5609,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Seç</target>
|
<target>Seç</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5938,6 +5966,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>Bağlantıyı paylaş</target>
|
<target>Bağlantıyı paylaş</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>Bu tek kullanımlık bağlantı davetini paylaş</target>
|
<target>Bu tek kullanımlık bağlantı davetini paylaş</target>
|
||||||
@@ -6264,6 +6296,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>TCP_TVLDEKAL</target>
|
<target>TCP_TVLDEKAL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>Fotoğraf çek</target>
|
<target>Fotoğraf çek</target>
|
||||||
@@ -7517,6 +7553,10 @@ Bağlantı isteği tekrarlansın mı?</target>
|
|||||||
<target>Sohbet profillerin</target>
|
<target>Sohbet profillerin</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>Kişiniz şu anda desteklenen maksimum boyuttan (%@) daha büyük bir dosya gönderdi.</target>
|
<target>Kişiniz şu anda desteklenen maksimum boyuttan (%@) daha büyük bir dosya gönderdi.</target>
|
||||||
|
|||||||
File diff suppressed because it is too large
Load Diff
@@ -135,6 +135,10 @@
|
|||||||
<target>%@ 要连接!</target>
|
<target>%@ 要连接!</target>
|
||||||
<note>notification title</note>
|
<note>notification title</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@" xml:space="preserve">
|
||||||
|
<source>%1$@, %2$@</source>
|
||||||
|
<note>format for date separator in chat</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve">
|
||||||
<source>%@, %@ and %lld members</source>
|
<source>%@, %@ and %lld members</source>
|
||||||
<target>%@, %@ 和 %lld 成员</target>
|
<target>%@, %@ 和 %lld 成员</target>
|
||||||
@@ -1665,6 +1669,10 @@ This is your own one-time link!</source>
|
|||||||
<target>核心版本: v%@</target>
|
<target>核心版本: v%@</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Corner" xml:space="preserve">
|
||||||
|
<source>Corner</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
<trans-unit id="Correct name to %@?" xml:space="preserve">
|
||||||
<source>Correct name to %@?</source>
|
<source>Correct name to %@?</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2618,6 +2626,10 @@ This is your own one-time link!</source>
|
|||||||
<target>更改地址错误</target>
|
<target>更改地址错误</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing connection profile" xml:space="preserve">
|
||||||
|
<source>Error changing connection profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error changing role" xml:space="preserve">
|
<trans-unit id="Error changing role" xml:space="preserve">
|
||||||
<source>Error changing role</source>
|
<source>Error changing role</source>
|
||||||
<target>更改角色错误</target>
|
<target>更改角色错误</target>
|
||||||
@@ -2628,6 +2640,10 @@ This is your own one-time link!</source>
|
|||||||
<target>更改设置错误</target>
|
<target>更改设置错误</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error changing to incognito!" xml:space="preserve">
|
||||||
|
<source>Error changing to incognito!</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
<trans-unit id="Error connecting to forwarding server %@. Please try later." xml:space="preserve">
|
||||||
<source>Error connecting to forwarding server %@. Please try later.</source>
|
<source>Error connecting to forwarding server %@. Please try later.</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -2841,6 +2857,10 @@ This is your own one-time link!</source>
|
|||||||
<target>停止聊天错误</target>
|
<target>停止聊天错误</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Error switching profile" xml:space="preserve">
|
||||||
|
<source>Error switching profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Error switching profile!" xml:space="preserve">
|
<trans-unit id="Error switching profile!" xml:space="preserve">
|
||||||
<source>Error switching profile!</source>
|
<source>Error switching profile!</source>
|
||||||
<target>切换资料错误!</target>
|
<target>切换资料错误!</target>
|
||||||
@@ -4033,6 +4053,10 @@ This is your link for group %@!</source>
|
|||||||
<source>Message servers</source>
|
<source>Message servers</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Message shape" xml:space="preserve">
|
||||||
|
<source>Message shape</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Message source remains private." xml:space="preserve">
|
<trans-unit id="Message source remains private." xml:space="preserve">
|
||||||
<source>Message source remains private.</source>
|
<source>Message source remains private.</source>
|
||||||
<target>消息来源保持私密。</target>
|
<target>消息来源保持私密。</target>
|
||||||
@@ -5512,6 +5536,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>选择</target>
|
<target>选择</target>
|
||||||
<note>chat item action</note>
|
<note>chat item action</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Select chat profile" xml:space="preserve">
|
||||||
|
<source>Select chat profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Selected %lld" xml:space="preserve">
|
<trans-unit id="Selected %lld" xml:space="preserve">
|
||||||
<source>Selected %lld</source>
|
<source>Selected %lld</source>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
@@ -5861,6 +5889,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>分享链接</target>
|
<target>分享链接</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Share profile" xml:space="preserve">
|
||||||
|
<source>Share profile</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
|
||||||
<source>Share this 1-time invite link</source>
|
<source>Share this 1-time invite link</source>
|
||||||
<target>分享此一次性邀请链接</target>
|
<target>分享此一次性邀请链接</target>
|
||||||
@@ -6185,6 +6217,10 @@ Enable in *Network & servers* settings.</source>
|
|||||||
<target>TCP_KEEPINTVL</target>
|
<target>TCP_KEEPINTVL</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Tail" xml:space="preserve">
|
||||||
|
<source>Tail</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Take picture" xml:space="preserve">
|
<trans-unit id="Take picture" xml:space="preserve">
|
||||||
<source>Take picture</source>
|
<source>Take picture</source>
|
||||||
<target>拍照</target>
|
<target>拍照</target>
|
||||||
@@ -7417,6 +7453,10 @@ Repeat connection request?</source>
|
|||||||
<target>您的聊天资料</target>
|
<target>您的聊天资料</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile." xml:space="preserve">
|
||||||
|
<source>Your connection was moved to %@ but an unexpected error occurred while redirecting you to the profile.</source>
|
||||||
|
<note>No comment provided by engineer.</note>
|
||||||
|
</trans-unit>
|
||||||
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
<trans-unit id="Your contact sent a file that is larger than currently supported maximum size (%@)." xml:space="preserve">
|
||||||
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
<source>Your contact sent a file that is larger than currently supported maximum size (%@).</source>
|
||||||
<target>您的联系人发送的文件大于当前支持的最大大小 (%@)。</target>
|
<target>您的联系人发送的文件大于当前支持的最大大小 (%@)。</target>
|
||||||
|
|||||||
@@ -314,7 +314,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="About SimpleX Chat" xml:space="preserve" approved="no">
|
<trans-unit id="About SimpleX Chat" xml:space="preserve" approved="no">
|
||||||
<source>About SimpleX Chat</source>
|
<source>About SimpleX Chat</source>
|
||||||
<target state="translated">關於 SimpleX 對話</target>
|
<target state="translated">關於 SimpleX Chat</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Accent color" xml:space="preserve" approved="no">
|
<trans-unit id="Accent color" xml:space="preserve" approved="no">
|
||||||
@@ -445,7 +445,7 @@
|
|||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Allow your contacts to send disappearing messages." xml:space="preserve" approved="no">
|
<trans-unit id="Allow your contacts to send disappearing messages." xml:space="preserve" approved="no">
|
||||||
<source>Allow your contacts to send disappearing messages.</source>
|
<source>Allow your contacts to send disappearing messages.</source>
|
||||||
<target state="translated">允許你的聯絡人傳送自動銷毀的訊息。</target>
|
<target state="translated">允許您的聯絡人傳送限時訊息。</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
<trans-unit id="Allow your contacts to send voice messages." xml:space="preserve" approved="no">
|
<trans-unit id="Allow your contacts to send voice messages." xml:space="preserve" approved="no">
|
||||||
@@ -5898,6 +5898,230 @@ It can happen because of some bug or when the connection is compromised.</source
|
|||||||
<target state="translated">%@ 和 %@ 已連接</target>
|
<target state="translated">%@ 和 %@ 已連接</target>
|
||||||
<note>No comment provided by engineer.</note>
|
<note>No comment provided by engineer.</note>
|
||||||
</trans-unit>
|
</trans-unit>
|
||||||
|
<trans-unit id="%@ downloaded" xml:space="preserve" approved="no">
|
||||||
|
<source>%@ downloaded</source>
|
||||||
|
<target state="translated">%@ 下載</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%@ uploaded" xml:space="preserve" approved="no">
|
||||||
|
<source>%@ uploaded</source>
|
||||||
|
<target state="translated">%@ 上傳</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Abort" xml:space="preserve" approved="no">
|
||||||
|
<source>Abort</source>
|
||||||
|
<target state="translated">中止</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="**Create group**: to create a new group." xml:space="preserve" approved="no">
|
||||||
|
<source>**Create group**: to create a new group.</source>
|
||||||
|
<target state="translated">**創建群組**: 創建一個新的群組。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Abort changing address" xml:space="preserve" approved="no">
|
||||||
|
<source>Abort changing address</source>
|
||||||
|
<target state="translated">中止更改地址</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Accept connection request?" xml:space="preserve" approved="no">
|
||||||
|
<source>Accept connection request?</source>
|
||||||
|
<target state="translated">接受連線請求?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Camera not available" xml:space="preserve" approved="no">
|
||||||
|
<source>Camera not available</source>
|
||||||
|
<target state="translated">相機不可用</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="All messages will be deleted - this cannot be undone!" xml:space="preserve" approved="no">
|
||||||
|
<source>All messages will be deleted - this cannot be undone!</source>
|
||||||
|
<target state="translated">所有訊息都將被刪除 - 這不能還原!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow irreversible message deletion only if your contact allows it to you. (24 hours)" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow irreversible message deletion only if your contact allows it to you. (24 hours)</source>
|
||||||
|
<target state="translated">只有你的聯絡人允許的情況下,才允許不可逆地將訊息刪除。(24小時)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow to irreversibly delete sent messages. (24 hours)</source>
|
||||||
|
<target state="translated">允許將不可撤銷的訊息刪除。(24小時)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow your contacts to irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow your contacts to irreversibly delete sent messages. (24 hours)</source>
|
||||||
|
<target state="translated">允許您的聯絡人不可復原地刪除已傳送的訊息。(24小時)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Bad desktop address" xml:space="preserve" approved="no">
|
||||||
|
<source>Bad desktop address</source>
|
||||||
|
<target state="translated">無效的桌面地址</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Error decrypting file" xml:space="preserve" approved="no">
|
||||||
|
<source>Error decrypting file</source>
|
||||||
|
<target state="translated">解密檔案時出錯</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Add contact" xml:space="preserve" approved="no">
|
||||||
|
<source>Add contact</source>
|
||||||
|
<target state="translated">新增聯絡人</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Advanced settings" xml:space="preserve" approved="no">
|
||||||
|
<source>Advanced settings</source>
|
||||||
|
<target state="translated">進階設定</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow calls?" xml:space="preserve" approved="no">
|
||||||
|
<source>Allow calls?</source>
|
||||||
|
<target state="translated">允許通話?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow to send files and media." xml:space="preserve" approved="no">
|
||||||
|
<source>Allow to send files and media.</source>
|
||||||
|
<target state="translated">允許傳送檔案和媒體。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Already joining the group!" xml:space="preserve" approved="no">
|
||||||
|
<source>Already joining the group!</source>
|
||||||
|
<target state="translated">已加入群組!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="App data migration" xml:space="preserve" approved="no">
|
||||||
|
<source>App data migration</source>
|
||||||
|
<target state="translated">應用資料轉移</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Apply" xml:space="preserve" approved="no">
|
||||||
|
<source>Apply</source>
|
||||||
|
<target state="translated">應用</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Apply to" xml:space="preserve" approved="no">
|
||||||
|
<source>Apply to</source>
|
||||||
|
<target state="translated">應用到</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Archive and upload" xml:space="preserve" approved="no">
|
||||||
|
<source>Archive and upload</source>
|
||||||
|
<target state="translated">儲存並上傳</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block" xml:space="preserve" approved="no">
|
||||||
|
<source>Block</source>
|
||||||
|
<target state="translated">封鎖</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block group members" xml:space="preserve" approved="no">
|
||||||
|
<source>Block group members</source>
|
||||||
|
<target state="translated">封鎖群組成員</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block member" xml:space="preserve" approved="no">
|
||||||
|
<source>Block member</source>
|
||||||
|
<target state="translated">封鎖成員</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!" xml:space="preserve" approved="no">
|
||||||
|
<source>Bulgarian, Finnish, Thai and Ukrainian - thanks to the users and [Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!</source>
|
||||||
|
<target state="translated">保加利亞語、芬蘭語、泰語和烏克蘭語——感謝使用者們和[Weblate](https://github.com/simplex-chat/simplex-chat/tree/stable#help-translating-simplex-chat)!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Can't call member" xml:space="preserve" approved="no">
|
||||||
|
<source>Can't call member</source>
|
||||||
|
<target state="translated">無法與成員通話</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Can't message member" xml:space="preserve" approved="no">
|
||||||
|
<source>Can't message member</source>
|
||||||
|
<target state="translated">無法傳送訊息給成員</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Cancel migration" xml:space="preserve" approved="no">
|
||||||
|
<source>Cancel migration</source>
|
||||||
|
<target state="translated">取消遷移</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Chat database exported" xml:space="preserve" approved="no">
|
||||||
|
<source>Chat database exported</source>
|
||||||
|
<target state="translated">導出聊天數據庫</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="0 sec" xml:space="preserve" approved="no">
|
||||||
|
<source>0 sec</source>
|
||||||
|
<target state="translated">0 秒</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays." xml:space="preserve" approved="no">
|
||||||
|
<source>All your contacts, conversations and files will be securely encrypted and uploaded in chunks to configured XFTP relays.</source>
|
||||||
|
<target state="translated">你的所有聯絡人、對話和文件將被安全加密並切塊上傳到設置的 XFTP 中繼。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Address change will be aborted. Old receiving address will be used." xml:space="preserve" approved="no">
|
||||||
|
<source>Address change will be aborted. Old receiving address will be used.</source>
|
||||||
|
<target state="translated">將取消地址更改。將使用舊聯絡地址。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Archiving database" xml:space="preserve" approved="no">
|
||||||
|
<source>Archiving database</source>
|
||||||
|
<target state="translated">正在儲存資料庫</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Cellular" xml:space="preserve" approved="no">
|
||||||
|
<source>Cellular</source>
|
||||||
|
<target state="translated">行動網路</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%@, %@ and %lld members" xml:space="preserve" approved="no">
|
||||||
|
<source>%@, %@ and %lld members</source>
|
||||||
|
<target state="translated">%@, %@ 和 %lld 成員</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%lld messages marked deleted" xml:space="preserve" approved="no">
|
||||||
|
<source>%lld messages marked deleted</source>
|
||||||
|
<target state="translated">%lld 條訊息已刪除</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Already connecting!" xml:space="preserve" approved="no">
|
||||||
|
<source>Already connecting!</source>
|
||||||
|
<target state="translated">已連接!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Block member?" xml:space="preserve" approved="no">
|
||||||
|
<source>Block member?</source>
|
||||||
|
<target state="translated">封鎖成員?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="(new)" xml:space="preserve" approved="no">
|
||||||
|
<source>(new)</source>
|
||||||
|
<target state="translated">(新)</target>
|
||||||
|
</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>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="A few more things" xml:space="preserve" approved="no">
|
||||||
|
<source>A few more things</source>
|
||||||
|
<target state="translated">其他</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Show last messages" xml:space="preserve" approved="no">
|
||||||
|
<source>Show last messages</source>
|
||||||
|
<target state="translated">顯示最新的訊息</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="App encrypts new local files (except videos)." xml:space="preserve" approved="no">
|
||||||
|
<source>App encrypts new local files (except videos).</source>
|
||||||
|
<target state="translated">應用程式將為新的本機文件(影片除外)加密。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Better groups" xml:space="preserve" approved="no">
|
||||||
|
<source>Better groups</source>
|
||||||
|
<target state="translated">更加的群組</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="%lld new interface languages" xml:space="preserve" approved="no">
|
||||||
|
<source>%lld new interface languages</source>
|
||||||
|
<target state="translated">%lld 種新的介面語言</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Blocked by admin" xml:space="preserve" approved="no">
|
||||||
|
<source>Blocked by admin</source>
|
||||||
|
<target state="translated">由管理員封鎖</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Both you and your contact can irreversibly delete sent messages. (24 hours)" xml:space="preserve" approved="no">
|
||||||
|
<source>Both you and your contact can irreversibly delete sent messages. (24 hours)</source>
|
||||||
|
<target state="translated">您與您的聯絡人都可以不可復原地删除已傳送的訊息。(24小時)</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Encrypt local files" xml:space="preserve" approved="no">
|
||||||
|
<source>Encrypt local files</source>
|
||||||
|
<target state="translated">加密本機檔案</target>
|
||||||
|
</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>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="- optionally notify deleted contacts. - profile names with spaces. - and more!" xml:space="preserve" approved="no">
|
||||||
|
<source>- optionally notify deleted contacts.
|
||||||
|
- profile names with spaces.
|
||||||
|
- and more!</source>
|
||||||
|
<target state="translated">- 可選擇通知已刪除的聯絡人
|
||||||
|
- 帶空格的共人資料名稱。
|
||||||
|
-以及更多!</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Abort changing address?" xml:space="preserve" approved="no">
|
||||||
|
<source>Abort changing address?</source>
|
||||||
|
<target state="translated">中止更改地址?</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Allow to send SimpleX links." xml:space="preserve" approved="no">
|
||||||
|
<source>Allow to send SimpleX links.</source>
|
||||||
|
<target state="translated">允許傳送 SimpleX 連結。</target>
|
||||||
|
</trans-unit>
|
||||||
|
<trans-unit id="Background" xml:space="preserve" approved="no">
|
||||||
|
<source>Background</source>
|
||||||
|
<target state="translated">後台</target>
|
||||||
|
</trans-unit>
|
||||||
</body>
|
</body>
|
||||||
</file>
|
</file>
|
||||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="zh-Hant" datatype="plaintext">
|
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="zh-Hant" datatype="plaintext">
|
||||||
|
|||||||
@@ -339,7 +339,9 @@ class NotificationService: UNNotificationServiceExtension {
|
|||||||
CXProvider.reportNewIncomingVoIPPushPayload([
|
CXProvider.reportNewIncomingVoIPPushPayload([
|
||||||
"displayName": invitation.contact.displayName,
|
"displayName": invitation.contact.displayName,
|
||||||
"contactId": invitation.contact.id,
|
"contactId": invitation.contact.id,
|
||||||
"media": invitation.callType.media.rawValue
|
"callUUID": invitation.callUUID ?? "",
|
||||||
|
"media": invitation.callType.media.rawValue,
|
||||||
|
"callTs": invitation.callTs.timeIntervalSince1970
|
||||||
]) { error in
|
]) { error in
|
||||||
logger.debug("reportNewIncomingVoIPPushPayload result: \(error)")
|
logger.debug("reportNewIncomingVoIPPushPayload result: \(error)")
|
||||||
deliver(error == nil ? nil : createCallInvitationNtf(invitation))
|
deliver(error == nil ? nil : createCallInvitationNtf(invitation))
|
||||||
@@ -571,17 +573,22 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
|||||||
// TODO profile update
|
// TODO profile update
|
||||||
case let .receivedContactRequest(user, contactRequest):
|
case let .receivedContactRequest(user, contactRequest):
|
||||||
return (UserContact(contactRequest: contactRequest).id, .nse(createContactRequestNtf(user, contactRequest)))
|
return (UserContact(contactRequest: contactRequest).id, .nse(createContactRequestNtf(user, contactRequest)))
|
||||||
case let .newChatItem(user, aChatItem):
|
case let .newChatItems(user, chatItems):
|
||||||
let cInfo = aChatItem.chatInfo
|
// Received items are created one at a time
|
||||||
var cItem = aChatItem.chatItem
|
if let chatItem = chatItems.first {
|
||||||
if !cInfo.ntfsEnabled {
|
let cInfo = chatItem.chatInfo
|
||||||
ntfBadgeCountGroupDefault.set(max(0, ntfBadgeCountGroupDefault.get() - 1))
|
var cItem = chatItem.chatItem
|
||||||
|
if !cInfo.ntfsEnabled {
|
||||||
|
ntfBadgeCountGroupDefault.set(max(0, ntfBadgeCountGroupDefault.get() - 1))
|
||||||
|
}
|
||||||
|
if let file = cItem.autoReceiveFile() {
|
||||||
|
cItem = autoReceiveFile(file) ?? cItem
|
||||||
|
}
|
||||||
|
let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(createMessageReceivedNtf(user, cInfo, cItem)) : .empty
|
||||||
|
return cItem.showNotification ? (chatItem.chatId, ntf) : nil
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
if let file = cItem.autoReceiveFile() {
|
|
||||||
cItem = autoReceiveFile(file) ?? cItem
|
|
||||||
}
|
|
||||||
let ntf: NSENotification = cInfo.ntfsEnabled ? .nse(createMessageReceivedNtf(user, cInfo, cItem)) : .empty
|
|
||||||
return cItem.showNotification ? (aChatItem.chatId, ntf) : nil
|
|
||||||
case let .rcvFileSndCancelled(_, aChatItem, _):
|
case let .rcvFileSndCancelled(_, aChatItem, _):
|
||||||
cleanupFile(aChatItem)
|
cleanupFile(aChatItem)
|
||||||
return nil
|
return nil
|
||||||
|
|||||||
@@ -54,32 +54,30 @@ func apiGetChats(userId: User.ID) throws -> Array<ChatData> {
|
|||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
|
|
||||||
func apiSendMessage(
|
func apiSendMessages(
|
||||||
chatInfo: ChatInfo,
|
chatInfo: ChatInfo,
|
||||||
cryptoFile: CryptoFile?,
|
composedMessages: [ComposedMessage]
|
||||||
msgContent: MsgContent
|
) throws -> [AChatItem] {
|
||||||
) throws -> AChatItem {
|
|
||||||
let r = sendSimpleXCmd(
|
let r = sendSimpleXCmd(
|
||||||
chatInfo.chatType == .local
|
chatInfo.chatType == .local
|
||||||
? .apiCreateChatItem(
|
? .apiCreateChatItems(
|
||||||
noteFolderId: chatInfo.apiId,
|
noteFolderId: chatInfo.apiId,
|
||||||
file: cryptoFile,
|
composedMessages: composedMessages
|
||||||
msg: msgContent
|
|
||||||
)
|
)
|
||||||
: .apiSendMessage(
|
: .apiSendMessages(
|
||||||
type: chatInfo.chatType,
|
type: chatInfo.chatType,
|
||||||
id: chatInfo.apiId,
|
id: chatInfo.apiId,
|
||||||
file: cryptoFile,
|
|
||||||
quotedItemId: nil,
|
|
||||||
msg: msgContent,
|
|
||||||
live: false,
|
live: false,
|
||||||
ttl: nil
|
ttl: nil,
|
||||||
|
composedMessages: composedMessages
|
||||||
)
|
)
|
||||||
)
|
)
|
||||||
if case let .newChatItem(_, chatItem) = r {
|
if case let .newChatItems(_, chatItems) = r {
|
||||||
return chatItem
|
return chatItems
|
||||||
} else {
|
} else {
|
||||||
if let filePath = cryptoFile?.filePath { removeFile(filePath) }
|
for composedMessage in composedMessages {
|
||||||
|
if let filePath = composedMessage.fileSource?.filePath { removeFile(filePath) }
|
||||||
|
}
|
||||||
throw r
|
throw r
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -104,7 +104,7 @@ class ShareModel: ObservableObject {
|
|||||||
// Decode base64 images on background thread
|
// Decode base64 images on background thread
|
||||||
let profileImages = chats.reduce(into: Dictionary<ChatInfo.ID, UIImage>()) { dict, chatData in
|
let profileImages = chats.reduce(into: Dictionary<ChatInfo.ID, UIImage>()) { dict, chatData in
|
||||||
if let profileImage = chatData.chatInfo.image,
|
if let profileImage = chatData.chatInfo.image,
|
||||||
let uiImage = UIImage(base64Encoded: profileImage) {
|
let uiImage = imageFromBase64(profileImage) {
|
||||||
dict[chatData.id] = uiImage
|
dict[chatData.id] = uiImage
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
@@ -141,23 +141,25 @@ class ShareModel: ObservableObject {
|
|||||||
do {
|
do {
|
||||||
SEChatState.shared.set(.sendingMessage)
|
SEChatState.shared.set(.sendingMessage)
|
||||||
await waitForOtherProcessesToSuspend()
|
await waitForOtherProcessesToSuspend()
|
||||||
let ci = try apiSendMessage(
|
let chatItems = try apiSendMessages(
|
||||||
chatInfo: selected.chatInfo,
|
chatInfo: selected.chatInfo,
|
||||||
cryptoFile: sharedContent.cryptoFile,
|
composedMessages: [ComposedMessage(fileSource: sharedContent.cryptoFile, msgContent: sharedContent.msgContent(comment: self.comment))]
|
||||||
msgContent: sharedContent.msgContent(comment: self.comment)
|
|
||||||
)
|
)
|
||||||
if selected.chatInfo.chatType == .local {
|
if selected.chatInfo.chatType == .local {
|
||||||
completion()
|
completion()
|
||||||
} else {
|
} else {
|
||||||
await MainActor.run { self.bottomBar = .loadingBar(progress: 0) }
|
// TODO batch send: share multiple items
|
||||||
if let e = await handleEvents(
|
if let ci = chatItems.first {
|
||||||
isGroupChat: ci.chatInfo.chatType == .group,
|
await MainActor.run { self.bottomBar = .loadingBar(progress: 0) }
|
||||||
isWithoutFile: sharedContent.cryptoFile == nil,
|
if let e = await handleEvents(
|
||||||
chatItemId: ci.chatItem.id
|
isGroupChat: ci.chatInfo.chatType == .group,
|
||||||
) {
|
isWithoutFile: sharedContent.cryptoFile == nil,
|
||||||
await MainActor.run { errorAlert = e }
|
chatItemId: ci.chatItem.id
|
||||||
} else {
|
) {
|
||||||
completion()
|
await MainActor.run { errorAlert = e }
|
||||||
|
} else {
|
||||||
|
completion()
|
||||||
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
} catch {
|
} catch {
|
||||||
|
|||||||
@@ -147,8 +147,8 @@ struct ShareView: View {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@ViewBuilder private func imagePreview(_ img: String) -> some View {
|
@ViewBuilder private func imagePreview(_ imgStr: String) -> some View {
|
||||||
if let img = UIImage(base64Encoded: img) {
|
if let img = imageFromBase64(imgStr) {
|
||||||
previewArea {
|
previewArea {
|
||||||
Image(uiImage: img)
|
Image(uiImage: img)
|
||||||
.resizable()
|
.resizable()
|
||||||
@@ -163,7 +163,7 @@ struct ShareView: View {
|
|||||||
@ViewBuilder private func linkPreview(_ linkPreview: LinkPreview) -> some View {
|
@ViewBuilder private func linkPreview(_ linkPreview: LinkPreview) -> some View {
|
||||||
previewArea {
|
previewArea {
|
||||||
HStack(alignment: .center, spacing: 8) {
|
HStack(alignment: .center, spacing: 8) {
|
||||||
if let uiImage = UIImage(base64Encoded: linkPreview.image) {
|
if let uiImage = imageFromBase64(linkPreview.image) {
|
||||||
Image(uiImage: uiImage)
|
Image(uiImage: uiImage)
|
||||||
.resizable()
|
.resizable()
|
||||||
.aspectRatio(contentMode: .fit)
|
.aspectRatio(contentMode: .fit)
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
/*
|
/* Bundle display name */
|
||||||
InfoPlist.strings
|
"CFBundleDisplayName" = "SimpleX SE";
|
||||||
SimpleX
|
|
||||||
|
/* Bundle name */
|
||||||
|
"CFBundleName" = "SimpleX SE";
|
||||||
|
|
||||||
|
/* Copyright (human-readable) */
|
||||||
|
"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Wszelkie prawa zastrzeżone.";
|
||||||
|
|
||||||
Created by EP on 30/07/2024.
|
|
||||||
Copyright © 2024 SimpleX Chat. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1,7 +1,111 @@
|
|||||||
/*
|
/* No comment provided by engineer. */
|
||||||
Localizable.strings
|
"%@" = "%@";
|
||||||
SimpleX
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"App is locked!" = "Aplikacja zablokowana!";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Cancel" = "Anuluj";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Cannot access keychain to save database password" = "Nie można uzyskać dostępu do pęku kluczy aby zapisać hasło do bazy danych";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Cannot forward message" = "Nie można przekazać wiadomości";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Comment" = "Komentarz";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Currently maximum supported file size is %@." = "Obecnie maksymalny obsługiwany rozmiar pliku to %@.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database downgrade required" = "Wymagane obniżenie wersji bazy danych";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database encrypted!" = "Baza danych zaszyfrowana!";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database error" = "Błąd bazy danych";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database passphrase is different from saved in the keychain." = "Hasło bazy danych jest inne niż zapisane w pęku kluczy.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database passphrase is required to open chat." = "Hasło do bazy danych jest wymagane do otwarcia czatu.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database upgrade required" = "Wymagana aktualizacja bazy danych";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Error preparing file" = "Błąd przygotowania pliku";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Error preparing message" = "Błąd przygotowania wiadomości";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Error: %@" = "Błąd: %@";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"File error" = "Błąd pliku";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Incompatible database version" = "Niekompatybilna wersja bazy danych";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Invalid migration confirmation" = "Nieprawidłowe potwierdzenie migracji";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Keychain error" = "Błąd pęku kluczy";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Large file!" = "Duży plik!";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"No active profile" = "Brak aktywnego profilu";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Ok" = "Ok";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Open the app to downgrade the database." = "Otwórz aplikację aby obniżyć wersję bazy danych.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Open the app to upgrade the database." = "Otwórz aplikację aby zaktualizować bazę danych.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Passphrase" = "Hasło";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Please create a profile in the SimpleX app" = "Proszę utworzyć profil w aplikacji SimpleX";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Selected chat preferences prohibit this message." = "Wybrane preferencje czatu zabraniają tej wiadomości.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Sending a message takes longer than expected." = "Wysłanie wiadomości trwa dłużej niż oczekiwano.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Sending message…" = "Wysyłanie wiadomości…";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Share" = "Udostępnij";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Slow network?" = "Wolna sieć?";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Unknown database error: %@" = "Nieznany błąd bazy danych: %@";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Unsupported format" = "Niewspierany format";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Wait" = "Czekaj";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Wrong database passphrase" = "Nieprawidłowe hasło bazy danych";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Możesz zezwolić na udostępnianie w ustawieniach Prywatność i bezpieczeństwo / Blokada SimpleX.";
|
||||||
|
|
||||||
Created by EP on 30/07/2024.
|
|
||||||
Copyright © 2024 SimpleX Chat. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1,7 +1,9 @@
|
|||||||
/*
|
/* Bundle display name */
|
||||||
InfoPlist.strings
|
"CFBundleDisplayName" = "SimpleX SE";
|
||||||
SimpleX
|
|
||||||
|
/* Bundle name */
|
||||||
|
"CFBundleName" = "SimpleX SE";
|
||||||
|
|
||||||
|
/* Copyright (human-readable) */
|
||||||
|
"NSHumanReadableCopyright" = "Copyright © 2024 SimpleX Chat. Всі права захищені.";
|
||||||
|
|
||||||
Created by EP on 30/07/2024.
|
|
||||||
Copyright © 2024 SimpleX Chat. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -1,7 +1,111 @@
|
|||||||
/*
|
/* No comment provided by engineer. */
|
||||||
Localizable.strings
|
"%@" = "%@";
|
||||||
SimpleX
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"App is locked!" = "Додаток заблоковано!";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Cancel" = "Скасувати";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Cannot access keychain to save database password" = "Не вдається отримати доступ до зв'язки ключів для збереження пароля до бази даних";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Cannot forward message" = "Неможливо переслати повідомлення";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Comment" = "Коментар";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Currently maximum supported file size is %@." = "Наразі максимальний підтримуваний розмір файлу - %@.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database downgrade required" = "Потрібне оновлення бази даних";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database encrypted!" = "База даних зашифрована!";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database error" = "Помилка в базі даних";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database passphrase is different from saved in the keychain." = "Парольна фраза бази даних відрізняється від збереженої у в’язці ключів.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database passphrase is required to open chat." = "Для відкриття чату потрібно ввести пароль до бази даних.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Database upgrade required" = "Потрібне оновлення бази даних";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Error preparing file" = "Помилка підготовки файлу";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Error preparing message" = "Повідомлення про підготовку до помилки";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Error: %@" = "Помилка: %@";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"File error" = "Помилка файлу";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Incompatible database version" = "Несумісна версія бази даних";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Invalid migration confirmation" = "Недійсне підтвердження міграції";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Keychain error" = "Помилка зв'язки ключів";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Large file!" = "Великий файл!";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"No active profile" = "Немає активного профілю";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Ok" = "Гаразд";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Open the app to downgrade the database." = "Відкрийте програму, щоб знизити версію бази даних.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Open the app to upgrade the database." = "Відкрийте програму, щоб оновити базу даних.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Passphrase" = "Парольна фраза";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Please create a profile in the SimpleX app" = "Будь ласка, створіть профіль у додатку SimpleX";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Selected chat preferences prohibit this message." = "Вибрані налаштування чату забороняють це повідомлення.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Sending a message takes longer than expected." = "Надсилання повідомлення займає більше часу, ніж очікувалося.";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Sending message…" = "Надсилаю повідомлення…";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Share" = "Поділіться";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Slow network?" = "Повільна мережа?";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Unknown database error: %@" = "Невідома помилка бази даних: %@";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Unsupported format" = "Непідтримуваний формат";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Wait" = "Зачекай";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"Wrong database passphrase" = "Неправильна ключова фраза до бази даних";
|
||||||
|
|
||||||
|
/* No comment provided by engineer. */
|
||||||
|
"You can allow sharing in Privacy & Security / SimpleX Lock settings." = "Ви можете дозволити спільний доступ у налаштуваннях Конфіденційність і безпека / SimpleX Lock.";
|
||||||
|
|
||||||
Created by EP on 30/07/2024.
|
|
||||||
Copyright © 2024 SimpleX Chat. All rights reserved.
|
|
||||||
*/
|
|
||||||
|
|||||||
@@ -167,6 +167,11 @@
|
|||||||
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; };
|
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; };
|
||||||
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
|
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
|
||||||
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
|
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
|
||||||
|
64A208622C8F2CCC00AE9D01 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A2085D2C8F2CCB00AE9D01 /* libgmpxx.a */; };
|
||||||
|
64A208632C8F2CCC00AE9D01 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A2085E2C8F2CCB00AE9D01 /* libffi.a */; };
|
||||||
|
64A208642C8F2CCC00AE9D01 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A2085F2C8F2CCB00AE9D01 /* libgmp.a */; };
|
||||||
|
64A208652C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A208602C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7-ghc9.6.3.a */; };
|
||||||
|
64A208662C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64A208612C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7.a */; };
|
||||||
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
|
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
|
||||||
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
|
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
|
||||||
64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */; };
|
64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */; };
|
||||||
@@ -177,6 +182,8 @@
|
|||||||
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; };
|
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; };
|
||||||
64EEB0F72C353F1C00972D62 /* ServersSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */; };
|
64EEB0F72C353F1C00972D62 /* ServersSummaryView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */; };
|
||||||
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
|
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
|
||||||
|
8C01E9C12C8EFC33008A4B0A /* objc.m in Sources */ = {isa = PBXBuildFile; fileRef = 8C01E9C02C8EFC33008A4B0A /* objc.m */; };
|
||||||
|
8C01E9C22C8EFF8F008A4B0A /* objc.h in Headers */ = {isa = PBXBuildFile; fileRef = 8C01E9BF2C8EFBB6008A4B0A /* objc.h */; };
|
||||||
8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; };
|
8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; };
|
||||||
8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */; };
|
8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7E3CE32C0DEAC400BFF63A /* ThemeTypes.swift */; };
|
||||||
8C74C3E72C1B901900039E77 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C852B072C1086D100BA61E8 /* Color.swift */; };
|
8C74C3E72C1B901900039E77 /* Color.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C852B072C1086D100BA61E8 /* Color.swift */; };
|
||||||
@@ -194,6 +201,7 @@
|
|||||||
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; };
|
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; };
|
||||||
8CE848A32C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */; };
|
8CE848A32C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */; };
|
||||||
B76E6C312C5C41D900EC11AA /* ContactListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */; };
|
B76E6C312C5C41D900EC11AA /* ContactListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */; };
|
||||||
|
CE176F202C87014C00145DBC /* InvertedForegroundStyle.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */; };
|
||||||
CE1EB0E42C459A660099D896 /* ShareAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EB0E32C459A660099D896 /* ShareAPI.swift */; };
|
CE1EB0E42C459A660099D896 /* ShareAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE1EB0E32C459A660099D896 /* ShareAPI.swift */; };
|
||||||
CE2AD9CE2C452A4D00E844E3 /* ChatUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */; };
|
CE2AD9CE2C452A4D00E844E3 /* ChatUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */; };
|
||||||
CE3097FB2C4C0C9F00180898 /* ErrorAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */; };
|
CE3097FB2C4C0C9F00180898 /* ErrorAlert.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */; };
|
||||||
@@ -218,11 +226,6 @@
|
|||||||
E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; };
|
E5DCF9712C590272007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF96F2C590272007928CC /* Localizable.strings */; };
|
||||||
E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; };
|
E5DCF9842C5902CE007928CC /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9822C5902CE007928CC /* Localizable.strings */; };
|
||||||
E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9962C5906FF007928CC /* InfoPlist.strings */; };
|
E5DCF9982C5906FF007928CC /* InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = E5DCF9962C5906FF007928CC /* InfoPlist.strings */; };
|
||||||
E5E218532C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E2184E2C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q-ghc9.6.3.a */; };
|
|
||||||
E5E218542C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E2184F2C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q.a */; };
|
|
||||||
E5E218552C6D4C0F0013B4C6 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E218502C6D4C0F0013B4C6 /* libgmp.a */; };
|
|
||||||
E5E218562C6D4C0F0013B4C6 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E218512C6D4C0F0013B4C6 /* libffi.a */; };
|
|
||||||
E5E218572C6D4C0F0013B4C6 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E5E218522C6D4C0F0013B4C6 /* libgmpxx.a */; };
|
|
||||||
/* End PBXBuildFile section */
|
/* End PBXBuildFile section */
|
||||||
|
|
||||||
/* Begin PBXContainerItemProxy section */
|
/* Begin PBXContainerItemProxy section */
|
||||||
@@ -505,6 +508,11 @@
|
|||||||
6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
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>"; };
|
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>"; };
|
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
|
||||||
|
64A2085D2C8F2CCB00AE9D01 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||||
|
64A2085E2C8F2CCB00AE9D01 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||||
|
64A2085F2C8F2CCB00AE9D01 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||||
|
64A208602C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||||
|
64A208612C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7.a"; sourceTree = "<group>"; };
|
||||||
64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.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>"; };
|
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>"; };
|
64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemInfoView.swift; sourceTree = "<group>"; };
|
||||||
@@ -516,6 +524,8 @@
|
|||||||
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = "<group>"; };
|
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = "<group>"; };
|
||||||
64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersSummaryView.swift; sourceTree = "<group>"; };
|
64EEB0F62C353F1C00972D62 /* ServersSummaryView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ServersSummaryView.swift; sourceTree = "<group>"; };
|
||||||
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = "<group>"; };
|
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = "<group>"; };
|
||||||
|
8C01E9BF2C8EFBB6008A4B0A /* objc.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = objc.h; sourceTree = "<group>"; };
|
||||||
|
8C01E9C02C8EFC33008A4B0A /* objc.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = objc.m; sourceTree = "<group>"; };
|
||||||
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||||
8C74C3EB2C1B92A900039E77 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
8C74C3EB2C1B92A900039E77 /* Theme.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Theme.swift; sourceTree = "<group>"; };
|
||||||
8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = "<group>"; };
|
8C74C3ED2C1B942300039E77 /* ChatWallpaper.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatWallpaper.swift; sourceTree = "<group>"; };
|
||||||
@@ -532,6 +542,7 @@
|
|||||||
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = "<group>"; };
|
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = "<group>"; };
|
||||||
8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableChatItemToolbars.swift; sourceTree = "<group>"; };
|
8CE848A22C5A0FA000D5C7C8 /* SelectableChatItemToolbars.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SelectableChatItemToolbars.swift; sourceTree = "<group>"; };
|
||||||
B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListNavLink.swift; sourceTree = "<group>"; };
|
B76E6C302C5C41D900EC11AA /* ContactListNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactListNavLink.swift; sourceTree = "<group>"; };
|
||||||
|
CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = InvertedForegroundStyle.swift; sourceTree = "<group>"; };
|
||||||
CE1EB0E32C459A660099D896 /* ShareAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAPI.swift; sourceTree = "<group>"; };
|
CE1EB0E32C459A660099D896 /* ShareAPI.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareAPI.swift; sourceTree = "<group>"; };
|
||||||
CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUtils.swift; sourceTree = "<group>"; };
|
CE2AD9CD2C452A4D00E844E3 /* ChatUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatUtils.swift; sourceTree = "<group>"; };
|
||||||
CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorAlert.swift; sourceTree = "<group>"; };
|
CE3097FA2C4C0C9F00180898 /* ErrorAlert.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ErrorAlert.swift; sourceTree = "<group>"; };
|
||||||
@@ -602,11 +613,6 @@
|
|||||||
E5DCF9A62C590731007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
E5DCF9A62C590731007928CC /* th */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = th; path = th.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
E5DCF9A72C590732007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
E5DCF9A72C590732007928CC /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
E5DCF9A82C590732007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
E5DCF9A82C590732007928CC /* uk */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = uk; path = uk.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||||
E5E2184E2C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q-ghc9.6.3.a"; sourceTree = "<group>"; };
|
|
||||||
E5E2184F2C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q.a"; sourceTree = "<group>"; };
|
|
||||||
E5E218502C6D4C0F0013B4C6 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
|
||||||
E5E218512C6D4C0F0013B4C6 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
|
||||||
E5E218522C6D4C0F0013B4C6 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
|
||||||
/* End PBXFileReference section */
|
/* End PBXFileReference section */
|
||||||
|
|
||||||
/* Begin PBXFrameworksBuildPhase section */
|
/* Begin PBXFrameworksBuildPhase section */
|
||||||
@@ -645,14 +651,14 @@
|
|||||||
isa = PBXFrameworksBuildPhase;
|
isa = PBXFrameworksBuildPhase;
|
||||||
buildActionMask = 2147483647;
|
buildActionMask = 2147483647;
|
||||||
files = (
|
files = (
|
||||||
|
64A208662C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7.a in Frameworks */,
|
||||||
|
64A208622C8F2CCC00AE9D01 /* libgmpxx.a in Frameworks */,
|
||||||
|
64A208652C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7-ghc9.6.3.a in Frameworks */,
|
||||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||||
E5E218542C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q.a in Frameworks */,
|
|
||||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||||
|
64A208632C8F2CCC00AE9D01 /* libffi.a in Frameworks */,
|
||||||
|
64A208642C8F2CCC00AE9D01 /* libgmp.a in Frameworks */,
|
||||||
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
|
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
|
||||||
E5E218572C6D4C0F0013B4C6 /* libgmpxx.a in Frameworks */,
|
|
||||||
E5E218562C6D4C0F0013B4C6 /* libffi.a in Frameworks */,
|
|
||||||
E5E218552C6D4C0F0013B4C6 /* libgmp.a in Frameworks */,
|
|
||||||
E5E218532C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q-ghc9.6.3.a in Frameworks */,
|
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
};
|
};
|
||||||
@@ -729,11 +735,11 @@
|
|||||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||||
isa = PBXGroup;
|
isa = PBXGroup;
|
||||||
children = (
|
children = (
|
||||||
E5E218512C6D4C0F0013B4C6 /* libffi.a */,
|
64A2085E2C8F2CCB00AE9D01 /* libffi.a */,
|
||||||
E5E218502C6D4C0F0013B4C6 /* libgmp.a */,
|
64A2085F2C8F2CCB00AE9D01 /* libgmp.a */,
|
||||||
E5E218522C6D4C0F0013B4C6 /* libgmpxx.a */,
|
64A2085D2C8F2CCB00AE9D01 /* libgmpxx.a */,
|
||||||
E5E2184E2C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q-ghc9.6.3.a */,
|
64A208602C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7-ghc9.6.3.a */,
|
||||||
E5E2184F2C6D4C0F0013B4C6 /* libHSsimplex-chat-6.0.0.8-9fvDFLivFrv8AINTqPH03q.a */,
|
64A208612C8F2CCC00AE9D01 /* libHSsimplex-chat-6.1.0.0-G2fNFSEVU486aN7U9YSuO7.a */,
|
||||||
);
|
);
|
||||||
path = Libraries;
|
path = Libraries;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -789,6 +795,7 @@
|
|||||||
8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */,
|
8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */,
|
||||||
CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */,
|
CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */,
|
||||||
CE7548092C622630009579B7 /* SwipeLabel.swift */,
|
CE7548092C622630009579B7 /* SwipeLabel.swift */,
|
||||||
|
CE176F1F2C87014C00145DBC /* InvertedForegroundStyle.swift */,
|
||||||
);
|
);
|
||||||
path = Helpers;
|
path = Helpers;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -976,6 +983,8 @@
|
|||||||
5CE2BA96284537A800EC33A6 /* dummy.m */,
|
5CE2BA96284537A800EC33A6 /* dummy.m */,
|
||||||
5CD67B8D2B0E858A00C510B1 /* hs_init.h */,
|
5CD67B8D2B0E858A00C510B1 /* hs_init.h */,
|
||||||
5CD67B8E2B0E858A00C510B1 /* hs_init.c */,
|
5CD67B8E2B0E858A00C510B1 /* hs_init.c */,
|
||||||
|
8C01E9BF2C8EFBB6008A4B0A /* objc.h */,
|
||||||
|
8C01E9C02C8EFC33008A4B0A /* objc.m */,
|
||||||
);
|
);
|
||||||
path = SimpleXChat;
|
path = SimpleXChat;
|
||||||
sourceTree = "<group>";
|
sourceTree = "<group>";
|
||||||
@@ -1113,6 +1122,7 @@
|
|||||||
files = (
|
files = (
|
||||||
5CE2BA77284530BF00EC33A6 /* SimpleXChat.h in Headers */,
|
5CE2BA77284530BF00EC33A6 /* SimpleXChat.h in Headers */,
|
||||||
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */,
|
5CD67B8F2B0E858A00C510B1 /* hs_init.h in Headers */,
|
||||||
|
8C01E9C22C8EFF8F008A4B0A /* objc.h in Headers */,
|
||||||
5CE2BA952845354B00EC33A6 /* SimpleX.h in Headers */,
|
5CE2BA952845354B00EC33A6 /* SimpleX.h in Headers */,
|
||||||
);
|
);
|
||||||
runOnlyForDeploymentPostprocessing = 0;
|
runOnlyForDeploymentPostprocessing = 0;
|
||||||
@@ -1489,6 +1499,7 @@
|
|||||||
5C9CC7A928C532AB00BEF955 /* DatabaseErrorView.swift in Sources */,
|
5C9CC7A928C532AB00BEF955 /* DatabaseErrorView.swift in Sources */,
|
||||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */,
|
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */,
|
||||||
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */,
|
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */,
|
||||||
|
CE176F202C87014C00145DBC /* InvertedForegroundStyle.swift in Sources */,
|
||||||
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */,
|
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */,
|
||||||
5C9C2DA7289957AE00CC63B1 /* AdvancedNetworkSettings.swift in Sources */,
|
5C9C2DA7289957AE00CC63B1 /* AdvancedNetworkSettings.swift in Sources */,
|
||||||
5CADE79A29211BB900072E13 /* PreferencesView.swift in Sources */,
|
5CADE79A29211BB900072E13 /* PreferencesView.swift in Sources */,
|
||||||
@@ -1539,6 +1550,7 @@
|
|||||||
8C74C3E72C1B901900039E77 /* Color.swift in Sources */,
|
8C74C3E72C1B901900039E77 /* Color.swift in Sources */,
|
||||||
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */,
|
5CD67B902B0E858A00C510B1 /* hs_init.c in Sources */,
|
||||||
5CE2BA91284533A300EC33A6 /* Notifications.swift in Sources */,
|
5CE2BA91284533A300EC33A6 /* Notifications.swift in Sources */,
|
||||||
|
8C01E9C12C8EFC33008A4B0A /* objc.m in Sources */,
|
||||||
5CE2BA79284530CC00EC33A6 /* SimpleXChat.docc in Sources */,
|
5CE2BA79284530CC00EC33A6 /* SimpleXChat.docc in Sources */,
|
||||||
5CE2BA90284533A300EC33A6 /* JSON.swift in Sources */,
|
5CE2BA90284533A300EC33A6 /* JSON.swift in Sources */,
|
||||||
5CE2BA8B284533A300EC33A6 /* ChatTypes.swift in Sources */,
|
5CE2BA8B284533A300EC33A6 /* ChatTypes.swift in Sources */,
|
||||||
@@ -1879,7 +1891,7 @@
|
|||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -1904,7 +1916,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES_THIN;
|
LLVM_LTO = YES_THIN;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||||
PRODUCT_NAME = SimpleX;
|
PRODUCT_NAME = SimpleX;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1928,7 +1940,7 @@
|
|||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEAD_CODE_STRIPPING = YES;
|
DEAD_CODE_STRIPPING = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
@@ -1953,7 +1965,7 @@
|
|||||||
"@executable_path/Frameworks",
|
"@executable_path/Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||||
PRODUCT_NAME = SimpleX;
|
PRODUCT_NAME = SimpleX;
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1969,11 +1981,11 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -1989,11 +2001,11 @@
|
|||||||
buildSettings = {
|
buildSettings = {
|
||||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
GENERATE_INFOPLIST_FILE = YES;
|
GENERATE_INFOPLIST_FILE = YES;
|
||||||
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-iOS";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2014,7 +2026,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
GCC_OPTIMIZATION_LEVEL = s;
|
GCC_OPTIMIZATION_LEVEL = s;
|
||||||
@@ -2029,7 +2041,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2051,7 +2063,7 @@
|
|||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||||
CODE_SIGN_IDENTITY = "Apple Development";
|
CODE_SIGN_IDENTITY = "Apple Development";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_BITCODE = NO;
|
ENABLE_BITCODE = NO;
|
||||||
ENABLE_CODE_COVERAGE = NO;
|
ENABLE_CODE_COVERAGE = NO;
|
||||||
@@ -2066,7 +2078,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||||
@@ -2088,7 +2100,7 @@
|
|||||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
@@ -2114,7 +2126,7 @@
|
|||||||
"$(PROJECT_DIR)/Libraries/sim",
|
"$(PROJECT_DIR)/Libraries/sim",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2139,7 +2151,7 @@
|
|||||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEFINES_MODULE = YES;
|
DEFINES_MODULE = YES;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||||
@@ -2165,7 +2177,7 @@
|
|||||||
"$(PROJECT_DIR)/Libraries/sim",
|
"$(PROJECT_DIR)/Libraries/sim",
|
||||||
);
|
);
|
||||||
LLVM_LTO = YES;
|
LLVM_LTO = YES;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2190,7 +2202,7 @@
|
|||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -2205,7 +2217,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
@@ -2224,7 +2236,7 @@
|
|||||||
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
|
||||||
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
|
||||||
CODE_SIGN_STYLE = Automatic;
|
CODE_SIGN_STYLE = Automatic;
|
||||||
CURRENT_PROJECT_VERSION = 233;
|
CURRENT_PROJECT_VERSION = 236;
|
||||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||||
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
ENABLE_USER_SCRIPT_SANDBOXING = YES;
|
||||||
GCC_C_LANGUAGE_STANDARD = gnu17;
|
GCC_C_LANGUAGE_STANDARD = gnu17;
|
||||||
@@ -2239,7 +2251,7 @@
|
|||||||
"@executable_path/../../Frameworks",
|
"@executable_path/../../Frameworks",
|
||||||
);
|
);
|
||||||
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
LOCALIZATION_PREFERS_STRING_CATALOGS = YES;
|
||||||
MARKETING_VERSION = 6.0.1;
|
MARKETING_VERSION = 6.0.4;
|
||||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
|
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-SE";
|
||||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||||
SDKROOT = iphoneos;
|
SDKROOT = iphoneos;
|
||||||
|
|||||||
@@ -42,13 +42,13 @@ public enum ChatCommand {
|
|||||||
case apiGetChats(userId: Int64)
|
case apiGetChats(userId: Int64)
|
||||||
case apiGetChat(type: ChatType, id: Int64, pagination: ChatPagination, search: String)
|
case apiGetChat(type: ChatType, id: Int64, pagination: ChatPagination, search: String)
|
||||||
case apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64)
|
case apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64)
|
||||||
case apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool, ttl: Int?)
|
case apiSendMessages(type: ChatType, id: Int64, live: Bool, ttl: Int?, composedMessages: [ComposedMessage])
|
||||||
case apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent)
|
case apiCreateChatItems(noteFolderId: Int64, composedMessages: [ComposedMessage])
|
||||||
case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool)
|
case apiUpdateChatItem(type: ChatType, id: Int64, itemId: Int64, msg: MsgContent, live: Bool)
|
||||||
case apiDeleteChatItem(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode)
|
case apiDeleteChatItem(type: ChatType, id: Int64, itemIds: [Int64], mode: CIDeleteMode)
|
||||||
case apiDeleteMemberChatItem(groupId: Int64, itemIds: [Int64])
|
case apiDeleteMemberChatItem(groupId: Int64, itemIds: [Int64])
|
||||||
case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction)
|
case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction)
|
||||||
case apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64, ttl: Int?)
|
case apiForwardChatItems(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemIds: [Int64], ttl: Int?)
|
||||||
case apiGetNtfToken
|
case apiGetNtfToken
|
||||||
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode)
|
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode)
|
||||||
case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
|
case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
|
||||||
@@ -97,6 +97,7 @@ public enum ChatCommand {
|
|||||||
case apiVerifyGroupMember(groupId: Int64, groupMemberId: Int64, connectionCode: String?)
|
case apiVerifyGroupMember(groupId: Int64, groupMemberId: Int64, connectionCode: String?)
|
||||||
case apiAddContact(userId: Int64, incognito: Bool)
|
case apiAddContact(userId: Int64, incognito: Bool)
|
||||||
case apiSetConnectionIncognito(connId: Int64, incognito: Bool)
|
case apiSetConnectionIncognito(connId: Int64, incognito: Bool)
|
||||||
|
case apiChangeConnectionUser(connId: Int64, userId: Int64)
|
||||||
case apiConnectPlan(userId: Int64, connReq: String)
|
case apiConnectPlan(userId: Int64, connReq: String)
|
||||||
case apiConnect(userId: Int64, incognito: Bool, connReq: String)
|
case apiConnect(userId: Int64, incognito: Bool, connReq: String)
|
||||||
case apiConnectContactViaAddress(userId: Int64, incognito: Bool, contactId: Int64)
|
case apiConnectContactViaAddress(userId: Int64, incognito: Bool, contactId: Int64)
|
||||||
@@ -128,6 +129,7 @@ public enum ChatCommand {
|
|||||||
// WebRTC calls /
|
// WebRTC calls /
|
||||||
case apiGetNetworkStatuses
|
case apiGetNetworkStatuses
|
||||||
case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64))
|
case apiChatRead(type: ChatType, id: Int64, itemRange: (Int64, Int64))
|
||||||
|
case apiChatItemsRead(type: ChatType, id: Int64, itemIds: [Int64])
|
||||||
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
|
case apiChatUnread(type: ChatType, id: Int64, unreadChat: Bool)
|
||||||
case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?)
|
case receiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?, inline: Bool?)
|
||||||
case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?)
|
case setFileToReceive(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool?)
|
||||||
@@ -190,20 +192,20 @@ public enum ChatCommand {
|
|||||||
case let .apiGetChat(type, id, pagination, search): return "/_get chat \(ref(type, id)) \(pagination.cmdString)" +
|
case let .apiGetChat(type, id, pagination, search): return "/_get chat \(ref(type, id)) \(pagination.cmdString)" +
|
||||||
(search == "" ? "" : " search=\(search)")
|
(search == "" ? "" : " search=\(search)")
|
||||||
case let .apiGetChatItemInfo(type, id, itemId): return "/_get item info \(ref(type, id)) \(itemId)"
|
case let .apiGetChatItemInfo(type, id, itemId): return "/_get item info \(ref(type, id)) \(itemId)"
|
||||||
case let .apiSendMessage(type, id, file, quotedItemId, mc, live, ttl):
|
case let .apiSendMessages(type, id, live, ttl, composedMessages):
|
||||||
let msg = encodeJSON(ComposedMessage(fileSource: file, quotedItemId: quotedItemId, msgContent: mc))
|
let msgs = encodeJSON(composedMessages)
|
||||||
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
|
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
|
||||||
return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msg)"
|
return "/_send \(ref(type, id)) live=\(onOff(live)) ttl=\(ttlStr) json \(msgs)"
|
||||||
case let .apiCreateChatItem(noteFolderId, file, mc):
|
case let .apiCreateChatItems(noteFolderId, composedMessages):
|
||||||
let msg = encodeJSON(ComposedMessage(fileSource: file, msgContent: mc))
|
let msgs = encodeJSON(composedMessages)
|
||||||
return "/_create *\(noteFolderId) json \(msg)"
|
return "/_create *\(noteFolderId) json \(msgs)"
|
||||||
case let .apiUpdateChatItem(type, id, itemId, mc, live): return "/_update item \(ref(type, id)) \(itemId) live=\(onOff(live)) \(mc.cmdString)"
|
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, itemIds, mode): return "/_delete item \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)"
|
case let .apiDeleteChatItem(type, id, itemIds, mode): return "/_delete item \(ref(type, id)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) \(mode.rawValue)"
|
||||||
case let .apiDeleteMemberChatItem(groupId, itemIds): return "/_delete member item #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ","))"
|
case let .apiDeleteMemberChatItem(groupId, itemIds): return "/_delete member item #\(groupId) \(itemIds.map({ "\($0)" }).joined(separator: ","))"
|
||||||
case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))"
|
case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))"
|
||||||
case let .apiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId, ttl):
|
case let .apiForwardChatItems(toChatType, toChatId, fromChatType, fromChatId, itemIds, ttl):
|
||||||
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
|
let ttlStr = ttl != nil ? "\(ttl!)" : "default"
|
||||||
return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemId) ttl=\(ttlStr)"
|
return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemIds.map({ "\($0)" }).joined(separator: ",")) ttl=\(ttlStr)"
|
||||||
case .apiGetNtfToken: return "/_ntf get "
|
case .apiGetNtfToken: return "/_ntf get "
|
||||||
case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)"
|
case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)"
|
||||||
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
|
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
|
||||||
@@ -262,6 +264,7 @@ public enum ChatCommand {
|
|||||||
case let .apiVerifyGroupMember(groupId, groupMemberId, .none): return "/_verify code #\(groupId) \(groupMemberId)"
|
case let .apiVerifyGroupMember(groupId, groupMemberId, .none): return "/_verify code #\(groupId) \(groupMemberId)"
|
||||||
case let .apiAddContact(userId, incognito): return "/_connect \(userId) incognito=\(onOff(incognito))"
|
case let .apiAddContact(userId, incognito): return "/_connect \(userId) incognito=\(onOff(incognito))"
|
||||||
case let .apiSetConnectionIncognito(connId, incognito): return "/_set incognito :\(connId) \(onOff(incognito))"
|
case let .apiSetConnectionIncognito(connId, incognito): return "/_set incognito :\(connId) \(onOff(incognito))"
|
||||||
|
case let .apiChangeConnectionUser(connId, userId): return "/_set conn user :\(connId) \(userId)"
|
||||||
case let .apiConnectPlan(userId, connReq): return "/_connect plan \(userId) \(connReq)"
|
case let .apiConnectPlan(userId, connReq): return "/_connect plan \(userId) \(connReq)"
|
||||||
case let .apiConnect(userId, incognito, connReq): return "/_connect \(userId) incognito=\(onOff(incognito)) \(connReq)"
|
case let .apiConnect(userId, incognito, connReq): return "/_connect \(userId) incognito=\(onOff(incognito)) \(connReq)"
|
||||||
case let .apiConnectContactViaAddress(userId, incognito, contactId): return "/_connect contact \(userId) incognito=\(onOff(incognito)) \(contactId)"
|
case let .apiConnectContactViaAddress(userId, incognito, contactId): return "/_connect contact \(userId) incognito=\(onOff(incognito)) \(contactId)"
|
||||||
@@ -291,6 +294,7 @@ public enum ChatCommand {
|
|||||||
case let .apiCallStatus(contact, callStatus): return "/_call status @\(contact.apiId) \(callStatus.rawValue)"
|
case let .apiCallStatus(contact, callStatus): return "/_call status @\(contact.apiId) \(callStatus.rawValue)"
|
||||||
case .apiGetNetworkStatuses: return "/_network_statuses"
|
case .apiGetNetworkStatuses: return "/_network_statuses"
|
||||||
case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)"
|
case let .apiChatRead(type, id, itemRange: (from, to)): return "/_read chat \(ref(type, id)) from=\(from) to=\(to)"
|
||||||
|
case let .apiChatItemsRead(type, id, itemIds): return "/_read chat items \(ref(type, id)) \(joinedIds(itemIds))"
|
||||||
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
|
case let .apiChatUnread(type, id, unreadChat): return "/_unread chat \(ref(type, id)) \(onOff(unreadChat))"
|
||||||
case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
|
case let .receiveFile(fileId, userApprovedRelays, encrypt, inline): return "/freceive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))\(onOffParam("inline", inline))"
|
||||||
case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))"
|
case let .setFileToReceive(fileId, userApprovedRelays, encrypt): return "/_set_file_to_receive \(fileId)\(onOffParam("approved_relays", userApprovedRelays))\(onOffParam("encrypt", encrypt))"
|
||||||
@@ -347,14 +351,14 @@ public enum ChatCommand {
|
|||||||
case .apiGetChats: return "apiGetChats"
|
case .apiGetChats: return "apiGetChats"
|
||||||
case .apiGetChat: return "apiGetChat"
|
case .apiGetChat: return "apiGetChat"
|
||||||
case .apiGetChatItemInfo: return "apiGetChatItemInfo"
|
case .apiGetChatItemInfo: return "apiGetChatItemInfo"
|
||||||
case .apiSendMessage: return "apiSendMessage"
|
case .apiSendMessages: return "apiSendMessages"
|
||||||
case .apiCreateChatItem: return "apiCreateChatItem"
|
case .apiCreateChatItems: return "apiCreateChatItems"
|
||||||
case .apiUpdateChatItem: return "apiUpdateChatItem"
|
case .apiUpdateChatItem: return "apiUpdateChatItem"
|
||||||
case .apiDeleteChatItem: return "apiDeleteChatItem"
|
case .apiDeleteChatItem: return "apiDeleteChatItem"
|
||||||
case .apiConnectContactViaAddress: return "apiConnectContactViaAddress"
|
case .apiConnectContactViaAddress: return "apiConnectContactViaAddress"
|
||||||
case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem"
|
case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem"
|
||||||
case .apiChatItemReaction: return "apiChatItemReaction"
|
case .apiChatItemReaction: return "apiChatItemReaction"
|
||||||
case .apiForwardChatItem: return "apiForwardChatItem"
|
case .apiForwardChatItems: return "apiForwardChatItems"
|
||||||
case .apiGetNtfToken: return "apiGetNtfToken"
|
case .apiGetNtfToken: return "apiGetNtfToken"
|
||||||
case .apiRegisterToken: return "apiRegisterToken"
|
case .apiRegisterToken: return "apiRegisterToken"
|
||||||
case .apiVerifyToken: return "apiVerifyToken"
|
case .apiVerifyToken: return "apiVerifyToken"
|
||||||
@@ -403,6 +407,7 @@ public enum ChatCommand {
|
|||||||
case .apiVerifyGroupMember: return "apiVerifyGroupMember"
|
case .apiVerifyGroupMember: return "apiVerifyGroupMember"
|
||||||
case .apiAddContact: return "apiAddContact"
|
case .apiAddContact: return "apiAddContact"
|
||||||
case .apiSetConnectionIncognito: return "apiSetConnectionIncognito"
|
case .apiSetConnectionIncognito: return "apiSetConnectionIncognito"
|
||||||
|
case .apiChangeConnectionUser: return "apiChangeConnectionUser"
|
||||||
case .apiConnectPlan: return "apiConnectPlan"
|
case .apiConnectPlan: return "apiConnectPlan"
|
||||||
case .apiConnect: return "apiConnect"
|
case .apiConnect: return "apiConnect"
|
||||||
case .apiDeleteChat: return "apiDeleteChat"
|
case .apiDeleteChat: return "apiDeleteChat"
|
||||||
@@ -431,6 +436,7 @@ public enum ChatCommand {
|
|||||||
case .apiCallStatus: return "apiCallStatus"
|
case .apiCallStatus: return "apiCallStatus"
|
||||||
case .apiGetNetworkStatuses: return "apiGetNetworkStatuses"
|
case .apiGetNetworkStatuses: return "apiGetNetworkStatuses"
|
||||||
case .apiChatRead: return "apiChatRead"
|
case .apiChatRead: return "apiChatRead"
|
||||||
|
case .apiChatItemsRead: return "apiChatItemsRead"
|
||||||
case .apiChatUnread: return "apiChatUnread"
|
case .apiChatUnread: return "apiChatUnread"
|
||||||
case .receiveFile: return "receiveFile"
|
case .receiveFile: return "receiveFile"
|
||||||
case .setFileToReceive: return "setFileToReceive"
|
case .setFileToReceive: return "setFileToReceive"
|
||||||
@@ -459,6 +465,10 @@ public enum ChatCommand {
|
|||||||
"\(type.rawValue)\(id)"
|
"\(type.rawValue)\(id)"
|
||||||
}
|
}
|
||||||
|
|
||||||
|
func joinedIds(_ ids: [Int64]) -> String {
|
||||||
|
ids.map { "\($0)" }.joined(separator: ",")
|
||||||
|
}
|
||||||
|
|
||||||
func protoServersStr(_ servers: [ServerCfg]) -> String {
|
func protoServersStr(_ servers: [ServerCfg]) -> String {
|
||||||
encodeJSON(ProtoServersConfig(servers: servers))
|
encodeJSON(ProtoServersConfig(servers: servers))
|
||||||
}
|
}
|
||||||
@@ -537,7 +547,7 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case networkConfig(networkConfig: NetCfg)
|
case networkConfig(networkConfig: NetCfg)
|
||||||
case contactInfo(user: UserRef, contact: Contact, connectionStats_: ConnectionStats?, customUserProfile: Profile?)
|
case contactInfo(user: UserRef, contact: Contact, connectionStats_: ConnectionStats?, customUserProfile: Profile?)
|
||||||
case groupMemberInfo(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?)
|
case groupMemberInfo(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?)
|
||||||
case queueInfo(user: UserRef, rcvMsgInfo: RcvMsgInfo?, queueInfo: QueueInfo)
|
case queueInfo(user: UserRef, rcvMsgInfo: RcvMsgInfo?, queueInfo: ServerQueueInfo)
|
||||||
case contactSwitchStarted(user: UserRef, contact: Contact, connectionStats: ConnectionStats)
|
case contactSwitchStarted(user: UserRef, contact: Contact, connectionStats: ConnectionStats)
|
||||||
case groupMemberSwitchStarted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats)
|
case groupMemberSwitchStarted(user: UserRef, groupInfo: GroupInfo, member: GroupMember, connectionStats: ConnectionStats)
|
||||||
case contactSwitchAborted(user: UserRef, contact: Contact, connectionStats: ConnectionStats)
|
case contactSwitchAborted(user: UserRef, contact: Contact, connectionStats: ConnectionStats)
|
||||||
@@ -555,6 +565,7 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case connectionVerified(user: UserRef, verified: Bool, expectedCode: String)
|
case connectionVerified(user: UserRef, verified: Bool, expectedCode: String)
|
||||||
case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection)
|
case invitation(user: UserRef, connReqInvitation: String, connection: PendingContactConnection)
|
||||||
case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection)
|
case connectionIncognitoUpdated(user: UserRef, toConnection: PendingContactConnection)
|
||||||
|
case connectionUserChanged(user: UserRef, fromConnection: PendingContactConnection, toConnection: PendingContactConnection, newUser: UserRef)
|
||||||
case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan)
|
case connectionPlan(user: UserRef, connectionPlan: ConnectionPlan)
|
||||||
case sentConfirmation(user: UserRef, connection: PendingContactConnection)
|
case sentConfirmation(user: UserRef, connection: PendingContactConnection)
|
||||||
case sentInvitation(user: UserRef, connection: PendingContactConnection)
|
case sentInvitation(user: UserRef, connection: PendingContactConnection)
|
||||||
@@ -588,7 +599,7 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case memberSubErrors(user: UserRef, memberSubErrors: [MemberSubError])
|
case memberSubErrors(user: UserRef, memberSubErrors: [MemberSubError])
|
||||||
case groupEmpty(user: UserRef, groupInfo: GroupInfo)
|
case groupEmpty(user: UserRef, groupInfo: GroupInfo)
|
||||||
case userContactLinkSubscribed
|
case userContactLinkSubscribed
|
||||||
case newChatItem(user: UserRef, chatItem: AChatItem)
|
case newChatItems(user: UserRef, chatItems: [AChatItem])
|
||||||
case chatItemStatusUpdated(user: UserRef, chatItem: AChatItem)
|
case chatItemStatusUpdated(user: UserRef, chatItem: AChatItem)
|
||||||
case chatItemUpdated(user: UserRef, chatItem: AChatItem)
|
case chatItemUpdated(user: UserRef, chatItem: AChatItem)
|
||||||
case chatItemNotChanged(user: UserRef, chatItem: AChatItem)
|
case chatItemNotChanged(user: UserRef, chatItem: AChatItem)
|
||||||
@@ -725,6 +736,7 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case .connectionVerified: return "connectionVerified"
|
case .connectionVerified: return "connectionVerified"
|
||||||
case .invitation: return "invitation"
|
case .invitation: return "invitation"
|
||||||
case .connectionIncognitoUpdated: return "connectionIncognitoUpdated"
|
case .connectionIncognitoUpdated: return "connectionIncognitoUpdated"
|
||||||
|
case .connectionUserChanged: return "connectionUserChanged"
|
||||||
case .connectionPlan: return "connectionPlan"
|
case .connectionPlan: return "connectionPlan"
|
||||||
case .sentConfirmation: return "sentConfirmation"
|
case .sentConfirmation: return "sentConfirmation"
|
||||||
case .sentInvitation: return "sentInvitation"
|
case .sentInvitation: return "sentInvitation"
|
||||||
@@ -758,7 +770,7 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case .memberSubErrors: return "memberSubErrors"
|
case .memberSubErrors: return "memberSubErrors"
|
||||||
case .groupEmpty: return "groupEmpty"
|
case .groupEmpty: return "groupEmpty"
|
||||||
case .userContactLinkSubscribed: return "userContactLinkSubscribed"
|
case .userContactLinkSubscribed: return "userContactLinkSubscribed"
|
||||||
case .newChatItem: return "newChatItem"
|
case .newChatItems: return "newChatItems"
|
||||||
case .chatItemStatusUpdated: return "chatItemStatusUpdated"
|
case .chatItemStatusUpdated: return "chatItemStatusUpdated"
|
||||||
case .chatItemUpdated: return "chatItemUpdated"
|
case .chatItemUpdated: return "chatItemUpdated"
|
||||||
case .chatItemNotChanged: return "chatItemNotChanged"
|
case .chatItemNotChanged: return "chatItemNotChanged"
|
||||||
@@ -893,6 +905,7 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)")
|
case let .connectionVerified(u, verified, expectedCode): return withUser(u, "verified: \(verified)\nconnectionCode: \(expectedCode)")
|
||||||
case let .invitation(u, connReqInvitation, connection): return withUser(u, "connReqInvitation: \(connReqInvitation)\nconnection: \(connection)")
|
case let .invitation(u, connReqInvitation, connection): return withUser(u, "connReqInvitation: \(connReqInvitation)\nconnection: \(connection)")
|
||||||
case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection))
|
case let .connectionIncognitoUpdated(u, toConnection): return withUser(u, String(describing: toConnection))
|
||||||
|
case let .connectionUserChanged(u, fromConnection, toConnection, newUser): return withUser(u, "fromConnection: \(String(describing: fromConnection))\ntoConnection: \(String(describing: toConnection))\newUserId: \(String(describing: newUser.userId))")
|
||||||
case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan))
|
case let .connectionPlan(u, connectionPlan): return withUser(u, String(describing: connectionPlan))
|
||||||
case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection))
|
case let .sentConfirmation(u, connection): return withUser(u, String(describing: connection))
|
||||||
case let .sentInvitation(u, connection): return withUser(u, String(describing: connection))
|
case let .sentInvitation(u, connection): return withUser(u, String(describing: connection))
|
||||||
@@ -926,7 +939,9 @@ public enum ChatResponse: Decodable, Error {
|
|||||||
case let .memberSubErrors(u, memberSubErrors): return withUser(u, String(describing: memberSubErrors))
|
case let .memberSubErrors(u, memberSubErrors): return withUser(u, String(describing: memberSubErrors))
|
||||||
case let .groupEmpty(u, groupInfo): return withUser(u, String(describing: groupInfo))
|
case let .groupEmpty(u, groupInfo): return withUser(u, String(describing: groupInfo))
|
||||||
case .userContactLinkSubscribed: return noDetails
|
case .userContactLinkSubscribed: return noDetails
|
||||||
case let .newChatItem(u, chatItem): return withUser(u, String(describing: chatItem))
|
case let .newChatItems(u, chatItems):
|
||||||
|
let itemsString = chatItems.map { chatItem in String(describing: chatItem) }.joined(separator: "\n")
|
||||||
|
return withUser(u, itemsString)
|
||||||
case let .chatItemStatusUpdated(u, chatItem): return withUser(u, String(describing: chatItem))
|
case let .chatItemStatusUpdated(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||||
case let .chatItemUpdated(u, chatItem): return withUser(u, String(describing: chatItem))
|
case let .chatItemUpdated(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||||
case let .chatItemNotChanged(u, chatItem): return withUser(u, String(describing: chatItem))
|
case let .chatItemNotChanged(u, chatItem): return withUser(u, String(describing: chatItem))
|
||||||
@@ -1094,12 +1109,12 @@ public enum GroupLinkPlan: Decodable, Hashable {
|
|||||||
case known(groupInfo: GroupInfo)
|
case known(groupInfo: GroupInfo)
|
||||||
}
|
}
|
||||||
|
|
||||||
struct NewUser: Encodable, Hashable {
|
struct NewUser: Encodable {
|
||||||
var profile: Profile?
|
var profile: Profile?
|
||||||
var pastTimestamp: Bool
|
var pastTimestamp: Bool
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum ChatPagination: Hashable {
|
public enum ChatPagination {
|
||||||
case last(count: Int)
|
case last(count: Int)
|
||||||
case after(chatItemId: Int64, count: Int)
|
case after(chatItemId: Int64, count: Int)
|
||||||
case before(chatItemId: Int64, count: Int)
|
case before(chatItemId: Int64, count: Int)
|
||||||
@@ -1113,10 +1128,16 @@ public enum ChatPagination: Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
struct ComposedMessage: Encodable {
|
public struct ComposedMessage: Encodable {
|
||||||
var fileSource: CryptoFile?
|
public var fileSource: CryptoFile?
|
||||||
var quotedItemId: Int64?
|
var quotedItemId: Int64?
|
||||||
var msgContent: MsgContent
|
var msgContent: MsgContent
|
||||||
|
|
||||||
|
public init(fileSource: CryptoFile? = nil, quotedItemId: Int64? = nil, msgContent: MsgContent) {
|
||||||
|
self.fileSource = fileSource
|
||||||
|
self.quotedItemId = quotedItemId
|
||||||
|
self.msgContent = msgContent
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ArchiveConfig: Encodable {
|
public struct ArchiveConfig: Encodable {
|
||||||
@@ -1315,7 +1336,7 @@ public struct ServerAddress: Decodable {
|
|||||||
)
|
)
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct NetCfg: Codable, Equatable, Hashable {
|
public struct NetCfg: Codable, Equatable {
|
||||||
public var socksProxy: String? = nil
|
public var socksProxy: String? = nil
|
||||||
var socksMode: SocksMode = .always
|
var socksMode: SocksMode = .always
|
||||||
public var hostMode: HostMode = .publicHost
|
public var hostMode: HostMode = .publicHost
|
||||||
@@ -1369,18 +1390,18 @@ public struct NetCfg: Codable, Equatable, Hashable {
|
|||||||
public var enableKeepAlive: Bool { tcpKeepAlive != nil }
|
public var enableKeepAlive: Bool { tcpKeepAlive != nil }
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum HostMode: String, Codable, Hashable {
|
public enum HostMode: String, Codable {
|
||||||
case onionViaSocks
|
case onionViaSocks
|
||||||
case onionHost = "onion"
|
case onionHost = "onion"
|
||||||
case publicHost = "public"
|
case publicHost = "public"
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SocksMode: String, Codable, Hashable {
|
public enum SocksMode: String, Codable {
|
||||||
case always = "always"
|
case always = "always"
|
||||||
case onion = "onion"
|
case onion = "onion"
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SMPProxyMode: String, Codable, Hashable, SelectableItem {
|
public enum SMPProxyMode: String, Codable, SelectableItem {
|
||||||
case always = "always"
|
case always = "always"
|
||||||
case unknown = "unknown"
|
case unknown = "unknown"
|
||||||
case unprotected = "unprotected"
|
case unprotected = "unprotected"
|
||||||
@@ -1400,7 +1421,7 @@ public enum SMPProxyMode: String, Codable, Hashable, SelectableItem {
|
|||||||
public static let values: [SMPProxyMode] = [.always, .unknown, .unprotected, .never]
|
public static let values: [SMPProxyMode] = [.always, .unknown, .unprotected, .never]
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum SMPProxyFallback: String, Codable, Hashable, SelectableItem {
|
public enum SMPProxyFallback: String, Codable, SelectableItem {
|
||||||
case allow = "allow"
|
case allow = "allow"
|
||||||
case allowProtected = "allowProtected"
|
case allowProtected = "allowProtected"
|
||||||
case prohibit = "prohibit"
|
case prohibit = "prohibit"
|
||||||
@@ -1418,7 +1439,7 @@ public enum SMPProxyFallback: String, Codable, Hashable, SelectableItem {
|
|||||||
public static let values: [SMPProxyFallback] = [.allow, .allowProtected, .prohibit]
|
public static let values: [SMPProxyFallback] = [.allow, .allowProtected, .prohibit]
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum OnionHosts: String, Identifiable, Hashable {
|
public enum OnionHosts: String, Identifiable {
|
||||||
case no
|
case no
|
||||||
case prefer
|
case prefer
|
||||||
case require
|
case require
|
||||||
@@ -1452,7 +1473,7 @@ public enum OnionHosts: String, Identifiable, Hashable {
|
|||||||
public static let values: [OnionHosts] = [.no, .prefer, .require]
|
public static let values: [OnionHosts] = [.no, .prefer, .require]
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum TransportSessionMode: String, Codable, Identifiable, Hashable {
|
public enum TransportSessionMode: String, Codable, Identifiable {
|
||||||
case user
|
case user
|
||||||
case entity
|
case entity
|
||||||
|
|
||||||
@@ -1468,7 +1489,7 @@ public enum TransportSessionMode: String, Codable, Identifiable, Hashable {
|
|||||||
public static let values: [TransportSessionMode] = [.user, .entity]
|
public static let values: [TransportSessionMode] = [.user, .entity]
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct KeepAliveOpts: Codable, Equatable, Hashable {
|
public struct KeepAliveOpts: Codable, Equatable {
|
||||||
public var keepIdle: Int // seconds
|
public var keepIdle: Int // seconds
|
||||||
public var keepIntvl: Int // seconds
|
public var keepIntvl: Int // seconds
|
||||||
public var keepCnt: Int // times
|
public var keepCnt: Int // times
|
||||||
@@ -1476,7 +1497,7 @@ public struct KeepAliveOpts: Codable, Equatable, Hashable {
|
|||||||
public static let defaults: KeepAliveOpts = KeepAliveOpts(keepIdle: 30, keepIntvl: 15, keepCnt: 4)
|
public static let defaults: KeepAliveOpts = KeepAliveOpts(keepIdle: 30, keepIntvl: 15, keepCnt: 4)
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum NetworkStatus: Decodable, Equatable, Hashable {
|
public enum NetworkStatus: Decodable, Equatable {
|
||||||
case unknown
|
case unknown
|
||||||
case connected
|
case connected
|
||||||
case disconnected
|
case disconnected
|
||||||
@@ -1514,7 +1535,7 @@ public enum NetworkStatus: Decodable, Equatable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct ConnNetworkStatus: Decodable, Hashable {
|
public struct ConnNetworkStatus: Decodable {
|
||||||
public var agentConnId: String
|
public var agentConnId: String
|
||||||
public var networkStatus: NetworkStatus
|
public var networkStatus: NetworkStatus
|
||||||
}
|
}
|
||||||
@@ -1539,7 +1560,7 @@ public enum MsgFilter: String, Codable, Hashable {
|
|||||||
case mentions
|
case mentions
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UserMsgReceiptSettings: Codable, Hashable {
|
public struct UserMsgReceiptSettings: Codable {
|
||||||
public var enable: Bool
|
public var enable: Bool
|
||||||
public var clearOverrides: Bool
|
public var clearOverrides: Bool
|
||||||
|
|
||||||
@@ -1588,7 +1609,7 @@ public enum SndSwitchStatus: String, Codable, Hashable {
|
|||||||
case sendingQTEST = "sending_qtest"
|
case sendingQTEST = "sending_qtest"
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum QueueDirection: String, Decodable, Hashable {
|
public enum QueueDirection: String, Decodable {
|
||||||
case rcv
|
case rcv
|
||||||
case snd
|
case snd
|
||||||
}
|
}
|
||||||
@@ -1643,12 +1664,12 @@ public struct AutoAccept: Codable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public protocol SelectableItem: Hashable, Identifiable {
|
public protocol SelectableItem: Identifiable, Equatable {
|
||||||
var label: LocalizedStringKey { get }
|
var label: LocalizedStringKey { get }
|
||||||
static var values: [Self] { get }
|
static var values: [Self] { get }
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct DeviceToken: Decodable, Hashable {
|
public struct DeviceToken: Decodable {
|
||||||
var pushProvider: PushProvider
|
var pushProvider: PushProvider
|
||||||
var token: String
|
var token: String
|
||||||
|
|
||||||
@@ -1662,12 +1683,12 @@ public struct DeviceToken: Decodable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PushEnvironment: String, Hashable {
|
public enum PushEnvironment: String {
|
||||||
case development
|
case development
|
||||||
case production
|
case production
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum PushProvider: String, Decodable, Hashable {
|
public enum PushProvider: String, Decodable {
|
||||||
case apns_dev
|
case apns_dev
|
||||||
case apns_prod
|
case apns_prod
|
||||||
|
|
||||||
@@ -1681,7 +1702,7 @@ public enum PushProvider: String, Decodable, Hashable {
|
|||||||
|
|
||||||
// This notification mode is for app core, UI uses AppNotificationsMode.off to mean completely disable,
|
// This notification mode is for app core, UI uses AppNotificationsMode.off to mean completely disable,
|
||||||
// and .local for periodic background checks
|
// and .local for periodic background checks
|
||||||
public enum NotificationsMode: String, Decodable, SelectableItem, Hashable {
|
public enum NotificationsMode: String, Decodable, SelectableItem {
|
||||||
case off = "OFF"
|
case off = "OFF"
|
||||||
case periodic = "PERIODIC"
|
case periodic = "PERIODIC"
|
||||||
case instant = "INSTANT"
|
case instant = "INSTANT"
|
||||||
@@ -1699,7 +1720,7 @@ public enum NotificationsMode: String, Decodable, SelectableItem, Hashable {
|
|||||||
public static var values: [NotificationsMode] = [.instant, .periodic, .off]
|
public static var values: [NotificationsMode] = [.instant, .periodic, .off]
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum NotificationPreviewMode: String, SelectableItem, Codable, Hashable {
|
public enum NotificationPreviewMode: String, SelectableItem, Codable {
|
||||||
case hidden
|
case hidden
|
||||||
case contact
|
case contact
|
||||||
case message
|
case message
|
||||||
@@ -1717,7 +1738,7 @@ public enum NotificationPreviewMode: String, SelectableItem, Codable, Hashable {
|
|||||||
public static var values: [NotificationPreviewMode] = [.message, .contact, .hidden]
|
public static var values: [NotificationPreviewMode] = [.message, .contact, .hidden]
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct RemoteCtrlInfo: Decodable, Hashable {
|
public struct RemoteCtrlInfo: Decodable {
|
||||||
public var remoteCtrlId: Int64
|
public var remoteCtrlId: Int64
|
||||||
public var ctrlDeviceName: String
|
public var ctrlDeviceName: String
|
||||||
public var sessionState: RemoteCtrlSessionState?
|
public var sessionState: RemoteCtrlSessionState?
|
||||||
@@ -1727,7 +1748,7 @@ public struct RemoteCtrlInfo: Decodable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum RemoteCtrlSessionState: Decodable, Hashable {
|
public enum RemoteCtrlSessionState: Decodable {
|
||||||
case starting
|
case starting
|
||||||
case searching
|
case searching
|
||||||
case connecting
|
case connecting
|
||||||
@@ -1742,17 +1763,17 @@ public enum RemoteCtrlStopReason: Decodable {
|
|||||||
case disconnected
|
case disconnected
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CtrlAppInfo: Decodable, Hashable {
|
public struct CtrlAppInfo: Decodable {
|
||||||
public var appVersionRange: AppVersionRange
|
public var appVersionRange: AppVersionRange
|
||||||
public var deviceName: String
|
public var deviceName: String
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct AppVersionRange: Decodable, Hashable {
|
public struct AppVersionRange: Decodable {
|
||||||
public var minVersion: String
|
public var minVersion: String
|
||||||
public var maxVersion: String
|
public var maxVersion: String
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CoreVersionInfo: Decodable, Hashable {
|
public struct CoreVersionInfo: Decodable {
|
||||||
public var version: String
|
public var version: String
|
||||||
public var simplexmqVersion: String
|
public var simplexmqVersion: String
|
||||||
public var simplexmqCommit: String
|
public var simplexmqCommit: String
|
||||||
@@ -1842,7 +1863,6 @@ public enum ChatErrorType: Decodable, Hashable {
|
|||||||
case inlineFileProhibited(fileId: Int64)
|
case inlineFileProhibited(fileId: Int64)
|
||||||
case invalidQuote
|
case invalidQuote
|
||||||
case invalidForward
|
case invalidForward
|
||||||
case forwardNoFile
|
|
||||||
case invalidChatItemUpdate
|
case invalidChatItemUpdate
|
||||||
case invalidChatItemDelete
|
case invalidChatItemDelete
|
||||||
case hasCurrentCall
|
case hasCurrentCall
|
||||||
@@ -1857,6 +1877,7 @@ public enum ChatErrorType: Decodable, Hashable {
|
|||||||
case agentCommandError(message: String)
|
case agentCommandError(message: String)
|
||||||
case invalidFileDescription(message: String)
|
case invalidFileDescription(message: String)
|
||||||
case connectionIncognitoChangeProhibited
|
case connectionIncognitoChangeProhibited
|
||||||
|
case connectionUserChangeProhibited
|
||||||
case peerChatVRangeIncompatible
|
case peerChatVRangeIncompatible
|
||||||
case internalError(message: String)
|
case internalError(message: String)
|
||||||
case exception(message: String)
|
case exception(message: String)
|
||||||
@@ -2090,14 +2111,14 @@ public enum RemoteCtrlError: Decodable, Hashable {
|
|||||||
case protocolError
|
case protocolError
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct MigrationFileLinkData: Codable, Hashable {
|
public struct MigrationFileLinkData: Codable {
|
||||||
let networkConfig: NetworkConfig?
|
let networkConfig: NetworkConfig?
|
||||||
|
|
||||||
public init(networkConfig: NetworkConfig) {
|
public init(networkConfig: NetworkConfig) {
|
||||||
self.networkConfig = networkConfig
|
self.networkConfig = networkConfig
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct NetworkConfig: Codable, Hashable {
|
public struct NetworkConfig: Codable {
|
||||||
let socksProxy: String?
|
let socksProxy: String?
|
||||||
let hostMode: HostMode?
|
let hostMode: HostMode?
|
||||||
let requiredHostMode: Bool?
|
let requiredHostMode: Bool?
|
||||||
@@ -2129,7 +2150,7 @@ public struct MigrationFileLinkData: Codable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct AppSettings: Codable, Equatable, Hashable {
|
public struct AppSettings: Codable, Equatable {
|
||||||
public var networkConfig: NetCfg? = nil
|
public var networkConfig: NetCfg? = nil
|
||||||
public var privacyEncryptLocalFiles: Bool? = nil
|
public var privacyEncryptLocalFiles: Bool? = nil
|
||||||
public var privacyAskToApproveRelays: Bool? = nil
|
public var privacyAskToApproveRelays: Bool? = nil
|
||||||
@@ -2224,7 +2245,7 @@ public struct AppSettings: Codable, Equatable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum AppSettingsNotificationMode: String, Codable, Hashable {
|
public enum AppSettingsNotificationMode: String, Codable {
|
||||||
case off
|
case off
|
||||||
case periodic
|
case periodic
|
||||||
case instant
|
case instant
|
||||||
@@ -2252,13 +2273,13 @@ public enum AppSettingsNotificationMode: String, Codable, Hashable {
|
|||||||
// case message
|
// case message
|
||||||
//}
|
//}
|
||||||
|
|
||||||
public enum AppSettingsLockScreenCalls: String, Codable, Hashable {
|
public enum AppSettingsLockScreenCalls: String, Codable {
|
||||||
case disable
|
case disable
|
||||||
case show
|
case show
|
||||||
case accept
|
case accept
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct UserNetworkInfo: Codable, Equatable, Hashable {
|
public struct UserNetworkInfo: Codable, Equatable {
|
||||||
public let networkType: UserNetworkType
|
public let networkType: UserNetworkType
|
||||||
public let online: Bool
|
public let online: Bool
|
||||||
|
|
||||||
@@ -2268,7 +2289,7 @@ public struct UserNetworkInfo: Codable, Equatable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum UserNetworkType: String, Codable, Hashable {
|
public enum UserNetworkType: String, Codable {
|
||||||
case none
|
case none
|
||||||
case cellular
|
case cellular
|
||||||
case wifi
|
case wifi
|
||||||
@@ -2286,7 +2307,7 @@ public enum UserNetworkType: String, Codable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct RcvMsgInfo: Codable, Hashable {
|
public struct RcvMsgInfo: Codable {
|
||||||
var msgId: Int64
|
var msgId: Int64
|
||||||
var msgDeliveryId: Int64
|
var msgDeliveryId: Int64
|
||||||
var msgDeliveryStatus: String
|
var msgDeliveryStatus: String
|
||||||
@@ -2294,7 +2315,16 @@ public struct RcvMsgInfo: Codable, Hashable {
|
|||||||
var agentMsgMeta: String
|
var agentMsgMeta: String
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct QueueInfo: Codable, Hashable {
|
public struct ServerQueueInfo: Codable {
|
||||||
|
var server: String
|
||||||
|
var rcvId: String
|
||||||
|
var sndId: String
|
||||||
|
var ntfId: String?
|
||||||
|
var status: String
|
||||||
|
var info: QueueInfo
|
||||||
|
}
|
||||||
|
|
||||||
|
public struct QueueInfo: Codable {
|
||||||
var qiSnd: Bool
|
var qiSnd: Bool
|
||||||
var qiNtf: Bool
|
var qiNtf: Bool
|
||||||
var qiSub: QSub?
|
var qiSub: QSub?
|
||||||
@@ -2302,25 +2332,25 @@ public struct QueueInfo: Codable, Hashable {
|
|||||||
var qiMsg: MsgInfo?
|
var qiMsg: MsgInfo?
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct QSub: Codable, Hashable {
|
public struct QSub: Codable {
|
||||||
var qSubThread: QSubThread
|
var qSubThread: QSubThread
|
||||||
var qDelivered: String?
|
var qDelivered: String?
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum QSubThread: String, Codable, Hashable {
|
public enum QSubThread: String, Codable {
|
||||||
case noSub
|
case noSub
|
||||||
case subPending
|
case subPending
|
||||||
case subThread
|
case subThread
|
||||||
case prohibitSub
|
case prohibitSub
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct MsgInfo: Codable, Hashable {
|
public struct MsgInfo: Codable {
|
||||||
var msgId: String
|
var msgId: String
|
||||||
var msgTs: Date
|
var msgTs: Date
|
||||||
var msgType: MsgType
|
var msgType: MsgType
|
||||||
}
|
}
|
||||||
|
|
||||||
public enum MsgType: String, Codable, Hashable {
|
public enum MsgType: String, Codable {
|
||||||
case message
|
case message
|
||||||
case quota
|
case quota
|
||||||
}
|
}
|
||||||
|
|||||||
@@ -42,6 +42,7 @@ public struct RcvCallInvitation: Decodable {
|
|||||||
public var contact: Contact
|
public var contact: Contact
|
||||||
public var callType: CallType
|
public var callType: CallType
|
||||||
public var sharedKey: String?
|
public var sharedKey: String?
|
||||||
|
public var callUUID: String?
|
||||||
public var callTs: Date
|
public var callTs: Date
|
||||||
public var callTypeText: LocalizedStringKey {
|
public var callTypeText: LocalizedStringKey {
|
||||||
get {
|
get {
|
||||||
@@ -52,10 +53,8 @@ public struct RcvCallInvitation: Decodable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public var callkitUUID: UUID? = UUID()
|
|
||||||
|
|
||||||
private enum CodingKeys: String, CodingKey {
|
private enum CodingKeys: String, CodingKey {
|
||||||
case user, contact, callType, sharedKey, callTs
|
case user, contact, callType, sharedKey, callUUID, callTs
|
||||||
}
|
}
|
||||||
|
|
||||||
public static let sampleData = RcvCallInvitation(
|
public static let sampleData = RcvCallInvitation(
|
||||||
|
|||||||
@@ -1500,6 +1500,12 @@ public struct ChatData: Decodable, Identifiable, Hashable, ChatLike {
|
|||||||
|
|
||||||
public var id: ChatId { get { chatInfo.id } }
|
public var id: ChatId { get { chatInfo.id } }
|
||||||
|
|
||||||
|
public init(chatInfo: ChatInfo, chatItems: [ChatItem], chatStats: ChatStats = ChatStats()) {
|
||||||
|
self.chatInfo = chatInfo
|
||||||
|
self.chatItems = chatItems
|
||||||
|
self.chatStats = chatStats
|
||||||
|
}
|
||||||
|
|
||||||
public static func invalidJSON(_ json: String) -> ChatData {
|
public static func invalidJSON(_ json: String) -> ChatData {
|
||||||
ChatData(
|
ChatData(
|
||||||
chatInfo: .invalidJSON(json: json),
|
chatInfo: .invalidJSON(json: json),
|
||||||
@@ -2682,6 +2688,13 @@ public enum CIDirection: Decodable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
public func sameDirection(_ dir: CIDirection) -> Bool {
|
||||||
|
switch (self, dir) {
|
||||||
|
case let (.groupRcv(m1), .groupRcv(m2)): m1.groupMemberId == m2.groupMemberId
|
||||||
|
default: sent == dir.sent
|
||||||
|
}
|
||||||
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public struct CIMeta: Decodable, Hashable {
|
public struct CIMeta: Decodable, Hashable {
|
||||||
@@ -2700,7 +2713,7 @@ public struct CIMeta: Decodable, Hashable {
|
|||||||
public var deletable: Bool
|
public var deletable: Bool
|
||||||
public var editable: Bool
|
public var editable: Bool
|
||||||
|
|
||||||
public var timestampText: Text { get { formatTimestampText(itemTs) } }
|
public var timestampText: Text { Text(formatTimestampMeta(itemTs)) }
|
||||||
public var recent: Bool { updatedAt + 10 > .now }
|
public var recent: Bool { updatedAt + 10 > .now }
|
||||||
public var isLive: Bool { itemLive == true }
|
public var isLive: Bool { itemLive == true }
|
||||||
public var disappearing: Bool { !isRcvNew && itemTimed?.deleteAt != nil }
|
public var disappearing: Bool { !isRcvNew && itemTimed?.deleteAt != nil }
|
||||||
@@ -2710,10 +2723,6 @@ public struct CIMeta: Decodable, Hashable {
|
|||||||
return false
|
return false
|
||||||
}
|
}
|
||||||
|
|
||||||
public func statusIcon(_ metaColor: Color/* = .secondary*/, _ primaryColor: Color = .accentColor) -> (String, Color)? {
|
|
||||||
itemStatus.statusIcon(metaColor, primaryColor)
|
|
||||||
}
|
|
||||||
|
|
||||||
public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> CIMeta {
|
public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> CIMeta {
|
||||||
CIMeta(
|
CIMeta(
|
||||||
itemId: id,
|
itemId: id,
|
||||||
@@ -2756,7 +2765,11 @@ let msgTimeFormat = Date.FormatStyle.dateTime.hour().minute()
|
|||||||
let msgDateFormat = Date.FormatStyle.dateTime.day(.twoDigits).month(.twoDigits)
|
let msgDateFormat = Date.FormatStyle.dateTime.day(.twoDigits).month(.twoDigits)
|
||||||
|
|
||||||
public func formatTimestampText(_ date: Date) -> Text {
|
public func formatTimestampText(_ date: Date) -> Text {
|
||||||
return Text(date, format: recent(date) ? msgTimeFormat : msgDateFormat)
|
Text(verbatim: date.formatted(recent(date) ? msgTimeFormat : msgDateFormat))
|
||||||
|
}
|
||||||
|
|
||||||
|
public func formatTimestampMeta(_ date: Date) -> String {
|
||||||
|
date.formatted(date: .omitted, time: .shortened)
|
||||||
}
|
}
|
||||||
|
|
||||||
private func recent(_ date: Date) -> Bool {
|
private func recent(_ date: Date) -> Bool {
|
||||||
@@ -2799,21 +2812,22 @@ public enum CIStatus: Decodable, Hashable {
|
|||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
public func statusIcon(_ metaColor: Color/* = .secondary*/, _ primaryColor: Color = .accentColor) -> (String, Color)? {
|
public func statusIcon(_ metaColor: Color, _ paleMetaColor: Color, _ primaryColor: Color = .accentColor) -> (Image, Color)? {
|
||||||
switch self {
|
switch self {
|
||||||
case .sndNew: return nil
|
case .sndNew: nil
|
||||||
case .sndSent: return ("checkmark", metaColor)
|
case let .sndSent(sndProgress):
|
||||||
case let .sndRcvd(msgRcptStatus, _):
|
(Image("checkmark.wide"), sndProgress == .partial ? paleMetaColor : metaColor)
|
||||||
|
case let .sndRcvd(msgRcptStatus, sndProgress):
|
||||||
switch msgRcptStatus {
|
switch msgRcptStatus {
|
||||||
case .ok: return ("checkmark", metaColor)
|
case .ok: (Image("checkmark.2"), sndProgress == .partial ? paleMetaColor : metaColor)
|
||||||
case .badMsgHash: return ("checkmark", .red)
|
case .badMsgHash: (Image("checkmark.2"), .red)
|
||||||
}
|
}
|
||||||
case .sndErrorAuth: return ("multiply", .red)
|
case .sndErrorAuth: (Image(systemName: "multiply"), .red)
|
||||||
case .sndError: return ("multiply", .red)
|
case .sndError: (Image(systemName: "multiply"), .red)
|
||||||
case .sndWarning: return ("exclamationmark.triangle.fill", .orange)
|
case .sndWarning: (Image(systemName: "exclamationmark.triangle.fill"), .orange)
|
||||||
case .rcvNew: return ("circlebadge.fill", primaryColor)
|
case .rcvNew: (Image(systemName: "circlebadge.fill"), primaryColor)
|
||||||
case .rcvRead: return nil
|
case .rcvRead: nil
|
||||||
case .invalid: return ("questionmark", metaColor)
|
case .invalid: (Image(systemName: "questionmark"), metaColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
@@ -2908,20 +2922,20 @@ public enum GroupSndStatus: Decodable, Hashable {
|
|||||||
case warning(agentError: SndError)
|
case warning(agentError: SndError)
|
||||||
case invalid(text: String)
|
case invalid(text: String)
|
||||||
|
|
||||||
public func statusIcon(_ metaColor: Color/* = .secondary*/, _ primaryColor: Color = .accentColor) -> (String, Color) {
|
public func statusIcon(_ metaColor: Color, _ primaryColor: Color = .accentColor) -> (Image, Color) {
|
||||||
switch self {
|
switch self {
|
||||||
case .new: return ("ellipsis", metaColor)
|
case .new: (Image(systemName: "ellipsis"), metaColor)
|
||||||
case .forwarded: return ("chevron.forward.2", metaColor)
|
case .forwarded: (Image(systemName: "chevron.forward.2"), metaColor)
|
||||||
case .inactive: return ("person.badge.minus", metaColor)
|
case .inactive: (Image(systemName: "person.badge.minus"), metaColor)
|
||||||
case .sent: return ("checkmark", metaColor)
|
case .sent: (Image("checkmark.wide"), metaColor)
|
||||||
case let .rcvd(msgRcptStatus):
|
case let .rcvd(msgRcptStatus):
|
||||||
switch msgRcptStatus {
|
switch msgRcptStatus {
|
||||||
case .ok: return ("checkmark", metaColor)
|
case .ok: (Image("checkmark.2"), metaColor)
|
||||||
case .badMsgHash: return ("checkmark", .red)
|
case .badMsgHash: (Image("checkmark.2"), .red)
|
||||||
}
|
}
|
||||||
case .error: return ("multiply", .red)
|
case .error: (Image(systemName: "multiply"), .red)
|
||||||
case .warning: return ("exclamationmark.triangle.fill", .orange)
|
case .warning: (Image(systemName: "exclamationmark.triangle.fill"), .orange)
|
||||||
case .invalid: return ("questionmark", metaColor)
|
case .invalid: (Image(systemName: "questionmark"), metaColor)
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|||||||
@@ -383,16 +383,34 @@ extension UIImage {
|
|||||||
}
|
}
|
||||||
return self
|
return self
|
||||||
}
|
}
|
||||||
|
}
|
||||||
|
|
||||||
public convenience init?(base64Encoded: String?) {
|
public func imageFromBase64(_ base64Encoded: String?) -> UIImage? {
|
||||||
if let base64Encoded, let data = Data(base64Encoded: dropImagePrefix(base64Encoded)) {
|
if let base64Encoded {
|
||||||
self.init(data: data)
|
if let img = imageCache.object(forKey: base64Encoded as NSString) {
|
||||||
|
return img
|
||||||
|
} else if let data = Data(base64Encoded: dropImagePrefix(base64Encoded)),
|
||||||
|
let img = UIImage(data: data) {
|
||||||
|
imageCacheQueue.async {
|
||||||
|
imageCache.setObject(img, forKey: base64Encoded as NSString)
|
||||||
|
}
|
||||||
|
return img
|
||||||
} else {
|
} else {
|
||||||
return nil
|
return nil
|
||||||
}
|
}
|
||||||
|
} else {
|
||||||
|
return nil
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
private let imageCacheQueue = DispatchQueue.global(qos: .background)
|
||||||
|
|
||||||
|
private var imageCache: NSCache<NSString, UIImage> = {
|
||||||
|
var cache = NSCache<NSString, UIImage>()
|
||||||
|
cache.countLimit = 1000
|
||||||
|
return cache
|
||||||
|
}()
|
||||||
|
|
||||||
public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) {
|
public func getLinkPreview(url: URL, cb: @escaping (LinkPreview?) -> Void) {
|
||||||
logger.debug("getLinkMetadata: fetching URL preview")
|
logger.debug("getLinkMetadata: fetching URL preview")
|
||||||
LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in
|
LPMetadataProvider().startFetchingMetadata(for: url){ metadata, error in
|
||||||
|
|||||||
@@ -10,6 +10,7 @@
|
|||||||
#define SimpleX_h
|
#define SimpleX_h
|
||||||
|
|
||||||
#include "hs_init.h"
|
#include "hs_init.h"
|
||||||
|
#include "objc.h"
|
||||||
|
|
||||||
extern void hs_init(int argc, char **argv[]);
|
extern void hs_init(int argc, char **argv[]);
|
||||||
|
|
||||||
|
|||||||
@@ -0,0 +1,20 @@
|
|||||||
|
//
|
||||||
|
// objc.h
|
||||||
|
// SimpleX (iOS)
|
||||||
|
//
|
||||||
|
// Created by Stanislav Dmitrenko on 09.09.2024.
|
||||||
|
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#ifndef objc_h
|
||||||
|
#define objc_h
|
||||||
|
|
||||||
|
#import <Foundation/Foundation.h>
|
||||||
|
|
||||||
|
@interface ObjC : NSObject
|
||||||
|
|
||||||
|
+ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error;
|
||||||
|
|
||||||
|
@end
|
||||||
|
|
||||||
|
#endif /* objc_h */
|
||||||
@@ -0,0 +1,25 @@
|
|||||||
|
//
|
||||||
|
// objc.m
|
||||||
|
// SimpleXChat
|
||||||
|
//
|
||||||
|
// Created by Stanislav Dmitrenko on 09.09.2024.
|
||||||
|
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||||
|
//
|
||||||
|
|
||||||
|
#import "objc.h"
|
||||||
|
|
||||||
|
@implementation ObjC
|
||||||
|
|
||||||
|
// https://stackoverflow.com/a/36454808
|
||||||
|
+ (BOOL)catchException:(void(^)(void))tryBlock error:(__autoreleasing NSError **)error {
|
||||||
|
@try {
|
||||||
|
tryBlock();
|
||||||
|
return YES;
|
||||||
|
}
|
||||||
|
@catch (NSException *exception) {
|
||||||
|
*error = [[NSError alloc] initWithDomain: exception.name code: 0 userInfo: exception.userInfo];
|
||||||
|
return NO;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
@end
|
||||||
@@ -506,7 +506,7 @@
|
|||||||
"Allow to send files and media." = "Se permite enviar archivos y multimedia.";
|
"Allow to send files and media." = "Se permite enviar archivos y multimedia.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Allow to send SimpleX links." = "Permitir enviar enlaces SimpleX.";
|
"Allow to send SimpleX links." = "Se permite enviar enlaces SimpleX.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Allow to send voice messages." = "Permites enviar mensajes de voz.";
|
"Allow to send voice messages." = "Permites enviar mensajes de voz.";
|
||||||
@@ -1606,7 +1606,7 @@
|
|||||||
"Do it later" = "Hacer más tarde";
|
"Do it later" = "Hacer más tarde";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Do not send history to new members." = "No enviar historial a miembros nuevos.";
|
"Do not send history to new members." = "No se envía el historial a los miembros nuevos.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Do NOT send messages directly, even if your or destination server does not support private routing." = "NO enviar mensajes directamente incluso si tu servidor o el de destino no soportan enrutamiento privado.";
|
"Do NOT send messages directly, even if your or destination server does not support private routing." = "NO enviar mensajes directamente incluso si tu servidor o el de destino no soportan enrutamiento privado.";
|
||||||
@@ -3415,7 +3415,7 @@
|
|||||||
"Prohibit sending files and media." = "No permitir el envío de archivos y multimedia.";
|
"Prohibit sending files and media." = "No permitir el envío de archivos y multimedia.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Prohibit sending SimpleX links." = "No permitir el envío de enlaces SimpleX.";
|
"Prohibit sending SimpleX links." = "No se permite enviar enlaces SimpleX.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Prohibit sending voice messages." = "No se permiten mensajes de voz.";
|
"Prohibit sending voice messages." = "No se permiten mensajes de voz.";
|
||||||
@@ -3917,7 +3917,7 @@
|
|||||||
"Send them from gallery or custom keyboards." = "Envíalos desde la galería o desde teclados personalizados.";
|
"Send them from gallery or custom keyboards." = "Envíalos desde la galería o desde teclados personalizados.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Send up to 100 last messages to new members." = "Enviar hasta 100 últimos mensajes a los miembros nuevos.";
|
"Send up to 100 last messages to new members." = "Se envían hasta 100 mensajes más recientes a los miembros nuevos.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Sender cancelled file transfer." = "El remitente ha cancelado la transferencia de archivos.";
|
"Sender cancelled file transfer." = "El remitente ha cancelado la transferencia de archivos.";
|
||||||
@@ -4979,7 +4979,7 @@
|
|||||||
"You can accept calls from lock screen, without device and app authentication." = "Puede aceptar llamadas desde la pantalla de bloqueo, sin autenticación de dispositivos y aplicaciones.";
|
"You can accept calls from lock screen, without device and app authentication." = "Puede aceptar llamadas desde la pantalla de bloqueo, sin autenticación de dispositivos y aplicaciones.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"You can change it in Appearance settings." = "Puede cambiarlo desde el menú Apariencia.";
|
"You can change it in Appearance settings." = "Puedes cambiar la posición de la barra desde el menú Apariencia.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"You can create it later" = "Puedes crearla más tarde";
|
"You can create it later" = "Puedes crearla más tarde";
|
||||||
|
|||||||
@@ -659,10 +659,10 @@
|
|||||||
"Bad desktop address" = "Hibás számítógép cím";
|
"Bad desktop address" = "Hibás számítógép cím";
|
||||||
|
|
||||||
/* integrity error chat item */
|
/* integrity error chat item */
|
||||||
"bad message hash" = "téves üzenet hash";
|
"bad message hash" = "hibás az üzenet ellenőrzőösszege";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Bad message hash" = "Téves üzenet hash";
|
"Bad message hash" = "Hibás az üzenet ellenőrzőösszege";
|
||||||
|
|
||||||
/* integrity error chat item */
|
/* integrity error chat item */
|
||||||
"bad message ID" = "téves üzenet ID";
|
"bad message ID" = "téves üzenet ID";
|
||||||
@@ -1432,7 +1432,7 @@
|
|||||||
"Delete link?" = "Hivatkozás törlése?";
|
"Delete link?" = "Hivatkozás törlése?";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Delete member message?" = "Csoporttag üzenet törlése?";
|
"Delete member message?" = "Csoporttag üzenetének törlése?";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Delete message?" = "Üzenet törlése?";
|
"Delete message?" = "Üzenet törlése?";
|
||||||
@@ -1495,7 +1495,7 @@
|
|||||||
"Delivery receipts are disabled!" = "A kézbesítési jelentések ki vannak kapcsolva!";
|
"Delivery receipts are disabled!" = "A kézbesítési jelentések ki vannak kapcsolva!";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Delivery receipts!" = "Kézbesítési igazolások!";
|
"Delivery receipts!" = "Üzenet kézbesítési jelentések!";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Description" = "Leírás";
|
"Description" = "Leírás";
|
||||||
@@ -1828,10 +1828,10 @@
|
|||||||
"Enter this device name…" = "Eszköznév megadása…";
|
"Enter this device name…" = "Eszköznév megadása…";
|
||||||
|
|
||||||
/* placeholder */
|
/* placeholder */
|
||||||
"Enter welcome message…" = "Üdvözlő üzenetet megadása…";
|
"Enter welcome message…" = "Üdvözlő üzenet megadása…";
|
||||||
|
|
||||||
/* placeholder */
|
/* placeholder */
|
||||||
"Enter welcome message… (optional)" = "Üdvözlő üzenetet megadása… (opcionális)";
|
"Enter welcome message… (optional)" = "Üdvözlő üzenet megadása… (opcionális)";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Enter your name…" = "Adjon meg egy nevet…";
|
"Enter your name…" = "Adjon meg egy nevet…";
|
||||||
@@ -2783,7 +2783,7 @@
|
|||||||
"Message delivery error" = "Üzenetkézbesítési hiba";
|
"Message delivery error" = "Üzenetkézbesítési hiba";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Message delivery receipts!" = "Üzenetkézbesítési bizonylatok!";
|
"Message delivery receipts!" = "Üzenet kézbesítési jelentések!";
|
||||||
|
|
||||||
/* item status text */
|
/* item status text */
|
||||||
"Message delivery warning" = "Üzenet kézbesítési figyelmeztetés";
|
"Message delivery warning" = "Üzenet kézbesítési figyelmeztetés";
|
||||||
@@ -2813,7 +2813,7 @@
|
|||||||
"message received" = "üzenet érkezett";
|
"message received" = "üzenet érkezett";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Message reception" = "Üzenetjelentés";
|
"Message reception" = "Üzenet kézbesítési jelentés";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"Message servers" = "Üzenetkiszolgálók";
|
"Message servers" = "Üzenetkiszolgálók";
|
||||||
@@ -3605,7 +3605,7 @@
|
|||||||
"removed" = "eltávolítva";
|
"removed" = "eltávolítva";
|
||||||
|
|
||||||
/* rcv group event chat item */
|
/* rcv group event chat item */
|
||||||
"removed %@" = "%@ eltávolítva";
|
"removed %@" = "eltávolította őt: %@";
|
||||||
|
|
||||||
/* profile update event chat item */
|
/* profile update event chat item */
|
||||||
"removed contact address" = "törölt kapcsolattartási cím";
|
"removed contact address" = "törölt kapcsolattartási cím";
|
||||||
@@ -4373,7 +4373,7 @@
|
|||||||
"The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "A titkosítás működik, és új titkosítási egyezményre nincs szükség. Ez kapcsolati hibákat eredményezhet!";
|
"The encryption is working and the new encryption agreement is not required. It may result in connection errors!" = "A titkosítás működik, és új titkosítási egyezményre nincs szükség. Ez kapcsolati hibákat eredményezhet!";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"The hash of the previous message is different." = "Az előző üzenet hash-e más.";
|
"The hash of the previous message is different." = "Az előző üzenet ellenőrzőösszege különbözik.";
|
||||||
|
|
||||||
/* No comment provided by engineer. */
|
/* No comment provided by engineer. */
|
||||||
"The ID of the next message is incorrect (less or equal to the previous).\nIt can happen because of some bug or when the connection is compromised." = "A következő üzenet azonosítója hibás (kisebb vagy egyenlő az előzővel).\nEz valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő.";
|
"The ID of the next message is incorrect (less or equal to the previous).\nIt can happen because of some bug or when the connection is compromised." = "A következő üzenet azonosítója hibás (kisebb vagy egyenlő az előzővel).\nEz valamilyen hiba, vagy sérült kapcsolat esetén fordulhat elő.";
|
||||||
|
|||||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user