mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2026-05-04 13:56:00 +00:00
Merge pull request #535 from simplex-chat/master
merge master to stable
This commit is contained in:
+1
-1
@@ -1 +1 @@
|
||||
* @epoberezkin @efim-poberezkin
|
||||
* @epoberezkin @jr-simplex
|
||||
|
||||
@@ -1,6 +1,6 @@
|
||||
<img src="images/simplex-chat-logo.svg" alt="SimpleX logo" width="100%">
|
||||
|
||||
# SimpleX - the first chat platform that is 100% private by design - it has no access to your connection graph!
|
||||
# SimpleX - the first messaging platform that has no user identifiers of any kind - 100% private by design!
|
||||
|
||||
[](https://github.com/simplex-chat/simplex-chat/actions?query=workflow%3Abuild)
|
||||
[](https://github.com/simplex-chat/simplex-chat/releases)
|
||||
@@ -19,280 +19,120 @@
|
||||
[<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/apk_icon.png" alt="APK" height="41">](https://github.com/simplex-chat/website/raw/master/simplex.apk)
|
||||
|
||||
- 🖲 Protects your messages and metadata - who you talk to and when.
|
||||
- 🔐 Double ratchet encryption.
|
||||
- 📱 Mobile apps for Android ([Google Play](https://play.google.com/store/apps/details?id=chat.simplex.app), [APK](https://github.com/simplex-chat/website/raw/master/simplex.apk)) and [iOS](https://apps.apple.com/us/app/simplex-chat/id1605771084). [See the announcement here](https://github.com/simplex-chat/simplex-chat/blob/master/blog/20220308-simplex-chat-mobile-apps.md).
|
||||
- 🔐 Double ratchet end-to-end encryption, with additional encryption layer.
|
||||
- 📱 Mobile apps for Android ([Google Play](https://play.google.com/store/apps/details?id=chat.simplex.app), [APK](https://github.com/simplex-chat/website/raw/master/simplex.apk)) and [iOS](https://apps.apple.com/us/app/simplex-chat/id1605771084).
|
||||
- 🚀 [TestFlight preview for iOS](https://testflight.apple.com/join/DWuT2LQu) with the new features 1-2 weeks earlier - **limited to 10,000 users**!
|
||||
- 🖥 Available as a [terminal (console) app / CLI](#zap-quick-installation-of-a-terminal-app) on Linux, MacOS, Windows.
|
||||
- 🖥 Available as a terminal (console) app / CLI on Linux, MacOS, Windows.
|
||||
|
||||
See [SimpleX overview](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) for more information on platform objectives and technical design.
|
||||
## Why privacy of communications matter
|
||||
|
||||
### :zap: Quick installation of a terminal app
|
||||
Everyone should care about privacy and security of their communications - innocuous conversations can put you in danger even if there is nothing to hide.
|
||||
|
||||
One of the most shocking stories is the experience of [Mohamedou Ould Salahi](https://en.wikipedia.org/wiki/Mohamedou_Ould_Slahi) that he wrote about in his memoir and that is shown in The Mauritanian movie. He was put into Guantanamo camp, without trial, and was tortured there for 15 years after a phone call to his relative in Afghanistan, under suspicion of being involved in 9/11 attacks, even though he lived in Germany for the 10 years prior to the attacks.
|
||||
|
||||
It is not enough to use an end-to-end encrypted messenger, we all should use the messengers that protect the privacy of our personal networks - who we are connected with.
|
||||
|
||||
## SimpleX unique approach to privacy and security
|
||||
|
||||
### Full privacy of your identity, profile, contacts and metadata
|
||||
|
||||
**Unlike any other existing messaging platform, SimpleX has no identifiers assigned to the users** - not even random numbers. This protects the privacy of who are you communicating with, hiding it from SimpleX platform servers and from any observers. [Read more](./docs/SIMPLEX.md#full-privacy-of-your-identity-profile-contacts-and-metadata).
|
||||
|
||||
### The best protection against spam and abuse
|
||||
|
||||
As you have no identifier on SimpleX platform, you cannot be contacted unless you share a one-time invitation link or an optional temporary user address. [Read more](./docs/SIMPLEX.md#the-best-protection-against-spam-and-abuse).
|
||||
|
||||
### Complete ownership, control and security of your data
|
||||
|
||||
SimpleX stores all user data on client devices, the messages are only held temporarily on SimpleX relay servers until they are received. [Read more](./docs/SIMPLEX.md#complete-ownership-control-and-security-of-your-data).
|
||||
|
||||
### Users own SimpleX network
|
||||
|
||||
You can use SimpleX with your own servers and still communicate with people using the servers that are pre-configured in the apps or any other SimpleX servers. [Read more](./docs/SIMPLEX.md#users-own-simplex-network).
|
||||
|
||||
## For developers
|
||||
|
||||
We plan that the SimpleX platform will grow into the platform supporting any distributed Internet application. This will allow you to build any service that people can access via chat, with custom web-based UI widgets that anybody with basic HTML/CSS/JavaScript knowledge can create in a few hours.
|
||||
|
||||
You already can:
|
||||
|
||||
- use SimpleX Chat library to integrate chat functionality into your apps.
|
||||
- use SimpleX Chat bot templates in Haskell to build your own chat bot services (TypeScript SDK is coming soon).
|
||||
|
||||
If you are considering developing with SimpleX platform please get in touch for any advice and support.
|
||||
|
||||
## News and updates
|
||||
|
||||
[Apr 04, 2022. Instant notifications for SimpleX Chat mobile apps](./blog/20220404-simplex-chat-instant-notifications.md). We would really appreciate any feedback on the design we are implementing.
|
||||
|
||||
[Mar 08, 2022 Mobile apps for iOS and Android released](./blog/20220308-simplex-chat-mobile-apps.md)
|
||||
|
||||
[Feb 14, 2022. SimpleX Chat: join our public beta for iOS](./blog/20220214-simplex-chat-ios-public-beta.md)
|
||||
|
||||
[All updates](./blog)
|
||||
|
||||
## Make a private connection
|
||||
|
||||
You need to share a link or scan a QR code (in person or during a video call) to make a connection and start messaging.
|
||||
|
||||
The channel through which you share the link does not have to be secure - it is enough that you can confirm who sent you the message and that your SimpleX connection is established.
|
||||
|
||||
<img src="https://github.com/simplex-chat/.github/blob/master/profile/images/conversation.png" alt="Make a private connection" width="594" height="360">
|
||||
|
||||
## :zap: Quick installation of a terminal app
|
||||
|
||||
```sh
|
||||
curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/master/install.sh | bash
|
||||
curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/stable/install.sh | bash
|
||||
```
|
||||
|
||||
Once the chat client is installed, simply run `simplex-chat` from your terminal.
|
||||
|
||||

|
||||
|
||||
## Table of contents
|
||||
Read more about [installing and using the terminal app](./docs/CLI.md).
|
||||
|
||||
- [Disclaimer](#disclaimer)
|
||||
- [Network topology](#network-topology)
|
||||
- [Terminal chat features](#terminal-chat-features)
|
||||
- [Installation](#🚀-installation)
|
||||
- [Download chat client](#download-chat-client)
|
||||
- [Linux and MacOS](#linux-and-macos)
|
||||
- [Windows](#windows)
|
||||
- [Build from source](#build-from-source)
|
||||
- [Using Docker](#using-docker)
|
||||
- [Using Haskell stack](#using-haskell-stack)
|
||||
- [Usage](#usage)
|
||||
- [Running the chat client](#running-the-chat-client)
|
||||
- [How to use SimpleX chat](#how-to-use-simplex-chat)
|
||||
- [Groups](#groups)
|
||||
- [Sending files](#sending-files)
|
||||
- [User contact addresses](#user-contact-addresses)
|
||||
- [Access chat history](#access-chat-history)
|
||||
- [Roadmap](#Roadmap)
|
||||
- [License](#license)
|
||||
## SimpleX Platform design
|
||||
|
||||
## Disclaimer
|
||||
SimpleX is a client-server network with a unique network topology that uses redundant, disposable message relay nodes to asynchronously pass messages via unidirectional (simplex) message queues, providing recipient and sender anonymity.
|
||||
|
||||
SimpleX Chat implements a new network topology for asynchronous communication combining the advantages and avoiding the disadvantages of federated and P2P networks.
|
||||
Unlike P2P networks, all messages are passed through one or several server nodes, that do not even need to have persistence. In fact, the current [SMP server implementation](https://github.com/simplex-chat/simplexmq#smp-server) uses in-memory message storage, persisting only the queue records. SimpleX provides better metadata protection than P2P designs, as no global participant identifiers are used to deliver messages, and avoids [the problems of P2P networks](./docs/SIMPLEX.md#comparison-with-p2p-messaging-protocols).
|
||||
|
||||
[SimpleXMQ security model](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) had many improvements in v1.0.0; the implementation has not been audited yet.
|
||||
Unlike federated networks, the server nodes **do not have records of the users**, **do not communicate with each other** and **do not store messages** after they are delivered to the recipients. There is no way to discover the full list of servers participating in SimpleX network. This design avoids the problem of metadata visibility that all federated networks have and better protects from the network-wide attacks.
|
||||
|
||||
We use SimpleX Chat all the time, but you may find some bugs. We would really appreciate if you use it and let us know anything that needs to be fixed or improved.
|
||||
Only the client devices have information about users, their contacts and groups.
|
||||
|
||||
## Network topology
|
||||
|
||||
SimpleX is a client-server network that uses redundant, disposable nodes to asynchronously pass messages via message queues, providing receiver and sender anonymity.
|
||||
|
||||
Unlike P2P networks, all messages are passed through one or several (for redundancy) servers, that do not even need to have persistence (in fact, the current [SMP server implementation](https://github.com/simplex-chat/simplexmq#smp-server) uses in-memory message storage, persisting only the queue records) - it provides better metadata protection than P2P designs, as no global participant ID is required, and avoids many [problems of P2P networks](https://github.com/simplex-chat/simplex-chat/blob/master/simplex.md#comparison-with-p2p-messaging-protocols).
|
||||
|
||||
Unlike federated networks, the participating server nodes **do not have records of the users**, **do not communicate with each other**, **do not store messages** after they are delivered to the recipients, and there is no way to discover the full list of participating servers. SimpleX network avoids the problem of metadata visibility that federated networks have and better protects the network, as servers do not communicate with each other. Each server node provides unidirectional "dumb pipes" to the users, that do authorization without authentication, having no knowledge of the the users or their contacts. Each queue is assigned two Ed448 keys - one for receiver and one for sender - and each queue access is authorized with a signature created using a respective key's private counterpart.
|
||||
|
||||
The routing of messages relies on the knowledge of client devices how user contacts and groups map at any given moment of time to these disposable queues on server nodes.
|
||||
|
||||
## Terminal chat features
|
||||
|
||||
- 1-to-1 chat with multiple people in the same terminal window.
|
||||
- Group messaging.
|
||||
- Sending files to contacts and groups.
|
||||
- User contact addresses - establish connections via multiple-use contact links.
|
||||
- Messages persisted in a local SQLite database.
|
||||
- Auto-populated recipient name - just type your messages to reply to the sender once the connection is established.
|
||||
- Demo SMP servers available and pre-configured in the app - or you can [deploy your own server](https://github.com/simplex-chat/simplexmq#using-smp-server-and-smp-agent).
|
||||
- No global identity or any names visible to the server(s), ensuring full privacy of your contacts and conversations.
|
||||
- Two layers of E2E encryption (double-ratchet for duplex connections, using X3DH key agreement with ephemeral Curve448 keys, and NaCl crypto_box for SMP queues, using Curve25519 keys) and out-of-band passing of recipient keys (see [How to use SimpleX chat](#how-to-use-simplex-chat)).
|
||||
- Message integrity validation (via including the digests of the previous messages).
|
||||
- Authentication of each command/message by SMP servers with automatically generated Ed448 keys.
|
||||
- TLS 1.3 transport encryption.
|
||||
- Additional encryption of messages from SMP server to recipient to reduce traffic correlation.
|
||||
|
||||
Public keys involved in key exchange are not used as identity, they are randomly generated for each contact.
|
||||
|
||||
See [Encryption Primitives Used](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md#encryption-primitives-used) for technical details.
|
||||
|
||||
<a name="🚀-installation"></a>
|
||||
|
||||
## 🚀 Installation
|
||||
|
||||
### Download chat client
|
||||
|
||||
#### Linux and MacOS
|
||||
|
||||
To **install** or **update** `simplex-chat`, you should run the install script. To do that, use the following cURL or Wget command:
|
||||
|
||||
```sh
|
||||
curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/master/install.sh | bash
|
||||
```
|
||||
|
||||
```sh
|
||||
wget -qO- https://raw.githubusercontent.com/simplex-chat/simplex-chat/master/install.sh | bash
|
||||
```
|
||||
|
||||
Once the chat client downloads, you can run it with `simplex-chat` command in your terminal.
|
||||
|
||||
Alternatively, you can manually download the chat binary for your system from the [latest stable release](https://github.com/simplex-chat/simplex-chat/releases) and make it executable as shown below.
|
||||
|
||||
```sh
|
||||
chmod +x <binary>
|
||||
mv <binary> ~/.local/bin/simplex-chat
|
||||
```
|
||||
|
||||
(or any other preferred location on `PATH`).
|
||||
|
||||
On MacOS you also need to [allow Gatekeeper to run it](https://support.apple.com/en-us/HT202491).
|
||||
|
||||
#### Windows
|
||||
|
||||
```sh
|
||||
move <binary> %APPDATA%/local/bin/simplex-chat.exe
|
||||
```
|
||||
|
||||
### Build from source
|
||||
|
||||
> **Please note:** to build the app use source code from [stable branch](https://github.com/simplex-chat/simplex-chat/tree/stable).
|
||||
|
||||
#### Using Docker
|
||||
|
||||
On Linux, you can build the chat executable using [docker build with custom output](https://docs.docker.com/engine/reference/commandline/build/#custom-build-outputs):
|
||||
|
||||
```shell
|
||||
$ git clone git@github.com:simplex-chat/simplex-chat.git
|
||||
$ cd simplex-chat
|
||||
$ git checkout stable
|
||||
$ DOCKER_BUILDKIT=1 docker build --output ~/.local/bin .
|
||||
```
|
||||
|
||||
> **Please note:** If you encounter `` version `GLIBC_2.28' not found `` error, rebuild it with `haskell:8.10.4-stretch` base image (change it in your local [Dockerfile](Dockerfile)).
|
||||
|
||||
#### Using Haskell stack
|
||||
|
||||
Install [Haskell stack](https://docs.haskellstack.org/en/stable/README/):
|
||||
|
||||
```shell
|
||||
curl -sSL https://get.haskellstack.org/ | sh
|
||||
```
|
||||
|
||||
and build the project:
|
||||
|
||||
```shell
|
||||
$ git clone git@github.com:simplex-chat/simplex-chat.git
|
||||
$ cd simplex-chat
|
||||
$ git checkout stable
|
||||
$ stack install
|
||||
```
|
||||
|
||||
## Usage
|
||||
|
||||
### Running the chat client
|
||||
|
||||
To start the chat client, run `simplex-chat` from the terminal.
|
||||
|
||||
By default, app data directory is created in the home directory (`~/.simplex`, or `%APPDATA%/simplex` on Windows), and two SQLite database files `simplex_v1_chat.db` and `simplex_v1_agent.db` are initialized in it.
|
||||
|
||||
To specify a different file path prefix for the database files use `-d` command line option:
|
||||
|
||||
```shell
|
||||
$ simplex-chat -d alice
|
||||
```
|
||||
|
||||
Running above, for example, would create `alice_v1_chat.db` and `alice_v1_agent.db` database files in current directory.
|
||||
|
||||
Three default SMP servers are hosted on Linode - they are [pre-configured in the app](https://github.com/simplex-chat/simplex-chat/blob/master/src/Simplex/Chat/Options.hs#L42).
|
||||
|
||||
If you deployed your own SMP server(s) you can configure client via `-s` option:
|
||||
|
||||
```shell
|
||||
$ simplex-chat -s smp://LcJUMfVhwD8yxjAiSaDzzGF3-kLG4Uh0Fl_ZIjrRwjI=@smp.example.com
|
||||
```
|
||||
|
||||
Base64url encoded string preceding the server address is the server's offline certificate fingerprint which is validated by client during TLS handshake.
|
||||
|
||||
You can still talk to people using default or any other server - it only affects the location of the message queue when you initiate the connection (and the reply queue can be on another server, as set by the other party's client).
|
||||
|
||||
Run `simplex-chat -h` to see all available options.
|
||||
|
||||
### How to use SimpleX chat
|
||||
|
||||
Once you have started the chat, you will be prompted to specify your "display name" and an optional "full name" to create a local chat profile. Your display name is an alias for your contacts to refer to you by - it is not unique and does not serve as a global identity. If some of your contacts chose the same display name, the chat client adds a numeric suffix to their local display name.
|
||||
|
||||
The diagram below shows how to connect and message a contact:
|
||||
|
||||
<div align="center">
|
||||
<img align="center" src="images/how-to-use-simplex.svg">
|
||||
</div>
|
||||
|
||||
Once you've set up your local profile, enter `/c` (for `/connect`) to create a new connection and generate an invitation. Send this invitation to your contact via any other channel.
|
||||
|
||||
You are able to create multiple invitations by entering `/connect` multiple times and sending these invitations to the corresponding contacts you'd like to connect with.
|
||||
|
||||
The invitation can only be used once and even if this is intercepted, the attacker would not be able to use it to send you the messages via this queue once your contact confirms that the connection is established. See agent protocol for explanation of [invitation format](https://github.com/simplex-chat/simplexmq/blob/master/protocol/agent-protocol.md#connection-request).
|
||||
|
||||
The contact who received the invitation should enter `/c <invitation>` to accept the connection. This establishes the connection, and both parties are notified.
|
||||
|
||||
They would then use `@<name> <message>` commands to send messages. You may also just start typing a message to send it to the contact that was the last.
|
||||
|
||||
Use `/help` in chat to see the list of available commands.
|
||||
|
||||
### Groups
|
||||
|
||||
To create a group use `/g <group>`, then add contacts to it with `/a <group> <name>`. You can then send messages to the group by entering `#<group> <message>`. Use `/help groups` for other commands.
|
||||
|
||||

|
||||
|
||||
> **Please note**: the groups are not stored on any server, they are maintained as a list of members in the app database to whom the messages will be sent.
|
||||
|
||||
### Sending files
|
||||
|
||||
You can send a file to your contact with `/f @<contact> <file_path>` - the recipient will have to accept it before it is sent. Use `/help files` for other commands.
|
||||
|
||||

|
||||
|
||||
You can send files to a group with `/f #<group> <file_path>`.
|
||||
|
||||
### User contact addresses
|
||||
|
||||
As an alternative to one-time invitation links, you can create a long-term address with `/ad` (for `/address`). The created address can then be shared via any channel, and used by other users as a link to make a contact request with `/c <user_contact_address>`.
|
||||
|
||||
You can accept or reject incoming requests with `/ac <name>` and `/rc <name>` commands.
|
||||
|
||||
User address is "long-term" in a sense that it is a multiple-use connection link - it can be used until it is deleted by the user, in which case all established connections would still remain active (unlike how it works with email, when changing the address results in people not being able to message you).
|
||||
|
||||
Use `/help address` for other commands.
|
||||
|
||||

|
||||
|
||||
### Access chat history
|
||||
|
||||
SimpleX chat stores all your contacts and conversations in a local SQLite database, making it private and portable by design, owned and controlled by user.
|
||||
|
||||
You can view and search your chat history by querying your database. Run the below script to create message views in your database.
|
||||
|
||||
```sh
|
||||
curl -o- https://raw.githubusercontent.com/simplex-chat/simplex-chat/master/message_views.sql | sqlite3 ~/.simplex/simplex_v1_chat.db
|
||||
```
|
||||
|
||||
Open SQLite Command Line Shell:
|
||||
|
||||
```sh
|
||||
sqlite3 ~/.simplex/simplex_v1_chat.db
|
||||
```
|
||||
|
||||
See [Message queries](./message_queries.md) for examples.
|
||||
|
||||
> **Please note:** SQLite foreign key constraints are disabled by default, and must be **[enabled separately for each database connection](https://sqlite.org/foreignkeys.html#fk_enable)**. The latter can be achieved by running `PRAGMA foreign_keys = ON;` command on an open database connection. By running data altering queries without enabling foreign keys prior to that, you may risk putting your database in an inconsistent state.
|
||||
|
||||
**Convenience queries**
|
||||
|
||||
Get all messages from today (`chat_dt` is in UTC):
|
||||
|
||||
```sql
|
||||
select * from all_messages_plain where date(chat_dt) > date('now', '-1 day') order by chat_dt;
|
||||
```
|
||||
|
||||
Get overnight messages in the morning:
|
||||
|
||||
```sql
|
||||
select * from all_messages_plain where chat_dt > datetime('now', '-15 hours') order by chat_dt;
|
||||
```
|
||||
See [SimpleX whitepaper](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) for more information on platform objectives and technical design.
|
||||
|
||||
## Roadmap
|
||||
|
||||
1. Mobile and desktop apps (in progress).
|
||||
2. SMP protocol improvements:
|
||||
- SMP queue redundancy and rotation.
|
||||
- Message delivery confirmation.
|
||||
- Support multiple devices.
|
||||
3. Privacy-preserving identity server for optional DNS-based contact/group addresses to simplify connection and discovery, but not used to deliver messages:
|
||||
- keep all your contacts and groups even if you lose the domain.
|
||||
- the server doesn't have information about your contacts and groups.
|
||||
4. Media server to optimize sending large files to groups.
|
||||
5. Channels server for large groups and broadcast channels.
|
||||
- ✅ Easy to deploy SimpleX server with in-memory message storage, without any dependencies.
|
||||
- ✅ Terminal (console) client with groups and files support.
|
||||
- ✅ One-click SimpleX server deployment on Linode.
|
||||
- ✅ End-to-end encryption using double-ratchet protocol with additional encryption layer.
|
||||
- ✅ Mobile apps v1 for Android and iOS.
|
||||
- ✅ Private instant notifications for Android using background service.
|
||||
- ✅ Haskell chat bot templates
|
||||
- 🏗 Privacy preserving instant notifications for iOS using Apple Push Notification service (in progress).
|
||||
- 🏗 Mobile app v2 - supporting files, images and groups etc. (in progress).
|
||||
- 🏗 Chat server and TypeScript client SDK to develop chat interfaces, integrations and chat bots (in progress).
|
||||
- Chat database portability and encryption.
|
||||
- End-to-end encrypted audio and video calls via the mobile apps.
|
||||
- Web widgets for custom interactivity in the chats.
|
||||
- SMP protocol improvements:
|
||||
- SMP queue redundancy and rotation.
|
||||
- Message delivery confirmation.
|
||||
- Supporting the same profile on multiple devices.
|
||||
- Privacy-preserving identity server for optional DNS-based contact/group addresses to simplify connection and discovery, but not used to deliver messages:
|
||||
- keep all your contacts and groups even if you lose the domain.
|
||||
- the server doesn't have information about your contacts and groups.
|
||||
- Media server to optimize sending large files to groups.
|
||||
- Channels server for large groups and broadcast channels.
|
||||
|
||||
## Disclaimer
|
||||
|
||||
[SimpleX protocols and security model](https://github.com/simplex-chat/simplexmq/blob/master/protocol/overview-tjr.md) was reviewed and had many improvements in v1.0.0; we are currently arranging for the independent implementation audit.
|
||||
|
||||
You are likely to discover some bugs - we would really appreciate if you use it and let us know anything that needs to be fixed or improved.
|
||||
|
||||
## License
|
||||
|
||||
|
||||
@@ -96,6 +96,9 @@ dependencies {
|
||||
//Camera Permission
|
||||
implementation "com.google.accompanist:accompanist-permissions:0.23.0"
|
||||
|
||||
// Link Previews
|
||||
implementation 'org.jsoup:jsoup:1.13.1'
|
||||
|
||||
testImplementation 'junit:junit:4.13.2'
|
||||
androidTestImplementation 'androidx.test.ext:junit:1.1.3'
|
||||
androidTestImplementation 'androidx.test.espresso:espresso-core:3.4.0'
|
||||
|
||||
@@ -25,9 +25,9 @@ import chat.simplex.app.views.WelcomeView
|
||||
import chat.simplex.app.views.chat.ChatView
|
||||
import chat.simplex.app.views.chatlist.ChatListView
|
||||
import chat.simplex.app.views.chatlist.openChat
|
||||
import chat.simplex.app.views.helpers.AlertManager
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.newchat.*
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.connectViaUri
|
||||
import chat.simplex.app.views.newchat.withUriAction
|
||||
import java.util.concurrent.TimeUnit
|
||||
|
||||
//import kotlinx.serialization.decodeFromString
|
||||
@@ -122,10 +122,18 @@ fun connectIfOpenedViaUri(uri: Uri, chatModel: ChatModel) {
|
||||
chatModel.appOpenUrl.value = uri
|
||||
} else {
|
||||
withUriAction(uri) { action ->
|
||||
val title = when (action) {
|
||||
"contact" -> generalGetString(R.string.connect_via_contact_link)
|
||||
"invitation" -> generalGetString(R.string.connect_via_invitation_link)
|
||||
else -> {
|
||||
Log.e(TAG, "URI has unexpected action. Alert shown.")
|
||||
action
|
||||
}
|
||||
}
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = "Connect via $action link?",
|
||||
text = "Your profile will be sent to the contact that you received this link from.",
|
||||
confirmText = "Connect",
|
||||
title = title,
|
||||
text = generalGetString(R.string.profile_will_be_sent_to_contact_sending_link),
|
||||
confirmText = generalGetString(R.string.connect_via_link_verb),
|
||||
onConfirm = {
|
||||
withApi {
|
||||
Log.d(TAG, "connectIfOpenedViaUri: connecting")
|
||||
|
||||
@@ -41,6 +41,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
|
||||
override fun onCreate() {
|
||||
super.onCreate()
|
||||
context = this
|
||||
ProcessLifecycleOwner.get().lifecycle.addObserver(this)
|
||||
withApi {
|
||||
val user = chatController.apiGetActiveUser()
|
||||
@@ -65,9 +66,10 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
}
|
||||
|
||||
companion object {
|
||||
lateinit var context: SimplexApp private set
|
||||
|
||||
init {
|
||||
val socketName = "local.socket.address.listen.native.cmd2"
|
||||
|
||||
val s = Semaphore(0)
|
||||
thread(name="stdout/stderr pipe") {
|
||||
Log.d(TAG, "starting server")
|
||||
@@ -82,7 +84,7 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
val inStreamReader = InputStreamReader(inStream)
|
||||
val input = BufferedReader(inStreamReader)
|
||||
|
||||
while(true) {
|
||||
while (true) {
|
||||
val line = input.readLine() ?: break
|
||||
Log.w("$TAG (stdout/stderr)", line)
|
||||
logbuffer.add(line)
|
||||
|
||||
File diff suppressed because one or more lines are too long
@@ -15,8 +15,8 @@ import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.*
|
||||
import chat.simplex.app.views.helpers.AlertManager
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import kotlinx.datetime.Clock
|
||||
@@ -80,15 +80,19 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt
|
||||
suspend fun sendCmd(cmd: CC): CR {
|
||||
return withContext(Dispatchers.IO) {
|
||||
val c = cmd.cmdString
|
||||
chatModel.terminalItems.add(TerminalItem.cmd(cmd))
|
||||
if (cmd !is CC.ApiParseMarkdown) {
|
||||
chatModel.terminalItems.add(TerminalItem.cmd(cmd))
|
||||
Log.d(TAG, "sendCmd: ${cmd.cmdType}")
|
||||
}
|
||||
val json = chatSendCmd(ctrl, c)
|
||||
Log.d(TAG, "sendCmd: ${cmd.cmdType}")
|
||||
val r = APIResponse.decodeStr(json)
|
||||
Log.d(TAG, "sendCmd response type ${r.resp.responseType}")
|
||||
if (r.resp is CR.Response || r.resp is CR.Invalid) {
|
||||
Log.d(TAG, "sendCmd response json $json")
|
||||
}
|
||||
chatModel.terminalItems.add(TerminalItem.resp(r.resp))
|
||||
if (r.resp !is CR.ParsedMarkdown) {
|
||||
chatModel.terminalItems.add(TerminalItem.resp(r.resp))
|
||||
}
|
||||
r.resp
|
||||
}
|
||||
}
|
||||
@@ -179,8 +183,8 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt
|
||||
else -> {
|
||||
Log.e(TAG, "setUserSMPServers bad response: ${r.responseType} ${r.details}")
|
||||
AlertManager.shared.showAlertMsg(
|
||||
"Error saving SMP servers",
|
||||
"Make sure SMP server addresses are in correct format, line separated and are not duplicated."
|
||||
generalGetString(R.string.error_saving_smp_servers),
|
||||
generalGetString(R.string.ensure_smp_server_address_are_correct_format_and_unique)
|
||||
)
|
||||
false
|
||||
}
|
||||
@@ -199,15 +203,17 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt
|
||||
when {
|
||||
r is CR.SentConfirmation || r is CR.SentInvitation -> return true
|
||||
r is CR.ContactAlreadyExists -> {
|
||||
AlertManager.shared.showAlertMsg("Contact already exists",
|
||||
"You are already connected to ${r.contact.displayName} via this link."
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.contact_already_exists),
|
||||
String.format(generalGetString(R.string.you_are_already_connected_to_vName_via_this_link), r.contact.displayName)
|
||||
)
|
||||
return false
|
||||
}
|
||||
r is CR.ChatCmdError && r.chatError is ChatError.ChatErrorChat
|
||||
&& r.chatError.errorType is ChatErrorType.InvalidConnReq -> {
|
||||
AlertManager.shared.showAlertMsg("Invalid connection link",
|
||||
"Please check that you used the correct link or ask your contact to send you another one."
|
||||
AlertManager.shared.showAlertMsg(
|
||||
generalGetString(R.string.invalid_connection_link),
|
||||
generalGetString(R.string.please_check_correct_link_and_maybe_ask_for_a_new_one)
|
||||
)
|
||||
return false
|
||||
}
|
||||
@@ -226,8 +232,8 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt
|
||||
val e = r.chatError
|
||||
if (e is ChatError.ChatErrorChat && e.errorType is ChatErrorType.ContactGroups) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
"Can't delete contact!",
|
||||
"Contact ${e.errorType.contact.displayName} cannot be deleted, it is a member of the group(s) ${e.errorType.groupNames}."
|
||||
generalGetString(R.string.cannot_delete_contact),
|
||||
String.format(generalGetString(R.string.contact_cannot_be_deleted_as_they_are_in_groups), e.errorType.contact.displayName, e.errorType.groupNames)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -244,6 +250,13 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiParseMarkdown(text: String): List<FormattedText>? {
|
||||
val r = sendCmd(CC.ApiParseMarkdown(text))
|
||||
if (r is CR.ParsedMarkdown) return r.formattedText
|
||||
Log.e(TAG, "apiParseMarkdown bad response: ${r.responseType} ${r.details}")
|
||||
return null
|
||||
}
|
||||
|
||||
suspend fun apiCreateUserAddress(): String? {
|
||||
val r = sendCmd(CC.CreateMyAddress())
|
||||
if (r is CR.UserContactLinkCreated) return r.connReqContact
|
||||
@@ -400,35 +413,22 @@ open class ChatController(private val ctrl: ChatCtrl, private val ntfManager: Nt
|
||||
Row {
|
||||
Icon(
|
||||
Icons.Outlined.Bolt,
|
||||
contentDescription = "Instant notifications",
|
||||
contentDescription = generalGetString(R.string.icon_descr_instant_notifications),
|
||||
)
|
||||
Text("Private instant notifications!", fontWeight = FontWeight.Bold)
|
||||
Text(generalGetString(R.string.private_instant_notifications), fontWeight = FontWeight.Bold)
|
||||
}
|
||||
},
|
||||
text = {
|
||||
Column {
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("To preserve your privacy, instead of push notifications the app has a ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
|
||||
append("SimpleX background service")
|
||||
}
|
||||
append(" – it uses a few percent of the battery per day.")
|
||||
},
|
||||
annotatedStringResource(R.string.to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery),
|
||||
Modifier.padding(bottom = 8.dp)
|
||||
)
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Medium)) {
|
||||
append("It can be disabled via settings")
|
||||
}
|
||||
append(" – notifications will still be shown while the app is running.")
|
||||
}
|
||||
)
|
||||
Text(annotatedStringResource(R.string.it_can_disabled_via_settings_notifications_still_shown))
|
||||
}
|
||||
},
|
||||
confirmButton = {
|
||||
Button(onClick = AlertManager.shared::hideAlert) { Text("Ok") }
|
||||
Button(onClick = AlertManager.shared::hideAlert) { Text(generalGetString(R.string.ok)) }
|
||||
}
|
||||
)
|
||||
}
|
||||
@@ -483,6 +483,7 @@ sealed class CC {
|
||||
class Connect(val connReq: String): CC()
|
||||
class ApiDeleteChat(val type: ChatType, val id: Long): CC()
|
||||
class ApiUpdateProfile(val profile: Profile): CC()
|
||||
class ApiParseMarkdown(val text: String): CC()
|
||||
class CreateMyAddress: CC()
|
||||
class DeleteMyAddress: CC()
|
||||
class ShowMyAddress: CC()
|
||||
@@ -507,6 +508,7 @@ sealed class CC {
|
||||
is Connect -> "/connect $connReq"
|
||||
is ApiDeleteChat -> "/_delete ${chatRef(type, id)}"
|
||||
is ApiUpdateProfile -> "/_profile ${json.encodeToString(profile)}"
|
||||
is ApiParseMarkdown -> "/_parse $text"
|
||||
is CreateMyAddress -> "/address"
|
||||
is DeleteMyAddress -> "/delete_address"
|
||||
is ShowMyAddress -> "/show_address"
|
||||
@@ -532,6 +534,7 @@ sealed class CC {
|
||||
is Connect -> "connect"
|
||||
is ApiDeleteChat -> "apiDeleteChat"
|
||||
is ApiUpdateProfile -> "updateProfile"
|
||||
is ApiParseMarkdown -> "apiParseMarkdown"
|
||||
is CreateMyAddress -> "createMyAddress"
|
||||
is DeleteMyAddress -> "deleteMyAddress"
|
||||
is ShowMyAddress -> "showMyAddress"
|
||||
@@ -592,6 +595,7 @@ sealed class CR {
|
||||
@Serializable @SerialName("contactDeleted") class ContactDeleted(val contact: Contact): CR()
|
||||
@Serializable @SerialName("userProfileNoChange") class UserProfileNoChange: CR()
|
||||
@Serializable @SerialName("userProfileUpdated") class UserProfileUpdated(val fromProfile: Profile, val toProfile: Profile): CR()
|
||||
@Serializable @SerialName("apiParsedMarkdown") class ParsedMarkdown(val formattedText: List<FormattedText>? = null): CR()
|
||||
@Serializable @SerialName("userContactLink") class UserContactLink(val connReqContact: String): CR()
|
||||
@Serializable @SerialName("userContactLinkCreated") class UserContactLinkCreated(val connReqContact: String): CR()
|
||||
@Serializable @SerialName("userContactLinkDeleted") class UserContactLinkDeleted: CR()
|
||||
@@ -632,6 +636,7 @@ sealed class CR {
|
||||
is ContactDeleted -> "contactDeleted"
|
||||
is UserProfileNoChange -> "userProfileNoChange"
|
||||
is UserProfileUpdated -> "userProfileUpdated"
|
||||
is ParsedMarkdown -> "apiParsedMarkdown"
|
||||
is UserContactLink -> "userContactLink"
|
||||
is UserContactLinkCreated -> "userContactLinkCreated"
|
||||
is UserContactLinkDeleted -> "userContactLinkDeleted"
|
||||
@@ -673,6 +678,7 @@ sealed class CR {
|
||||
is ContactDeleted -> json.encodeToString(contact)
|
||||
is UserProfileNoChange -> noDetails()
|
||||
is UserProfileUpdated -> json.encodeToString(toProfile)
|
||||
is ParsedMarkdown -> json.encodeToString(formattedText)
|
||||
is UserContactLink -> connReqContact
|
||||
is UserContactLinkCreated -> connReqContact
|
||||
is UserContactLinkDeleted -> noDetails()
|
||||
@@ -700,7 +706,7 @@ sealed class CR {
|
||||
is Invalid -> str
|
||||
}
|
||||
|
||||
fun noDetails(): String ="${responseType}: no details"
|
||||
fun noDetails(): String ="${responseType}: " + generalGetString(R.string.no_details)
|
||||
}
|
||||
|
||||
abstract class TerminalItem {
|
||||
|
||||
@@ -7,7 +7,7 @@ val Purple500 = Color(0xFF6200EE)
|
||||
val Purple700 = Color(0xFF3700B3)
|
||||
val Teal200 = Color(0xFF03DAC5)
|
||||
val Gray = Color(0x22222222)
|
||||
val SimplexBlue = Color(0, 136, 255, 255)
|
||||
val SimplexBlue = Color(0, 136, 255, 255) // If this value changes also need to update #0088ff in string resource files
|
||||
val SimplexGreen = Color(98, 196, 103, 255)
|
||||
val SecretColor = Color(0x40808080)
|
||||
val LightGray = Color(241, 242, 246, 255)
|
||||
|
||||
@@ -5,7 +5,7 @@ import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
|
||||
private val DarkColorPalette = darkColors(
|
||||
primary = SimplexBlue,
|
||||
primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files
|
||||
primaryVariant = SimplexGreen,
|
||||
secondary = DarkGray,
|
||||
// background = Color.Black,
|
||||
@@ -20,7 +20,7 @@ private val DarkColorPalette = darkColors(
|
||||
// onError: Color = Color.Black,
|
||||
)
|
||||
private val LightColorPalette = lightColors(
|
||||
primary = SimplexBlue,
|
||||
primary = SimplexBlue, // If this value changes also need to update #0088ff in string resource files
|
||||
primaryVariant = SimplexGreen,
|
||||
secondary = LightGray,
|
||||
// background = Color.White,
|
||||
|
||||
@@ -19,9 +19,7 @@ import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chat.SendMsgView
|
||||
import chat.simplex.app.views.helpers.CloseSheetBar
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.newchat.ModalManager
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -43,7 +41,15 @@ fun TerminalLayout(terminalItems: List<TerminalItem>, close: () -> Unit, sendCom
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
Scaffold(
|
||||
topBar = { CloseSheetBar(close) },
|
||||
bottomBar = { SendMsgView(msg = remember { mutableStateOf("") }, sendCommand) },
|
||||
bottomBar = {
|
||||
SendMsgView(
|
||||
msg = remember { mutableStateOf("") },
|
||||
linkPreview = remember { mutableStateOf(null) },
|
||||
cancelledLinks = remember { mutableSetOf() },
|
||||
parseMarkdown = { null },
|
||||
sendMessage = sendCommand
|
||||
)
|
||||
},
|
||||
modifier = Modifier.navigationBarsWithImePadding()
|
||||
) { contentPadding ->
|
||||
Surface(
|
||||
|
||||
@@ -17,6 +17,7 @@ import chat.simplex.app.R
|
||||
import chat.simplex.app.SimplexService
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.model.Profile
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
@@ -39,22 +40,22 @@ fun WelcomeView(chatModel: ChatModel) {
|
||||
) {
|
||||
Image(
|
||||
painter = painterResource(R.drawable.logo),
|
||||
contentDescription = "Simplex Logo",
|
||||
contentDescription = generalGetString(R.string.image_descr_simplex_logo),
|
||||
modifier = Modifier.padding(vertical = 15.dp)
|
||||
)
|
||||
Text(
|
||||
"You control your chat!",
|
||||
generalGetString(R.string.you_control_your_chat),
|
||||
style = MaterialTheme.typography.h4,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Text(
|
||||
"The messaging and application platform protecting your privacy and security.",
|
||||
generalGetString(R.string.the_messaging_and_app_platform_protecting_your_privacy_and_security),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Spacer(Modifier.height(8.dp))
|
||||
Text(
|
||||
"We don't store any of your contacts or messages (once delivered) on the servers.",
|
||||
generalGetString(R.string.we_do_not_store_contacts_or_messages_on_servers),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
@@ -79,19 +80,19 @@ fun CreateProfilePanel(chatModel: ChatModel) {
|
||||
modifier=Modifier.fillMaxSize()
|
||||
) {
|
||||
Text(
|
||||
"Create profile",
|
||||
generalGetString(R.string.create_profile),
|
||||
style = MaterialTheme.typography.h4,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.padding(vertical = 5.dp)
|
||||
)
|
||||
Text(
|
||||
"Your profile is stored on your device and shared only with your contacts.",
|
||||
generalGetString(R.string.your_profile_is_stored_on_your_decide_and_shared_only_with_your_contacts),
|
||||
style = MaterialTheme.typography.body1,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Spacer(Modifier.height(10.dp))
|
||||
Text(
|
||||
"Display Name",
|
||||
generalGetString(R.string.display_name),
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.padding(bottom = 3.dp)
|
||||
@@ -113,7 +114,7 @@ fun CreateProfilePanel(chatModel: ChatModel) {
|
||||
),
|
||||
singleLine = true
|
||||
)
|
||||
val errorText = if(!isValidDisplayName(displayName)) "Display name cannot contain whitespace." else ""
|
||||
val errorText = if(!isValidDisplayName(displayName)) generalGetString(R.string.display_name_cannot_contain_whitespace) else ""
|
||||
|
||||
Text(
|
||||
errorText,
|
||||
@@ -123,7 +124,7 @@ fun CreateProfilePanel(chatModel: ChatModel) {
|
||||
|
||||
Spacer(Modifier.height(3.dp))
|
||||
Text(
|
||||
"Full Name (Optional)",
|
||||
generalGetString(R.string.full_name_optional__prompt),
|
||||
style = MaterialTheme.typography.h6,
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.padding(bottom = 5.dp)
|
||||
@@ -157,6 +158,6 @@ fun CreateProfilePanel(chatModel: ChatModel) {
|
||||
}
|
||||
},
|
||||
enabled = (displayName.isNotEmpty() && isValidDisplayName(displayName))
|
||||
) { Text("Create") }
|
||||
) { Text(generalGetString(R.string.create_profile_button)) }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,6 +16,7 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.helpers.*
|
||||
@@ -29,9 +30,9 @@ fun ChatInfoView(chatModel: ChatModel, close: () -> Unit) {
|
||||
close = close,
|
||||
deleteContact = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = "Delete contact?",
|
||||
text = "Contact and all messages will be deleted - this cannot be undone!",
|
||||
confirmText = "Delete",
|
||||
title = generalGetString(R.string.delete_contact__question),
|
||||
text = generalGetString(R.string.delete_contact_all_messages_deleted_cannot_undo_warning),
|
||||
confirmText = generalGetString(R.string.delete_verb),
|
||||
onConfirm = {
|
||||
val cInfo = chat.chatInfo
|
||||
withApi {
|
||||
@@ -96,7 +97,8 @@ fun ChatInfoLayout(chat: Chat, close: () -> Unit, deleteContact: () -> Unit) {
|
||||
|
||||
Box(Modifier.padding(48.dp)) {
|
||||
SimpleButton(
|
||||
"Delete contact", icon = Icons.Outlined.Delete,
|
||||
generalGetString(R.string.button_delete_contact),
|
||||
icon = Icons.Outlined.Delete,
|
||||
color = Color.Red,
|
||||
click = deleteContact
|
||||
)
|
||||
@@ -110,13 +112,13 @@ fun ServerImage(chat: Chat) {
|
||||
val status = chat.serverInfo.networkStatus
|
||||
when {
|
||||
status is Chat.NetworkStatus.Connected ->
|
||||
Icon(Icons.Filled.Circle, "Connected", tint = MaterialTheme.colors.primaryVariant)
|
||||
Icon(Icons.Filled.Circle, generalGetString(R.string.icon_descr_server_status_connected), tint = MaterialTheme.colors.primaryVariant)
|
||||
status is Chat.NetworkStatus.Disconnected ->
|
||||
Icon(Icons.Filled.Pending, "Disconnected", tint = HighOrLowlight)
|
||||
Icon(Icons.Filled.Pending, generalGetString(R.string.icon_descr_server_status_disconnected), tint = HighOrLowlight)
|
||||
status is Chat.NetworkStatus.Error ->
|
||||
Icon(Icons.Filled.Error, "Error", tint = HighOrLowlight)
|
||||
Icon(Icons.Filled.Error, generalGetString(R.string.icon_descr_server_status_error), tint = HighOrLowlight)
|
||||
else ->
|
||||
Icon(Icons.Outlined.Circle, "Pending", tint = HighOrLowlight)
|
||||
Icon(Icons.Outlined.Circle, generalGetString(R.string.icon_descr_server_status_pending), tint = HighOrLowlight)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -22,17 +22,16 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.TAG
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.*
|
||||
import chat.simplex.app.views.chat.item.ChatItemView
|
||||
import chat.simplex.app.views.chatlist.openChat
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.ModalManager
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.launch
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@Composable
|
||||
@@ -44,7 +43,9 @@ fun ChatView(chatModel: ChatModel) {
|
||||
} else {
|
||||
val quotedItem = remember { mutableStateOf<ChatItem?>(null) }
|
||||
val editingItem = remember { mutableStateOf<ChatItem?>(null) }
|
||||
val linkPreview = remember { mutableStateOf<LinkPreview?>(null) }
|
||||
var msg = remember { mutableStateOf("") }
|
||||
|
||||
BackHandler { chatModel.chatId.value = null }
|
||||
// TODO a more advanced version would mark as read only if in view
|
||||
LaunchedEffect(chat.chatItems) {
|
||||
@@ -62,7 +63,7 @@ fun ChatView(chatModel: ChatModel) {
|
||||
}
|
||||
}
|
||||
}
|
||||
ChatLayout(user, chat, chatModel.chatItems, msg, quotedItem, editingItem,
|
||||
ChatLayout(user, chat, chatModel.chatItems, msg, quotedItem, editingItem, linkPreview,
|
||||
back = { chatModel.chatId.value = null },
|
||||
info = { ModalManager.shared.showCustomModal { close -> ChatInfoView(chatModel, close) } },
|
||||
openDirectChat = { contactId ->
|
||||
@@ -85,17 +86,19 @@ fun ChatView(chatModel: ChatModel) {
|
||||
)
|
||||
if (updatedItem != null) chatModel.upsertChatItem(cInfo, updatedItem.chatItem)
|
||||
} else {
|
||||
val linkPreviewData = linkPreview.value
|
||||
val newItem = chatModel.controller.apiSendMessage(
|
||||
type = cInfo.chatType,
|
||||
id = cInfo.apiId,
|
||||
quotedItemId = quotedItem.value?.meta?.itemId,
|
||||
mc = MsgContent.MCText(msg)
|
||||
mc = if (linkPreviewData != null) MsgContent.MCLink(msg, linkPreviewData) else MsgContent.MCText(msg)
|
||||
)
|
||||
if (newItem != null) chatModel.addChatItem(cInfo, newItem.chatItem)
|
||||
}
|
||||
// hide "in progress"
|
||||
editingItem.value = null
|
||||
quotedItem.value = null
|
||||
linkPreview.value = null
|
||||
}
|
||||
},
|
||||
resetMessage = { msg.value = "" },
|
||||
@@ -110,7 +113,8 @@ fun ChatView(chatModel: ChatModel) {
|
||||
)
|
||||
if (toItem != null) chatModel.removeChatItem(cInfo, toItem.chatItem)
|
||||
}
|
||||
}
|
||||
},
|
||||
parseMarkdown = { text -> runBlocking { chatModel.controller.apiParseMarkdown(text) } }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -123,12 +127,14 @@ fun ChatLayout(
|
||||
msg: MutableState<String>,
|
||||
quotedItem: MutableState<ChatItem?>,
|
||||
editingItem: MutableState<ChatItem?>,
|
||||
linkPreview: MutableState<LinkPreview?>,
|
||||
back: () -> Unit,
|
||||
info: () -> Unit,
|
||||
openDirectChat: (Long) -> Unit,
|
||||
sendMessage: (String) -> Unit,
|
||||
resetMessage: () -> Unit,
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit
|
||||
deleteMessage: (Long, CIDeleteMode) -> Unit,
|
||||
parseMarkdown: (String) -> List<FormattedText>?
|
||||
) {
|
||||
Surface(
|
||||
Modifier
|
||||
@@ -138,7 +144,7 @@ fun ChatLayout(
|
||||
ProvideWindowInsets(windowInsetsAnimationsEnabled = true) {
|
||||
Scaffold(
|
||||
topBar = { ChatInfoToolbar(chat, back, info) },
|
||||
bottomBar = { ComposeView(msg, quotedItem, editingItem, sendMessage, resetMessage) },
|
||||
bottomBar = { ComposeView(msg, quotedItem, editingItem, linkPreview, sendMessage, resetMessage, parseMarkdown) },
|
||||
modifier = Modifier.navigationBarsWithImePadding()
|
||||
) { contentPadding ->
|
||||
Box(Modifier.padding(contentPadding)) {
|
||||
@@ -163,7 +169,7 @@ fun ChatInfoToolbar(chat: Chat, back: () -> Unit, info: () -> Unit) {
|
||||
IconButton(onClick = back) {
|
||||
Icon(
|
||||
Icons.Outlined.ArrowBackIos,
|
||||
"Back",
|
||||
generalGetString(R.string.back),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(10.dp)
|
||||
)
|
||||
@@ -335,12 +341,14 @@ fun PreviewChatLayout() {
|
||||
msg = remember { mutableStateOf("") },
|
||||
quotedItem = remember { mutableStateOf(null) },
|
||||
editingItem = remember { mutableStateOf(null) },
|
||||
linkPreview = remember { mutableStateOf(null) },
|
||||
back = {},
|
||||
info = {},
|
||||
openDirectChat = {},
|
||||
sendMessage = {},
|
||||
resetMessage = {},
|
||||
deleteMessage = { _, _ -> }
|
||||
deleteMessage = { _, _ -> },
|
||||
parseMarkdown = { null }
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -378,12 +386,14 @@ fun PreviewGroupChatLayout() {
|
||||
msg = remember { mutableStateOf("") },
|
||||
quotedItem = remember { mutableStateOf(null) },
|
||||
editingItem = remember { mutableStateOf(null) },
|
||||
linkPreview = remember { mutableStateOf(null) },
|
||||
back = {},
|
||||
info = {},
|
||||
openDirectChat = {},
|
||||
sendMessage = {},
|
||||
resetMessage = {},
|
||||
deleteMessage = { _, _ -> }
|
||||
deleteMessage = { _, _ -> },
|
||||
parseMarkdown = { null }
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,9 @@
|
||||
package chat.simplex.app.views.chat
|
||||
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.MutableState
|
||||
import chat.simplex.app.model.ChatItem
|
||||
import androidx.compose.runtime.*
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.views.helpers.ComposeLinkView
|
||||
|
||||
// TODO ComposeState
|
||||
|
||||
@@ -12,10 +12,24 @@ fun ComposeView(
|
||||
msg: MutableState<String>,
|
||||
quotedItem: MutableState<ChatItem?>,
|
||||
editingItem: MutableState<ChatItem?>,
|
||||
linkPreview: MutableState<LinkPreview?>,
|
||||
sendMessage: (String) -> Unit,
|
||||
resetMessage: () -> Unit
|
||||
resetMessage: () -> Unit,
|
||||
parseMarkdown: (String) -> List<FormattedText>?
|
||||
) {
|
||||
val cancelledLinks = remember { mutableSetOf<String>() }
|
||||
|
||||
fun cancelPreview() {
|
||||
val uri = linkPreview.value?.uri
|
||||
if (uri != null) {
|
||||
cancelledLinks.add(uri)
|
||||
}
|
||||
linkPreview.value = null
|
||||
}
|
||||
|
||||
Column {
|
||||
val lp = linkPreview.value
|
||||
if (lp != null) ComposeLinkView(lp, ::cancelPreview)
|
||||
when {
|
||||
quotedItem.value != null -> {
|
||||
ContextItemView(quotedItem)
|
||||
@@ -25,6 +39,6 @@ fun ComposeView(
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
SendMsgView(msg, sendMessage, editing = editingItem.value != null)
|
||||
SendMsgView(msg, linkPreview, cancelledLinks, parseMarkdown, sendMessage, editing = editingItem.value != null)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -12,10 +12,12 @@ import androidx.compose.ui.text.buildAnnotatedString
|
||||
import androidx.compose.ui.text.withStyle
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.CIDirection
|
||||
import chat.simplex.app.model.ChatItem
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chat.item.*
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@Composable
|
||||
@@ -50,7 +52,7 @@ fun ContextItemView(
|
||||
}) {
|
||||
Icon(
|
||||
Icons.Outlined.Close,
|
||||
contentDescription = "Cancel",
|
||||
contentDescription = generalGetString(R.string.cancel_verb),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(10.dp)
|
||||
)
|
||||
|
||||
@@ -20,22 +20,82 @@ import androidx.compose.ui.graphics.SolidColor
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chat.item.*
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
@Composable
|
||||
fun SendMsgView(msg: MutableState<String>, sendMessage: (String) -> Unit, editing: Boolean = false) {
|
||||
fun SendMsgView(
|
||||
msg: MutableState<String>,
|
||||
linkPreview: MutableState<LinkPreview?>,
|
||||
cancelledLinks: MutableSet<String>,
|
||||
parseMarkdown: (String) -> List<FormattedText>?,
|
||||
sendMessage: (String) -> Unit,
|
||||
editing: Boolean = false
|
||||
) {
|
||||
val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground)
|
||||
var textStyle by remember { mutableStateOf(smallFont) }
|
||||
val linkUrl = remember { mutableStateOf<String?>(null) }
|
||||
val prevLinkUrl = remember { mutableStateOf<String?>(null) }
|
||||
val pendingLinkUrl = remember { mutableStateOf<String?>(null) }
|
||||
|
||||
fun isSimplexLink(link: String): Boolean =
|
||||
link.startsWith("https://simplex.chat",true) || link.startsWith("http://simplex.chat", true)
|
||||
|
||||
fun parseMessage(msg: String): String? {
|
||||
val parsedMsg = parseMarkdown(msg)
|
||||
val link = parsedMsg?.firstOrNull { ft -> ft.format is Format.Uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) }
|
||||
return link?.text
|
||||
}
|
||||
|
||||
fun loadLinkPreview(url: String, wait: Long? = null) {
|
||||
if (pendingLinkUrl.value == url) {
|
||||
withApi {
|
||||
if (wait != null) delay(wait)
|
||||
val lp = getLinkPreview(url)
|
||||
if (pendingLinkUrl.value == url) {
|
||||
linkPreview.value = lp
|
||||
pendingLinkUrl.value = null
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
fun showLinkPreview(s: String) {
|
||||
prevLinkUrl.value = linkUrl.value
|
||||
linkUrl.value = parseMessage(s)
|
||||
val url = linkUrl.value
|
||||
if (url != null) {
|
||||
if (url != linkPreview.value?.uri && url != pendingLinkUrl.value) {
|
||||
pendingLinkUrl.value = url
|
||||
loadLinkPreview(url, wait = if (prevLinkUrl.value == url) null else 1500L)
|
||||
}
|
||||
} else {
|
||||
linkPreview.value = null
|
||||
}
|
||||
}
|
||||
|
||||
fun resetLinkPreview() {
|
||||
linkUrl.value = null
|
||||
prevLinkUrl.value = null
|
||||
pendingLinkUrl.value = null
|
||||
cancelledLinks.clear()
|
||||
}
|
||||
|
||||
BasicTextField(
|
||||
value = msg.value,
|
||||
onValueChange = {
|
||||
msg.value = it
|
||||
textStyle = if (isShortEmoji(it)) {
|
||||
if (it.codePoints().count() < 4) largeEmojiFont else mediumEmojiFont
|
||||
onValueChange = { s ->
|
||||
msg.value = s
|
||||
if (isShortEmoji(s)) {
|
||||
textStyle = if (s.codePoints().count() < 4) largeEmojiFont else mediumEmojiFont
|
||||
} else {
|
||||
smallFont
|
||||
textStyle = smallFont
|
||||
if (s.isNotEmpty()) showLinkPreview(s)
|
||||
else resetLinkPreview()
|
||||
}
|
||||
},
|
||||
textStyle = textStyle,
|
||||
@@ -67,7 +127,7 @@ fun SendMsgView(msg: MutableState<String>, sendMessage: (String) -> Unit, editin
|
||||
val color = if (msg.value.isNotEmpty()) MaterialTheme.colors.primary else Color.Gray
|
||||
Icon(
|
||||
if (editing) Icons.Filled.Check else Icons.Outlined.ArrowUpward,
|
||||
"Send Message",
|
||||
generalGetString(R.string.icon_descr_send_message),
|
||||
tint = Color.White,
|
||||
modifier = Modifier
|
||||
.size(36.dp)
|
||||
@@ -99,6 +159,9 @@ fun PreviewSendMsgView() {
|
||||
SimpleXTheme {
|
||||
SendMsgView(
|
||||
msg = remember { mutableStateOf("") },
|
||||
linkPreview = remember {mutableStateOf<LinkPreview?>(null) },
|
||||
cancelledLinks = mutableSetOf(),
|
||||
parseMarkdown = { null },
|
||||
sendMessage = { msg -> println(msg) }
|
||||
)
|
||||
}
|
||||
@@ -115,7 +178,10 @@ fun PreviewSendMsgViewEditing() {
|
||||
SimpleXTheme {
|
||||
SendMsgView(
|
||||
msg = remember { mutableStateOf("") },
|
||||
linkPreview = remember {mutableStateOf<LinkPreview?>(null) },
|
||||
cancelledLinks = mutableSetOf(),
|
||||
sendMessage = { msg -> println(msg) },
|
||||
parseMarkdown = { null },
|
||||
editing = true
|
||||
)
|
||||
}
|
||||
|
||||
@@ -4,43 +4,64 @@ import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.Icon
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.filled.Edit
|
||||
import androidx.compose.material.icons.filled.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.model.CIDirection
|
||||
import chat.simplex.app.model.ChatItem
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimplexBlue
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@Composable
|
||||
fun CIMetaView(chatItem: ChatItem) {
|
||||
Row(
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
if (!chatItem.isDeletedContent) {
|
||||
if (chatItem.meta.itemEdited) {
|
||||
Icon(
|
||||
Icons.Filled.Edit,
|
||||
modifier = Modifier.height(12.dp),
|
||||
contentDescription = "Edited",
|
||||
modifier = Modifier.height(12.dp).padding(end = 1.dp),
|
||||
contentDescription = generalGetString(R.string.icon_descr_edited),
|
||||
tint = HighOrLowlight,
|
||||
)
|
||||
}
|
||||
// TODO status
|
||||
CIStatusView(chatItem.meta.itemStatus)
|
||||
}
|
||||
Text(
|
||||
chatItem.timestampText,
|
||||
color = HighOrLowlight,
|
||||
fontSize = 14.sp
|
||||
fontSize = 14.sp,
|
||||
modifier = Modifier.padding(start = 3.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Composable
|
||||
fun CIStatusView(status: CIStatus) {
|
||||
when (status) {
|
||||
is CIStatus.SndSent -> {
|
||||
Icon(Icons.Filled.Check, generalGetString(R.string.icon_descr_sent_msg_status_sent), Modifier.height(12.dp), tint = HighOrLowlight)
|
||||
}
|
||||
is CIStatus.SndErrorAuth -> {
|
||||
Icon(Icons.Filled.Close, generalGetString(R.string.icon_descr_sent_msg_status_unauthorized_send), Modifier.height(12.dp), tint = Color.Red)
|
||||
}
|
||||
is CIStatus.SndError -> {
|
||||
Icon(Icons.Filled.WarningAmber, generalGetString(R.string.icon_descr_sent_msg_status_send_failed), Modifier.height(12.dp), tint = Color.Yellow)
|
||||
}
|
||||
is CIStatus.RcvNew -> {
|
||||
Icon(Icons.Filled.Circle, generalGetString(R.string.icon_descr_received_msg_status_unread), Modifier.height(12.dp), tint = SimplexBlue)
|
||||
}
|
||||
else -> {}
|
||||
}
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaView() {
|
||||
@@ -51,6 +72,48 @@ fun PreviewCIMetaView() {
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaViewUnread() {
|
||||
CIMetaView(
|
||||
chatItem = ChatItem.getSampleData(
|
||||
1, CIDirection.DirectSnd(), Clock.System.now(), "hello",
|
||||
status = CIStatus.RcvNew()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaViewSendFailed() {
|
||||
CIMetaView(
|
||||
chatItem = ChatItem.getSampleData(
|
||||
1, CIDirection.DirectSnd(), Clock.System.now(), "hello",
|
||||
status = CIStatus.SndError(AgentErrorType.CMD(CommandErrorType.SYNTAX()))
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaViewSendNoAuth() {
|
||||
CIMetaView(
|
||||
chatItem = ChatItem.getSampleData(
|
||||
1, CIDirection.DirectSnd(), Clock.System.now(), "hello", status = CIStatus.SndErrorAuth()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaViewSendSent() {
|
||||
CIMetaView(
|
||||
chatItem = ChatItem.getSampleData(
|
||||
1, CIDirection.DirectSnd(), Clock.System.now(), "hello", status = CIStatus.SndSent()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaViewEdited() {
|
||||
@@ -62,6 +125,30 @@ fun PreviewCIMetaViewEdited() {
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaViewEditedUnread() {
|
||||
CIMetaView(
|
||||
chatItem = ChatItem.getSampleData(
|
||||
1, CIDirection.DirectRcv(), Clock.System.now(), "hello",
|
||||
itemEdited = true,
|
||||
status=CIStatus.RcvNew()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaViewEditedSent() {
|
||||
CIMetaView(
|
||||
chatItem = ChatItem.getSampleData(
|
||||
1, CIDirection.DirectSnd(), Clock.System.now(), "hello",
|
||||
itemEdited = true,
|
||||
status=CIStatus.SndSent()
|
||||
)
|
||||
)
|
||||
}
|
||||
|
||||
@Preview
|
||||
@Composable
|
||||
fun PreviewCIMetaViewDeletedContent() {
|
||||
|
||||
@@ -16,8 +16,8 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.platform.UriHandler
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import kotlinx.datetime.Clock
|
||||
@@ -55,21 +55,21 @@ fun ChatItemView(
|
||||
}
|
||||
if (cItem.isMsgContent) {
|
||||
DropdownMenu(expanded = showMenu, onDismissRequest = { showMenu = false }) {
|
||||
ItemAction("Reply", Icons.Outlined.Reply, onClick = {
|
||||
ItemAction(generalGetString(R.string.reply_verb), Icons.Outlined.Reply, onClick = {
|
||||
editingItem.value = null
|
||||
quotedItem.value = cItem
|
||||
showMenu = false
|
||||
})
|
||||
ItemAction("Share", Icons.Outlined.Share, onClick = {
|
||||
ItemAction(generalGetString(R.string.share_verb), Icons.Outlined.Share, onClick = {
|
||||
shareText(cxt, cItem.content.text)
|
||||
showMenu = false
|
||||
})
|
||||
ItemAction("Copy", Icons.Outlined.ContentCopy, onClick = {
|
||||
ItemAction(generalGetString(R.string.copy_verb), Icons.Outlined.ContentCopy, onClick = {
|
||||
copyText(cxt, cItem.content.text)
|
||||
showMenu = false
|
||||
})
|
||||
if (cItem.chatDir.sent && cItem.meta.editable) {
|
||||
ItemAction("Edit", Icons.Filled.Edit, onClick = {
|
||||
ItemAction(generalGetString(R.string.edit_verb), Icons.Filled.Edit, onClick = {
|
||||
quotedItem.value = null
|
||||
editingItem.value = cItem
|
||||
msg.value = cItem.content.text
|
||||
@@ -77,7 +77,7 @@ fun ChatItemView(
|
||||
})
|
||||
}
|
||||
ItemAction(
|
||||
"Delete",
|
||||
generalGetString(R.string.delete_verb),
|
||||
Icons.Outlined.Delete,
|
||||
onClick = {
|
||||
showMenu = false
|
||||
@@ -109,8 +109,8 @@ private fun ItemAction(text: String, icon: ImageVector, onClick: () -> Unit, col
|
||||
|
||||
fun deleteMessageAlertDialog(chatItem: ChatItem, deleteMessage: (Long, CIDeleteMode) -> Unit) {
|
||||
AlertManager.shared.showAlertDialogButtons(
|
||||
title = "Delete message?",
|
||||
text = "Message will be deleted - this cannot be undone!",
|
||||
title = generalGetString(R.string.delete_message__question),
|
||||
text = generalGetString(R.string.delete_message_cannot_be_undone_warning),
|
||||
buttons = {
|
||||
Row(
|
||||
Modifier
|
||||
@@ -121,13 +121,13 @@ fun deleteMessageAlertDialog(chatItem: ChatItem, deleteMessage: (Long, CIDeleteM
|
||||
Button(onClick = {
|
||||
deleteMessage(chatItem.id, CIDeleteMode.cidmInternal)
|
||||
AlertManager.shared.hideAlert()
|
||||
}) { Text("For me only") }
|
||||
}) { Text(generalGetString(R.string.for_me_only)) }
|
||||
// if (chatItem.meta.editable) {
|
||||
// Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
// Button(onClick = {
|
||||
// deleteMessage(chatItem.id, CIDeleteMode.cidmBroadcast)
|
||||
// AlertManager.shared.hideAlert()
|
||||
// }) { Text("For everyone") }
|
||||
// }) { Text(generalGetString(R.string.for_everybody)) }
|
||||
// }
|
||||
}
|
||||
}
|
||||
|
||||
@@ -1,9 +1,8 @@
|
||||
package chat.simplex.app.views.chat.item
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.BorderStroke
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.layout.Row
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.shape.RoundedCornerShape
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -11,10 +10,10 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import androidx.compose.ui.tooling.preview.*
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.model.ChatItem
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
|
||||
|
||||
@@ -15,6 +15,7 @@ import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.ChatItemLinkView
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
val SentColorLight = Color(0x1E45B8FF)
|
||||
@@ -45,8 +46,8 @@ fun FramedItemView(user: User, ci: ChatItem, uriHandler: UriHandler? = null, sho
|
||||
)
|
||||
}
|
||||
}
|
||||
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
|
||||
if (ci.formattedText == null && isShortEmoji(ci.content.text)) {
|
||||
if (ci.formattedText == null && isShortEmoji(ci.content.text)) {
|
||||
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
|
||||
Column(
|
||||
Modifier
|
||||
.padding(bottom = 2.dp)
|
||||
@@ -56,11 +57,19 @@ fun FramedItemView(user: User, ci: ChatItem, uriHandler: UriHandler? = null, sho
|
||||
EmojiText(ci.content.text)
|
||||
Text("")
|
||||
}
|
||||
} else {
|
||||
MarkdownText(
|
||||
ci.content, ci.formattedText, if (showMember) ci.memberDisplayName else null,
|
||||
metaText = ci.timestampText, edited = ci.meta.itemEdited, uriHandler = uriHandler, senderBold = true
|
||||
)
|
||||
}
|
||||
} else {
|
||||
Column(Modifier.fillMaxWidth()) {
|
||||
val mc = ci.content.msgContent
|
||||
if (mc is MsgContent.MCLink) {
|
||||
ChatItemLinkView(mc.preview)
|
||||
}
|
||||
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
|
||||
MarkdownText(
|
||||
ci.content, ci.formattedText, if (showMember) ci.memberDisplayName else null,
|
||||
metaText = ci.timestampText, edited = ci.meta.itemEdited, uriHandler = uriHandler, senderBold = true
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -47,7 +47,7 @@ fun MarkdownText (
|
||||
senderBold: Boolean = false,
|
||||
modifier: Modifier = Modifier
|
||||
) {
|
||||
val reserve = if (edited) " " else " "
|
||||
val reserve = if (edited) " " else " "
|
||||
if (formattedText == null) {
|
||||
val annotatedText = buildAnnotatedString {
|
||||
appendSender(this, sender, senderBold)
|
||||
|
||||
@@ -1,4 +1,4 @@
|
||||
package chat.simplex.app.views.chat
|
||||
package chat.simplex.app.views.chatlist
|
||||
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.clickable
|
||||
@@ -10,11 +10,15 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.SpanStyle
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.annotatedStringResource
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import chat.simplex.app.views.usersettings.simplexTeamUri
|
||||
|
||||
val bold = SpanStyle(fontWeight = FontWeight.Bold)
|
||||
@@ -27,18 +31,13 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
|
||||
) {
|
||||
val uriHandler = LocalUriHandler.current
|
||||
|
||||
Text("Thank you for installing SimpleX Chat!")
|
||||
Text(generalGetString(R.string.thank_you_for_installing_simplex), lineHeight = 22.sp)
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("You can ")
|
||||
withStyle(SpanStyle(color = MaterialTheme.colors.primary)) {
|
||||
append("connect to SimpleX Chat founder")
|
||||
}
|
||||
append(".")
|
||||
},
|
||||
annotatedStringResource(R.string.you_can_connect_to_simplex_chat_founder),
|
||||
modifier = Modifier.clickable(onClick = {
|
||||
uriHandler.openUri(simplexTeamUri)
|
||||
})
|
||||
}),
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
|
||||
Column(
|
||||
@@ -47,33 +46,24 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Text(
|
||||
"To start a new chat",
|
||||
style = MaterialTheme.typography.h2
|
||||
generalGetString(R.string.to_start_a_new_chat_help_header),
|
||||
style = MaterialTheme.typography.h2,
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text("Tap button")
|
||||
Text(generalGetString(R.string.chat_help_tap_button))
|
||||
Icon(
|
||||
Icons.Outlined.PersonAdd,
|
||||
"Add Contact",
|
||||
generalGetString(R.string.add_contact),
|
||||
modifier = if (addContact != null) Modifier.clickable(onClick = addContact) else Modifier,
|
||||
)
|
||||
Text("above, then:")
|
||||
Text(generalGetString(R.string.above_then_preposition_continuation))
|
||||
}
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
withStyle(bold) { append("Add new contact") }
|
||||
append(": to create your one-time QR Code for your contact.")
|
||||
}
|
||||
)
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
withStyle(bold) { append("Scan QR code") }
|
||||
append(": to connect to your contact who shows QR code to you.")
|
||||
}
|
||||
)
|
||||
Text(annotatedStringResource(R.string.add_new_contact_to_create_one_time_QR_code), lineHeight = 22.sp)
|
||||
Text(annotatedStringResource(R.string.scan_QR_code_to_connect_to_contact_who_shows_QR_code), lineHeight = 22.sp)
|
||||
}
|
||||
|
||||
Column(
|
||||
@@ -81,24 +71,10 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(10.dp)
|
||||
) {
|
||||
Text("To connect via link", style = MaterialTheme.typography.h2)
|
||||
Text("If you received SimpleX Chat invitation link you can open it in your browser:")
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("\uD83D\uDCBB desktop: scan displayed QR code from the app, via ")
|
||||
withStyle(bold) { append("Scan QR code") }
|
||||
append(".")
|
||||
}
|
||||
)
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("\uD83D\uDCF1 mobile: tap ")
|
||||
withStyle(bold) { append("Open in mobile app") }
|
||||
append(", then tap ")
|
||||
withStyle(bold) { append("Connect") }
|
||||
append(" in the app.")
|
||||
}
|
||||
)
|
||||
Text(generalGetString(R.string.to_connect_via_link_title), style = MaterialTheme.typography.h2)
|
||||
Text(generalGetString(R.string.if_you_received_simplex_invitation_link_you_can_open_in_browser), lineHeight = 22.sp)
|
||||
Text(annotatedStringResource(R.string.desktop_scan_QR_code_from_app_via_scan_QR_code), lineHeight = 22.sp)
|
||||
Text(annotatedStringResource(R.string.mobile_tap_open_in_mobile_app_then_tap_connect_in_app), lineHeight = 22.sp)
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -112,6 +88,6 @@ fun ChatHelpView(addContact: (() -> Unit)? = null) {
|
||||
@Composable
|
||||
fun PreviewChatHelpLayout() {
|
||||
SimpleXTheme {
|
||||
ChatHelpView({})
|
||||
ChatHelpView {}
|
||||
}
|
||||
}
|
||||
|
||||
+6
-7
@@ -11,11 +11,10 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.*
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.AlertManager
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@Composable
|
||||
@@ -42,9 +41,9 @@ suspend fun openChat(chatModel: ChatModel, cInfo: ChatInfo) {
|
||||
|
||||
fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel: ChatModel) {
|
||||
AlertManager.shared.showAlertDialog(
|
||||
title = "Accept connection request?",
|
||||
text = "If you choose to reject sender will NOT be notified.",
|
||||
confirmText = "Accept",
|
||||
title = generalGetString(R.string.accept_connection_request__question),
|
||||
text = generalGetString(R.string.if_you_choose_to_reject_the_sender_will_not_be_notified),
|
||||
confirmText = generalGetString(R.string.accept_contact_button),
|
||||
onConfirm = {
|
||||
withApi {
|
||||
val contact = chatModel.controller.apiAcceptContactRequest(contactRequest.apiId)
|
||||
@@ -54,7 +53,7 @@ fun contactRequestAlertDialog(contactRequest: ChatInfo.ContactRequest, chatModel
|
||||
}
|
||||
}
|
||||
},
|
||||
dismissText = "Reject",
|
||||
dismissText = generalGetString(R.string.reject_contact_button),
|
||||
onDismiss = {
|
||||
withApi {
|
||||
chatModel.controller.apiRejectContactRequest(contactRequest.apiId)
|
||||
|
||||
@@ -14,11 +14,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.ToolbarDark
|
||||
import chat.simplex.app.ui.theme.ToolbarLight
|
||||
import chat.simplex.app.views.chat.ChatHelpView
|
||||
import chat.simplex.app.views.newchat.ModalManager
|
||||
import chat.simplex.app.views.helpers.ModalManager
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import chat.simplex.app.views.newchat.NewChatSheet
|
||||
import chat.simplex.app.views.usersettings.SettingsView
|
||||
import kotlinx.coroutines.CoroutineScope
|
||||
@@ -110,25 +111,28 @@ fun Help(scaffoldCtrl: ScaffoldController, displayName: String?) {
|
||||
.fillMaxWidth()
|
||||
.padding(16.dp)
|
||||
) {
|
||||
val welcomeMsg = if (displayName != null) {
|
||||
String.format(generalGetString(R.string.personal_welcome), displayName)
|
||||
} else generalGetString(R.string.welcome)
|
||||
Text(
|
||||
text = if (displayName != null) "Welcome ${displayName}!" else "Welcome!",
|
||||
text = welcomeMsg,
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
ChatHelpView({ scaffoldCtrl.toggleSheet() })
|
||||
ChatHelpView { scaffoldCtrl.toggleSheet() }
|
||||
Row(
|
||||
Modifier.padding(top = 30.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
"This text is available in settings",
|
||||
generalGetString(R.string.this_text_is_available_in_settings),
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Icon(
|
||||
Icons.Outlined.Settings,
|
||||
"Settings",
|
||||
generalGetString(R.string.icon_descr_settings),
|
||||
tint = MaterialTheme.colors.onBackground,
|
||||
modifier = Modifier.clickable(onClick = { scaffoldCtrl.toggleDrawer() })
|
||||
)
|
||||
@@ -150,13 +154,13 @@ fun ChatListToolbar(scaffoldCtrl: ScaffoldController) {
|
||||
IconButton(onClick = { scaffoldCtrl.toggleDrawer() }) {
|
||||
Icon(
|
||||
Icons.Outlined.Menu,
|
||||
"Settings",
|
||||
generalGetString(R.string.icon_descr_settings),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(10.dp)
|
||||
)
|
||||
}
|
||||
Text(
|
||||
"Your chats",
|
||||
generalGetString(R.string.your_chats),
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
modifier = Modifier.padding(5.dp)
|
||||
@@ -164,7 +168,7 @@ fun ChatListToolbar(scaffoldCtrl: ScaffoldController) {
|
||||
IconButton(onClick = { scaffoldCtrl.toggleSheet() }) {
|
||||
Icon(
|
||||
Icons.Outlined.PersonAdd,
|
||||
"Add Contact",
|
||||
generalGetString(R.string.add_contact),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(10.dp)
|
||||
)
|
||||
|
||||
@@ -14,13 +14,13 @@ import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.Chat
|
||||
import chat.simplex.app.model.getTimestampText
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chat.item.MarkdownText
|
||||
import chat.simplex.app.views.helpers.ChatInfoImage
|
||||
import chat.simplex.app.views.helpers.badgeLayout
|
||||
import chat.simplex.app.views.helpers.*
|
||||
|
||||
@Composable
|
||||
fun ChatPreviewView(chat: Chat) {
|
||||
@@ -63,7 +63,7 @@ fun ChatPreviewView(chat: Chat) {
|
||||
val n = chat.chatStats.unreadCount
|
||||
if (n > 0) {
|
||||
Text(
|
||||
if (n < 1000) "$n" else "${n / 1000}k",
|
||||
if (n < 1000) "$n" else "${n / 1000}" + generalGetString(R.string.thousand_abbreviation),
|
||||
color = MaterialTheme.colors.onPrimary,
|
||||
fontSize = 14.sp,
|
||||
modifier = Modifier
|
||||
|
||||
+3
-1
@@ -8,10 +8,12 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.Chat
|
||||
import chat.simplex.app.model.getTimestampText
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.views.helpers.ChatInfoImage
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
|
||||
@Composable
|
||||
fun ContactRequestView(chat: Chat) {
|
||||
@@ -31,7 +33,7 @@ fun ContactRequestView(chat: Chat) {
|
||||
color = MaterialTheme.colors.primary
|
||||
)
|
||||
Text(
|
||||
"wants to connect to you!",
|
||||
generalGetString(R.string.contact_wants_to_connect_with_you),
|
||||
maxLines = 2,
|
||||
overflow = TextOverflow.Ellipsis
|
||||
)
|
||||
|
||||
@@ -4,6 +4,7 @@ import android.util.Log
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.mutableStateOf
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.TAG
|
||||
|
||||
class AlertManager {
|
||||
@@ -40,9 +41,9 @@ class AlertManager {
|
||||
fun showAlertDialog(
|
||||
title: String,
|
||||
text: String? = null,
|
||||
confirmText: String = "Ok",
|
||||
confirmText: String = generalGetString(R.string.ok),
|
||||
onConfirm: (() -> Unit)? = null,
|
||||
dismissText: String = "Cancel",
|
||||
dismissText: String = generalGetString(R.string.cancel_verb),
|
||||
onDismiss: (() -> Unit)? = null
|
||||
) {
|
||||
val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) }
|
||||
@@ -69,7 +70,7 @@ class AlertManager {
|
||||
|
||||
fun showAlertMsg(
|
||||
title: String, text: String? = null,
|
||||
confirmText: String = "Ok", onConfirm: (() -> Unit)? = null
|
||||
confirmText: String = generalGetString(R.string.ok), onConfirm: (() -> Unit)? = null
|
||||
) {
|
||||
val alertText: (@Composable () -> Unit)? = if (text == null) null else { -> Text(text) }
|
||||
showAlert {
|
||||
|
||||
@@ -17,6 +17,7 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.Chat
|
||||
import chat.simplex.app.model.ChatInfo
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
@@ -39,7 +40,7 @@ fun ProfileImage(
|
||||
if (image == null) {
|
||||
Icon(
|
||||
icon,
|
||||
contentDescription = "profile image placeholder",
|
||||
contentDescription = generalGetString(R.string.icon_descr_profile_image_placeholder),
|
||||
tint = MaterialTheme.colors.secondary,
|
||||
modifier = Modifier.fillMaxSize()
|
||||
)
|
||||
@@ -47,7 +48,7 @@ fun ProfileImage(
|
||||
val imageBitmap = base64ToBitmap(image).asImageBitmap()
|
||||
Image(
|
||||
imageBitmap,
|
||||
"profile image",
|
||||
generalGetString(R.string.image_descr_profile_image),
|
||||
contentScale = ContentScale.Crop,
|
||||
modifier = Modifier.size(size).padding(size / 12).clip(CircleShape)
|
||||
)
|
||||
|
||||
@@ -10,6 +10,7 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
|
||||
@Composable
|
||||
@@ -24,7 +25,7 @@ fun CloseSheetBar(close: () -> Unit) {
|
||||
IconButton(onClick = close) {
|
||||
Icon(
|
||||
Icons.Outlined.Close,
|
||||
"Close button",
|
||||
generalGetString(R.string.icon_descr_close_button),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(10.dp)
|
||||
)
|
||||
|
||||
@@ -27,37 +27,45 @@ import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.core.content.ContextCompat
|
||||
import androidx.core.content.FileProvider
|
||||
import chat.simplex.app.BuildConfig
|
||||
import chat.simplex.app.TAG
|
||||
import chat.simplex.app.*
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.views.newchat.ActionButton
|
||||
import java.io.ByteArrayOutputStream
|
||||
import java.io.File
|
||||
import kotlin.math.min
|
||||
import kotlin.math.sqrt
|
||||
|
||||
// Inspired by https://github.com/MakeItEasyDev/Jetpack-Compose-Capture-Image-Or-Choose-from-Gallery
|
||||
|
||||
fun bitmapToBase64(bitmap: Bitmap, squareCrop: Boolean = true): String {
|
||||
val size = 104
|
||||
var height = size
|
||||
var width = size
|
||||
private fun cropToSquare(image: Bitmap): Bitmap {
|
||||
var xOffset = 0
|
||||
var yOffset = 0
|
||||
if (bitmap.height < bitmap.width) {
|
||||
width = height * bitmap.width / bitmap.height
|
||||
xOffset = (width - height) / 2
|
||||
val side = min(image.height, image.width)
|
||||
if (image.height < image.width) {
|
||||
xOffset = (image.width - side) / 2
|
||||
} else {
|
||||
height = width * bitmap.height / bitmap.width
|
||||
yOffset = (height - width) / 2
|
||||
yOffset = (image.height - side) / 2
|
||||
}
|
||||
var image = bitmap
|
||||
while (image.width / 2 > width) {
|
||||
image = Bitmap.createScaledBitmap(image, image.width / 2, image.height / 2, true)
|
||||
}
|
||||
image = Bitmap.createScaledBitmap(image, width, height, true)
|
||||
if (squareCrop) {
|
||||
image = Bitmap.createBitmap(image, xOffset, yOffset, size, size)
|
||||
return Bitmap.createBitmap(image, xOffset, yOffset, side, side)
|
||||
}
|
||||
|
||||
fun resizeImageToDataSize(image: Bitmap, maxDataSize: Int): String {
|
||||
var img = image
|
||||
var str = compressImage(img)
|
||||
while (str.length > maxDataSize) {
|
||||
val ratio = sqrt(str.length.toDouble() / maxDataSize.toDouble())
|
||||
val clippedRatio = min(ratio, 2.0)
|
||||
val width = (img.width.toDouble() / clippedRatio).toInt()
|
||||
val height = img.height * width / img.width
|
||||
img = Bitmap.createScaledBitmap(img, width, height, true)
|
||||
str = compressImage(img)
|
||||
}
|
||||
return str
|
||||
}
|
||||
|
||||
private fun compressImage(bitmap: Bitmap): String {
|
||||
val stream = ByteArrayOutputStream()
|
||||
image.compress(Bitmap.CompressFormat.JPEG, 85, stream)
|
||||
bitmap.compress(Bitmap.CompressFormat.JPEG, 85, stream)
|
||||
return "data:image/jpg;base64," + Base64.encodeToString(stream.toByteArray(), Base64.NO_WRAP)
|
||||
}
|
||||
|
||||
@@ -126,12 +134,12 @@ fun GetImageBottomSheet(
|
||||
if (uri != null) {
|
||||
val source = ImageDecoder.createSource(context.contentResolver, uri)
|
||||
val bitmap = ImageDecoder.decodeBitmap(source)
|
||||
profileImageStr.value = bitmapToBase64(bitmap)
|
||||
profileImageStr.value = resizeImageToDataSize(cropToSquare(bitmap), maxDataSize = 12500)
|
||||
}
|
||||
}
|
||||
|
||||
val cameraLauncher = rememberCameraLauncher { bitmap: Bitmap? ->
|
||||
if (bitmap != null) profileImageStr.value = bitmapToBase64(bitmap)
|
||||
if (bitmap != null) profileImageStr.value = resizeImageToDataSize(cropToSquare(bitmap), maxDataSize = 12500)
|
||||
}
|
||||
|
||||
val permissionLauncher = rememberPermissionLauncher { isGranted: Boolean ->
|
||||
@@ -140,7 +148,7 @@ fun GetImageBottomSheet(
|
||||
else galleryLauncher.launch("image/*")
|
||||
hideBottomSheet()
|
||||
} else {
|
||||
Toast.makeText(context, "Permission Denied!", Toast.LENGTH_SHORT).show()
|
||||
Toast.makeText(context, generalGetString(R.string.toast_camera_permission_denied), Toast.LENGTH_SHORT).show()
|
||||
}
|
||||
}
|
||||
|
||||
@@ -158,7 +166,7 @@ fun GetImageBottomSheet(
|
||||
.padding(horizontal = 8.dp, vertical = 30.dp),
|
||||
horizontalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
ActionButton(null, "Use Camera", icon = Icons.Outlined.PhotoCamera) {
|
||||
ActionButton(null, generalGetString(R.string.use_camera_button), icon = Icons.Outlined.PhotoCamera) {
|
||||
when (PackageManager.PERMISSION_GRANTED) {
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.CAMERA) -> {
|
||||
cameraLauncher.launch(null)
|
||||
@@ -170,7 +178,7 @@ fun GetImageBottomSheet(
|
||||
}
|
||||
}
|
||||
}
|
||||
ActionButton(null, "From Gallery", icon = Icons.Outlined.Collections) {
|
||||
ActionButton(null, generalGetString(R.string.from_gallery_button), icon = Icons.Outlined.Collections) {
|
||||
when (PackageManager.PERMISSION_GRANTED) {
|
||||
ContextCompat.checkSelfPermission(context, Manifest.permission.READ_EXTERNAL_STORAGE) -> {
|
||||
galleryLauncher.launch("image/*")
|
||||
|
||||
@@ -0,0 +1,141 @@
|
||||
package chat.simplex.app.views.helpers
|
||||
|
||||
import android.content.res.Configuration
|
||||
import android.graphics.BitmapFactory
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.material.icons.Icons
|
||||
import androidx.compose.material.icons.outlined.Close
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.LinkPreview
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chat.item.SentColorLight
|
||||
import kotlinx.coroutines.Dispatchers
|
||||
import kotlinx.coroutines.withContext
|
||||
import org.jsoup.Jsoup
|
||||
|
||||
private const val OG_SELECT_QUERY = "meta[property^=og:]"
|
||||
|
||||
suspend fun getLinkPreview(url: String): LinkPreview? {
|
||||
return withContext(Dispatchers.IO) {
|
||||
try {
|
||||
val response = Jsoup.connect(url)
|
||||
.ignoreContentType(true)
|
||||
.timeout(10000)
|
||||
.followRedirects(true)
|
||||
.execute()
|
||||
val doc = response.parse()
|
||||
val ogTags = doc.select(OG_SELECT_QUERY)
|
||||
val imageUri = ogTags.firstOrNull { it.attr("property") == "og:image" }?.attr("content")
|
||||
if (imageUri != null) {
|
||||
try {
|
||||
val stream = java.net.URL(imageUri).openStream()
|
||||
val image = resizeImageToDataSize(BitmapFactory.decodeStream(stream), maxDataSize = 14000)
|
||||
// TODO add once supported in iOS
|
||||
// val description = ogTags.firstOrNull {
|
||||
// it.attr("property") == "og:description"
|
||||
// }?.attr("content") ?: ""
|
||||
val title = ogTags.firstOrNull { it.attr("property") == "og:title" }?.attr("content")
|
||||
if (title != null) {
|
||||
return@withContext LinkPreview(url, title, description = "", image)
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
}
|
||||
} catch (e: Exception) {
|
||||
e.printStackTrace()
|
||||
}
|
||||
return@withContext null
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
|
||||
@Composable
|
||||
fun ComposeLinkView(linkPreview: LinkPreview, cancelPreview: () -> Unit) {
|
||||
Row(
|
||||
Modifier.fillMaxWidth().padding(top = 8.dp).background(SentColorLight),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
val imageBitmap = base64ToBitmap(linkPreview.image).asImageBitmap()
|
||||
Image(
|
||||
imageBitmap,
|
||||
generalGetString(R.string.image_descr_link_preview),
|
||||
modifier = Modifier.width(80.dp).height(60.dp).padding(end = 8.dp)
|
||||
)
|
||||
Column(Modifier.fillMaxWidth().weight(1F)) {
|
||||
Text(linkPreview.title, maxLines = 1, overflow = TextOverflow.Ellipsis)
|
||||
Text(
|
||||
linkPreview.uri, maxLines = 1, overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.body2
|
||||
)
|
||||
}
|
||||
IconButton(onClick = cancelPreview, modifier = Modifier.padding(0.dp)) {
|
||||
Icon(
|
||||
Icons.Outlined.Close,
|
||||
contentDescription = generalGetString(R.string.icon_descr_cancel_link_preview),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(10.dp)
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatItemLinkView(linkPreview: LinkPreview) {
|
||||
Column {
|
||||
Image(
|
||||
base64ToBitmap(linkPreview.image).asImageBitmap(),
|
||||
generalGetString(R.string.image_descr_link_preview),
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
contentScale = ContentScale.FillWidth,
|
||||
)
|
||||
Column(Modifier.padding(top = 6.dp).padding(horizontal = 12.dp)) {
|
||||
Text(linkPreview.title, maxLines = 3, overflow = TextOverflow.Ellipsis, lineHeight = 22.sp, modifier = Modifier.padding(bottom = 4.dp))
|
||||
if (linkPreview.description != "") {
|
||||
Text(linkPreview.description, maxLines = 12, overflow = TextOverflow.Ellipsis, fontSize = 14.sp, lineHeight = 20.sp)
|
||||
}
|
||||
Text(linkPreview.uri, maxLines = 1, overflow = TextOverflow.Ellipsis, fontSize = 12.sp, color = HighOrLowlight)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
showBackground = true,
|
||||
name = "ChatItemLinkView (Dark Mode)"
|
||||
)
|
||||
@Composable
|
||||
fun PreviewChatItemLinkView() {
|
||||
SimpleXTheme {
|
||||
ChatItemLinkView(LinkPreview.sampleData)
|
||||
}
|
||||
}
|
||||
|
||||
@Preview(showBackground = true)
|
||||
@Preview(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
showBackground = true,
|
||||
name = "ComposeLinkView (Dark Mode)"
|
||||
)
|
||||
@Composable
|
||||
fun PreviewComposeLinkView() {
|
||||
SimpleXTheme {
|
||||
ComposeLinkView(LinkPreview.sampleData) { -> }
|
||||
}
|
||||
}
|
||||
@@ -1,4 +1,4 @@
|
||||
package chat.simplex.app.views.newchat
|
||||
package chat.simplex.app.views.helpers
|
||||
|
||||
import android.util.Log
|
||||
import androidx.activity.compose.BackHandler
|
||||
@@ -11,7 +11,6 @@ import androidx.compose.runtime.mutableStateOf
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.TAG
|
||||
import chat.simplex.app.views.helpers.CloseSheetBar
|
||||
|
||||
@Composable
|
||||
fun ModalView(close: () -> Unit, content: @Composable () -> Unit) {
|
||||
|
||||
@@ -1,9 +1,23 @@
|
||||
package chat.simplex.app.views.helpers
|
||||
|
||||
import android.content.res.Resources
|
||||
import android.graphics.Rect
|
||||
import android.graphics.Typeface
|
||||
import android.text.Spanned
|
||||
import android.text.SpannedString
|
||||
import android.text.style.*
|
||||
import android.view.ViewTreeObserver
|
||||
import androidx.annotation.StringRes
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.platform.LocalView
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.*
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.*
|
||||
import androidx.compose.ui.text.style.BaselineShift
|
||||
import androidx.compose.ui.text.style.TextDecoration
|
||||
import androidx.compose.ui.unit.*
|
||||
import androidx.core.text.HtmlCompat
|
||||
import chat.simplex.app.SimplexApp
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
fun withApi(action: suspend CoroutineScope.() -> Unit): Job =
|
||||
@@ -38,3 +52,146 @@ fun getKeyboardState(): State<KeyboardState> {
|
||||
|
||||
return keyboardState
|
||||
}
|
||||
|
||||
// Resource to annotated string from
|
||||
// https://stackoverflow.com/questions/68549248/android-jetpack-compose-how-to-show-styled-text-from-string-resources
|
||||
|
||||
fun generalGetString(id: Int) : String {
|
||||
return SimplexApp.context.getString(id)
|
||||
}
|
||||
|
||||
@Composable
|
||||
@ReadOnlyComposable
|
||||
private fun resources(): Resources {
|
||||
LocalConfiguration.current
|
||||
return LocalContext.current.resources
|
||||
}
|
||||
|
||||
fun Spanned.toHtmlWithoutParagraphs(): String {
|
||||
return HtmlCompat.toHtml(this, HtmlCompat.TO_HTML_PARAGRAPH_LINES_CONSECUTIVE)
|
||||
.substringAfter("<p dir=\"ltr\">").substringBeforeLast("</p>")
|
||||
}
|
||||
|
||||
fun Resources.getText(@StringRes id: Int, vararg args: Any): CharSequence {
|
||||
val escapedArgs = args.map {
|
||||
if (it is Spanned) it.toHtmlWithoutParagraphs() else it
|
||||
}.toTypedArray()
|
||||
val resource = SpannedString(getText(id))
|
||||
val htmlResource = resource.toHtmlWithoutParagraphs()
|
||||
val formattedHtml = String.format(htmlResource, *escapedArgs)
|
||||
return HtmlCompat.fromHtml(formattedHtml, HtmlCompat.FROM_HTML_MODE_LEGACY)
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun annotatedStringResource(@StringRes id: Int): AnnotatedString {
|
||||
val resources = resources()
|
||||
val density = LocalDensity.current
|
||||
return remember(id) {
|
||||
val text = resources.getText(id)
|
||||
spannableStringToAnnotatedString(text, density)
|
||||
}
|
||||
}
|
||||
|
||||
private fun spannableStringToAnnotatedString(
|
||||
text: CharSequence,
|
||||
density: Density,
|
||||
): AnnotatedString {
|
||||
return if (text is Spanned) {
|
||||
with(density) {
|
||||
buildAnnotatedString {
|
||||
append((text.toString()))
|
||||
text.getSpans(0, text.length, Any::class.java).forEach {
|
||||
val start = text.getSpanStart(it)
|
||||
val end = text.getSpanEnd(it)
|
||||
when (it) {
|
||||
is StyleSpan -> when (it.style) {
|
||||
Typeface.NORMAL -> addStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontStyle = FontStyle.Normal,
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
Typeface.BOLD -> addStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontStyle = FontStyle.Normal
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
Typeface.ITALIC -> addStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Normal,
|
||||
fontStyle = FontStyle.Italic
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
Typeface.BOLD_ITALIC -> addStyle(
|
||||
SpanStyle(
|
||||
fontWeight = FontWeight.Bold,
|
||||
fontStyle = FontStyle.Italic
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
}
|
||||
is TypefaceSpan -> addStyle(
|
||||
SpanStyle(
|
||||
fontFamily = when (it.family) {
|
||||
FontFamily.SansSerif.name -> FontFamily.SansSerif
|
||||
FontFamily.Serif.name -> FontFamily.Serif
|
||||
FontFamily.Monospace.name -> FontFamily.Monospace
|
||||
FontFamily.Cursive.name -> FontFamily.Cursive
|
||||
else -> FontFamily.Default
|
||||
}
|
||||
),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is AbsoluteSizeSpan -> addStyle(
|
||||
SpanStyle(fontSize = if (it.dip) it.size.dp.toSp() else it.size.toSp()),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is RelativeSizeSpan -> addStyle(
|
||||
SpanStyle(fontSize = it.sizeChange.em),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is StrikethroughSpan -> addStyle(
|
||||
SpanStyle(textDecoration = TextDecoration.LineThrough),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is UnderlineSpan -> addStyle(
|
||||
SpanStyle(textDecoration = TextDecoration.Underline),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is SuperscriptSpan -> addStyle(
|
||||
SpanStyle(baselineShift = BaselineShift.Superscript),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is SubscriptSpan -> addStyle(
|
||||
SpanStyle(baselineShift = BaselineShift.Subscript),
|
||||
start,
|
||||
end
|
||||
)
|
||||
is ForegroundColorSpan -> addStyle(
|
||||
SpanStyle(color = Color(it.foregroundColor)),
|
||||
start,
|
||||
end
|
||||
)
|
||||
else -> addStyle(SpanStyle(color = Color.White), start, end)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
AnnotatedString(text.toString())
|
||||
}
|
||||
}
|
||||
|
||||
@@ -10,15 +10,16 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.SimpleButton
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import chat.simplex.app.views.helpers.shareText
|
||||
|
||||
@Composable
|
||||
@@ -42,11 +43,11 @@ fun AddContactLayout(connReq: String, share: () -> Unit) {
|
||||
verticalArrangement = Arrangement.SpaceBetween,
|
||||
) {
|
||||
Text(
|
||||
"Add contact",
|
||||
generalGetString(R.string.add_contact),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
|
||||
)
|
||||
Text(
|
||||
"Show QR code to your contact\nto scan from the app",
|
||||
generalGetString(R.string.show_QR_code_for_your_contact_to_scan_from_the_app__multiline),
|
||||
style = MaterialTheme.typography.h3,
|
||||
textAlign = TextAlign.Center,
|
||||
)
|
||||
@@ -57,20 +58,14 @@ fun AddContactLayout(connReq: String, share: () -> Unit) {
|
||||
.padding(vertical = 3.dp)
|
||||
)
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("If you cannot meet in person, you can ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append("scan QR code in the video call")
|
||||
}
|
||||
append(", or you can share the invitation link via any other channel.")
|
||||
},
|
||||
generalGetString(R.string.if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel),
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.caption.copy(fontSize=if(screenHeight > 600.dp) 20.sp else 16.sp),
|
||||
lineHeight = 22.sp,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(bottom = if(screenHeight > 600.dp) 16.dp else 8.dp)
|
||||
)
|
||||
SimpleButton("Share invitation link", icon = Icons.Outlined.Share, click = share)
|
||||
SimpleButton(generalGetString(R.string.share_invitation_link), icon = Icons.Outlined.Share, click = share)
|
||||
Spacer(Modifier.height(10.dp))
|
||||
}
|
||||
}
|
||||
|
||||
+15
-26
@@ -8,15 +8,15 @@ import androidx.compose.material.*
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.AlertManager
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.helpers.*
|
||||
|
||||
@Composable
|
||||
fun ConnectContactView(chatModel: ChatModel, close: () -> Unit) {
|
||||
@@ -31,8 +31,8 @@ fun ConnectContactView(chatModel: ChatModel, close: () -> Unit) {
|
||||
}
|
||||
} catch (e: RuntimeException) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = "Invalid QR code",
|
||||
text = "This QR code is not a link!"
|
||||
title = generalGetString(R.string.invalid_QR_code),
|
||||
text = generalGetString(R.string.this_QR_code_is_not_a_link)
|
||||
)
|
||||
}
|
||||
close()
|
||||
@@ -48,8 +48,8 @@ fun withUriAction(uri: Uri, run: suspend (String) -> Unit) {
|
||||
withApi { run(action) }
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = "Invalid link!",
|
||||
text = "This link is not a valid connection link!"
|
||||
title = generalGetString(R.string.invalid_contact_link),
|
||||
text = generalGetString(R.string.this_link_is_not_a_valid_connection_link)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -57,12 +57,11 @@ fun withUriAction(uri: Uri, run: suspend (String) -> Unit) {
|
||||
suspend fun connectViaUri(chatModel: ChatModel, action: String, uri: Uri) {
|
||||
val r = chatModel.controller.apiConnect(uri.toString())
|
||||
if (r) {
|
||||
val whenConnected =
|
||||
if (action == "contact") "your connection request is accepted"
|
||||
else "your contact's device is online"
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = "Connection request sent!",
|
||||
text = "You will be connected when $whenConnected, please wait or check later!"
|
||||
title = generalGetString(R.string.connection_request_sent),
|
||||
text =
|
||||
if (action == "contact") generalGetString(R.string.you_will_be_connected_when_your_connection_request_is_accepted)
|
||||
else generalGetString(R.string.you_will_be_connected_when_your_contacts_device_is_online)
|
||||
)
|
||||
}
|
||||
}
|
||||
@@ -75,11 +74,11 @@ fun ConnectContactLayout(qrCodeScanner: @Composable () -> Unit, close: () -> Uni
|
||||
verticalArrangement = Arrangement.spacedBy(12.dp)
|
||||
) {
|
||||
Text(
|
||||
"Scan QR code",
|
||||
generalGetString(R.string.scan_QR_code),
|
||||
style = MaterialTheme.typography.h1.copy(fontWeight = FontWeight.Normal),
|
||||
)
|
||||
Text(
|
||||
"Your chat profile will be sent\nto your contact",
|
||||
generalGetString(R.string.your_chat_profile_will_be_sent_to_your_contact),
|
||||
style = MaterialTheme.typography.h3,
|
||||
textAlign = TextAlign.Center,
|
||||
modifier = Modifier.padding(bottom = 4.dp)
|
||||
@@ -90,18 +89,8 @@ fun ConnectContactLayout(qrCodeScanner: @Composable () -> Unit, close: () -> Uni
|
||||
.aspectRatio(ratio = 1F)
|
||||
) { qrCodeScanner() }
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("If you cannot meet in person, you can ")
|
||||
withStyle(SpanStyle(fontWeight = FontWeight.Bold)) {
|
||||
append("scan QR code in the video call")
|
||||
}
|
||||
append(", or you can create the invitation link.")
|
||||
},
|
||||
textAlign = TextAlign.Center,
|
||||
style = MaterialTheme.typography.caption,
|
||||
modifier = Modifier
|
||||
.padding(horizontal = 16.dp)
|
||||
.padding(top = 4.dp)
|
||||
annotatedStringResource(R.string.if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link),
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -14,11 +14,12 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chatlist.ScaffoldController
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import com.google.accompanist.permissions.rememberPermissionState
|
||||
|
||||
@Composable
|
||||
@@ -57,8 +58,10 @@ fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) {
|
||||
.weight(1F)
|
||||
.fillMaxWidth()) {
|
||||
ActionButton(
|
||||
"Add contact", "(create QR code\nor link)",
|
||||
Icons.Outlined.PersonAdd, click = addContact
|
||||
generalGetString(R.string.add_contact),
|
||||
generalGetString(R.string.create_QR_code_or_link__bracketed__multiline),
|
||||
Icons.Outlined.PersonAdd,
|
||||
click = addContact
|
||||
)
|
||||
}
|
||||
Box(
|
||||
@@ -66,8 +69,10 @@ fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) {
|
||||
.weight(1F)
|
||||
.fillMaxWidth()) {
|
||||
ActionButton(
|
||||
"Scan QR code", "(in person or in video call)",
|
||||
Icons.Outlined.QrCode, click = scanCode
|
||||
generalGetString(R.string.scan_QR_code),
|
||||
generalGetString(R.string.in_person_or_in_video_call__bracketed),
|
||||
Icons.Outlined.QrCode,
|
||||
click = scanCode
|
||||
)
|
||||
}
|
||||
Box(
|
||||
@@ -75,8 +80,10 @@ fun NewChatSheetLayout(addContact: () -> Unit, scanCode: () -> Unit) {
|
||||
.weight(1F)
|
||||
.fillMaxWidth()) {
|
||||
ActionButton(
|
||||
"Create Group", "(coming soon!)",
|
||||
Icons.Outlined.GroupAdd, disabled = true
|
||||
generalGetString(R.string.create_group),
|
||||
generalGetString(R.string.coming_soon__bracketed),
|
||||
Icons.Outlined.GroupAdd,
|
||||
disabled = true
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -7,7 +7,9 @@ import androidx.compose.runtime.Composable
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.asImageBitmap
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
import com.google.zxing.BarcodeFormat
|
||||
import com.google.zxing.EncodeHintType
|
||||
import com.google.zxing.qrcode.QRCodeWriter
|
||||
@@ -16,7 +18,7 @@ import com.google.zxing.qrcode.QRCodeWriter
|
||||
fun QRCode(connReq: String, modifier: Modifier = Modifier) {
|
||||
Image(
|
||||
bitmap = qrCodeBitmap(connReq, 1024).asImageBitmap(),
|
||||
contentDescription = "QR Code",
|
||||
contentDescription = generalGetString(R.string.image_descr_qr_code),
|
||||
modifier = modifier
|
||||
)
|
||||
}
|
||||
|
||||
@@ -3,6 +3,8 @@ package chat.simplex.app.views.usersettings
|
||||
import android.content.res.Configuration
|
||||
import androidx.compose.foundation.layout.Column
|
||||
import androidx.compose.foundation.layout.padding
|
||||
import androidx.compose.foundation.rememberScrollState
|
||||
import androidx.compose.foundation.verticalScroll
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
import androidx.compose.runtime.Composable
|
||||
@@ -10,9 +12,11 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.chat.ChatHelpView
|
||||
import chat.simplex.app.views.chatlist.ChatHelpView
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
|
||||
@Composable
|
||||
fun HelpView(chatModel: ChatModel) {
|
||||
@@ -24,9 +28,12 @@ fun HelpView(chatModel: ChatModel) {
|
||||
|
||||
@Composable
|
||||
fun HelpLayout(displayName: String) {
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Column(
|
||||
Modifier.verticalScroll(rememberScrollState()),
|
||||
horizontalAlignment = Alignment.Start
|
||||
){
|
||||
Text(
|
||||
"Welcome $displayName!",
|
||||
String.format(generalGetString(R.string.personal_welcome), displayName),
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1,
|
||||
)
|
||||
|
||||
+20
-11
@@ -10,29 +10,38 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.Format
|
||||
import chat.simplex.app.model.FormatColor
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.generalGetString
|
||||
|
||||
@Composable
|
||||
fun MarkdownHelpView() {
|
||||
Column {
|
||||
Text(
|
||||
"How to use markdown",
|
||||
generalGetString(R.string.how_to_use_markdown),
|
||||
style = MaterialTheme.typography.h1,
|
||||
)
|
||||
Text(
|
||||
"You can use markdown to format messages:",
|
||||
generalGetString(R.string.you_can_use_markdown_to_format_messages__prompt),
|
||||
Modifier.padding(vertical = 16.dp)
|
||||
)
|
||||
MdFormat("*bold*", "bold", Format.Bold())
|
||||
MdFormat("_italic_", "italic", Format.Italic())
|
||||
MdFormat("~strike~", "strike", Format.StrikeThrough())
|
||||
MdFormat("`a + b`", "a + b", Format.Snippet())
|
||||
val bold = generalGetString(R.string.bold)
|
||||
val italic = generalGetString(R.string.italic)
|
||||
val strikethrough = generalGetString(R.string.strikethrough)
|
||||
val equation = generalGetString(R.string.a_plus_b)
|
||||
val colored = generalGetString(R.string.colored)
|
||||
val secret = generalGetString(R.string.secret)
|
||||
|
||||
MdFormat("*$bold*", bold, Format.Bold())
|
||||
MdFormat("_${italic}_", italic, Format.Italic())
|
||||
MdFormat("~$strikethrough~", strikethrough, Format.StrikeThrough())
|
||||
MdFormat("`$equation`", equation, Format.Snippet())
|
||||
Row {
|
||||
MdSyntax("!1 colored!")
|
||||
MdSyntax("!1 $colored!")
|
||||
Text(buildAnnotatedString {
|
||||
withStyle(Format.Colored(FormatColor.red).style) { append("colored") }
|
||||
withStyle(Format.Colored(FormatColor.red).style) { append(colored) }
|
||||
append(" (")
|
||||
appendColor(this, "1", FormatColor.red, ", ")
|
||||
appendColor(this, "2", FormatColor.green, ", ")
|
||||
@@ -43,10 +52,10 @@ fun MarkdownHelpView() {
|
||||
})
|
||||
}
|
||||
Row {
|
||||
MdSyntax("#secret#")
|
||||
MdSyntax("#$secret#")
|
||||
SelectionContainer {
|
||||
Text(buildAnnotatedString {
|
||||
withStyle(Format.Secret().style) { append("secret") }
|
||||
withStyle(Format.Secret().style) { append(secret) }
|
||||
})
|
||||
}
|
||||
}
|
||||
@@ -56,7 +65,7 @@ fun MarkdownHelpView() {
|
||||
@Composable
|
||||
fun MdSyntax(markdown: String) {
|
||||
Text(markdown, Modifier
|
||||
.width(100.dp)
|
||||
.width(120.dp)
|
||||
.padding(bottom = 4.dp))
|
||||
}
|
||||
|
||||
|
||||
@@ -20,11 +20,11 @@ import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.AlertManager
|
||||
import chat.simplex.app.views.helpers.withApi
|
||||
import chat.simplex.app.views.helpers.*
|
||||
|
||||
@Composable
|
||||
fun SMPServersView(chatModel: ChatModel) {
|
||||
@@ -60,9 +60,9 @@ fun SMPServersView(chatModel: ChatModel) {
|
||||
if (userSMPServers != null) {
|
||||
if (userSMPServers.isNotEmpty()) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = "Use SimpleX Chat servers?",
|
||||
text = "Saved SMP servers will be removed.",
|
||||
confirmText = "Confirm",
|
||||
title = generalGetString(R.string.use_simplex_chat_servers__question),
|
||||
text = generalGetString(R.string.saved_SMP_servers_will_br_removed),
|
||||
confirmText = generalGetString(R.string.confirm_verb),
|
||||
onConfirm = {
|
||||
saveSMPServers(listOf())
|
||||
isUserSMPServers = false
|
||||
@@ -108,14 +108,14 @@ fun SMPServersLayout(
|
||||
verticalArrangement = Arrangement.spacedBy(8.dp)
|
||||
) {
|
||||
Text(
|
||||
"Your SMP servers",
|
||||
generalGetString(R.string.your_SMP_servers),
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1
|
||||
)
|
||||
Row(
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
Text("Configure SMP servers", Modifier.padding(end = 24.dp))
|
||||
Text(generalGetString(R.string.configure_SMP_servers), Modifier.padding(end = 24.dp))
|
||||
Switch(
|
||||
checked = isUserSMPServers,
|
||||
onCheckedChange = isUserSMPServersOnOff,
|
||||
@@ -127,9 +127,9 @@ fun SMPServersLayout(
|
||||
}
|
||||
|
||||
if (!isUserSMPServers) {
|
||||
Text("Using SimpleX Chat servers.")
|
||||
Text(generalGetString(R.string.using_simplex_chat_servers), lineHeight = 22.sp)
|
||||
} else {
|
||||
Text("Enter one SMP server per line:")
|
||||
Text(generalGetString(R.string.enter_one_SMP_server_per_line))
|
||||
if (editSMPServers) {
|
||||
BasicTextField(
|
||||
value = userSMPServersStr,
|
||||
@@ -173,14 +173,14 @@ fun SMPServersLayout(
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Row {
|
||||
Text(
|
||||
"Cancel",
|
||||
generalGetString(R.string.cancel_verb),
|
||||
color = MaterialTheme.colors.primary,
|
||||
modifier = Modifier
|
||||
.clickable(onClick = cancelEdit)
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 8.dp))
|
||||
Text(
|
||||
"Save",
|
||||
generalGetString(R.string.save_servers_button),
|
||||
color = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.clickable(onClick = {
|
||||
val servers = userSMPServersStr.split("\n")
|
||||
@@ -219,7 +219,7 @@ fun SMPServersLayout(
|
||||
) {
|
||||
Column(horizontalAlignment = Alignment.Start) {
|
||||
Text(
|
||||
"Edit",
|
||||
generalGetString(R.string.edit_verb),
|
||||
color = MaterialTheme.colors.primary,
|
||||
modifier = Modifier
|
||||
.clickable(onClick = editOn)
|
||||
@@ -241,9 +241,9 @@ fun howToButton() {
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
modifier = Modifier.clickable { uriHandler.openUri("https://github.com/simplex-chat/simplexmq#using-smp-server-and-smp-agent") }
|
||||
) {
|
||||
Text("How to", color = MaterialTheme.colors.primary)
|
||||
Text(generalGetString(R.string.how_to), color = MaterialTheme.colors.primary)
|
||||
Icon(
|
||||
Icons.Outlined.OpenInNew, "How to", tint = MaterialTheme.colors.primary,
|
||||
Icons.Outlined.OpenInNew, generalGetString(R.string.how_to), tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.padding(horizontal = 5.dp)
|
||||
)
|
||||
}
|
||||
|
||||
+20
-28
@@ -12,7 +12,6 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.res.painterResource
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.Dp
|
||||
@@ -24,8 +23,7 @@ import chat.simplex.app.model.Profile
|
||||
import chat.simplex.app.ui.theme.HighOrLowlight
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.TerminalView
|
||||
import chat.simplex.app.views.helpers.ProfileImage
|
||||
import chat.simplex.app.views.newchat.ModalManager
|
||||
import chat.simplex.app.views.helpers.*
|
||||
|
||||
@Composable
|
||||
fun SettingsView(chatModel: ChatModel) {
|
||||
@@ -71,7 +69,7 @@ fun SettingsLayout(
|
||||
.padding(top = 16.dp)
|
||||
) {
|
||||
Text(
|
||||
"Your settings",
|
||||
generalGetString(R.string.your_settings),
|
||||
style = MaterialTheme.typography.h1,
|
||||
modifier = Modifier.padding(start = 8.dp)
|
||||
)
|
||||
@@ -93,39 +91,39 @@ fun SettingsLayout(
|
||||
SettingsSectionView(showModal { UserAddressView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.QrCode,
|
||||
contentDescription = "Address",
|
||||
contentDescription = generalGetString(R.string.icon_descr_address),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text("Your SimpleX contact address")
|
||||
Text(generalGetString(R.string.your_simplex_contact_address))
|
||||
}
|
||||
Spacer(Modifier.height(24.dp))
|
||||
|
||||
SettingsSectionView(showModal { HelpView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.HelpOutline,
|
||||
contentDescription = "Chat help",
|
||||
contentDescription = generalGetString(R.string.icon_descr_help),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text("How to use SimpleX Chat")
|
||||
Text(generalGetString(R.string.how_to_use_simplex_chat))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView(showModal { MarkdownHelpView() }) {
|
||||
Icon(
|
||||
Icons.Outlined.TextFormat,
|
||||
contentDescription = "Markdown help",
|
||||
contentDescription = generalGetString(R.string.markdown_help),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text("Markdown in messages")
|
||||
Text(generalGetString(R.string.markdown_in_messages))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView({ uriHandler.openUri(simplexTeamUri) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Tag,
|
||||
contentDescription = "SimpleX Team",
|
||||
contentDescription = generalGetString(R.string.icon_descr_simplex_team),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
"Chat with the founder",
|
||||
generalGetString(R.string.chat_with_the_founder),
|
||||
color = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
@@ -133,11 +131,11 @@ fun SettingsLayout(
|
||||
SettingsSectionView({ uriHandler.openUri("mailto:chat@simplex.chat") }) {
|
||||
Icon(
|
||||
Icons.Outlined.Email,
|
||||
contentDescription = "Email",
|
||||
contentDescription = generalGetString(R.string.icon_descr_email),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
"Send us email",
|
||||
generalGetString(R.string.send_us_an_email),
|
||||
color = MaterialTheme.colors.primary
|
||||
)
|
||||
}
|
||||
@@ -146,19 +144,20 @@ fun SettingsLayout(
|
||||
SettingsSectionView(showModal { SMPServersView(it) }) {
|
||||
Icon(
|
||||
Icons.Outlined.Dns,
|
||||
contentDescription = "SMP servers",
|
||||
contentDescription = generalGetString(R.string.smp_servers),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text("SMP servers")
|
||||
Text(generalGetString(R.string.smp_servers))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView() {
|
||||
Icon(
|
||||
Icons.Outlined.Bolt,
|
||||
contentDescription = "Private notifications",
|
||||
contentDescription = generalGetString(R.string.private_notifications),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text("Private notifications", Modifier
|
||||
Text(
|
||||
generalGetString(R.string.private_notifications), Modifier
|
||||
.padding(end = 24.dp)
|
||||
.fillMaxWidth()
|
||||
.weight(1F))
|
||||
@@ -176,10 +175,10 @@ fun SettingsLayout(
|
||||
SettingsSectionView(showTerminal) {
|
||||
Icon(
|
||||
painter = painterResource(id = R.drawable.ic_outline_terminal),
|
||||
contentDescription = "Chat console",
|
||||
contentDescription = generalGetString(R.string.chat_console),
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text("Chat console")
|
||||
Text(generalGetString(R.string.chat_console))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView({ uriHandler.openUri("https://github.com/simplex-chat/simplex-chat") }) {
|
||||
@@ -188,14 +187,7 @@ fun SettingsLayout(
|
||||
contentDescription = "GitHub",
|
||||
)
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
Text(
|
||||
buildAnnotatedString {
|
||||
append("Install ")
|
||||
withStyle(SpanStyle(color = MaterialTheme.colors.primary)) {
|
||||
append("SimpleX Chat for terminal")
|
||||
}
|
||||
}
|
||||
)
|
||||
Text(annotatedStringResource(R.string.install_simplex_chat_for_terminal))
|
||||
}
|
||||
Divider(Modifier.padding(horizontal = 8.dp))
|
||||
SettingsSectionView() {
|
||||
|
||||
+16
-9
@@ -13,6 +13,8 @@ import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.ui.theme.SimpleButton
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
@@ -32,9 +34,9 @@ fun UserAddressView(chatModel: ChatModel) {
|
||||
share = { userAddress: String -> shareText(cxt, userAddress) },
|
||||
deleteAddress = {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title = "Delete address?",
|
||||
text = "All your contacts will remain connected.",
|
||||
confirmText = "Delete",
|
||||
title = generalGetString(R.string.delete_address__question),
|
||||
text = generalGetString(R.string.all_your_contacts_will_remain_connected),
|
||||
confirmText = generalGetString(R.string.delete_verb),
|
||||
onConfirm = {
|
||||
withApi {
|
||||
chatModel.controller.apiDeleteUserAddress()
|
||||
@@ -58,14 +60,14 @@ fun UserAddressLayout(
|
||||
verticalArrangement = Arrangement.Top
|
||||
) {
|
||||
Text(
|
||||
"Your chat address",
|
||||
generalGetString(R.string.your_chat_address),
|
||||
Modifier.padding(bottom = 16.dp),
|
||||
style = MaterialTheme.typography.h1,
|
||||
)
|
||||
Text(
|
||||
"You can share your address as a link or as a QR code - anybody will be able to connect to you, " +
|
||||
"and if you later delete it - you won't lose your contacts.",
|
||||
generalGetString(R.string.you_can_share_your_address_anybody_will_be_able_to_connect),
|
||||
Modifier.padding(bottom = 12.dp),
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
Column(
|
||||
Modifier.fillMaxWidth(),
|
||||
@@ -73,7 +75,12 @@ fun UserAddressLayout(
|
||||
verticalArrangement = Arrangement.SpaceEvenly
|
||||
) {
|
||||
if (userAddress == null) {
|
||||
SimpleButton("Create address", icon = Icons.Outlined.QrCode, click = createAddress)
|
||||
Text(
|
||||
generalGetString(R.string.if_you_delete_address_you_wont_lose_contacts),
|
||||
Modifier.padding(bottom = 12.dp),
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
SimpleButton(generalGetString(R.string.create_address), icon = Icons.Outlined.QrCode, click = createAddress)
|
||||
} else {
|
||||
QRCode(userAddress, Modifier.weight(1f, fill = false).aspectRatio(1f))
|
||||
Row(
|
||||
@@ -82,11 +89,11 @@ fun UserAddressLayout(
|
||||
modifier = Modifier.padding(vertical = 10.dp)
|
||||
) {
|
||||
SimpleButton(
|
||||
"Share link",
|
||||
generalGetString(R.string.share_link),
|
||||
icon = Icons.Outlined.Share,
|
||||
click = { share(userAddress) })
|
||||
SimpleButton(
|
||||
"Delete address",
|
||||
generalGetString(R.string.delete_address),
|
||||
icon = Icons.Outlined.Delete,
|
||||
color = Color.Red,
|
||||
click = deleteAddress
|
||||
|
||||
+13
-12
@@ -19,11 +19,12 @@ import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.text.input.KeyboardCapitalization
|
||||
import androidx.compose.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.app.R
|
||||
import chat.simplex.app.model.ChatModel
|
||||
import chat.simplex.app.model.Profile
|
||||
import chat.simplex.app.ui.theme.SimpleXTheme
|
||||
import chat.simplex.app.views.helpers.*
|
||||
import chat.simplex.app.views.newchat.ModalView
|
||||
import com.google.accompanist.insets.ProvideWindowInsets
|
||||
import com.google.accompanist.insets.navigationBarsWithImePadding
|
||||
import kotlinx.coroutines.launch
|
||||
@@ -89,16 +90,16 @@ fun UserProfileLayout(
|
||||
horizontalAlignment = Alignment.Start
|
||||
) {
|
||||
Text(
|
||||
"Your chat profile",
|
||||
generalGetString(R.string.your_chat_profile),
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
style = MaterialTheme.typography.h1,
|
||||
color = MaterialTheme.colors.onBackground
|
||||
)
|
||||
Text(
|
||||
"Your profile is stored on your device and shared only with your contacts.\n\n" +
|
||||
"SimpleX servers cannot see your profile.",
|
||||
generalGetString(R.string.your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it),
|
||||
Modifier.padding(bottom = 24.dp),
|
||||
color = MaterialTheme.colors.onBackground
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
lineHeight = 22.sp
|
||||
)
|
||||
if (editProfile.value) {
|
||||
Column(
|
||||
@@ -124,14 +125,14 @@ fun UserProfileLayout(
|
||||
ProfileNameTextField(displayName)
|
||||
ProfileNameTextField(fullName)
|
||||
Row {
|
||||
TextButton("Cancel") {
|
||||
TextButton(generalGetString(R.string.cancel_verb)) {
|
||||
displayName.value = profile.displayName
|
||||
fullName.value = profile.fullName
|
||||
profileImage.value = profile.image
|
||||
editProfile.value = false
|
||||
}
|
||||
Spacer(Modifier.padding(horizontal = 8.dp))
|
||||
TextButton("Save (and notify contacts)") {
|
||||
TextButton(generalGetString(R.string.save_and_notify_contacts)) {
|
||||
saveProfile(displayName.value, fullName.value, profileImage.value)
|
||||
}
|
||||
}
|
||||
@@ -154,9 +155,9 @@ fun UserProfileLayout(
|
||||
}
|
||||
}
|
||||
}
|
||||
ProfileNameRow("Display name:", profile.displayName)
|
||||
ProfileNameRow("Full name:", profile.fullName)
|
||||
TextButton("Edit") { editProfile.value = true }
|
||||
ProfileNameRow(generalGetString(R.string.display_name__field), profile.displayName)
|
||||
ProfileNameRow(generalGetString(R.string.full_name__field), profile.fullName)
|
||||
TextButton(generalGetString(R.string.edit_verb)) { editProfile.value = true }
|
||||
}
|
||||
}
|
||||
if (savedKeyboardState != keyboardState) {
|
||||
@@ -223,7 +224,7 @@ fun EditImageButton(click: () -> Unit) {
|
||||
) {
|
||||
Icon(
|
||||
Icons.Outlined.PhotoCamera,
|
||||
contentDescription = "Edit image",
|
||||
contentDescription = generalGetString(R.string.edit_image),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
modifier = Modifier.size(36.dp)
|
||||
)
|
||||
@@ -235,7 +236,7 @@ fun DeleteImageButton(click: () -> Unit) {
|
||||
IconButton(onClick = click) {
|
||||
Icon(
|
||||
Icons.Outlined.Close,
|
||||
contentDescription = "Delete image",
|
||||
contentDescription = generalGetString(R.string.delete_image),
|
||||
tint = MaterialTheme.colors.primary,
|
||||
)
|
||||
}
|
||||
|
||||
@@ -0,0 +1,218 @@
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="app_name"><xliff:g id="appName">SimpleX</xliff:g></string>
|
||||
|
||||
<string name="thousand_abbreviation">т</string>
|
||||
|
||||
<!-- Connect via Link - MainActivity.kt -->
|
||||
<string name="connect_via_contact_link">Соединиться через ссылку-контакт?</string>
|
||||
<string name="connect_via_invitation_link">Соединиться через ссылку-приглашение?</string>
|
||||
<string name="profile_will_be_sent_to_contact_sending_link">Ваш профиль будет отправлен контакту, от которого вы получили эту ссылку.</string>
|
||||
<string name="connect_via_link_verb">Соединиться</string>
|
||||
|
||||
<!-- Server info - ChatModel.kt -->
|
||||
<string name="server_connected">Соединение установлено</string>
|
||||
<string name="server_connecting">Соединение устанавливается…</string>
|
||||
<string name="connected_to_server_to_receive_messages_from_contact">Установлено соединение с сервером, через который вы получаете сообщения от этого контакта.</string>
|
||||
<string name="trying_to_connect_to_server_to_receive_messages_with_error">Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта (ошибка: <xliff:g id="errorMsg">%1$s</xliff:g>).</string>
|
||||
<string name="trying_to_connect_to_server_to_receive_messages">Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта.</string>
|
||||
|
||||
<!-- Item Content - ChatModel.kt -->
|
||||
<string name="deleted_description">удалено</string>
|
||||
<string name="sending_files_not_yet_supported">отправка файлов не поддерживается</string>
|
||||
<string name="receiving_files_not_yet_supported">получение файлов не поддерживается</string>
|
||||
<string name="sender_you_pronoun">вы</string>
|
||||
<string name="unknown_message_format">неизвестный формат сообщения</string>
|
||||
<string name="invalid_message_format">неверный формат сообщения</string>
|
||||
|
||||
<!-- SMP Server Information - SimpleXAPI.kt -->
|
||||
<string name="error_saving_smp_servers">Ошибка при сохранении SMP серверов</string>
|
||||
<string name="ensure_smp_server_address_are_correct_format_and_unique">Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.</string>
|
||||
|
||||
<!-- API Error Responses - SimpleXAPI.kt -->
|
||||
<string name="contact_already_exists">Существующий контакт</string>
|
||||
<string name="you_are_already_connected_to_vName_via_this_link">Вы уже соединены с <xliff:g id="contactName" example="Alice">%1$s!</xliff:g> через эту ссылку.</string>
|
||||
<string name="invalid_connection_link">Ошибка в ссылке контакта</string>
|
||||
<string name="please_check_correct_link_and_maybe_ask_for_a_new_one">Пожалуйста, проверьте, что вы использовали правильную ссылку, или попросите ваш контакт отправить вам новую.</string>
|
||||
<string name="cannot_delete_contact">Невозможно удалить контакт!</string>
|
||||
<string name="contact_cannot_be_deleted_as_they_are_in_groups">Контакт <xliff:g id="contactName" example="Jane Doe">%1$s!</xliff:g> не может быть удален, так как является членом групп(ы) <xliff:g id="groups" example="[team, chess club]">%2$s</xliff:g>.</string>
|
||||
<string name="icon_descr_instant_notifications">Мгновенные уведомления</string>
|
||||
|
||||
<!-- background service notice - SimpleXAPI.kt -->
|
||||
<string name="private_instant_notifications">Приватные мгновенные уведомления!</string>
|
||||
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery">Чтобы защитить ваши личные данные, вместо уведомлений от сервера приложение запускает <b>фоновый сервис <xliff:g id="appName">SimpleX</xliff:g></b>, который потребляет несколько процентов батареи в день.</string>
|
||||
<string name="it_can_disabled_via_settings_notifications_still_shown"><b>Он может быть выключен через Настройки</b> – вы продолжите получать уведомления о сообщениях пока приложение запущено.</string>
|
||||
|
||||
<!-- SimpleX Chat foreground Service -->
|
||||
<string name="simplex_service_notification_title"><xliff:g id="appNameFull">SimpleX Chat</xliff:g> сервис</string>
|
||||
<string name="simplex_service_notification_text">Приём сообщений…</string>
|
||||
|
||||
<!-- Chat Actions - ChatItemView.kt (and general) -->
|
||||
<string name="reply_verb">Ответить</string>
|
||||
<string name="share_verb">Поделиться</string>
|
||||
<string name="copy_verb">Скопировать</string>
|
||||
<string name="edit_verb">Редактировать</string>
|
||||
<string name="delete_verb">Удалить</string>
|
||||
<string name="delete_message__question">Удалить сообщение?</string>
|
||||
<string name="delete_message_cannot_be_undone_warning">Сообщение будет удалено – это действие нельзя отменить!</string>
|
||||
<string name="for_me_only">Только для меня</string>
|
||||
<string name="for_everybody">Для всех</string>
|
||||
|
||||
<!-- CIMetaView.kt -->
|
||||
<string name="icon_descr_edited">отредактировано</string>
|
||||
<string name="icon_descr_sent_msg_status_sent">отправлено</string>
|
||||
<string name="icon_descr_sent_msg_status_unauthorized_send">ошибка авторизации при отправке</string>
|
||||
<string name="icon_descr_sent_msg_status_send_failed">ошибка при отправке</string>
|
||||
<string name="icon_descr_received_msg_status_unread">не прочитано</string>
|
||||
|
||||
<!-- ChatListView.kt -->
|
||||
<string name="personal_welcome">Здравствуйте <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="welcome">Здравствуйте!</string>
|
||||
<string name="this_text_is_available_in_settings">Этот текст можно найти в Настройках</string>
|
||||
<string name="your_chats">Ваши чаты</string>
|
||||
|
||||
<!-- Chat Info Actions - ChatInfoView.kt -->
|
||||
<string name="delete_contact__question">Удалить контакт?</string>
|
||||
<string name="delete_contact_all_messages_deleted_cannot_undo_warning">Контакт и все сообщения будут удалены - это действие нельзя отменить!</string>
|
||||
<string name="button_delete_contact">Удалить контакт</string>
|
||||
<string name="icon_descr_server_status_connected">Соединение с сервером установлено</string>
|
||||
<string name="icon_descr_server_status_disconnected">Соединение с сервером не установлено</string>
|
||||
<string name="icon_descr_server_status_error">Ошибка соединения с сервером</string>
|
||||
<string name="icon_descr_server_status_pending">Ожидается соединение с сервером</string>
|
||||
|
||||
<!-- Message Actions - SendMsgView.kt -->
|
||||
<string name="icon_descr_send_message">Отправить сообщение</string>
|
||||
|
||||
<!-- General Actions / Responses -->
|
||||
<string name="back">Назад</string>
|
||||
<string name="cancel_verb">Отменить</string>
|
||||
<string name="confirm_verb">Подтвердить</string>
|
||||
<string name="ok">Oк</string>
|
||||
<string name="no_details">нет описания</string>
|
||||
<string name="add_contact">Добавить контакт</string>
|
||||
<string name="scan_QR_code">Сканировать QR код</string>
|
||||
|
||||
<!-- NewChatSheet -->
|
||||
<string name="create_QR_code_or_link__bracketed__multiline">(создать QR код или ссылку)</string>
|
||||
<string name="in_person_or_in_video_call__bracketed">(при встрече или через видео звонок)</string>
|
||||
<string name="create_group">Создать группу</string>
|
||||
<string name="coming_soon__bracketed">(скоро!)</string>
|
||||
|
||||
<!-- GetImageView -->
|
||||
<string name="toast_camera_permission_denied">Разрешение не получено!</string>
|
||||
<string name="use_camera_button">Использовать камеру</string>
|
||||
<string name="from_gallery_button">Открыть галерею</string>
|
||||
|
||||
<!-- help - ChatHelpView.kt -->
|
||||
<string name="thank_you_for_installing_simplex">Спасибо что установили <xliff:g id="appNameFull">SimpleX Chat</xliff:g>!</string>
|
||||
<string name="you_can_connect_to_simplex_chat_founder">Вы можете <font color="#0088ff">соединиться с разработчиками</font>, чтобы задать любые вопросы или получать уведомления о новых версиях.</string>
|
||||
<string name="to_start_a_new_chat_help_header">Чтобы начать новый чат</string>
|
||||
<string name="chat_help_tap_button">Нажмите кнопку</string>
|
||||
<string name="above_then_preposition_continuation">сверху, затем:</string>
|
||||
<string name="add_new_contact_to_create_one_time_QR_code"><b>Добавить новый контакт</b>: чтобы создать одноразовый QR код/ссылку для вашего контакта.</string>
|
||||
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><b>Сканировать QR код</b>: чтобы соединиться с контактом, который показывает вам QR код.</string>
|
||||
<string name="to_connect_via_link_title">Чтобы соединиться через ссылку</string>
|
||||
<string name="if_you_received_simplex_invitation_link_you_can_open_in_browser">Если вы получили ссылку с приглашением из <xliff:g id="appName">SimpleX Chat</xliff:g>, вы можете открыть ее в браузере:</string>
|
||||
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code">💻 на компьютере: сосканируйте показанный QR код из приложения через <b>Сканировать QR код</b>.</string>
|
||||
<string name="mobile_tap_open_in_mobile_app_then_tap_connect_in_app">📱 на мобильном: намжите кнопку <b>Open in mobile app</b> на веб странице, затем нажмите <b>Соединиться</b> в приложении.</string>
|
||||
|
||||
<!-- Contact Request Alert Dialogue - CharListNavLinkView.kt -->
|
||||
<string name="accept_connection_request__question">Принять запрос на соединение?</string>
|
||||
<string name="if_you_choose_to_reject_the_sender_will_not_be_notified">Отправителю НЕ будет послано уведомление, если вы отклоните запрос на соединение.</string>
|
||||
<string name="accept_contact_button">Принять</string>
|
||||
<string name="reject_contact_button">Отклонить</string>
|
||||
|
||||
<!-- Contact Request Information - ContactRequestView.kt -->
|
||||
<string name="contact_wants_to_connect_with_you">хочет соединиться с вами!</string>
|
||||
|
||||
<!-- Image Placeholder - ChatInfoImage.kt -->
|
||||
<string name="icon_descr_profile_image_placeholder">аватар не установлен</string>
|
||||
<string name="image_descr_profile_image">аватар</string>
|
||||
|
||||
<!-- Content Descriptions -->
|
||||
<string name="icon_descr_close_button">закрыть</string>
|
||||
<string name="image_descr_link_preview">изображение превью ссылки</string>
|
||||
<string name="icon_descr_cancel_link_preview">удалить превью ссылки</string>
|
||||
<string name="icon_descr_settings">Настройки</string>
|
||||
<string name="image_descr_qr_code">QR код</string>
|
||||
<string name="icon_descr_address"><xliff:g id="appName">SimpleX</xliff:g> адрес</string>
|
||||
<string name="icon_descr_help">Помощь</string>
|
||||
<string name="icon_descr_simplex_team"><xliff:g id="appName">SimpleX</xliff:g> команда</string>
|
||||
<string name="image_descr_simplex_logo"><xliff:g id="appName">SimpleX</xliff:g> логотип</string>
|
||||
<string name="icon_descr_email">Email</string>
|
||||
|
||||
<!-- Add Contact - AddContactView.kt -->
|
||||
<string name="invalid_QR_code">Неверный QR код</string>
|
||||
<string name="this_QR_code_is_not_a_link">Этот QR код не является ссылкой!</string>
|
||||
<string name="invalid_contact_link">Неверная ссылка!</string>
|
||||
<string name="this_link_is_not_a_valid_connection_link">Эта ссылка не является ссылкой-приглашением!</string>
|
||||
<string name="connection_request_sent">Запрос на соединение послан!</string>
|
||||
<string name="you_will_be_connected_when_your_connection_request_is_accepted">Соединение будет установлено когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!</string>
|
||||
<string name="you_will_be_connected_when_your_contacts_device_is_online">Соединение будет установлено когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже!</string>
|
||||
<string name="show_QR_code_for_your_contact_to_scan_from_the_app__multiline">Покажите QR код вашему контакту, чтобы сосканировать его из приложения</string>
|
||||
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">Если вы не можете встретиться лично, вы можете <b>показать QR код во время видео звонка</b> или отправить ссылку через любой другой канал связи.</string>
|
||||
<string name="your_chat_profile_will_be_sent_to_your_contact">Ваш профиль будет отправлен\nвашему контакту</string>
|
||||
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">Если вы не можете встретиться лично, вы можете <b>сосканировать QR код во время видео звонка</b>, или ваш контакт может отправить вам ссылку.</string>
|
||||
<string name="share_invitation_link">Поделиться ссылкой</string>
|
||||
|
||||
<!-- settings - SettingsView.kt -->
|
||||
<string name="your_settings">Настройки</string>
|
||||
<string name="your_simplex_contact_address">Ваш <xliff:g id="appName">SimpleX</xliff:g> адрес</string>
|
||||
<string name="how_to_use_simplex_chat">Как использовать <xliff:g id="appNameFull">SimpleX Chat</xliff:g></string>
|
||||
<string name="markdown_help">Форматирование сообщений</string>
|
||||
<string name="markdown_in_messages">Форматирование сообщений</string>
|
||||
<string name="chat_with_the_founder">Соединиться с разработчиками</string>
|
||||
<string name="send_us_an_email">Отправить email</string>
|
||||
<string name="private_notifications">Приватные уведомления</string>
|
||||
<string name="chat_console">Консоль</string>
|
||||
<string name="smp_servers">SMP серверы</string>
|
||||
<string name="install_simplex_chat_for_terminal"><font color="#0088ff"><xliff:g id="appNameFull">SimpleX Chat</xliff:g> для терминала</font></string>
|
||||
<string name="use_simplex_chat_servers__question">Использовать серверы предосталенные <xliff:g id="appNameFull">SimpleX Chat</xliff:g>?</string>
|
||||
<string name="saved_SMP_servers_will_br_removed">Сохраненные SMP серверы будут удалены.</string>
|
||||
<string name="your_SMP_servers">Ваши SMP серверы</string>
|
||||
<string name="configure_SMP_servers">Настройка SMP серверов</string>
|
||||
<string name="using_simplex_chat_servers">Используются серверы предоставленные <xliff:g id="appNameFull">SimpleX Chat</xliff:g>.</string>
|
||||
<string name="enter_one_SMP_server_per_line">Введите SMP серверы, каждый сервер в отдельной строке:</string>
|
||||
<string name="how_to">Информация</string>
|
||||
<string name="save_servers_button">Сохранить</string>
|
||||
|
||||
<!-- Address Items - UserAddressView.kt -->
|
||||
<string name="create_address">Создать адрес</string>
|
||||
<string name="delete_address__question">Удалить адрес?</string>
|
||||
<string name="all_your_contacts_will_remain_connected">Все контакты, которые соединились через этот адрес, сохранятся.</string>
|
||||
<string name="your_chat_address">Ваш <xliff:g id="appName">SimpleX</xliff:g> адрес</string>
|
||||
<string name="you_can_share_your_address_anybody_will_be_able_to_connect">Вы можете использовать адрес как ссылку или как QR код - через него можно с вами соединиться.</string>
|
||||
<string name="if_you_delete_address_you_wont_lose_contacts">Вы сможете удалить адрес, сохранив контакты, которые через него соединились.</string>
|
||||
<string name="share_link">Поделиться\nссылкой</string>
|
||||
<string name="delete_address">Удалить\nадрес</string>
|
||||
|
||||
<!-- User profile details - UserProfileView.kt -->
|
||||
<string name="display_name__field">Имя профиля:</string>
|
||||
<string name="full_name__field">"Полное имя:</string>
|
||||
<string name="your_chat_profile">Ваш профиль</string>
|
||||
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам.\n\n<xliff:g id="appName">SimpleX</xliff:g> серверы не могут получить доступ к вашему профилю.</string>
|
||||
<string name="edit_image">Поменять аватар</string>
|
||||
<string name="delete_image">Удалить аватар</string>
|
||||
<string name="save_and_notify_contacts">Сохранить (и послать обновление контактам)</string>
|
||||
|
||||
<!-- Welcome Prompts - WelcomeView.kt -->
|
||||
<string name="you_control_your_chat">Вы котролируете ваш чат!</string>
|
||||
<string name="the_messaging_and_app_platform_protecting_your_privacy_and_security">Платформа для сообщений и приложений, которая защищает вашу личную информацию и безопасность.</string>
|
||||
<string name="we_do_not_store_contacts_or_messages_on_servers">Мы не храним ваши контакты и сообщения (после доставки) на серверах.</string>
|
||||
<string name="create_profile">Создать профиль</string>
|
||||
<string name="your_profile_is_stored_on_your_decide_and_shared_only_with_your_contacts">Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам.</string>
|
||||
<string name="display_name_cannot_contain_whitespace">Имя профиля не может содержать пробелы.</string>
|
||||
<string name="display_name">Имя профиля</string>
|
||||
<string name="full_name_optional__prompt">Полное имя (не обязательно)</string>
|
||||
<string name="create_profile_button">Создать</string>
|
||||
|
||||
<!-- markdown demo - MarkdownHelpView.kt -->
|
||||
<string name="how_to_use_markdown">Как форматировать</string>
|
||||
<string name="you_can_use_markdown_to_format_messages__prompt">Вы можете форматировать сообщения:</string>
|
||||
<string name="bold">жирный</string>
|
||||
<string name="italic">курсив</string>
|
||||
<string name="strikethrough">зачеркнуть</string>
|
||||
<string name="a_plus_b">a + b</string>
|
||||
<string name="colored">цвет</string>
|
||||
<string name="secret">секрет</string>
|
||||
|
||||
</resources>
|
||||
@@ -1,7 +1,218 @@
|
||||
<resources>
|
||||
<string name="app_name">SimpleX</string>
|
||||
<resources xmlns:xliff="urn:oasis:names:tc:xliff:document:1.2">
|
||||
<string name="app_name"><xliff:g id="appName">SimpleX</xliff:g></string>
|
||||
|
||||
<string name="thousand_abbreviation">k</string>
|
||||
|
||||
<!-- Connect via Link - MainActivity.kt -->
|
||||
<string name="connect_via_contact_link">Connect via contact link?</string>
|
||||
<string name="connect_via_invitation_link">Connect via invitation link?</string>
|
||||
<string name="profile_will_be_sent_to_contact_sending_link">Your profile will be sent to the contact that you received this link from.</string>
|
||||
<string name="connect_via_link_verb">Connect</string>
|
||||
|
||||
<!-- Server info - ChatModel.kt -->
|
||||
<string name="server_connected">Server connected</string>
|
||||
<string name="server_connecting">Connecting server…</string>
|
||||
<string name="connected_to_server_to_receive_messages_from_contact">You are connected to the server used to receive messages from this contact.</string>
|
||||
<string name="trying_to_connect_to_server_to_receive_messages_with_error">Trying to connect to the server used to receive messages from this contact (error: <xliff:g id="errorMsg">%1$s</xliff:g>).</string>
|
||||
<string name="trying_to_connect_to_server_to_receive_messages">Trying to connect to the server used to receive messages from this contact.</string>
|
||||
|
||||
<!-- Item Content - ChatModel.kt -->
|
||||
<string name="deleted_description">deleted</string>
|
||||
<string name="sending_files_not_yet_supported">sending files is not supported yet</string>
|
||||
<string name="receiving_files_not_yet_supported">receiving files is not supported yet</string>
|
||||
<string name="sender_you_pronoun">you</string>
|
||||
<string name="unknown_message_format">unknown message format</string>
|
||||
<string name="invalid_message_format">invalid message format</string>
|
||||
|
||||
<!-- SMP Server Information - SimpleXAPI.kt -->
|
||||
<string name="error_saving_smp_servers">Error saving SMP servers</string>
|
||||
<string name="ensure_smp_server_address_are_correct_format_and_unique">Make sure SMP server addresses are in correct format, line separated and are not duplicated.</string>
|
||||
|
||||
<!-- API Error Responses - SimpleXAPI.kt -->
|
||||
<string name="contact_already_exists">Contact already exists</string>
|
||||
<string name="you_are_already_connected_to_vName_via_this_link">You are already connected to <xliff:g id="contactName" example="Alice">%1$s!</xliff:g> via this link.</string>
|
||||
<string name="invalid_connection_link">Invalid connection link</string>
|
||||
<string name="please_check_correct_link_and_maybe_ask_for_a_new_one">Please check that you used the correct link or ask your contact to send you another one.</string>
|
||||
<string name="cannot_delete_contact">Can\'t delete contact!</string>
|
||||
<string name="contact_cannot_be_deleted_as_they_are_in_groups">Contact <xliff:g id="contactName" example="Jane Doe">%1$s!</xliff:g> cannot be deleted, they are a member of the group(s) <xliff:g id="groups" example="[team, chess club]">%2$s</xliff:g>.</string>
|
||||
<string name="icon_descr_instant_notifications">Instant notifications</string>
|
||||
|
||||
<!-- background service notice - SimpleXAPI.kt -->
|
||||
<string name="private_instant_notifications">Private instant notifications!</string>
|
||||
<string name="to_preserve_privacy_simplex_has_background_service_instead_of_push_notifications_it_uses_a_few_pc_battery">To preserve your privacy, instead of push notifications the app has a <b><xliff:g id="appName">SimpleX</xliff:g> background service</b> – it uses a few percent of the battery per day.</string>
|
||||
<string name="it_can_disabled_via_settings_notifications_still_shown"><b>It can be disabled via settings</b> – notifications will still be shown while the app is running.</string>
|
||||
|
||||
<!-- SimpleX Chat foreground Service -->
|
||||
<string name="simplex_service_notification_title">SimpleX Chat service</string>
|
||||
<string name="simplex_service_notification_text">Waiting for incoming messages</string>
|
||||
<string name="simplex_service_notification_title"><xliff:g id="appNameFull">SimpleX Chat</xliff:g> service</string>
|
||||
<string name="simplex_service_notification_text">Receiving messages…</string>
|
||||
|
||||
<!-- Chat Actions - ChatItemView.kt (and general) -->
|
||||
<string name="reply_verb">Reply</string>
|
||||
<string name="share_verb">Share</string>
|
||||
<string name="copy_verb">Copy</string>
|
||||
<string name="edit_verb">Edit</string>
|
||||
<string name="delete_verb">Delete</string>
|
||||
<string name="delete_message__question">Delete message?</string>
|
||||
<string name="delete_message_cannot_be_undone_warning">Message will be deleted - this cannot be undone!</string>
|
||||
<string name="for_me_only">For me only</string>
|
||||
<string name="for_everybody">For everybody</string>
|
||||
|
||||
<!-- CIMetaView.kt -->
|
||||
<string name="icon_descr_edited">edited</string>
|
||||
<string name="icon_descr_sent_msg_status_sent">sent</string>
|
||||
<string name="icon_descr_sent_msg_status_unauthorized_send">unauthorized send</string>
|
||||
<string name="icon_descr_sent_msg_status_send_failed">send failed</string>
|
||||
<string name="icon_descr_received_msg_status_unread">unread</string>
|
||||
|
||||
<!-- ChatListView.kt -->
|
||||
<string name="personal_welcome">Welcome <xliff:g>%1$s</xliff:g>!</string>
|
||||
<string name="welcome">Welcome!</string>
|
||||
<string name="this_text_is_available_in_settings">This text is available in settings</string>
|
||||
<string name="your_chats">Your chats</string>
|
||||
|
||||
<!-- Chat Info Actions - ChatInfoView.kt -->
|
||||
<string name="delete_contact__question">Delete contact?</string>
|
||||
<string name="delete_contact_all_messages_deleted_cannot_undo_warning">Contact and all messages will be deleted - this cannot be undone!</string>
|
||||
<string name="button_delete_contact">Delete contact</string>
|
||||
<string name="icon_descr_server_status_connected">Connected</string>
|
||||
<string name="icon_descr_server_status_disconnected">Disconnected</string>
|
||||
<string name="icon_descr_server_status_error">Error</string>
|
||||
<string name="icon_descr_server_status_pending">Pending</string>
|
||||
|
||||
<!-- Message Actions - SendMsgView.kt -->
|
||||
<string name="icon_descr_send_message">Send Message</string>
|
||||
|
||||
<!-- General Actions / Responses -->
|
||||
<string name="back">Back</string>
|
||||
<string name="cancel_verb">Cancel</string>
|
||||
<string name="confirm_verb">Confirm</string>
|
||||
<string name="ok">Ok</string>
|
||||
<string name="no_details">no details</string>
|
||||
<string name="add_contact">Add contact</string>
|
||||
<string name="scan_QR_code">Scan QR code</string>
|
||||
|
||||
<!-- NewChatSheet -->
|
||||
<string name="create_QR_code_or_link__bracketed__multiline">(create QR code\nor link)</string>
|
||||
<string name="in_person_or_in_video_call__bracketed">(in person or in video call)</string>
|
||||
<string name="create_group">Create Group</string>
|
||||
<string name="coming_soon__bracketed">(coming soon!)</string>
|
||||
|
||||
<!-- GetImageView -->
|
||||
<string name="toast_camera_permission_denied">Permission Denied!</string>
|
||||
<string name="use_camera_button">Use Camera</string>
|
||||
<string name="from_gallery_button">From Gallery</string>
|
||||
|
||||
<!-- help - ChatHelpView.kt -->
|
||||
<string name="thank_you_for_installing_simplex">Thank you for installing <xliff:g id="appNameFull">SimpleX Chat</xliff:g>!</string>
|
||||
<string name="you_can_connect_to_simplex_chat_founder">You can <font color="#0088ff">connect to <xliff:g id="appNameFull">SimpleX Chat</xliff:g> developers to ask any questions and to receive updates</font>.</string>
|
||||
<string name="to_start_a_new_chat_help_header">To start a new chat</string>
|
||||
<string name="chat_help_tap_button">Tap button</string>
|
||||
<string name="above_then_preposition_continuation">above, then:</string>
|
||||
<string name="add_new_contact_to_create_one_time_QR_code"><b>Add new contact</b>: to create your one-time QR Code for your contact.</string>
|
||||
<string name="scan_QR_code_to_connect_to_contact_who_shows_QR_code"><b>Scan QR code</b>: to connect to your contact who shows QR code to you.</string>
|
||||
<string name="to_connect_via_link_title">To connect via link</string>
|
||||
<string name="if_you_received_simplex_invitation_link_you_can_open_in_browser">If you received <xliff:g id="appName">SimpleX Chat</xliff:g> invitation link, you can open it in your browser:</string>
|
||||
<string name="desktop_scan_QR_code_from_app_via_scan_QR_code">💻 desktop: scan displayed QR code from the app, via <b>Scan QR code</b>.</string>
|
||||
<string name="mobile_tap_open_in_mobile_app_then_tap_connect_in_app">📱 mobile: tap <b>Open in mobile app</b>, then tap <b>Connect</b> in the app.</string>
|
||||
|
||||
<!-- Contact Request Alert Dialogue - CharListNavLinkView.kt -->
|
||||
<string name="accept_connection_request__question">Accept connection request?</string>
|
||||
<string name="if_you_choose_to_reject_the_sender_will_not_be_notified">If you choose to reject sender will NOT be notified.</string>
|
||||
<string name="accept_contact_button">Accept</string>
|
||||
<string name="reject_contact_button">Reject</string>
|
||||
|
||||
<!-- Contact Request Information - ContactRequestView.kt -->
|
||||
<string name="contact_wants_to_connect_with_you">wants to connect to you!</string>
|
||||
|
||||
<!-- Image Placeholder - ChatInfoImage.kt -->
|
||||
<string name="icon_descr_profile_image_placeholder">profile image placeholder</string>
|
||||
<string name="image_descr_profile_image">profile image</string>
|
||||
|
||||
<!-- Content Descriptions -->
|
||||
<string name="icon_descr_close_button">Close button</string>
|
||||
<string name="image_descr_link_preview">link preview image</string>
|
||||
<string name="icon_descr_cancel_link_preview">cancel link preview</string>
|
||||
<string name="icon_descr_settings">Settings</string>
|
||||
<string name="image_descr_qr_code">QR Code</string>
|
||||
<string name="icon_descr_address"><xliff:g id="appName">SimpleX</xliff:g> Address</string>
|
||||
<string name="icon_descr_help">help</string>
|
||||
<string name="icon_descr_simplex_team"><xliff:g id="appName">SimpleX</xliff:g> Team</string>
|
||||
<string name="image_descr_simplex_logo"><xliff:g id="appName">SimpleX</xliff:g> Logo</string>
|
||||
<string name="icon_descr_email">Email</string>
|
||||
|
||||
<!-- Add Contact - AddContactView.kt -->
|
||||
<string name="invalid_QR_code">Invalid QR code</string>
|
||||
<string name="this_QR_code_is_not_a_link">This QR code is not a link!</string>
|
||||
<string name="invalid_contact_link">Invalid link!</string>
|
||||
<string name="this_link_is_not_a_valid_connection_link">This link is not a valid connection link!</string>
|
||||
<string name="connection_request_sent">Connection request sent!</string>
|
||||
<string name="you_will_be_connected_when_your_connection_request_is_accepted">You will be connected when your connection request is accepted, please wait or check later!</string>
|
||||
<string name="you_will_be_connected_when_your_contacts_device_is_online">You will be connected when your contact\'s device is online, please wait or check later!</string>
|
||||
<string name="show_QR_code_for_your_contact_to_scan_from_the_app__multiline">Show QR code for your contact\nto scan from the app</string>
|
||||
<string name="if_you_cannot_meet_in_person_show_QR_in_video_call_or_via_another_channel">If you cannot meet in person, you can <b>show QR code in the video call</b>, or you can share the invitation link via any other channel.</string>
|
||||
<string name="your_chat_profile_will_be_sent_to_your_contact">Your chat profile will be sent\nto your contact</string>
|
||||
<string name="if_you_cannot_meet_in_person_scan_QR_in_video_call_or_ask_for_invitation_link">If you cannot meet in person, you can <b>scan QR code in the video call</b>, or your contact can share an invitation link.</string>
|
||||
<string name="share_invitation_link">Share invitation link</string>
|
||||
|
||||
<!-- settings - SettingsView.kt -->
|
||||
<string name="your_settings">Your settings</string>
|
||||
<string name="your_simplex_contact_address">Your <xliff:g id="appName">SimpleX</xliff:g> contact address</string>
|
||||
<string name="how_to_use_simplex_chat">How to use <xliff:g id="appNameFull">SimpleX Chat</xliff:g></string>
|
||||
<string name="markdown_help">Markdown help</string>
|
||||
<string name="markdown_in_messages">Markdown in messages</string>
|
||||
<string name="chat_with_the_founder">Connect to the developers</string>
|
||||
<string name="send_us_an_email">Send us email</string>
|
||||
<string name="private_notifications">Private notifications</string>
|
||||
<string name="chat_console">Chat console</string>
|
||||
<string name="smp_servers">SMP servers</string>
|
||||
<string name="install_simplex_chat_for_terminal">Install <font color="#0088ff"><xliff:g id="appNameFull">SimpleX Chat</xliff:g> for terminal</font></string>
|
||||
<string name="use_simplex_chat_servers__question">Use <xliff:g id="appNameFull">SimpleX Chat</xliff:g> servers?</string>
|
||||
<string name="saved_SMP_servers_will_br_removed">Saved SMP servers will be removed.</string>
|
||||
<string name="your_SMP_servers">Your SMP servers</string>
|
||||
<string name="configure_SMP_servers">Configure SMP servers</string>
|
||||
<string name="using_simplex_chat_servers">Using <xliff:g id="appNameFull">SimpleX Chat</xliff:g> servers.</string>
|
||||
<string name="enter_one_SMP_server_per_line">Enter one SMP server per line:</string>
|
||||
<string name="how_to">How to</string>
|
||||
<string name="save_servers_button">Save</string>
|
||||
|
||||
<!-- Address Items - UserAddressView.kt -->
|
||||
<string name="create_address">Create address</string>
|
||||
<string name="delete_address__question">Delete address?</string>
|
||||
<string name="all_your_contacts_will_remain_connected">All your contacts will remain connected.</string>
|
||||
<string name="your_chat_address">Your chat address</string>
|
||||
<string name="you_can_share_your_address_anybody_will_be_able_to_connect">You can share your address as a link or as a QR code - anybody will be able to connect to you.</string>
|
||||
<string name="if_you_delete_address_you_wont_lose_contacts">If you later delete it - you won\'t lose your contacts.</string>
|
||||
<string name="share_link">Share link</string>
|
||||
<string name="delete_address">Delete address</string>
|
||||
|
||||
<!-- User profile details - UserProfileView.kt -->
|
||||
<string name="display_name__field">Display name:</string>
|
||||
<string name="full_name__field">"Full name:</string>
|
||||
<string name="your_chat_profile">Your chat profile</string>
|
||||
<string name="your_profile_is_stored_on_device_and_shared_only_with_contacts_simplex_cannot_see_it">Your profile is stored on your device and shared only with your contacts.\n\n<xliff:g id="appName">SimpleX</xliff:g> servers cannot see your profile.</string>
|
||||
<string name="edit_image">Edit image</string>
|
||||
<string name="delete_image">Delete image</string>
|
||||
<string name="save_and_notify_contacts">Save (and notify contacts)</string>
|
||||
|
||||
<!-- Welcome Prompts - WelcomeView.kt -->
|
||||
<string name="you_control_your_chat">You control your chat!</string>
|
||||
<string name="the_messaging_and_app_platform_protecting_your_privacy_and_security">The messaging and application platform protecting your privacy and security.</string>
|
||||
<string name="we_do_not_store_contacts_or_messages_on_servers">We don\'t store any of your contacts or messages (once delivered) on the servers.</string>
|
||||
<string name="create_profile">Create profile</string>
|
||||
<string name="your_profile_is_stored_on_your_decide_and_shared_only_with_your_contacts">Your profile is stored on your device and shared only with your contacts.</string>
|
||||
<string name="display_name_cannot_contain_whitespace">Display name cannot contain whitespace.</string>
|
||||
<string name="display_name">Display Name</string>
|
||||
<string name="full_name_optional__prompt">Full Name (Optional)</string>
|
||||
<string name="create_profile_button">Create</string>
|
||||
|
||||
<!-- markdown demo - MarkdownHelpView.kt -->
|
||||
<string name="how_to_use_markdown">How to use markdown</string>
|
||||
<string name="you_can_use_markdown_to_format_messages__prompt">You can use markdown to format messages:</string>
|
||||
<string name="bold">bold</string>
|
||||
<string name="italic">italic</string>
|
||||
<string name="strikethrough">strike</string>
|
||||
<string name="a_plus_b">a + b</string>
|
||||
<string name="colored">colored</string>
|
||||
<string name="secret">secret</string>
|
||||
|
||||
</resources>
|
||||
|
||||
@@ -7,7 +7,7 @@ buildscript {
|
||||
mavenCentral()
|
||||
}
|
||||
dependencies {
|
||||
classpath 'com.android.tools.build:gradle:7.1.2'
|
||||
classpath 'com.android.tools.build:gradle:7.1.3'
|
||||
classpath "org.jetbrains.kotlin:kotlin-gradle-plugin:1.6.10"
|
||||
classpath "org.jetbrains.kotlin:kotlin-serialization:1.3.2"
|
||||
|
||||
@@ -16,8 +16,8 @@ buildscript {
|
||||
}
|
||||
}// Top-level build file where you can add configuration options common to all sub-projects/modules.
|
||||
plugins {
|
||||
id 'com.android.application' version '7.1.2' apply false
|
||||
id 'com.android.library' version '7.1.2' apply false
|
||||
id 'com.android.application' version '7.1.3' apply false
|
||||
id 'com.android.library' version '7.1.3' apply false
|
||||
id 'org.jetbrains.kotlin.android' version '1.6.10' apply false
|
||||
id 'org.jetbrains.kotlin.plugin.serialization' version '1.6.10'
|
||||
}
|
||||
|
||||
@@ -0,0 +1,29 @@
|
||||
# Localization
|
||||
|
||||
## Creating localization keys
|
||||
|
||||
There are three ways XCode generates localization keys from strings:
|
||||
|
||||
1. Automatically, from the texts used in standard components `Text`, `Label`, `Button`, etc.
|
||||
|
||||
2. All strings passed to view variables and function parameters declared as `LocalizedStringKey` type. Only string constants (possibly, with interpolation) or other variables of type `LocalizedStringKey` can be passed to these parameters. See, for example, ContentView.swift.
|
||||
|
||||
3. All strings wrapped in `NSLocalizedString`. Please note that such strings do not support swift interpolation, instead formatted strings should be used:
|
||||
|
||||
```swift
|
||||
String.localizedStringWithFormat(NSLocalizedString("You can now send messages to %@", comment: "notification body")
|
||||
```
|
||||
|
||||
## Adding strings to the existing localizations
|
||||
|
||||
1. Choose `Product -> Export Localizations...` in the menu, choose `ios` folder as the destination and `SimpleX Localizations` as the folder name, confirm to overwrite it (make sure not to save to subfolder).
|
||||
2. Add `target` keys to the localizations that were added or changed.
|
||||
3. Choose `Product -> Import Localizations...` for any non-Enlish folders - that would update Localizable files.
|
||||
|
||||
Localizable files values can be edited directly, the changes will be included in the next export. Following the process above though guarantees that all strings are localized.
|
||||
|
||||
## Development
|
||||
|
||||
Make sure to enable the option `Show non-localized strings` in `Product -> Scheme -> Edit scheme...` menu - it will be showing all non-localized strings as all caps.
|
||||
|
||||
Read more about editing XLIFF and string files here: https://developer.apple.com/documentation/xcode/editing-xliff-and-strings-files
|
||||
@@ -36,7 +36,7 @@ struct ContentView: View {
|
||||
|
||||
func notificationAlert() -> Alert {
|
||||
Alert(
|
||||
title: Text("Notification are disabled!"),
|
||||
title: Text("Notifications are disabled!"),
|
||||
message: Text("The app can notify you when you receive messages or contact requests - please open settings to enable."),
|
||||
primaryButton: .default(Text("Open Settings")) {
|
||||
DispatchQueue.main.async {
|
||||
@@ -61,7 +61,7 @@ final class AlertManager: ObservableObject {
|
||||
}
|
||||
}
|
||||
|
||||
func showAlertMsg(title: String, message: String? = nil) {
|
||||
func showAlertMsg(title: LocalizedStringKey, message: LocalizedStringKey? = nil) {
|
||||
if let message = message {
|
||||
showAlert(Alert(title: Text(title), message: Text(message)))
|
||||
} else {
|
||||
|
||||
@@ -384,7 +384,7 @@ final class Chat: ObservableObject, Identifiable {
|
||||
case disconnected
|
||||
case error(String)
|
||||
|
||||
var statusString: String {
|
||||
var statusString: LocalizedStringKey {
|
||||
get {
|
||||
switch self {
|
||||
case .connected: return "Server connected"
|
||||
@@ -394,7 +394,7 @@ final class Chat: ObservableObject, Identifiable {
|
||||
}
|
||||
}
|
||||
|
||||
var statusExplanation: String {
|
||||
var statusExplanation: LocalizedStringKey {
|
||||
get {
|
||||
switch self {
|
||||
case .connected: return "You are connected to the server used to receive messages from this contact."
|
||||
@@ -719,10 +719,19 @@ enum CIContent: Decodable, ItemContent {
|
||||
switch self {
|
||||
case let .sndMsgContent(mc): return mc.text
|
||||
case let .rcvMsgContent(mc): return mc.text
|
||||
case .sndDeleted: return "deleted"
|
||||
case .rcvDeleted: return "deleted"
|
||||
case .sndFileInvitation: return "sending files is not supported yet"
|
||||
case .rcvFileInvitation: return "receiving files is not supported yet"
|
||||
case .sndDeleted: return NSLocalizedString("deleted", comment: "deleted chat item")
|
||||
case .rcvDeleted: return NSLocalizedString("deleted", comment: "deleted chat item")
|
||||
case .sndFileInvitation: return NSLocalizedString("sending files is not supported yet", comment: "to be removed")
|
||||
case .rcvFileInvitation: return NSLocalizedString("receiving files is not supported yet", comment: "to be removed")
|
||||
}
|
||||
}
|
||||
}
|
||||
var msgContent: MsgContent? {
|
||||
get {
|
||||
switch self {
|
||||
case let .sndMsgContent(mc): return mc
|
||||
case let .rcvMsgContent(mc): return mc
|
||||
default: return nil
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -761,6 +770,7 @@ struct CIQuote: Decodable, ItemContent {
|
||||
|
||||
enum MsgContent {
|
||||
case text(String)
|
||||
case link(text: String, preview: LinkPreview)
|
||||
// TODO include original JSON, possibly using https://github.com/zoul/generic-json-swift
|
||||
case unknown(type: String, text: String)
|
||||
|
||||
@@ -768,6 +778,7 @@ enum MsgContent {
|
||||
get {
|
||||
switch self {
|
||||
case let .text(text): return text
|
||||
case let .link(text, _): return text
|
||||
case let .unknown(_, text): return text
|
||||
}
|
||||
}
|
||||
@@ -777,6 +788,8 @@ enum MsgContent {
|
||||
get {
|
||||
switch self {
|
||||
case let .text(text): return "text \(text)"
|
||||
case let .link(text: text, preview: preview):
|
||||
return "json {\"type\":\"link\",\"text\":\(encodeJSON(text)),\"preview\":\(encodeJSON(preview))}"
|
||||
default: return ""
|
||||
}
|
||||
}
|
||||
@@ -785,9 +798,11 @@ enum MsgContent {
|
||||
enum CodingKeys: String, CodingKey {
|
||||
case type
|
||||
case text
|
||||
case preview
|
||||
}
|
||||
}
|
||||
|
||||
// TODO define Encodable
|
||||
extension MsgContent: Decodable {
|
||||
init(from decoder: Decoder) throws {
|
||||
do {
|
||||
@@ -797,6 +812,10 @@ extension MsgContent: Decodable {
|
||||
case "text":
|
||||
let text = try container.decode(String.self, forKey: CodingKeys.text)
|
||||
self = .text(text)
|
||||
case "link":
|
||||
let text = try container.decode(String.self, forKey: CodingKeys.text)
|
||||
let preview = try container.decode(LinkPreview.self, forKey: CodingKeys.preview)
|
||||
self = .link(text: text, preview: preview)
|
||||
default:
|
||||
let text = try? container.decode(String.self, forKey: CodingKeys.text)
|
||||
self = .unknown(type: type, text: text ?? "unknown message format")
|
||||
@@ -812,7 +831,7 @@ struct FormattedText: Decodable {
|
||||
var format: Format?
|
||||
}
|
||||
|
||||
enum Format: Decodable {
|
||||
enum Format: Decodable, Equatable {
|
||||
case bold
|
||||
case italic
|
||||
case strikeThrough
|
||||
@@ -849,3 +868,12 @@ enum FormatColor: String, Decodable {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Struct to use with simplex API
|
||||
struct LinkPreview: Codable {
|
||||
var uri: URL
|
||||
var title: String
|
||||
// TODO remove once optional in haskell
|
||||
var description: String = ""
|
||||
var image: String
|
||||
}
|
||||
|
||||
@@ -92,22 +92,22 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
identifier: ntfCategoryContactRequest,
|
||||
actions: [UNNotificationAction(
|
||||
identifier: ntfActionAccept,
|
||||
title: "Accept"
|
||||
title: NSLocalizedString("Accept", comment: "accept contact request via notification")
|
||||
)],
|
||||
intentIdentifiers: [],
|
||||
hiddenPreviewsBodyPlaceholder: "New contact request"
|
||||
hiddenPreviewsBodyPlaceholder: NSLocalizedString("New contact request", comment: "notification")
|
||||
),
|
||||
UNNotificationCategory(
|
||||
identifier: ntfCategoryContactConnected,
|
||||
actions: [],
|
||||
intentIdentifiers: [],
|
||||
hiddenPreviewsBodyPlaceholder: "Contact is connected"
|
||||
hiddenPreviewsBodyPlaceholder: NSLocalizedString("Contact is connected", comment: "notification")
|
||||
),
|
||||
UNNotificationCategory(
|
||||
identifier: ntfCategoryMessageReceived,
|
||||
actions: [],
|
||||
intentIdentifiers: [],
|
||||
hiddenPreviewsBodyPlaceholder: "New message"
|
||||
hiddenPreviewsBodyPlaceholder: NSLocalizedString("New message", comment: "notifications")
|
||||
)
|
||||
])
|
||||
}
|
||||
@@ -139,8 +139,8 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
logger.debug("NtfManager.notifyContactRequest")
|
||||
addNotification(
|
||||
categoryIdentifier: ntfCategoryContactRequest,
|
||||
title: "\(contactRequest.displayName) wants to connect!",
|
||||
body: "Accept contact request from \(contactRequest.chatViewName)?",
|
||||
title: String.localizedStringWithFormat(NSLocalizedString("%@ wants to connect!", comment: "notification title"), contactRequest.displayName),
|
||||
body: String.localizedStringWithFormat(NSLocalizedString("Accept contact request from %@?", comment: "notification body"), contactRequest.chatViewName),
|
||||
targetContentIdentifier: nil,
|
||||
userInfo: ["chatId": contactRequest.id, "contactRequestId": contactRequest.apiId]
|
||||
)
|
||||
@@ -150,8 +150,8 @@ class NtfManager: NSObject, UNUserNotificationCenterDelegate, ObservableObject {
|
||||
logger.debug("NtfManager.notifyContactConnected")
|
||||
addNotification(
|
||||
categoryIdentifier: ntfCategoryContactConnected,
|
||||
title: "\(contact.displayName) is connected!",
|
||||
body: "You can now send messages to \(contact.chatViewName)",
|
||||
title: String.localizedStringWithFormat(NSLocalizedString("%@ is connected!", comment: "notification title"), contact.displayName),
|
||||
body: String.localizedStringWithFormat(NSLocalizedString("You can now send messages to %@", comment: "notification body"), contact.chatViewName),
|
||||
targetContentIdentifier: contact.id
|
||||
// userInfo: ["chatId": contact.id, "contactId": contact.apiId]
|
||||
)
|
||||
|
||||
@@ -13,7 +13,7 @@ import BackgroundTasks
|
||||
|
||||
private var chatController: chat_ctrl?
|
||||
private let jsonDecoder = getJSONDecoder()
|
||||
private let jsonEncoder = getJSONEncoder()
|
||||
let jsonEncoder = getJSONEncoder()
|
||||
|
||||
enum ChatCommand {
|
||||
case showActiveUser
|
||||
@@ -31,6 +31,7 @@ enum ChatCommand {
|
||||
case connect(connReq: String)
|
||||
case apiDeleteChat(type: ChatType, id: Int64)
|
||||
case apiUpdateProfile(profile: Profile)
|
||||
case apiParseMarkdown(text: String)
|
||||
case createMyAddress
|
||||
case deleteMyAddress
|
||||
case showMyAddress
|
||||
@@ -57,6 +58,7 @@ enum ChatCommand {
|
||||
case let .connect(connReq): return "/connect \(connReq)"
|
||||
case let .apiDeleteChat(type, id): return "/_delete \(ref(type, id))"
|
||||
case let .apiUpdateProfile(profile): return "/_profile \(encodeJSON(profile))"
|
||||
case let .apiParseMarkdown(text): return "/_parse \(text)"
|
||||
case .createMyAddress: return "/address"
|
||||
case .deleteMyAddress: return "/delete_address"
|
||||
case .showMyAddress: return "/show_address"
|
||||
@@ -86,6 +88,7 @@ enum ChatCommand {
|
||||
case .connect: return "connect"
|
||||
case .apiDeleteChat: return "apiDeleteChat"
|
||||
case .apiUpdateProfile: return "apiUpdateProfile"
|
||||
case .apiParseMarkdown: return "apiParseMarkdown"
|
||||
case .createMyAddress: return "createMyAddress"
|
||||
case .deleteMyAddress: return "deleteMyAddress"
|
||||
case .showMyAddress: return "showMyAddress"
|
||||
@@ -125,6 +128,7 @@ enum ChatResponse: Decodable, Error {
|
||||
case contactDeleted(contact: Contact)
|
||||
case userProfileNoChange
|
||||
case userProfileUpdated(fromProfile: Profile, toProfile: Profile)
|
||||
case apiParsedMarkdown(formattedText: [FormattedText]?)
|
||||
case userContactLink(connReqContact: String)
|
||||
case userContactLinkCreated(connReqContact: String)
|
||||
case userContactLinkDeleted
|
||||
@@ -166,6 +170,7 @@ enum ChatResponse: Decodable, Error {
|
||||
case .contactDeleted: return "contactDeleted"
|
||||
case .userProfileNoChange: return "userProfileNoChange"
|
||||
case .userProfileUpdated: return "userProfileUpdated"
|
||||
case .apiParsedMarkdown: return "apiParsedMarkdown"
|
||||
case .userContactLink: return "userContactLink"
|
||||
case .userContactLinkCreated: return "userContactLinkCreated"
|
||||
case .userContactLinkDeleted: return "userContactLinkDeleted"
|
||||
@@ -210,6 +215,7 @@ enum ChatResponse: Decodable, Error {
|
||||
case let .contactDeleted(contact): return String(describing: contact)
|
||||
case .userProfileNoChange: return noDetails
|
||||
case let .userProfileUpdated(_, toProfile): return String(describing: toProfile)
|
||||
case let .apiParsedMarkdown(formattedText): return String(describing: formattedText)
|
||||
case let .userContactLink(connReq): return connReq
|
||||
case let .userContactLinkCreated(connReq): return connReq
|
||||
case .userContactLinkDeleted: return noDetails
|
||||
@@ -323,9 +329,11 @@ func chatSendCmdSync(_ cmd: ChatCommand, bgTask: Bool = true, bgDelay: Double? =
|
||||
if case let .response(_, json) = resp {
|
||||
logger.debug("chatSendCmd \(cmd.cmdType) response: \(json)")
|
||||
}
|
||||
DispatchQueue.main.async {
|
||||
ChatModel.shared.terminalItems.append(.cmd(.now, cmd))
|
||||
ChatModel.shared.terminalItems.append(.resp(.now, resp))
|
||||
if case .apiParseMarkdown = cmd {} else {
|
||||
DispatchQueue.main.async {
|
||||
ChatModel.shared.terminalItems.append(.cmd(.now, cmd))
|
||||
ChatModel.shared.terminalItems.append(.resp(.now, resp))
|
||||
}
|
||||
}
|
||||
return resp
|
||||
}
|
||||
@@ -459,13 +467,13 @@ func apiConnect(connReq: String) async throws -> Bool {
|
||||
case .chatCmdError(.errorAgent(.BROKER(.TIMEOUT))):
|
||||
am.showAlertMsg(
|
||||
title: "Connection timeout",
|
||||
message: "Please check your network connection and try again"
|
||||
message: "Please check your network connection and try again."
|
||||
)
|
||||
return false
|
||||
case .chatCmdError(.errorAgent(.BROKER(.NETWORK))):
|
||||
am.showAlertMsg(
|
||||
title: "Connection error",
|
||||
message: "Please check your network connection and try again"
|
||||
message: "Please check your network connection and try again."
|
||||
)
|
||||
return false
|
||||
default: throw r
|
||||
@@ -487,6 +495,12 @@ func apiUpdateProfile(profile: Profile) async throws -> Profile? {
|
||||
}
|
||||
}
|
||||
|
||||
func apiParseMarkdown(text: String) throws -> [FormattedText]? {
|
||||
let r = chatSendCmdSync(.apiParseMarkdown(text: text))
|
||||
if case let .apiParsedMarkdown(formattedText) = r { return formattedText }
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiCreateUserAddress() async throws -> String {
|
||||
let r = await chatSendCmd(.createMyAddress)
|
||||
if case let .userContactLinkCreated(connReq) = r { return connReq }
|
||||
@@ -774,7 +788,7 @@ private func getJSONObject(_ cjson: UnsafePointer<CChar>) -> NSDictionary? {
|
||||
return try? JSONSerialization.jsonObject(with: d) as? NSDictionary
|
||||
}
|
||||
|
||||
private func encodeJSON<T: Encodable>(_ value: T) -> String {
|
||||
func encodeJSON<T: Encodable>(_ value: T) -> String {
|
||||
let data = try! jsonEncoder.encode(value)
|
||||
return String(decoding: data, as: UTF8.self)
|
||||
}
|
||||
|
||||
@@ -1,11 +0,0 @@
|
||||
//
|
||||
// Use this file to import your target's public headers that you would like to expose to Swift.
|
||||
//
|
||||
|
||||
extern void hs_init(int argc, char **argv[]);
|
||||
|
||||
typedef void* chat_ctrl;
|
||||
|
||||
extern chat_ctrl chat_init(char *path);
|
||||
extern char *chat_send_cmd(chat_ctrl ctl, char *cmd);
|
||||
extern char *chat_recv_msg(chat_ctrl ctl);
|
||||
@@ -64,7 +64,7 @@ struct ChatInfoView: View {
|
||||
private func deleteContactAlert(_ contact: Contact) -> Alert {
|
||||
Alert(
|
||||
title: Text("Delete contact?"),
|
||||
message: Text("Contact and all messages will be deleted"),
|
||||
message: Text("Contact and all messages will be deleted - this cannot be undone!"),
|
||||
primaryButton: .destructive(Text("Delete")) {
|
||||
Task {
|
||||
do {
|
||||
|
||||
@@ -8,8 +8,8 @@
|
||||
|
||||
import SwiftUI
|
||||
|
||||
private let sentColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.12)
|
||||
private let sentColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.17)
|
||||
let sentColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.12)
|
||||
let sentColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.17)
|
||||
private let sentQuoteColorLight = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.11)
|
||||
private let sentQuoteColorDark = Color(.sRGB, red: 0.27, green: 0.72, blue: 1, opacity: 0.09)
|
||||
|
||||
@@ -51,6 +51,9 @@ struct FramedItemView: View {
|
||||
.frame(minWidth: msgWidth, alignment: .center)
|
||||
.padding(.bottom, 2)
|
||||
} else {
|
||||
if case let .link(_, preview) = chatItem.content.msgContent {
|
||||
ChatItemLinkView(linkPreview: preview)
|
||||
}
|
||||
MsgContentView(
|
||||
content: chatItem.content,
|
||||
formattedText: chatItem.formattedText,
|
||||
@@ -84,7 +87,7 @@ struct FramedItemView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func msgDeliveryError(_ err: String) {
|
||||
private func msgDeliveryError(_ err: LocalizedStringKey) {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Message delivery error",
|
||||
message: err
|
||||
|
||||
@@ -17,6 +17,7 @@ struct ChatView: View {
|
||||
@State var message: String = ""
|
||||
@State var quotedItem: ChatItem? = nil
|
||||
@State var editingItem: ChatItem? = nil
|
||||
@State var linkPreview: LinkPreview? = nil
|
||||
@State var deletingItem: ChatItem? = nil
|
||||
@State private var inProgress: Bool = false
|
||||
@FocusState private var keyboardVisible: Bool
|
||||
@@ -85,6 +86,7 @@ struct ChatView: View {
|
||||
message: $message,
|
||||
quotedItem: $quotedItem,
|
||||
editingItem: $editingItem,
|
||||
linkPreview: $linkPreview,
|
||||
sendMessage: sendMessage,
|
||||
resetMessage: { message = "" },
|
||||
inProgress: inProgress,
|
||||
@@ -98,7 +100,7 @@ struct ChatView: View {
|
||||
Button { chatModel.chatId = nil } label: {
|
||||
HStack(spacing: 4) {
|
||||
Image(systemName: "chevron.backward")
|
||||
Text("Chats")
|
||||
Text("Chats", comment: "back button to return to chats list")
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -200,7 +202,7 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func sendMessage(_ msg: String) {
|
||||
func sendMessage(_ text: String) {
|
||||
logger.debug("ChatView sendMessage")
|
||||
Task {
|
||||
logger.debug("ChatView sendMessage: in Task")
|
||||
@@ -210,21 +212,29 @@ struct ChatView: View {
|
||||
type: chat.chatInfo.chatType,
|
||||
id: chat.chatInfo.apiId,
|
||||
itemId: ei.id,
|
||||
msg: .text(msg)
|
||||
msg: .text(text)
|
||||
)
|
||||
DispatchQueue.main.async {
|
||||
editingItem = nil
|
||||
linkPreview = nil
|
||||
let _ = chatModel.upsertChatItem(chat.chatInfo, chatItem)
|
||||
}
|
||||
} else {
|
||||
let mc: MsgContent
|
||||
if let preview = linkPreview {
|
||||
mc = .link(text: text, preview: preview)
|
||||
} else {
|
||||
mc = .text(text)
|
||||
}
|
||||
let chatItem = try await apiSendMessage(
|
||||
type: chat.chatInfo.chatType,
|
||||
id: chat.chatInfo.apiId,
|
||||
quotedItemId: quotedItem?.meta.itemId,
|
||||
msg: .text(msg)
|
||||
msg: mc
|
||||
)
|
||||
DispatchQueue.main.async {
|
||||
quotedItem = nil
|
||||
linkPreview = nil
|
||||
chatModel.addChatItem(chat.chatInfo, chatItem)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -19,21 +19,34 @@ struct ComposeView: View {
|
||||
@Binding var message: String
|
||||
@Binding var quotedItem: ChatItem?
|
||||
@Binding var editingItem: ChatItem?
|
||||
@Binding var linkPreview: LinkPreview?
|
||||
|
||||
var sendMessage: (String) -> Void
|
||||
var resetMessage: () -> Void
|
||||
var inProgress: Bool = false
|
||||
@FocusState.Binding var keyboardVisible: Bool
|
||||
@State var editing: Bool = false
|
||||
@State var linkUrl: URL? = nil
|
||||
@State var prevLinkUrl: URL? = nil
|
||||
@State var pendingLinkUrl: URL? = nil
|
||||
@State var cancelledLinks: Set<String> = []
|
||||
|
||||
|
||||
var body: some View {
|
||||
VStack(spacing: 0) {
|
||||
if let metadata = linkPreview {
|
||||
ComposeLinkView(linkPreview: metadata, cancelPreview: cancelPreview)
|
||||
}
|
||||
if (quotedItem != nil) {
|
||||
ContextItemView(contextItem: $quotedItem, editing: $editing)
|
||||
} else if (editingItem != nil) {
|
||||
ContextItemView(contextItem: $editingItem, editing: $editing, resetMessage: resetMessage)
|
||||
}
|
||||
SendMessageView(
|
||||
sendMessage: sendMessage,
|
||||
sendMessage: { text in
|
||||
sendMessage(text)
|
||||
resetLinkPreview()
|
||||
},
|
||||
inProgress: inProgress,
|
||||
message: $message,
|
||||
keyboardVisible: $keyboardVisible,
|
||||
@@ -41,10 +54,79 @@ struct ComposeView: View {
|
||||
)
|
||||
.background(.background)
|
||||
}
|
||||
.onChange(of: message) { _ in
|
||||
if message.count > 0 {
|
||||
showLinkPreview(message)
|
||||
} else {
|
||||
resetLinkPreview()
|
||||
}
|
||||
}
|
||||
.onChange(of: editingItem == nil) { _ in
|
||||
editing = (editingItem != nil)
|
||||
}
|
||||
}
|
||||
|
||||
private func showLinkPreview(_ s: String) {
|
||||
prevLinkUrl = linkUrl
|
||||
linkUrl = parseMessage(s)
|
||||
if let url = linkUrl {
|
||||
if url != linkPreview?.uri && url != pendingLinkUrl {
|
||||
pendingLinkUrl = url
|
||||
if prevLinkUrl == url {
|
||||
loadLinkPreview(url)
|
||||
} else {
|
||||
DispatchQueue.main.asyncAfter(deadline: .now() + 1.5) {
|
||||
loadLinkPreview(url)
|
||||
}
|
||||
}
|
||||
}
|
||||
} else {
|
||||
linkPreview = nil
|
||||
}
|
||||
}
|
||||
|
||||
private func parseMessage(_ msg: String) -> URL? {
|
||||
do {
|
||||
let parsedMsg = try apiParseMarkdown(text: msg)
|
||||
let uri = parsedMsg?.first(where: { ft in
|
||||
ft.format == .uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text)
|
||||
})
|
||||
if let uri = uri { return URL(string: uri.text) }
|
||||
else { return nil }
|
||||
} catch {
|
||||
logger.error("apiParseMarkdown error: \(error.localizedDescription)")
|
||||
return nil
|
||||
}
|
||||
}
|
||||
|
||||
private func isSimplexLink(_ link: String) -> Bool {
|
||||
link.starts(with: "https://simplex.chat") || link.starts(with: "http://simplex.chat")
|
||||
}
|
||||
|
||||
private func cancelPreview() {
|
||||
if let uri = linkPreview?.uri.absoluteString {
|
||||
cancelledLinks.insert(uri)
|
||||
}
|
||||
linkPreview = nil
|
||||
}
|
||||
|
||||
private func loadLinkPreview(_ url: URL) {
|
||||
if pendingLinkUrl == url {
|
||||
getLinkPreview(url: url) { lp in
|
||||
if pendingLinkUrl == url {
|
||||
linkPreview = lp
|
||||
pendingLinkUrl = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func resetLinkPreview() {
|
||||
linkUrl = nil
|
||||
prevLinkUrl = nil
|
||||
pendingLinkUrl = nil
|
||||
cancelledLinks = []
|
||||
}
|
||||
}
|
||||
|
||||
struct ComposeView_Previews: PreviewProvider {
|
||||
@@ -53,12 +135,14 @@ struct ComposeView_Previews: PreviewProvider {
|
||||
@FocusState var keyboardVisible: Bool
|
||||
@State var item: ChatItem? = ChatItem.getSample(1, .directSnd, .now, "hello")
|
||||
@State var nilItem: ChatItem? = nil
|
||||
@State var linkPreview: LinkPreview? = nil
|
||||
|
||||
return Group {
|
||||
ComposeView(
|
||||
message: $message,
|
||||
quotedItem: $item,
|
||||
editingItem: $nilItem,
|
||||
linkPreview: $linkPreview,
|
||||
sendMessage: { print ($0) },
|
||||
resetMessage: {},
|
||||
keyboardVisible: $keyboardVisible
|
||||
@@ -67,6 +151,7 @@ struct ComposeView_Previews: PreviewProvider {
|
||||
message: $message,
|
||||
quotedItem: $nilItem,
|
||||
editingItem: $item,
|
||||
linkPreview: $linkPreview,
|
||||
sendMessage: { print ($0) },
|
||||
resetMessage: {},
|
||||
keyboardVisible: $keyboardVisible
|
||||
|
||||
@@ -11,7 +11,7 @@ import SwiftUI
|
||||
struct SendMessageView: View {
|
||||
var sendMessage: (String) -> Void
|
||||
var inProgress: Bool = false
|
||||
@Binding var message: String //Lorem ipsum dolor sit amet, consectetur" // adipiscing elit, sed do eiusmod tempor incididunt ut labor7 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."
|
||||
@Binding var message: String
|
||||
@Namespace var namespace
|
||||
@FocusState.Binding var keyboardVisible: Bool
|
||||
@Binding var editing: Bool
|
||||
@@ -91,7 +91,6 @@ struct SendMessageView_Previews: PreviewProvider {
|
||||
@State var editingOff: Bool = false
|
||||
@State var editingOn: Bool = true
|
||||
@State var item: ChatItem? = ChatItem.getSample(1, .directSnd, .now, "hello")
|
||||
@State var nilItem: ChatItem? = nil
|
||||
|
||||
return Group {
|
||||
VStack {
|
||||
|
||||
@@ -16,9 +16,9 @@ struct ChatHelp: View {
|
||||
VStack(alignment: .leading, spacing: 10) {
|
||||
Text("Thank you for installing SimpleX Chat!")
|
||||
|
||||
HStack(spacing: 4) {
|
||||
Text("You can")
|
||||
Button("connect to SimpleX Chat founder.") {
|
||||
VStack(alignment: .leading, spacing: 0) {
|
||||
Text("To ask any questions and to receive updates:")
|
||||
Button("connect to SimpleX Chat developers.") {
|
||||
showSettings = false
|
||||
DispatchQueue.main.async {
|
||||
UIApplication.shared.open(simplexTeamURL)
|
||||
|
||||
@@ -116,7 +116,7 @@ struct ChatListNavLink: View {
|
||||
private func deleteContactAlert(_ contact: Contact) -> Alert {
|
||||
Alert(
|
||||
title: Text("Delete contact?"),
|
||||
message: Text("Contact and all messages will be deleted"),
|
||||
message: Text("Contact and all messages will be deleted - this cannot be undone!"),
|
||||
primaryButton: .destructive(Text("Delete")) {
|
||||
Task {
|
||||
do {
|
||||
|
||||
@@ -20,19 +20,13 @@ struct ChatListView: View {
|
||||
let v = NavigationView {
|
||||
List {
|
||||
if chatModel.chats.isEmpty {
|
||||
VStack(alignment: .leading) {
|
||||
ChatHelp(showSettings: $showSettings)
|
||||
HStack {
|
||||
Text("This text is available in settings")
|
||||
SettingsButton()
|
||||
}
|
||||
.padding(.leading)
|
||||
ChatHelp(showSettings: $showSettings)
|
||||
} else {
|
||||
ForEach(filteredChats()) { chat in
|
||||
ChatListNavLink(chat: chat)
|
||||
.padding(.trailing, -16)
|
||||
}
|
||||
}
|
||||
ForEach(filteredChats()) { chat in
|
||||
ChatListNavLink(chat: chat)
|
||||
.padding(.trailing, -16)
|
||||
}
|
||||
}
|
||||
.onChange(of: chatModel.chatId) { _ in
|
||||
if chatModel.chatId == nil, let chatId = chatModel.chatToTop {
|
||||
@@ -80,22 +74,23 @@ struct ChatListView: View {
|
||||
logger.debug("ChatListView.connectViaUrlAlert path: \(path)")
|
||||
if (path == "/contact" || path == "/invitation") {
|
||||
path.removeFirst()
|
||||
let action = path
|
||||
let action: ConnReqType = path == "contact" ? .contact : .invitation
|
||||
let link = url.absoluteString.replacingOccurrences(of: "///\(path)", with: "/\(path)")
|
||||
let title: LocalizedStringKey
|
||||
if case .contact = action { title = "Connect via contact link?" }
|
||||
else { title = "Connect via invitation link?" }
|
||||
return Alert(
|
||||
title: Text("Connect via \(action) link?"),
|
||||
title: Text(title),
|
||||
message: Text("Your profile will be sent to the contact that you received this link from"),
|
||||
primaryButton: .default(Text("Connect")) {
|
||||
DispatchQueue.main.async {
|
||||
Task {
|
||||
do {
|
||||
let ok = try await apiConnect(connReq: link)
|
||||
if ok {
|
||||
connectionReqSentAlert(action == "contact" ? .contact : .invitation)
|
||||
}
|
||||
if ok { connectionReqSentAlert(action) }
|
||||
} catch {
|
||||
let err = error.localizedDescription
|
||||
AlertManager.shared.showAlertMsg(title: "Connection error", message: err)
|
||||
AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(err)")
|
||||
logger.debug("ChatListView.connectViaUrlAlert: apiConnect error: \(err)")
|
||||
}
|
||||
}
|
||||
|
||||
File diff suppressed because one or more lines are too long
File diff suppressed because one or more lines are too long
@@ -7,44 +7,161 @@
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import PhotosUI
|
||||
|
||||
struct ImagePicker: UIViewControllerRepresentable {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
var source: UIImagePickerController.SourceType
|
||||
func dropPrefix(_ s: String, _ prefix: String) -> String {
|
||||
s.hasPrefix(prefix) ? String(s.dropFirst(prefix.count)) : s
|
||||
}
|
||||
|
||||
func dropImagePrefix(_ s: String) -> String {
|
||||
dropPrefix(dropPrefix(s, "data:image/png;base64,"), "data:image/jpg;base64,")
|
||||
}
|
||||
|
||||
private func resizeImage(_ image: UIImage, newBounds: CGRect, drawIn: CGRect) -> UIImage {
|
||||
let format = UIGraphicsImageRendererFormat()
|
||||
format.scale = 1.0
|
||||
format.opaque = true
|
||||
return UIGraphicsImageRenderer(bounds: newBounds, format: format).image { _ in
|
||||
image.draw(in: drawIn)
|
||||
}
|
||||
}
|
||||
|
||||
func cropToSquare(_ image: UIImage) -> UIImage {
|
||||
let size = image.size
|
||||
let side = min(size.width, size.height)
|
||||
let newSize = CGSize(width: side, height: side)
|
||||
var origin = CGPoint.zero
|
||||
if size.width > side {
|
||||
origin.x -= (size.width - side) / 2
|
||||
} else if size.height > side {
|
||||
origin.y -= (size.height - side) / 2
|
||||
}
|
||||
return resizeImage(image, newBounds: CGRect(origin: .zero, size: newSize), drawIn: CGRect(origin: origin, size: size))
|
||||
}
|
||||
|
||||
|
||||
func reduceSize(_ image: UIImage, ratio: CGFloat) -> UIImage {
|
||||
let newSize = CGSize(width: floor(image.size.width / ratio), height: floor(image.size.height / ratio))
|
||||
let bounds = CGRect(origin: .zero, size: newSize)
|
||||
return resizeImage(image, newBounds: bounds, drawIn: bounds)
|
||||
}
|
||||
|
||||
func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int) -> String? {
|
||||
var img = image
|
||||
var str = compressImage(img)
|
||||
var dataSize = str?.count ?? 0
|
||||
while dataSize != 0 && dataSize > maxDataSize {
|
||||
let ratio = sqrt(Double(dataSize) / Double(maxDataSize))
|
||||
let clippedRatio = min(ratio, 2.0)
|
||||
img = reduceSize(img, ratio: clippedRatio)
|
||||
str = compressImage(img)
|
||||
dataSize = str?.count ?? 0
|
||||
}
|
||||
logger.debug("resizeImageToDataSize final \(dataSize)")
|
||||
return str
|
||||
}
|
||||
|
||||
func compressImage(_ image: UIImage, _ compressionQuality: CGFloat = 0.85) -> String? {
|
||||
if let data = image.jpegData(compressionQuality: compressionQuality) {
|
||||
return "data:image/jpg;base64,\(data.base64EncodedString())"
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
enum ImageSource {
|
||||
case imageLibrary
|
||||
case camera
|
||||
}
|
||||
|
||||
struct LibraryImagePicker: UIViewControllerRepresentable {
|
||||
typealias UIViewControllerType = PHPickerViewController
|
||||
@Binding var image: UIImage?
|
||||
@Binding var imageUrl: URL?
|
||||
|
||||
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
|
||||
let parent: ImagePicker
|
||||
|
||||
init(_ parent: ImagePicker) {
|
||||
var didFinishPicking: (_ didSelectItems: Bool) -> Void
|
||||
|
||||
class Coordinator: PHPickerViewControllerDelegate {
|
||||
let parent: LibraryImagePicker
|
||||
|
||||
init(_ parent: LibraryImagePicker) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
|
||||
func picker(_ picker: PHPickerViewController, didFinishPicking results: [PHPickerResult]) {
|
||||
parent.didFinishPicking(!results.isEmpty)
|
||||
guard !results.isEmpty else {
|
||||
return
|
||||
}
|
||||
|
||||
if let chosenImageProvider = results.first?.itemProvider {
|
||||
if chosenImageProvider.canLoadObject(ofClass: UIImage.self) {
|
||||
chosenImageProvider.loadObject(ofClass: UIImage.self) { [weak self] image, error in
|
||||
DispatchQueue.main.async {
|
||||
self?.loadImage(object: image, error: error)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func loadImage(object: Any?, error: Error? = nil) {
|
||||
if let error = error {
|
||||
logger.error("Couldn't load image with error: \(error.localizedDescription)")
|
||||
}
|
||||
parent.image = object as? UIImage
|
||||
}
|
||||
}
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(self)
|
||||
}
|
||||
|
||||
func makeUIViewController(context: Context) -> PHPickerViewController {
|
||||
var config = PHPickerConfiguration()
|
||||
config.filter = .images
|
||||
config.selectionLimit = 1
|
||||
let controller = PHPickerViewController(configuration: config)
|
||||
controller.delegate = context.coordinator
|
||||
return controller
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: PHPickerViewController, context: Context) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
struct CameraImagePicker: UIViewControllerRepresentable {
|
||||
@Environment(\.presentationMode) var presentationMode
|
||||
@Binding var image: UIImage?
|
||||
|
||||
class Coordinator: NSObject, UINavigationControllerDelegate, UIImagePickerControllerDelegate {
|
||||
let parent: CameraImagePicker
|
||||
|
||||
init(_ parent: CameraImagePicker) {
|
||||
self.parent = parent
|
||||
}
|
||||
|
||||
func imagePickerController(_ picker: UIImagePickerController,
|
||||
didFinishPickingMediaWithInfo info: [UIImagePickerController.InfoKey: Any]) {
|
||||
if let uiImage = info[.originalImage] as? UIImage {
|
||||
parent.imageUrl = info[.imageURL] as? URL
|
||||
parent.image = uiImage
|
||||
}
|
||||
parent.presentationMode.wrappedValue.dismiss()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
func makeCoordinator() -> Coordinator {
|
||||
Coordinator(self)
|
||||
}
|
||||
|
||||
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<ImagePicker>) -> UIImagePickerController {
|
||||
func makeUIViewController(context: UIViewControllerRepresentableContext<CameraImagePicker>) -> UIImagePickerController {
|
||||
let picker = UIImagePickerController()
|
||||
picker.sourceType = source
|
||||
picker.sourceType = .camera
|
||||
picker.allowsEditing = false
|
||||
picker.delegate = context.coordinator
|
||||
return picker
|
||||
}
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<ImagePicker>) {
|
||||
|
||||
func updateUIViewController(_ uiViewController: UIImagePickerController, context: UIViewControllerRepresentableContext<CameraImagePicker>) {
|
||||
|
||||
}
|
||||
}
|
||||
|
||||
@@ -26,14 +26,6 @@ struct ProfileImage: View {
|
||||
.foregroundColor(color)
|
||||
}
|
||||
}
|
||||
|
||||
func dropPrefix(_ s: String, _ prefix: String) -> String {
|
||||
s.hasPrefix(prefix) ? String(s.dropFirst(prefix.count)) : s
|
||||
}
|
||||
|
||||
func dropImagePrefix(_ s: String) -> String {
|
||||
dropPrefix(dropPrefix(s, "data:image/png;base64,"), "data:image/jpg;base64,")
|
||||
}
|
||||
}
|
||||
|
||||
struct ProfileImage_Previews: PreviewProvider {
|
||||
|
||||
@@ -22,9 +22,7 @@ struct AddContactView: View {
|
||||
.multilineTextAlignment(.center)
|
||||
QRCode(uri: connReqInvitation)
|
||||
.padding()
|
||||
(Text("If you cannot meet in person, you can ") +
|
||||
Text("scan QR code in the video call").bold() +
|
||||
Text(", or you can share the invitation link via any other channel."))
|
||||
Text("If you cannot meet in person, you can **show QR code in the video call**, or you can share the invitation link via any other channel.")
|
||||
.font(.subheadline)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal)
|
||||
|
||||
@@ -26,7 +26,11 @@ struct ConnectContactView: View {
|
||||
.aspectRatio(1, contentMode: .fit)
|
||||
.border(.gray)
|
||||
}
|
||||
.padding(13.0)
|
||||
.padding(12)
|
||||
Text("If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.")
|
||||
.font(.subheadline)
|
||||
.multilineTextAlignment(.center)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -10,7 +10,7 @@ import SwiftUI
|
||||
|
||||
struct CreateGroupView: View {
|
||||
var body: some View {
|
||||
Text("CreateGroupView")
|
||||
EmptyView()
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -65,7 +65,7 @@ struct NewChatButton: View {
|
||||
}
|
||||
|
||||
func connectionErrorAlert(_ error: Error) {
|
||||
AlertManager.shared.showAlertMsg(title: "Connection error", message: error.localizedDescription)
|
||||
AlertManager.shared.showAlertMsg(title: "Connection error", message: "Error: \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
|
||||
@@ -75,12 +75,11 @@ enum ConnReqType: Equatable {
|
||||
}
|
||||
|
||||
func connectionReqSentAlert(_ type: ConnReqType) {
|
||||
let whenConnected = type == .contact
|
||||
? "your connection request is accepted"
|
||||
: "your contact's device is online"
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Connection request sent!",
|
||||
message: "You will be connected when \(whenConnected), please wait or check later!"
|
||||
message: type == .contact
|
||||
? "You will be connected when your connection request is accepted, please wait or check later!"
|
||||
: "You will be connected when your contact's device is online, please wait or check later!"
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -10,6 +10,8 @@ import SwiftUI
|
||||
|
||||
private let terminalFont = Font.custom("Menlo", size: 16)
|
||||
|
||||
private let maxItemSize: Int = 50000
|
||||
|
||||
struct TerminalView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@State var inProgress: Bool = false
|
||||
@@ -24,11 +26,18 @@ struct TerminalView: View {
|
||||
LazyVStack {
|
||||
ForEach(chatModel.terminalItems) { item in
|
||||
NavigationLink {
|
||||
let s = item.details
|
||||
ScrollView {
|
||||
Text(item.details)
|
||||
.textSelection(.enabled)
|
||||
Text(s.prefix(maxItemSize))
|
||||
.padding()
|
||||
}
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarTrailing) {
|
||||
Button { showShareSheet(items: [s]) } label: {
|
||||
Image(systemName: "square.and.arrow.up")
|
||||
}
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
Text(item.id.formatted(date: .omitted, time: .standard))
|
||||
|
||||
@@ -30,9 +30,9 @@ struct MarkdownHelp: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func mdFormat(_ format: String, _ example: Text) -> some View {
|
||||
private func mdFormat(_ format: LocalizedStringKey, _ example: Text) -> some View {
|
||||
HStack {
|
||||
Text(format).frame(width: 88, alignment: .leading)
|
||||
Text(format).frame(width: 120, alignment: .leading)
|
||||
example
|
||||
}
|
||||
}
|
||||
|
||||
@@ -82,7 +82,7 @@ struct SMPServers: View {
|
||||
saveUserSMPServers()
|
||||
}
|
||||
.alert(isPresented: $showBadServersAlert) {
|
||||
Alert(title: Text("Error saving SMP servers"), message: Text("Make sure SMP server addresses are in correct format, line separated and are not duplicated"))
|
||||
Alert(title: Text("Error saving SMP servers"), message: Text("Make sure SMP server addresses are in correct format, line separated and are not duplicated."))
|
||||
}
|
||||
Spacer()
|
||||
howToButton()
|
||||
|
||||
@@ -100,7 +100,7 @@ struct SettingsView: View {
|
||||
UIApplication.shared.open(simplexTeamURL)
|
||||
}
|
||||
} label: {
|
||||
Text("Chat with the founder")
|
||||
Text("Chat with the developers")
|
||||
}
|
||||
}
|
||||
HStack {
|
||||
|
||||
@@ -14,7 +14,7 @@ struct UserAddress: View {
|
||||
|
||||
var body: some View {
|
||||
VStack (alignment: .leading) {
|
||||
Text("You can share your address as a link or as a QR code - anybody will be able to connect to you, and if you later delete it - you won't lose your contacts.")
|
||||
Text("You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it.")
|
||||
.padding(.bottom)
|
||||
if let userAdress = chatModel.userAddress {
|
||||
QRCode(uri: userAdress)
|
||||
|
||||
@@ -14,9 +14,8 @@ struct UserProfile: View {
|
||||
@State private var editProfile = false
|
||||
@State private var showChooseSource = false
|
||||
@State private var showImagePicker = false
|
||||
@State private var imageSource: UIImagePickerController.SourceType = .photoLibrary
|
||||
@State private var pickedImage: UIImage? = nil
|
||||
@State private var tmpImageUrl: URL? = nil
|
||||
@State private var imageSource: ImageSource = .imageLibrary
|
||||
@State private var chosenImage: UIImage? = nil
|
||||
|
||||
var body: some View {
|
||||
let user: User = chatModel.currentUser!
|
||||
@@ -84,29 +83,23 @@ struct UserProfile: View {
|
||||
showImagePicker = true
|
||||
}
|
||||
Button("Choose from library") {
|
||||
imageSource = .photoLibrary
|
||||
imageSource = .imageLibrary
|
||||
showImagePicker = true
|
||||
}
|
||||
}
|
||||
.sheet(isPresented: $showImagePicker) {
|
||||
ImagePicker(source: imageSource, image: $pickedImage, imageUrl: $tmpImageUrl)
|
||||
switch imageSource {
|
||||
case .imageLibrary:
|
||||
LibraryImagePicker(image: $chosenImage) {
|
||||
didSelectItem in showImagePicker = false
|
||||
}
|
||||
case .camera:
|
||||
CameraImagePicker(image: $chosenImage)
|
||||
}
|
||||
}
|
||||
.onChange(of: pickedImage) { image in
|
||||
if let image = image,
|
||||
let data = resizeToSquare(image, 104).jpegData(compressionQuality: 0.85) {
|
||||
let imageStr = "data:image/jpg;base64,\(data.base64EncodedString())"
|
||||
if imageStr.count <= 12500 {
|
||||
profile.image = imageStr
|
||||
} else {
|
||||
logger.error("UserProfile: resized image is too big \(imageStr.count)")
|
||||
}
|
||||
if let tmpImageUrl = tmpImageUrl {
|
||||
do {
|
||||
try FileManager.default.removeItem(at: tmpImageUrl)
|
||||
} catch {
|
||||
logger.error("UserProfile: file deletion error \(error.localizedDescription)")
|
||||
}
|
||||
}
|
||||
.onChange(of: chosenImage) { image in
|
||||
if let image = image {
|
||||
profile.image = resizeImageToDataSize(cropToSquare(image), maxDataSize: 12500)
|
||||
} else {
|
||||
profile.image = nil
|
||||
}
|
||||
@@ -168,30 +161,6 @@ struct UserProfile: View {
|
||||
}
|
||||
}
|
||||
|
||||
func resize(_ image: UIImage, to newSize: CGSize) -> UIImage {
|
||||
let format = UIGraphicsImageRendererFormat()
|
||||
format.scale = 1.0
|
||||
format.opaque = true
|
||||
return UIGraphicsImageRenderer(bounds: CGRect(origin: .zero, size: newSize), format: format).image { _ in
|
||||
let size = image.size
|
||||
let hScale = newSize.height / size.height
|
||||
let vScale = newSize.width / size.width
|
||||
let scale = max(hScale, vScale) // scaleToFill
|
||||
let resizeSize = CGSize(width: size.width * scale, height: size.height * scale)
|
||||
var middle = CGPoint.zero
|
||||
if resizeSize.width > newSize.width {
|
||||
middle.x -= (resizeSize.width - newSize.width) / 2
|
||||
} else if resizeSize.height > newSize.height {
|
||||
middle.y -= (resizeSize.height - newSize.height) / 2
|
||||
}
|
||||
image.draw(in: CGRect(origin: middle, size: resizeSize))
|
||||
}
|
||||
}
|
||||
|
||||
func resizeToSquare(_ image: UIImage, _ side: CGFloat) -> UIImage {
|
||||
resize(image, to: CGSize(width: side, height: side))
|
||||
}
|
||||
|
||||
struct UserProfile_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let chatModel1 = ChatModel()
|
||||
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"locale" : "en"
|
||||
}
|
||||
],
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
},
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,801 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
||||
<file original="en.lproj/Localizable.strings" source-language="en" target-language="en" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<source> </source>
|
||||
<target> </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" (" xml:space="preserve">
|
||||
<source> (</source>
|
||||
<target> (</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" (can be copied)" xml:space="preserve">
|
||||
<source> (can be copied)</source>
|
||||
<target> (can be copied)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="!1 colored!" xml:space="preserve">
|
||||
<source>!1 colored!</source>
|
||||
<target>!1 colored!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="#secret#" xml:space="preserve">
|
||||
<source>#secret#</source>
|
||||
<target>#secret#</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve">
|
||||
<source>%@ is connected!</source>
|
||||
<target>%@ is connected!</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ wants to connect!" xml:space="preserve">
|
||||
<source>%@ wants to connect!</source>
|
||||
<target>%@ wants to connect!</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld" xml:space="preserve">
|
||||
<source>%lld</source>
|
||||
<target>%lld</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lldk" xml:space="preserve">
|
||||
<source>%lldk</source>
|
||||
<target>%lldk</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(shared only with your contacts)" xml:space="preserve">
|
||||
<source>(shared only with your contacts)</source>
|
||||
<target>(shared only with your contacts)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
<source>)</source>
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code for your contact.</source>
|
||||
<target>**Add new contact**: to create your one-time QR Code for your contact.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact who shows QR code to you." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact who shows QR code to you.</source>
|
||||
<target>**Scan QR code**: to connect to your contact who shows QR code to you.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="*bold*" xml:space="preserve">
|
||||
<source>*bold*</source>
|
||||
<target>*bold*</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=", " xml:space="preserve">
|
||||
<source>, </source>
|
||||
<target>, </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="6" xml:space="preserve">
|
||||
<source>6</source>
|
||||
<target>6</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=": " xml:space="preserve">
|
||||
<source>: </source>
|
||||
<target>: </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=": %@" xml:space="preserve">
|
||||
<source>: %@</source>
|
||||
<target>: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept" xml:space="preserve">
|
||||
<source>Accept</source>
|
||||
<target>Accept</target>
|
||||
<note>accept contact request via notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept contact" xml:space="preserve">
|
||||
<source>Accept contact</source>
|
||||
<target>Accept contact</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept contact request from %@?" xml:space="preserve">
|
||||
<source>Accept contact request from %@?</source>
|
||||
<target>Accept contact request from %@?</target>
|
||||
<note>notification body</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<target>Add contact</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All your contacts will remain connected" xml:space="preserve">
|
||||
<source>All your contacts will remain connected</source>
|
||||
<target>All your contacts will remain connected</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cancel" xml:space="preserve">
|
||||
<source>Cancel</source>
|
||||
<target>Cancel</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat console" xml:space="preserve">
|
||||
<source>Chat console</source>
|
||||
<target>Chat console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat with the developers" xml:space="preserve">
|
||||
<source>Chat with the developers</source>
|
||||
<target>Chat with the developers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chats" xml:space="preserve">
|
||||
<source>Chats</source>
|
||||
<target>Chats</target>
|
||||
<note>back button to return to chats list</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Choose from library" xml:space="preserve">
|
||||
<source>Choose from library</source>
|
||||
<target>Choose from library</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Configure SMP servers" xml:space="preserve">
|
||||
<source>Configure SMP servers</source>
|
||||
<target>Configure SMP servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm" xml:space="preserve">
|
||||
<source>Confirm</source>
|
||||
<target>Confirm</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect" xml:space="preserve">
|
||||
<source>Connect</source>
|
||||
<target>Connect</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via contact link?" xml:space="preserve">
|
||||
<source>Connect via contact link?</source>
|
||||
<target>Connect via contact link?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via invitation link?" xml:space="preserve">
|
||||
<source>Connect via invitation link?</source>
|
||||
<target>Connect via invitation link?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server…" xml:space="preserve">
|
||||
<source>Connecting server…</source>
|
||||
<target>Connecting server…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server… (error: %@)" xml:space="preserve">
|
||||
<source>Connecting server… (error: %@)</source>
|
||||
<target>Connecting server… (error: %@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting..." xml:space="preserve">
|
||||
<source>Connecting...</source>
|
||||
<target>Connecting...</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection error" xml:space="preserve">
|
||||
<source>Connection error</source>
|
||||
<target>Connection error</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection request" xml:space="preserve">
|
||||
<source>Connection request</source>
|
||||
<target>Connection request</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection request sent!" xml:space="preserve">
|
||||
<source>Connection request sent!</source>
|
||||
<target>Connection request sent!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection timeout" xml:space="preserve">
|
||||
<source>Connection timeout</source>
|
||||
<target>Connection timeout</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact already exists" xml:space="preserve">
|
||||
<source>Contact already exists</source>
|
||||
<target>Contact already exists</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact and all messages will be deleted - this cannot be undone!" xml:space="preserve">
|
||||
<source>Contact and all messages will be deleted - this cannot be undone!</source>
|
||||
<target>Contact and all messages will be deleted - this cannot be undone!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact is connected" xml:space="preserve">
|
||||
<source>Contact is connected</source>
|
||||
<target>Contact is connected</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<target>Copy</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create" xml:space="preserve">
|
||||
<source>Create</source>
|
||||
<target>Create</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create address" xml:space="preserve">
|
||||
<source>Create address</source>
|
||||
<target>Create address</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create group" xml:space="preserve">
|
||||
<source>Create group</source>
|
||||
<target>Create group</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Create profile</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete" xml:space="preserve">
|
||||
<source>Delete</source>
|
||||
<target>Delete</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete address" xml:space="preserve">
|
||||
<source>Delete address</source>
|
||||
<target>Delete address</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete address?" xml:space="preserve">
|
||||
<source>Delete address?</source>
|
||||
<target>Delete address?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete contact" xml:space="preserve">
|
||||
<source>Delete contact</source>
|
||||
<target>Delete contact</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete contact?" xml:space="preserve">
|
||||
<source>Delete contact?</source>
|
||||
<target>Delete contact?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete for me" xml:space="preserve">
|
||||
<source>Delete for me</source>
|
||||
<target>Delete for me</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete group" xml:space="preserve">
|
||||
<source>Delete group</source>
|
||||
<target>Delete group</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete message?" xml:space="preserve">
|
||||
<source>Delete message?</source>
|
||||
<target>Delete message?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Develop" xml:space="preserve">
|
||||
<source>Develop</source>
|
||||
<target>Develop</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Display name" xml:space="preserve">
|
||||
<source>Display name</source>
|
||||
<target>Display name</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Edit" xml:space="preserve">
|
||||
<source>Edit</source>
|
||||
<target>Edit</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter one SMP server per line:" xml:space="preserve">
|
||||
<source>Enter one SMP server per line:</source>
|
||||
<target>Enter one SMP server per line:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error saving SMP servers" xml:space="preserve">
|
||||
<source>Error saving SMP servers</source>
|
||||
<target>Error saving SMP servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error: %@" xml:space="preserve">
|
||||
<source>Error: %@</source>
|
||||
<target>Error: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error: URL is invalid" xml:space="preserve">
|
||||
<source>Error: URL is invalid</source>
|
||||
<target>Error: URL is invalid</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Full name (optional)" xml:space="preserve">
|
||||
<source>Full name (optional)</source>
|
||||
<target>Full name (optional)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group deletion is not supported" xml:space="preserve">
|
||||
<source>Group deletion is not supported</source>
|
||||
<target>Group deletion is not supported</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Help" xml:space="preserve">
|
||||
<source>Help</source>
|
||||
<target>Help</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="How to" xml:space="preserve">
|
||||
<source>How to</source>
|
||||
<target>How to</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="How to use SimpleX Chat" xml:space="preserve">
|
||||
<source>How to use SimpleX Chat</source>
|
||||
<target>How to use SimpleX Chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="How to use markdown" xml:space="preserve">
|
||||
<source>How to use markdown</source>
|
||||
<target>How to use markdown</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **show QR code in the video call**, or you can share the invitation link via any other channel." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **show QR code in the video call**, or you can share the invitation link via any other channel.</source>
|
||||
<target>If you cannot meet in person, you can **show QR code in the video call**, or you can share the invitation link via any other channel.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you received SimpleX Chat invitation link you can open it in your browser:" xml:space="preserve">
|
||||
<source>If you received SimpleX Chat invitation link you can open it in your browser:</source>
|
||||
<target>If you received SimpleX Chat invitation link you can open it in your browser:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" xml:space="preserve">
|
||||
<source>Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)</source>
|
||||
<target>Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Invalid connection link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make sure SMP server addresses are in correct format, line separated and are not duplicated." xml:space="preserve">
|
||||
<source>Make sure SMP server addresses are in correct format, line separated and are not duplicated.</source>
|
||||
<target>Make sure SMP server addresses are in correct format, line separated and are not duplicated.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Markdown in messages" xml:space="preserve">
|
||||
<source>Markdown in messages</source>
|
||||
<target>Markdown in messages</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message delivery error" xml:space="preserve">
|
||||
<source>Message delivery error</source>
|
||||
<target>Message delivery error</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
|
||||
<source>Most likely this contact has deleted the connection with you.</source>
|
||||
<target>Most likely this contact has deleted the connection with you.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>New contact request</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New message" xml:space="preserve">
|
||||
<source>New message</source>
|
||||
<target>New message</target>
|
||||
<note>notifications</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notifications are disabled!" xml:space="preserve">
|
||||
<source>Notifications are disabled!</source>
|
||||
<target>Notifications are disabled!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open Settings" xml:space="preserve">
|
||||
<source>Open Settings</source>
|
||||
<target>Open Settings</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please check that you used the correct link or ask your contact to send you another one." xml:space="preserve">
|
||||
<source>Please check that you used the correct link or ask your contact to send you another one.</source>
|
||||
<target>Please check that you used the correct link or ask your contact to send you another one.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please check your network connection and try again." xml:space="preserve">
|
||||
<source>Please check your network connection and try again.</source>
|
||||
<target>Please check your network connection and try again.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile image" xml:space="preserve">
|
||||
<source>Profile image</source>
|
||||
<target>Profile image</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read" xml:space="preserve">
|
||||
<source>Read</source>
|
||||
<target>Read</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject" xml:space="preserve">
|
||||
<source>Reject</source>
|
||||
<target>Reject</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject contact (sender NOT notified)" xml:space="preserve">
|
||||
<source>Reject contact (sender NOT notified)</source>
|
||||
<target>Reject contact (sender NOT notified)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject contact request" xml:space="preserve">
|
||||
<source>Reject contact request</source>
|
||||
<target>Reject contact request</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reply" xml:space="preserve">
|
||||
<source>Reply</source>
|
||||
<target>Reply</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SMP servers" xml:space="preserve">
|
||||
<source>SMP servers</source>
|
||||
<target>SMP servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save" xml:space="preserve">
|
||||
<source>Save</source>
|
||||
<target>Save</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save (and notify contacts)" xml:space="preserve">
|
||||
<source>Save (and notify contacts)</source>
|
||||
<target>Save (and notify contacts)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved SMP servers will be removed" xml:space="preserve">
|
||||
<source>Saved SMP servers will be removed</source>
|
||||
<target>Saved SMP servers will be removed</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code" xml:space="preserve">
|
||||
<source>Scan QR code</source>
|
||||
<target>Scan QR code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Server connected" xml:space="preserve">
|
||||
<source>Server connected</source>
|
||||
<target>Server connected</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Settings" xml:space="preserve">
|
||||
<source>Settings</source>
|
||||
<target>Settings</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share" xml:space="preserve">
|
||||
<source>Share</source>
|
||||
<target>Share</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share invitation link" xml:space="preserve">
|
||||
<source>Share invitation link</source>
|
||||
<target>Share invitation link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share link" xml:space="preserve">
|
||||
<source>Share link</source>
|
||||
<target>Share link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show QR code to your contact to scan from the app" xml:space="preserve">
|
||||
<source>Show QR code to your contact
|
||||
to scan from the app</source>
|
||||
<target>Show QR code to your contact
|
||||
to scan from the app</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start new chat" xml:space="preserve">
|
||||
<source>Start new chat</source>
|
||||
<target>Start new chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Take picture" xml:space="preserve">
|
||||
<source>Take picture</source>
|
||||
<target>Take picture</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap button " xml:space="preserve">
|
||||
<source>Tap button </source>
|
||||
<target>Tap button </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Thank you for installing SimpleX Chat!" xml:space="preserve">
|
||||
<source>Thank you for installing SimpleX Chat!</source>
|
||||
<target>Thank you for installing SimpleX Chat!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The app can notify you when you receive messages or contact requests - please open settings to enable." xml:space="preserve">
|
||||
<source>The app can notify you when you receive messages or contact requests - please open settings to enable.</source>
|
||||
<target>The app can notify you when you receive messages or contact requests - please open settings to enable.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The messaging and application platform 100% private by design!" xml:space="preserve">
|
||||
<source>The messaging and application platform 100% private by design!</source>
|
||||
<target>The messaging and application platform 100% private by design!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The sender will NOT be notified" xml:space="preserve">
|
||||
<source>The sender will NOT be notified</source>
|
||||
<target>The sender will NOT be notified</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="To ask any questions and to receive updates:" xml:space="preserve">
|
||||
<source>To ask any questions and to receive updates:</source>
|
||||
<target>To ask any questions and to receive updates:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="To connect via link" xml:space="preserve">
|
||||
<source>To connect via link</source>
|
||||
<target>To connect via link</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="To start a new chat" xml:space="preserve">
|
||||
<source>To start a new chat</source>
|
||||
<target>To start a new chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Trying to connect to the server used to receive messages from this contact (error: %@)." xml:space="preserve">
|
||||
<source>Trying to connect to the server used to receive messages from this contact (error: %@).</source>
|
||||
<target>Trying to connect to the server used to receive messages from this contact (error: %@).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Trying to connect to the server used to receive messages from this contact." xml:space="preserve">
|
||||
<source>Trying to connect to the server used to receive messages from this contact.</source>
|
||||
<target>Trying to connect to the server used to receive messages from this contact.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unexpected error: %@" xml:space="preserve">
|
||||
<source>Unexpected error: %@</source>
|
||||
<target>Unexpected error: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use SimpleX Chat servers?" xml:space="preserve">
|
||||
<source>Use SimpleX Chat servers?</source>
|
||||
<target>Use SimpleX Chat servers?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
|
||||
<source>Using SimpleX Chat servers.</source>
|
||||
<target>Using SimpleX Chat servers.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome %@!" xml:space="preserve">
|
||||
<source>Welcome %@!</source>
|
||||
<target>Welcome %@!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You" xml:space="preserve">
|
||||
<source>You</source>
|
||||
<target>You</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You are already connected to %@ via this link." xml:space="preserve">
|
||||
<source>You are already connected to %@ via this link.</source>
|
||||
<target>You are already connected to %@ via this link.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You are connected to the server used to receive messages from this contact." xml:space="preserve">
|
||||
<source>You are connected to the server used to receive messages from this contact.</source>
|
||||
<target>You are connected to the server used to receive messages from this contact.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can now send messages to %@" xml:space="preserve">
|
||||
<source>You can now send messages to %@</source>
|
||||
<target>You can now send messages to %@</target>
|
||||
<note>notification body</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it." xml:space="preserve">
|
||||
<source>You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it.</source>
|
||||
<target>You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can use markdown to format messages:" xml:space="preserve">
|
||||
<source>You can use markdown to format messages:</source>
|
||||
<target>You can use markdown to format messages:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You control your chat!" xml:space="preserve">
|
||||
<source>You control your chat!</source>
|
||||
<target>You control your chat!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You will be connected when your connection request is accepted, please wait or check later!" xml:space="preserve">
|
||||
<source>You will be connected when your connection request is accepted, please wait or check later!</source>
|
||||
<target>You will be connected when your connection request is accepted, please wait or check later!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You will be connected when your contact's device is online, please wait or check later!" xml:space="preserve">
|
||||
<source>You will be connected when your contact's device is online, please wait or check later!</source>
|
||||
<target>You will be connected when your contact's device is online, please wait or check later!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your SMP servers" xml:space="preserve">
|
||||
<source>Your SMP servers</source>
|
||||
<target>Your SMP servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your SimpleX contact address" xml:space="preserve">
|
||||
<source>Your SimpleX contact address</source>
|
||||
<target>Your SimpleX contact address</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chat address" xml:space="preserve">
|
||||
<source>Your chat address</source>
|
||||
<target>Your chat address</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chat profile" xml:space="preserve">
|
||||
<source>Your chat profile</source>
|
||||
<target>Your chat profile</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chat profile will be sent to your contact" xml:space="preserve">
|
||||
<source>Your chat profile will be sent to your contact</source>
|
||||
<target>Your chat profile will be sent to your contact</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chats" xml:space="preserve">
|
||||
<source>Your chats</source>
|
||||
<target>Your chats</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." xml:space="preserve">
|
||||
<source>Your profile is stored on your device and shared only with your contacts.
|
||||
SimpleX servers cannot see your profile.</source>
|
||||
<target>Your profile is stored on your device and shared only with your contacts.
|
||||
SimpleX servers cannot see your profile.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile will be sent to the contact that you received this link from" xml:space="preserve">
|
||||
<source>Your profile will be sent to the contact that you received this link from</source>
|
||||
<target>Your profile will be sent to the contact that you received this link from</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile, contacts and messages (once delivered) are only stored locally on your device." xml:space="preserve">
|
||||
<source>Your profile, contacts and messages (once delivered) are only stored locally on your device.</source>
|
||||
<target>Your profile, contacts and messages (once delivered) are only stored locally on your device.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your settings" xml:space="preserve">
|
||||
<source>Your settings</source>
|
||||
<target>Your settings</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="[Send us email](mailto:chat@simplex.chat)" xml:space="preserve">
|
||||
<source>[Send us email](mailto:chat@simplex.chat)</source>
|
||||
<target>[Send us email](mailto:chat@simplex.chat)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="_italic_" xml:space="preserve">
|
||||
<source>_italic_</source>
|
||||
<target>_italic_</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="`a + b`" xml:space="preserve">
|
||||
<source>`a + b`</source>
|
||||
<target>`a + b`</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="above, then:" xml:space="preserve">
|
||||
<source>above, then:</source>
|
||||
<target>above, then:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="bold" xml:space="preserve">
|
||||
<source>bold</source>
|
||||
<target>bold</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="colored" xml:space="preserve">
|
||||
<source>colored</source>
|
||||
<target>colored</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connect to SimpleX Chat developers." xml:space="preserve">
|
||||
<source>connect to SimpleX Chat developers.</source>
|
||||
<target>connect to SimpleX Chat developers.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="deleted" xml:space="preserve">
|
||||
<source>deleted</source>
|
||||
<target>deleted</target>
|
||||
<note>deleted chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="italic" xml:space="preserve">
|
||||
<source>italic</source>
|
||||
<target>italic</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="receiving files is not supported yet" xml:space="preserve">
|
||||
<source>receiving files is not supported yet</source>
|
||||
<target>receiving files is not supported yet</target>
|
||||
<note>to be removed</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="secret" xml:space="preserve">
|
||||
<source>secret</source>
|
||||
<target>secret</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sending files is not supported yet" xml:space="preserve">
|
||||
<source>sending files is not supported yet</source>
|
||||
<target>sending files is not supported yet</target>
|
||||
<note>to be removed</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="strike" xml:space="preserve">
|
||||
<source>strike</source>
|
||||
<target>strike</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@ (%@)" xml:space="preserve">
|
||||
<source>v%@ (%@)</source>
|
||||
<target>v%@ (%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="wants to connect to you!" xml:space="preserve">
|
||||
<source>wants to connect to you!</source>
|
||||
<target>wants to connect to you!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="~strike~" xml:space="preserve">
|
||||
<source>~strike~</source>
|
||||
<target>~strike~</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="💻 desktop: scan displayed QR code from the app, via **Scan QR code**." xml:space="preserve">
|
||||
<source>💻 desktop: scan displayed QR code from the app, via **Scan QR code**.</source>
|
||||
<target>💻 desktop: scan displayed QR code from the app, via **Scan QR code**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app." xml:space="preserve">
|
||||
<source>📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app.</source>
|
||||
<target>📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="en" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="CFBundleName" xml:space="preserve">
|
||||
<source>SimpleX</source>
|
||||
<target>SimpleX</target>
|
||||
<note>Bundle name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSCameraUsageDescription" xml:space="preserve">
|
||||
<source>SimpleX needs camera access to scan QR codes to connect to other app users</source>
|
||||
<target>SimpleX needs camera access to scan QR codes to connect to other app users</target>
|
||||
<note>Privacy - Camera Usage Description</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "0.000",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.533"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
},
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
+4
@@ -0,0 +1,4 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX";
|
||||
/* Privacy - Camera Usage Description */
|
||||
"NSCameraUsageDescription" = "SimpleX needs camera access to scan QR codes to connect to other app users";
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"developmentRegion" : "en",
|
||||
"project" : "SimpleX.xcodeproj",
|
||||
"targetLocale" : "en",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "13E113",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "13.3"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
+15
@@ -0,0 +1,15 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"idiom" : "universal",
|
||||
"locale" : "ru"
|
||||
}
|
||||
],
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
},
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
@@ -0,0 +1,800 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<xliff xmlns="urn:oasis:names:tc:xliff:document:1.2" xmlns:xsi="http://www.w3.org/2001/XMLSchema-instance" version="1.2" xsi:schemaLocation="urn:oasis:names:tc:xliff:document:1.2 http://docs.oasis-open.org/xliff/v1.2/os/xliff-core-1.2-strict.xsd">
|
||||
<file original="en.lproj/Localizable.strings" source-language="en" target-language="ru" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id=" " xml:space="preserve">
|
||||
<source> </source>
|
||||
<target> </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" (" xml:space="preserve">
|
||||
<source> (</source>
|
||||
<target> (</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=" (can be copied)" xml:space="preserve">
|
||||
<source> (can be copied)</source>
|
||||
<target> (можно скопировать)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="!1 colored!" xml:space="preserve">
|
||||
<source>!1 colored!</source>
|
||||
<target>!1 цвет!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="#secret#" xml:space="preserve">
|
||||
<source>#secret#</source>
|
||||
<target>#секрет#</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ is connected!" xml:space="preserve">
|
||||
<source>%@ is connected!</source>
|
||||
<target>Установлено соединение с %@!</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%@ wants to connect!" xml:space="preserve">
|
||||
<source>%@ wants to connect!</source>
|
||||
<target>%@ хочет соединиться!</target>
|
||||
<note>notification title</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lld" xml:space="preserve">
|
||||
<source>%lld</source>
|
||||
<target>%lld</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="%lldk" xml:space="preserve">
|
||||
<source>%lldk</source>
|
||||
<target>%lldk</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="(shared only with your contacts)" xml:space="preserve">
|
||||
<source>(shared only with your contacts)</source>
|
||||
<target>(отправляется только вашим контактам)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=")" xml:space="preserve">
|
||||
<source>)</source>
|
||||
<target>)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Add new contact**: to create your one-time QR Code for your contact." xml:space="preserve">
|
||||
<source>**Add new contact**: to create your one-time QR Code for your contact.</source>
|
||||
<target>**Добавить новый контакт**: чтобы создать одноразовый QR код или ссылку для вашего контакта.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="**Scan QR code**: to connect to your contact who shows QR code to you." xml:space="preserve">
|
||||
<source>**Scan QR code**: to connect to your contact who shows QR code to you.</source>
|
||||
<target>**Сканировать QR код**: чтобы соединиться с вашим контактом (который показывает вам QR код).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="*bold*" xml:space="preserve">
|
||||
<source>*bold*</source>
|
||||
<target>\*жирный*</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=", " xml:space="preserve">
|
||||
<source>, </source>
|
||||
<target>, </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="6" xml:space="preserve">
|
||||
<source>6</source>
|
||||
<target>6</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=": " xml:space="preserve">
|
||||
<source>: </source>
|
||||
<target>: </target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id=": %@" xml:space="preserve">
|
||||
<source>: %@</source>
|
||||
<target>: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept" xml:space="preserve">
|
||||
<source>Accept</source>
|
||||
<target>Принять</target>
|
||||
<note>accept contact request via notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept contact" xml:space="preserve">
|
||||
<source>Accept contact</source>
|
||||
<target>Принять запрос</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Accept contact request from %@?" xml:space="preserve">
|
||||
<source>Accept contact request from %@?</source>
|
||||
<target>Принять запрос на соединение от %@?</target>
|
||||
<note>notification body</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Add contact" xml:space="preserve">
|
||||
<source>Add contact</source>
|
||||
<target>Добавить контакт</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="All your contacts will remain connected" xml:space="preserve">
|
||||
<source>All your contacts will remain connected</source>
|
||||
<target>Все контакты, которые соединились через этот адрес, сохранятся.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cancel" xml:space="preserve">
|
||||
<source>Cancel</source>
|
||||
<target>Отменить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat console" xml:space="preserve">
|
||||
<source>Chat console</source>
|
||||
<target>Консоль</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chat with the developers" xml:space="preserve">
|
||||
<source>Chat with the developers</source>
|
||||
<target>Соединиться с разработчиками</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Chats" xml:space="preserve">
|
||||
<source>Chats</source>
|
||||
<target>Назад</target>
|
||||
<note>back button to return to chats list</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Choose from library" xml:space="preserve">
|
||||
<source>Choose from library</source>
|
||||
<target>Выбрать из библиотеки</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Configure SMP servers" xml:space="preserve">
|
||||
<source>Configure SMP servers</source>
|
||||
<target>Настройка SMP серверов</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Confirm" xml:space="preserve">
|
||||
<source>Confirm</source>
|
||||
<target>Подтвердить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect" xml:space="preserve">
|
||||
<source>Connect</source>
|
||||
<target>Соединиться</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via contact link?" xml:space="preserve">
|
||||
<source>Connect via contact link?</source>
|
||||
<target>Соединиться через ссылку-контакт?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connect via invitation link?" xml:space="preserve">
|
||||
<source>Connect via invitation link?</source>
|
||||
<target>Соединиться через ссылку-приглашение?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server…" xml:space="preserve">
|
||||
<source>Connecting server…</source>
|
||||
<target>Устанавливается соединение с сервером…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting server… (error: %@)" xml:space="preserve">
|
||||
<source>Connecting server… (error: %@)</source>
|
||||
<target>Устанавливается соединение с сервером… (ошибка: %@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connecting..." xml:space="preserve">
|
||||
<source>Connecting...</source>
|
||||
<target>Устанавливается соединение…</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection error" xml:space="preserve">
|
||||
<source>Connection error</source>
|
||||
<target>Ошибка соединения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection request" xml:space="preserve">
|
||||
<source>Connection request</source>
|
||||
<target>Запрос на соединение</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection request sent!" xml:space="preserve">
|
||||
<source>Connection request sent!</source>
|
||||
<target>Запрос на соединение отправлен!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Connection timeout" xml:space="preserve">
|
||||
<source>Connection timeout</source>
|
||||
<target>Превышено время соединения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact already exists" xml:space="preserve">
|
||||
<source>Contact already exists</source>
|
||||
<target>Существующий контакт</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact and all messages will be deleted - this cannot be undone!" xml:space="preserve">
|
||||
<source>Contact and all messages will be deleted - this cannot be undone!</source>
|
||||
<target>Контакт и все сообщения будут удалены - это действие нельзя отменить!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Contact is connected" xml:space="preserve">
|
||||
<source>Contact is connected</source>
|
||||
<target>Соединение с контактом установлено</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Copy" xml:space="preserve">
|
||||
<source>Copy</source>
|
||||
<target>Скопировать</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create" xml:space="preserve">
|
||||
<source>Create</source>
|
||||
<target>Создать</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create address" xml:space="preserve">
|
||||
<source>Create address</source>
|
||||
<target>Создать адрес</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create group" xml:space="preserve">
|
||||
<source>Create group</source>
|
||||
<target>Создать группу</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Create profile" xml:space="preserve">
|
||||
<source>Create profile</source>
|
||||
<target>Создать профиль</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete" xml:space="preserve">
|
||||
<source>Delete</source>
|
||||
<target>Удалить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete address" xml:space="preserve">
|
||||
<source>Delete address</source>
|
||||
<target>Удалить адрес</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete address?" xml:space="preserve">
|
||||
<source>Delete address?</source>
|
||||
<target>Удалить адрес?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete contact" xml:space="preserve">
|
||||
<source>Delete contact</source>
|
||||
<target>Удалить контакт</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete contact?" xml:space="preserve">
|
||||
<source>Delete contact?</source>
|
||||
<target>Удалить контакт?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete for me" xml:space="preserve">
|
||||
<source>Delete for me</source>
|
||||
<target>Удалить для меня</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete group" xml:space="preserve">
|
||||
<source>Delete group</source>
|
||||
<target>Удалить группу</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Delete message?" xml:space="preserve">
|
||||
<source>Delete message?</source>
|
||||
<target>Удалить сообщение?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Develop" xml:space="preserve">
|
||||
<source>Develop</source>
|
||||
<target>Для разработчиков</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Display name" xml:space="preserve">
|
||||
<source>Display name</source>
|
||||
<target>Имя профиля</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Edit" xml:space="preserve">
|
||||
<source>Edit</source>
|
||||
<target>Редактировать</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enter one SMP server per line:" xml:space="preserve">
|
||||
<source>Enter one SMP server per line:</source>
|
||||
<target>Введите SMP серверы, каждый на отдельной строке:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error saving SMP servers" xml:space="preserve">
|
||||
<source>Error saving SMP servers</source>
|
||||
<target>Ошибка при сохранении SMP серверов</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error: %@" xml:space="preserve">
|
||||
<source>Error: %@</source>
|
||||
<target>Ошибка: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Error: URL is invalid" xml:space="preserve">
|
||||
<source>Error: URL is invalid</source>
|
||||
<target>Ошибка: неверная ссылка</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Full name (optional)" xml:space="preserve">
|
||||
<source>Full name (optional)</source>
|
||||
<target>Полное имя (не обязательно)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group deletion is not supported" xml:space="preserve">
|
||||
<source>Group deletion is not supported</source>
|
||||
<target>Удаление групп не поддерживается</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Help" xml:space="preserve">
|
||||
<source>Help</source>
|
||||
<target>Помощь</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="How to" xml:space="preserve">
|
||||
<source>How to</source>
|
||||
<target>Информация</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="How to use SimpleX Chat" xml:space="preserve">
|
||||
<source>How to use SimpleX Chat</source>
|
||||
<target>Как использовать SimpleX Chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="How to use markdown" xml:space="preserve">
|
||||
<source>How to use markdown</source>
|
||||
<target>Как форматировать</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link.</source>
|
||||
<target>Если вы не можете встретиться лично, вы можете **сосканировать QR код во время видеозвонка**, или ваш контакт может отправить вам ссылку.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you cannot meet in person, you can **show QR code in the video call**, or you can share the invitation link via any other channel." xml:space="preserve">
|
||||
<source>If you cannot meet in person, you can **show QR code in the video call**, or you can share the invitation link via any other channel.</source>
|
||||
<target>Если вы не можете встретиться лично, вы можете **показать QR код во время видеозвонка** или отправить ссылку через любой другой канал связи.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="If you received SimpleX Chat invitation link you can open it in your browser:" xml:space="preserve">
|
||||
<source>If you received SimpleX Chat invitation link you can open it in your browser:</source>
|
||||
<target>Если вы получили ссылку с приглашением из SimpleX Chat, вы можете открыть её в браузере:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" xml:space="preserve">
|
||||
<source>Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)</source>
|
||||
<target>[SimpleX Chat для терминала](https://github.com/simplex-chat/simplex-chat)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Invalid connection link" xml:space="preserve">
|
||||
<source>Invalid connection link</source>
|
||||
<target>Ошибка в ссылке контакта</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Make sure SMP server addresses are in correct format, line separated and are not duplicated." xml:space="preserve">
|
||||
<source>Make sure SMP server addresses are in correct format, line separated and are not duplicated.</source>
|
||||
<target>Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Markdown in messages" xml:space="preserve">
|
||||
<source>Markdown in messages</source>
|
||||
<target>Форматирование сообщений</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Message delivery error" xml:space="preserve">
|
||||
<source>Message delivery error</source>
|
||||
<target>Ошибка доставки сообщения</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Most likely this contact has deleted the connection with you." xml:space="preserve">
|
||||
<source>Most likely this contact has deleted the connection with you.</source>
|
||||
<target>Скорее всего, этот контакт удалил соединение с вами.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New contact request" xml:space="preserve">
|
||||
<source>New contact request</source>
|
||||
<target>Новый запрос на соединение</target>
|
||||
<note>notification</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="New message" xml:space="preserve">
|
||||
<source>New message</source>
|
||||
<target>Новое сообщение</target>
|
||||
<note>notifications</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Notifications are disabled!" xml:space="preserve">
|
||||
<source>Notifications are disabled!</source>
|
||||
<target>Уведомления выключены</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Open Settings" xml:space="preserve">
|
||||
<source>Open Settings</source>
|
||||
<target>Открыть Настройки</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please check that you used the correct link or ask your contact to send you another one." xml:space="preserve">
|
||||
<source>Please check that you used the correct link or ask your contact to send you another one.</source>
|
||||
<target>Пожалуйста, проверьте, что вы использовали правильную ссылку или попросите, чтобы ваш контакт отправил вам другую ссылку.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Please check your network connection and try again." xml:space="preserve">
|
||||
<source>Please check your network connection and try again.</source>
|
||||
<target>Пожалуйста, проверьте ваше соединение с сетью и попробуйте еще раз.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Profile image" xml:space="preserve">
|
||||
<source>Profile image</source>
|
||||
<target>Аватар</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Read" xml:space="preserve">
|
||||
<source>Read</source>
|
||||
<target>Прочитано</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject" xml:space="preserve">
|
||||
<source>Reject</source>
|
||||
<target>Отклонить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject contact (sender NOT notified)" xml:space="preserve">
|
||||
<source>Reject contact (sender NOT notified)</source>
|
||||
<target>Отклонить (не уведомляя отправителя)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reject contact request" xml:space="preserve">
|
||||
<source>Reject contact request</source>
|
||||
<target>Отклонить запрос</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Reply" xml:space="preserve">
|
||||
<source>Reply</source>
|
||||
<target>Ответить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SMP servers" xml:space="preserve">
|
||||
<source>SMP servers</source>
|
||||
<target>SMP серверы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save" xml:space="preserve">
|
||||
<source>Save</source>
|
||||
<target>Сохранить</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Save (and notify contacts)" xml:space="preserve">
|
||||
<source>Save (and notify contacts)</source>
|
||||
<target>Сохранить (и уведомить контакты)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved SMP servers will be removed" xml:space="preserve">
|
||||
<source>Saved SMP servers will be removed</source>
|
||||
<target>Сохраненные SMP серверы будут удалены</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Scan QR code" xml:space="preserve">
|
||||
<source>Scan QR code</source>
|
||||
<target>Сканировать QR код</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Server connected" xml:space="preserve">
|
||||
<source>Server connected</source>
|
||||
<target>Установлено соединение с сервером</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Settings" xml:space="preserve">
|
||||
<source>Settings</source>
|
||||
<target>Настройки</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share" xml:space="preserve">
|
||||
<source>Share</source>
|
||||
<target>Поделиться</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share invitation link" xml:space="preserve">
|
||||
<source>Share invitation link</source>
|
||||
<target>Поделиться ссылкой</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Share link" xml:space="preserve">
|
||||
<source>Share link</source>
|
||||
<target>Поделиться ссылкой</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Show QR code to your contact to scan from the app" xml:space="preserve">
|
||||
<source>Show QR code to your contact
|
||||
to scan from the app</source>
|
||||
<target>Покажите QR код вашему контакту для сканирования в приложении</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Start new chat" xml:space="preserve">
|
||||
<source>Start new chat</source>
|
||||
<target>Начать новый разговор</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Take picture" xml:space="preserve">
|
||||
<source>Take picture</source>
|
||||
<target>Сделать фото</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Tap button " xml:space="preserve">
|
||||
<source>Tap button </source>
|
||||
<target>Нажмите кнопку</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Thank you for installing SimpleX Chat!" xml:space="preserve">
|
||||
<source>Thank you for installing SimpleX Chat!</source>
|
||||
<target>Спасибо, что Вы установили SimpleX Chat!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The app can notify you when you receive messages or contact requests - please open settings to enable." xml:space="preserve">
|
||||
<source>The app can notify you when you receive messages or contact requests - please open settings to enable.</source>
|
||||
<target>Приложение может посылать вам уведомления о сообщениях и запросах на соединение - уведомления можно включить в Настройках.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The messaging and application platform 100% private by design!" xml:space="preserve">
|
||||
<source>The messaging and application platform 100% private by design!</source>
|
||||
<target>Платформа для сообщений и приложений, которая защищает вашу личную информацию и безопасность.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="The sender will NOT be notified" xml:space="preserve">
|
||||
<source>The sender will NOT be notified</source>
|
||||
<target>Отправитель не будет уведомлён</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="To ask any questions and to receive updates:" xml:space="preserve">
|
||||
<source>To ask any questions and to receive updates:</source>
|
||||
<target>Задать вопросы и получать уведомления о новых версиях:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="To connect via link" xml:space="preserve">
|
||||
<source>To connect via link</source>
|
||||
<target>Соединиться через ссылку</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="To start a new chat" xml:space="preserve">
|
||||
<source>To start a new chat</source>
|
||||
<target>Начать новый разговор</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Trying to connect to the server used to receive messages from this contact (error: %@)." xml:space="preserve">
|
||||
<source>Trying to connect to the server used to receive messages from this contact (error: %@).</source>
|
||||
<target>Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта (ошибка: %@).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Trying to connect to the server used to receive messages from this contact." xml:space="preserve">
|
||||
<source>Trying to connect to the server used to receive messages from this contact.</source>
|
||||
<target>Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Unexpected error: %@" xml:space="preserve">
|
||||
<source>Unexpected error: %@</source>
|
||||
<target>Неожиданная ошибка: %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Use SimpleX Chat servers?" xml:space="preserve">
|
||||
<source>Use SimpleX Chat servers?</source>
|
||||
<target>Использовать серверы предосталенные SimpleX Chat?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
|
||||
<source>Using SimpleX Chat servers.</source>
|
||||
<target>Используются серверы, предоставленные SimpleX Chat.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Welcome %@!" xml:space="preserve">
|
||||
<source>Welcome %@!</source>
|
||||
<target>Здравствуйте %@!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You" xml:space="preserve">
|
||||
<source>You</source>
|
||||
<target>Вы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You are already connected to %@ via this link." xml:space="preserve">
|
||||
<source>You are already connected to %@ via this link.</source>
|
||||
<target>Вы уже соединены с %@ через эту ссылку.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You are connected to the server used to receive messages from this contact." xml:space="preserve">
|
||||
<source>You are connected to the server used to receive messages from this contact.</source>
|
||||
<target>Установлено соединение с сервером, через который вы получается сообщения от этого контакта.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can now send messages to %@" xml:space="preserve">
|
||||
<source>You can now send messages to %@</source>
|
||||
<target>Вы теперь можете отправлять сообщения %@</target>
|
||||
<note>notification body</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it." xml:space="preserve">
|
||||
<source>You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it.</source>
|
||||
<target>Вы можете использовать ваш адрес как ссылку или как QR код - кто угодно сможет соединиться с вами. Вы сможете удалить адрес, сохранив контакты, которые через него соединились.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You can use markdown to format messages:" xml:space="preserve">
|
||||
<source>You can use markdown to format messages:</source>
|
||||
<target>Вы можете форматировать сообщения:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You control your chat!" xml:space="preserve">
|
||||
<source>You control your chat!</source>
|
||||
<target>Вы котролируете Ваш чат!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You will be connected when your connection request is accepted, please wait or check later!" xml:space="preserve">
|
||||
<source>You will be connected when your connection request is accepted, please wait or check later!</source>
|
||||
<target>Соединение будет установлено, когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="You will be connected when your contact's device is online, please wait or check later!" xml:space="preserve">
|
||||
<source>You will be connected when your contact's device is online, please wait or check later!</source>
|
||||
<target>Соединение будет установлено, когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your SMP servers" xml:space="preserve">
|
||||
<source>Your SMP servers</source>
|
||||
<target>Ваши SMP серверы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your SimpleX contact address" xml:space="preserve">
|
||||
<source>Your SimpleX contact address</source>
|
||||
<target>Ваш SimpleX адрес</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chat address" xml:space="preserve">
|
||||
<source>Your chat address</source>
|
||||
<target>Ваш SimpleX адрес</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chat profile" xml:space="preserve">
|
||||
<source>Your chat profile</source>
|
||||
<target>Ваш профиль</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chat profile will be sent to your contact" xml:space="preserve">
|
||||
<source>Your chat profile will be sent to your contact</source>
|
||||
<target>Ваш профиль будет отправлен вашему контакту</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your chats" xml:space="preserve">
|
||||
<source>Your chats</source>
|
||||
<target>Ваши чаты</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile is stored on your device and shared only with your contacts. SimpleX servers cannot see your profile." xml:space="preserve">
|
||||
<source>Your profile is stored on your device and shared only with your contacts.
|
||||
SimpleX servers cannot see your profile.</source>
|
||||
<target>Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам.
|
||||
SimpleX серверы не могут получить доступ к вашему профилю.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile will be sent to the contact that you received this link from" xml:space="preserve">
|
||||
<source>Your profile will be sent to the contact that you received this link from</source>
|
||||
<target>Ваш профиль будет отправлен контакту, от которого вы получили эту ссылку.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your profile, contacts and messages (once delivered) are only stored locally on your device." xml:space="preserve">
|
||||
<source>Your profile, contacts and messages (once delivered) are only stored locally on your device.</source>
|
||||
<target>Ваш профиль, контакты и сообщения (после доставки) хранятся только на вашем устройстве.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Your settings" xml:space="preserve">
|
||||
<source>Your settings</source>
|
||||
<target>Настройки</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="[Send us email](mailto:chat@simplex.chat)" xml:space="preserve">
|
||||
<source>[Send us email](mailto:chat@simplex.chat)</source>
|
||||
<target>[Отправить email](mailto:chat@simplex.chat)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="_italic_" xml:space="preserve">
|
||||
<source>_italic_</source>
|
||||
<target>\_курсив_</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="`a + b`" xml:space="preserve">
|
||||
<source>`a + b`</source>
|
||||
<target>\`a + b`</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="above, then:" xml:space="preserve">
|
||||
<source>above, then:</source>
|
||||
<target>наверху, затем:</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="bold" xml:space="preserve">
|
||||
<source>bold</source>
|
||||
<target>жирный</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="colored" xml:space="preserve">
|
||||
<source>colored</source>
|
||||
<target>цвет</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="connect to SimpleX Chat developers." xml:space="preserve">
|
||||
<source>connect to SimpleX Chat developers.</source>
|
||||
<target>соединиться с разработчиками.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="deleted" xml:space="preserve">
|
||||
<source>deleted</source>
|
||||
<target>удалено</target>
|
||||
<note>deleted chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="italic" xml:space="preserve">
|
||||
<source>italic</source>
|
||||
<target>курсив</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="receiving files is not supported yet" xml:space="preserve">
|
||||
<source>receiving files is not supported yet</source>
|
||||
<target>получение файлов не поддерживается</target>
|
||||
<note>to be removed</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="secret" xml:space="preserve">
|
||||
<source>secret</source>
|
||||
<target>секрет</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sending files is not supported yet" xml:space="preserve">
|
||||
<source>sending files is not supported yet</source>
|
||||
<target>отправка файлов не поддерживается</target>
|
||||
<note>to be removed</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="strike" xml:space="preserve">
|
||||
<source>strike</source>
|
||||
<target>зачеркнуть</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="v%@ (%@)" xml:space="preserve">
|
||||
<source>v%@ (%@)</source>
|
||||
<target>v%@ (%@)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="wants to connect to you!" xml:space="preserve">
|
||||
<source>wants to connect to you!</source>
|
||||
<target>хочет соединиться с вами!</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="~strike~" xml:space="preserve">
|
||||
<source>~strike~</source>
|
||||
<target>\~зачеркнуть~</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="💻 desktop: scan displayed QR code from the app, via **Scan QR code**." xml:space="preserve">
|
||||
<source>💻 desktop: scan displayed QR code from the app, via **Scan QR code**.</source>
|
||||
<target>💻 на компьютере: сосканируйте QR код из приложения через **Сканировать QR код**.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app." xml:space="preserve">
|
||||
<source>📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app.</source>
|
||||
<target>📱 на мобильном: намжите кнопку **Open in mobile app** на веб странице, затем нажмите **Соединиться** в приложении.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="ru" datatype="plaintext">
|
||||
<header>
|
||||
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
|
||||
</header>
|
||||
<body>
|
||||
<trans-unit id="CFBundleName" xml:space="preserve">
|
||||
<source>SimpleX</source>
|
||||
<target>SimpleX</target>
|
||||
<note>Bundle name</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="NSCameraUsageDescription" xml:space="preserve">
|
||||
<source>SimpleX needs camera access to scan QR codes to connect to other app users</source>
|
||||
<target>SimpleX использует камеру для сканирования QR кодов при соединении с другими пользователями</target>
|
||||
<note>Privacy - Camera Usage Description</note>
|
||||
</trans-unit>
|
||||
</body>
|
||||
</file>
|
||||
</xliff>
|
||||
+23
@@ -0,0 +1,23 @@
|
||||
{
|
||||
"colors" : [
|
||||
{
|
||||
"color" : {
|
||||
"color-space" : "srgb",
|
||||
"components" : {
|
||||
"red" : "0.000",
|
||||
"alpha" : "1.000",
|
||||
"blue" : "1.000",
|
||||
"green" : "0.533"
|
||||
}
|
||||
},
|
||||
"idiom" : "universal"
|
||||
}
|
||||
],
|
||||
"properties" : {
|
||||
"localizable" : true
|
||||
},
|
||||
"info" : {
|
||||
"version" : 1,
|
||||
"author" : "xcode"
|
||||
}
|
||||
}
|
||||
+6
@@ -0,0 +1,6 @@
|
||||
{
|
||||
"info" : {
|
||||
"author" : "xcode",
|
||||
"version" : 1
|
||||
}
|
||||
}
|
||||
Binary file not shown.
+4
@@ -0,0 +1,4 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX";
|
||||
/* Privacy - Camera Usage Description */
|
||||
"NSCameraUsageDescription" = "SimpleX needs camera access to scan QR codes to connect to other app users";
|
||||
@@ -0,0 +1,12 @@
|
||||
{
|
||||
"developmentRegion" : "en",
|
||||
"project" : "SimpleX.xcodeproj",
|
||||
"targetLocale" : "ru",
|
||||
"toolInfo" : {
|
||||
"toolBuildNumber" : "13E113",
|
||||
"toolID" : "com.apple.dt.xcode",
|
||||
"toolName" : "Xcode",
|
||||
"toolVersion" : "13.3"
|
||||
},
|
||||
"version" : "1.0"
|
||||
}
|
||||
@@ -1,5 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict/>
|
||||
</plist>
|
||||
@@ -7,127 +7,66 @@
|
||||
objects = {
|
||||
|
||||
/* Begin PBXBuildFile section */
|
||||
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */; };
|
||||
3CDBCF4827FF621E00354CDD /* ChatItemLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* ChatItemLinkView.swift */; };
|
||||
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
||||
5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C063D2627A4564100AEC577 /* ChatPreviewView.swift */; };
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C116CDD27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C1A4C1F27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260C27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
5C2E261027A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E261127A30FEA00F70299 /* TerminalView.swift */; };
|
||||
5C2E261327A30FEA00F70299 /* TerminalView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E261127A30FEA00F70299 /* TerminalView.swift */; };
|
||||
5C35CFC827B2782E00FB6C6D /* BGManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFC727B2782E00FB6C6D /* BGManager.swift */; };
|
||||
5C35CFC927B2782E00FB6C6D /* BGManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFC727B2782E00FB6C6D /* BGManager.swift */; };
|
||||
5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */; };
|
||||
5C35CFCC27B2E91D00FB6C6D /* NtfManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */; };
|
||||
5C36026827F44386009F19D9 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026327F44385009F19D9 /* libffi.a */; };
|
||||
5C36026927F44386009F19D9 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026327F44385009F19D9 /* libffi.a */; };
|
||||
5C36026A27F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026427F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou.a */; };
|
||||
5C36026B27F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026427F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou.a */; };
|
||||
5C36026C27F44386009F19D9 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026527F44386009F19D9 /* libgmpxx.a */; };
|
||||
5C36026D27F44386009F19D9 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026527F44386009F19D9 /* libgmpxx.a */; };
|
||||
5C36026E27F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026627F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou-ghc8.10.7.a */; };
|
||||
5C36026F27F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026627F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou-ghc8.10.7.a */; };
|
||||
5C36027027F44386009F19D9 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026727F44386009F19D9 /* libgmp.a */; };
|
||||
5C36027127F44386009F19D9 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C36026727F44386009F19D9 /* libgmp.a */; };
|
||||
5C3A88CE27DF50170060F1C2 /* DetermineWidth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */; };
|
||||
5C3A88CF27DF50170060F1C2 /* DetermineWidth.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */; };
|
||||
5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88D027DF57800060F1C2 /* FramedItemView.swift */; };
|
||||
5C3A88D227DF57800060F1C2 /* FramedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C3A88D027DF57800060F1C2 /* FramedItemView.swift */; };
|
||||
5C411598280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411593280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a */; };
|
||||
5C41159A280048E90054D6CB /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411594280048E90054D6CB /* libffi.a */; };
|
||||
5C41159C280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411595280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a */; };
|
||||
5C41159E280048E90054D6CB /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411596280048E90054D6CB /* libgmpxx.a */; };
|
||||
5C4115A0280048E90054D6CB /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C411597280048E90054D6CB /* libgmp.a */; };
|
||||
5C5346A827B59A6A004DF848 /* ChatHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5346A727B59A6A004DF848 /* ChatHelp.swift */; };
|
||||
5C5346A927B59A6A004DF848 /* ChatHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5346A727B59A6A004DF848 /* ChatHelp.swift */; };
|
||||
5C577F7D27C83AA10006112D /* MarkdownHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */; };
|
||||
5C577F7E27C83AA10006112D /* MarkdownHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C577F7C27C83AA10006112D /* MarkdownHelp.swift */; };
|
||||
5C5F2B6D27EBC3FE006A9D5F /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6C27EBC3FE006A9D5F /* ImagePicker.swift */; };
|
||||
5C5F2B6E27EBC3FE006A9D5F /* ImagePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6C27EBC3FE006A9D5F /* ImagePicker.swift */; };
|
||||
5C5F2B7027EBC704006A9D5F /* ProfileImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */; };
|
||||
5C5F2B7127EBC704006A9D5F /* ProfileImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */; };
|
||||
5C6AD81327A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; };
|
||||
5C6AD81427A834E300348BD7 /* NewChatButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C6AD81227A834E300348BD7 /* NewChatButton.swift */; };
|
||||
5C7505A227B65FDB00BE3227 /* CIMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A127B65FDB00BE3227 /* CIMetaView.swift */; };
|
||||
5C7505A327B65FDB00BE3227 /* CIMetaView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A127B65FDB00BE3227 /* CIMetaView.swift */; };
|
||||
5C7505A527B679EE00BE3227 /* NavLinkPlain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */; };
|
||||
5C7505A627B679EE00BE3227 /* NavLinkPlain.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A427B679EE00BE3227 /* NavLinkPlain.swift */; };
|
||||
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A727B6D34800BE3227 /* ChatInfoToolbar.swift */; };
|
||||
5C7505A927B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C7505A727B6D34800BE3227 /* ChatInfoToolbar.swift */; };
|
||||
5C764E80279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; };
|
||||
5C764E81279C7276000C6508 /* dummy.m in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E7F279C7276000C6508 /* dummy.m */; };
|
||||
5C764E82279C748B000C6508 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7B279C71D4000C6508 /* libiconv.tbd */; };
|
||||
5C764E83279C748B000C6508 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7C279C71DB000C6508 /* libz.tbd */; };
|
||||
5C764E84279C748C000C6508 /* libiconv.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7B279C71D4000C6508 /* libiconv.tbd */; };
|
||||
5C764E85279C748C000C6508 /* libz.tbd in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C764E7C279C71DB000C6508 /* libz.tbd */; };
|
||||
5C764E89279CBCB3000C6508 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E88279CBCB3000C6508 /* ChatModel.swift */; };
|
||||
5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C764E88279CBCB3000C6508 /* ChatModel.swift */; };
|
||||
5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */ = {isa = PBXBuildFile; productRef = 5C8F01CC27A6F0D8007D2C8D /* CodeScanner */; };
|
||||
5C971E1D27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */; };
|
||||
5C971E1E27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */; };
|
||||
5C971E2127AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E2027AEBF8300C8A3CE /* ChatInfoImage.swift */; };
|
||||
5C971E2227AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C971E2027AEBF8300C8A3CE /* ChatInfoImage.swift */; };
|
||||
5C9FD96B27A56D4D0075386C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96A27A56D4D0075386C /* JSON.swift */; };
|
||||
5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96A27A56D4D0075386C /* JSON.swift */; };
|
||||
5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */; };
|
||||
5C9FD96F27A5D6ED0075386C /* SendMessageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C9FD96D27A5D6ED0075386C /* SendMessageView.swift */; };
|
||||
5CA059DC279559F40002BEB4 /* Tests_iOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DB279559F40002BEB4 /* Tests_iOS.swift */; };
|
||||
5CA059DE279559F40002BEB4 /* Tests_iOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059DD279559F40002BEB4 /* Tests_iOSLaunchTests.swift */; };
|
||||
5CA059E8279559F40002BEB4 /* Tests_macOS.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059E7279559F40002BEB4 /* Tests_macOS.swift */; };
|
||||
5CA059EA279559F40002BEB4 /* Tests_macOSLaunchTests.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */; };
|
||||
5CA059EB279559F40002BEB4 /* SimpleXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C3279559F40002BEB4 /* SimpleXApp.swift */; };
|
||||
5CA059EC279559F40002BEB4 /* SimpleXApp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C3279559F40002BEB4 /* SimpleXApp.swift */; };
|
||||
5CA059ED279559F40002BEB4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C4279559F40002BEB4 /* ContentView.swift */; };
|
||||
5CA059EE279559F40002BEB4 /* ContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA059C4279559F40002BEB4 /* ContentView.swift */; };
|
||||
5CA059EF279559F40002BEB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5CA059C5279559F40002BEB4 /* Assets.xcassets */; };
|
||||
5CA059F0279559F40002BEB4 /* Assets.xcassets in Resources */ = {isa = PBXBuildFile; fileRef = 5CA059C5279559F40002BEB4 /* Assets.xcassets */; };
|
||||
5CA05A4C27974EB60002BEB4 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */; };
|
||||
5CA05A4D27974EB60002BEB4 /* WelcomeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */; };
|
||||
5CA14D2327F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D1E27F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a */; };
|
||||
5CA14D2427F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D1E27F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a */; };
|
||||
5CA14D2527F6DE37009B11CE /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D1F27F6DE37009B11CE /* libgmp.a */; };
|
||||
5CA14D2627F6DE37009B11CE /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D1F27F6DE37009B11CE /* libgmp.a */; };
|
||||
5CA14D2727F6DE37009B11CE /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D2027F6DE37009B11CE /* libgmpxx.a */; };
|
||||
5CA14D2827F6DE37009B11CE /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D2027F6DE37009B11CE /* libgmpxx.a */; };
|
||||
5CA14D2927F6DE37009B11CE /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D2127F6DE37009B11CE /* libffi.a */; };
|
||||
5CA14D2A27F6DE37009B11CE /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D2127F6DE37009B11CE /* libffi.a */; };
|
||||
5CA14D2B27F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D2227F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a */; };
|
||||
5CA14D2C27F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CA14D2227F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a */; };
|
||||
5CB924D427A853F100ACCCDD /* SettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D327A853F100ACCCDD /* SettingsButton.swift */; };
|
||||
5CB924D527A853F100ACCCDD /* SettingsButton.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D327A853F100ACCCDD /* SettingsButton.swift */; };
|
||||
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; };
|
||||
5CB924D827A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; };
|
||||
5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; };
|
||||
5CB924E227A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; };
|
||||
5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E327A8683A00ACCCDD /* UserAddress.swift */; };
|
||||
5CB924E527A8683A00ACCCDD /* UserAddress.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E327A8683A00ACCCDD /* UserAddress.swift */; };
|
||||
5CB9250D27A9432000ACCCDD /* ChatListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */; };
|
||||
5CB9250E27A9432000ACCCDD /* ChatListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */; };
|
||||
5CC1C99227A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; };
|
||||
5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */; };
|
||||
5CC1C99527A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; };
|
||||
5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */; };
|
||||
5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FA2809BF11000C35E3 /* Localizable.strings */; };
|
||||
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */ = {isa = PBXBuildFile; fileRef = 5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */; };
|
||||
5CCD403427A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; };
|
||||
5CCD403527A5F6DF00368C90 /* AddContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403327A5F6DF00368C90 /* AddContactView.swift */; };
|
||||
5CCD403727A5F9A200368C90 /* ConnectContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */; };
|
||||
5CCD403827A5F9A200368C90 /* ConnectContactView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403627A5F9A200368C90 /* ConnectContactView.swift */; };
|
||||
5CCD403A27A5F9BE00368C90 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */; };
|
||||
5CCD403B27A5F9BE00368C90 /* CreateGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */; };
|
||||
5CE4407227ADB1D0007B033A /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407127ADB1D0007B033A /* Emoji.swift */; };
|
||||
5CE4407327ADB1D0007B033A /* Emoji.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407127ADB1D0007B033A /* Emoji.swift */; };
|
||||
5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; };
|
||||
5CE4407A27ADB701007B033A /* EmojiItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CE4407827ADB701007B033A /* EmojiItemView.swift */; };
|
||||
5CEACCE327DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; };
|
||||
5CEACCE427DE9246000BD591 /* ComposeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCE227DE9246000BD591 /* ComposeView.swift */; };
|
||||
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
|
||||
5CEACCEE27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
|
||||
640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; };
|
||||
640F50E427CF991C001E05C2 /* SMPServers.swift in Sources */ = {isa = PBXBuildFile; fileRef = 640F50E227CF991C001E05C2 /* SMPServers.swift */; };
|
||||
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
|
||||
64AA1C6A27EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
|
||||
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
|
||||
64AA1C6D27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
|
||||
/* End PBXBuildFile section */
|
||||
|
||||
/* Begin PBXContainerItemProxy section */
|
||||
@@ -138,16 +77,11 @@
|
||||
remoteGlobalIDString = 5CA059C9279559F40002BEB4;
|
||||
remoteInfo = "SimpleX (iOS)";
|
||||
};
|
||||
5CA059E4279559F40002BEB4 /* PBXContainerItemProxy */ = {
|
||||
isa = PBXContainerItemProxy;
|
||||
containerPortal = 5CA059BE279559F40002BEB4 /* Project object */;
|
||||
proxyType = 1;
|
||||
remoteGlobalIDString = 5CA059CF279559F40002BEB4;
|
||||
remoteInfo = "SimpleX (macOS)";
|
||||
};
|
||||
/* End PBXContainerItemProxy section */
|
||||
|
||||
/* Begin PBXFileReference section */
|
||||
3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeLinkView.swift; sourceTree = "<group>"; };
|
||||
3CDBCF4727FF621E00354CDD /* ChatItemLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemLinkView.swift; sourceTree = "<group>"; };
|
||||
5C063D2627A4564100AEC577 /* ChatPreviewView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatPreviewView.swift; sourceTree = "<group>"; };
|
||||
5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactRequestView.swift; sourceTree = "<group>"; };
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||
@@ -157,13 +91,13 @@
|
||||
5C2E261127A30FEA00F70299 /* TerminalView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = TerminalView.swift; sourceTree = "<group>"; };
|
||||
5C35CFC727B2782E00FB6C6D /* BGManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = BGManager.swift; sourceTree = "<group>"; };
|
||||
5C35CFCA27B2E91D00FB6C6D /* NtfManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NtfManager.swift; sourceTree = "<group>"; };
|
||||
5C36026327F44385009F19D9 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C36026427F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou.a"; sourceTree = "<group>"; };
|
||||
5C36026527F44386009F19D9 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C36026627F44386009F19D9 /* libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.4.0-35IBkEJuAyg38MasSQs4Ou-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5C36026727F44386009F19D9 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DetermineWidth.swift; sourceTree = "<group>"; };
|
||||
5C3A88D027DF57800060F1C2 /* FramedItemView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = FramedItemView.swift; sourceTree = "<group>"; };
|
||||
5C411593280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a"; sourceTree = "<group>"; };
|
||||
5C411594280048E90054D6CB /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C411595280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5C411596280048E90054D6CB /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C411597280048E90054D6CB /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C422A7C27A9A6FA0097A1E1 /* SimpleX (iOS).entitlements */ = {isa = PBXFileReference; lastKnownFileType = text.plist.entitlements; path = "SimpleX (iOS).entitlements"; sourceTree = "<group>"; };
|
||||
5C5346A727B59A6A004DF848 /* ChatHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatHelp.swift; sourceTree = "<group>"; };
|
||||
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MarkdownHelp.swift; sourceTree = "<group>"; };
|
||||
@@ -176,7 +110,6 @@
|
||||
5C764E7B279C71D4000C6508 /* libiconv.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libiconv.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/lib/libiconv.tbd; sourceTree = DEVELOPER_DIR; };
|
||||
5C764E7C279C71DB000C6508 /* libz.tbd */ = {isa = PBXFileReference; lastKnownFileType = "sourcecode.text-based-dylib-definition"; name = libz.tbd; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS15.2.sdk/usr/lib/libz.tbd; sourceTree = DEVELOPER_DIR; };
|
||||
5C764E7D279C7275000C6508 /* SimpleX (iOS)-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SimpleX (iOS)-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
5C764E7E279C7275000C6508 /* SimpleX (macOS)-Bridging-Header.h */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.h; path = "SimpleX (macOS)-Bridging-Header.h"; sourceTree = "<group>"; };
|
||||
5C764E7F279C7276000C6508 /* dummy.m */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.c.objc; path = dummy.m; sourceTree = "<group>"; };
|
||||
5C764E88279CBCB3000C6508 /* ChatModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatModel.swift; sourceTree = "<group>"; };
|
||||
5C971E1C27AEBEF600C8A3CE /* ChatInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatInfoView.swift; sourceTree = "<group>"; };
|
||||
@@ -187,19 +120,10 @@
|
||||
5CA059C4279559F40002BEB4 /* ContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContentView.swift; sourceTree = "<group>"; };
|
||||
5CA059C5279559F40002BEB4 /* Assets.xcassets */ = {isa = PBXFileReference; lastKnownFileType = folder.assetcatalog; path = Assets.xcassets; sourceTree = "<group>"; };
|
||||
5CA059CA279559F40002BEB4 /* SimpleX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleX.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5CA059D0279559F40002BEB4 /* SimpleX.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = SimpleX.app; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5CA059D7279559F40002BEB4 /* Tests iOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests iOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5CA059DB279559F40002BEB4 /* Tests_iOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOS.swift; sourceTree = "<group>"; };
|
||||
5CA059DD279559F40002BEB4 /* Tests_iOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_iOSLaunchTests.swift; sourceTree = "<group>"; };
|
||||
5CA059E3279559F40002BEB4 /* Tests macOS.xctest */ = {isa = PBXFileReference; explicitFileType = wrapper.cfbundle; includeInIndex = 0; path = "Tests macOS.xctest"; sourceTree = BUILT_PRODUCTS_DIR; };
|
||||
5CA059E7279559F40002BEB4 /* Tests_macOS.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOS.swift; sourceTree = "<group>"; };
|
||||
5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Tests_macOSLaunchTests.swift; sourceTree = "<group>"; };
|
||||
5CA05A4B27974EB60002BEB4 /* WelcomeView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = WelcomeView.swift; sourceTree = "<group>"; };
|
||||
5CA14D1E27F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a"; sourceTree = "<group>"; };
|
||||
5CA14D1F27F6DE37009B11CE /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CA14D2027F6DE37009B11CE /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CA14D2127F6DE37009B11CE /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CA14D2227F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a"; sourceTree = "<group>"; };
|
||||
5CB924D327A853F100ACCCDD /* SettingsButton.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsButton.swift; sourceTree = "<group>"; };
|
||||
5CB924D627A8563F00ACCCDD /* SettingsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SettingsView.swift; sourceTree = "<group>"; };
|
||||
5CB924E027A867BA00ACCCDD /* UserProfile.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserProfile.swift; sourceTree = "<group>"; };
|
||||
@@ -207,6 +131,8 @@
|
||||
5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatListNavLink.swift; sourceTree = "<group>"; };
|
||||
5CC1C99127A6C7F5000D9FF6 /* QRCode.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = QRCode.swift; sourceTree = "<group>"; };
|
||||
5CC1C99427A6CF7F000D9FF6 /* ShareSheet.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ShareSheet.swift; sourceTree = "<group>"; };
|
||||
5CC2C0FB2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = ru.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5CC2C0FE2809BF11000C35E3 /* ru */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = ru; path = "ru.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5CCD403327A5F6DF00368C90 /* AddContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AddContactView.swift; sourceTree = "<group>"; };
|
||||
5CCD403627A5F9A200368C90 /* ConnectContactView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ConnectContactView.swift; sourceTree = "<group>"; };
|
||||
5CCD403927A5F9BE00368C90 /* CreateGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CreateGroupView.swift; sourceTree = "<group>"; };
|
||||
@@ -224,28 +150,14 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CA14D2727F6DE37009B11CE /* libgmpxx.a in Frameworks */,
|
||||
5C8F01CD27A6F0D8007D2C8D /* CodeScanner in Frameworks */,
|
||||
5CA14D2527F6DE37009B11CE /* libgmp.a in Frameworks */,
|
||||
5CA14D2B27F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a in Frameworks */,
|
||||
5C411598280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a in Frameworks */,
|
||||
5C4115A0280048E90054D6CB /* libgmp.a in Frameworks */,
|
||||
5C41159C280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a in Frameworks */,
|
||||
5C764E83279C748B000C6508 /* libz.tbd in Frameworks */,
|
||||
5CA14D2927F6DE37009B11CE /* libffi.a in Frameworks */,
|
||||
5CA14D2327F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a in Frameworks */,
|
||||
5C41159A280048E90054D6CB /* libffi.a in Frameworks */,
|
||||
5C764E82279C748B000C6508 /* libiconv.tbd in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CA059CD279559F40002BEB4 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CA14D2C27F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a in Frameworks */,
|
||||
5CA14D2427F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a in Frameworks */,
|
||||
5CA14D2A27F6DE37009B11CE /* libffi.a in Frameworks */,
|
||||
5CA14D2827F6DE37009B11CE /* libgmpxx.a in Frameworks */,
|
||||
5C764E85279C748C000C6508 /* libz.tbd in Frameworks */,
|
||||
5CA14D2627F6DE37009B11CE /* libgmp.a in Frameworks */,
|
||||
5C764E84279C748C000C6508 /* libiconv.tbd in Frameworks */,
|
||||
5C41159E280048E90054D6CB /* libgmpxx.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -256,13 +168,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CA059E0279559F40002BEB4 /* Frameworks */ = {
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXFrameworksBuildPhase section */
|
||||
|
||||
/* Begin PBXGroup section */
|
||||
@@ -297,11 +202,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CA14D2127F6DE37009B11CE /* libffi.a */,
|
||||
5CA14D1F27F6DE37009B11CE /* libgmp.a */,
|
||||
5CA14D2027F6DE37009B11CE /* libgmpxx.a */,
|
||||
5CA14D2227F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to-ghc8.10.7.a */,
|
||||
5CA14D1E27F6DE37009B11CE /* libHSsimplex-chat-1.4.1-BTiQTwPdJ1X1QlTjXA35to.a */,
|
||||
5C411594280048E90054D6CB /* libffi.a */,
|
||||
5C411597280048E90054D6CB /* libgmp.a */,
|
||||
5C411596280048E90054D6CB /* libgmpxx.a */,
|
||||
5C411595280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx-ghc8.10.7.a */,
|
||||
5C411593280048E90054D6CB /* libHSsimplex-chat-1.5.0-3uBn0HoMpg08OGLfasXsOx.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -336,6 +241,8 @@
|
||||
5C3A88CD27DF50170060F1C2 /* DetermineWidth.swift */,
|
||||
5C5F2B6C27EBC3FE006A9D5F /* ImagePicker.swift */,
|
||||
5C5F2B6F27EBC704006A9D5F /* ProfileImage.swift */,
|
||||
3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */,
|
||||
3CDBCF4727FF621E00354CDD /* ChatItemLinkView.swift */,
|
||||
);
|
||||
path = Helpers;
|
||||
sourceTree = "<group>";
|
||||
@@ -343,12 +250,12 @@
|
||||
5CA059BD279559F40002BEB4 = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */,
|
||||
5CC2C0FA2809BF11000C35E3 /* Localizable.strings */,
|
||||
5C422A7C27A9A6FA0097A1E1 /* SimpleX (iOS).entitlements */,
|
||||
5C764E5C279C70B7000C6508 /* Libraries */,
|
||||
5CA059C2279559F40002BEB4 /* Shared */,
|
||||
5CA059D1279559F40002BEB4 /* macOS */,
|
||||
5CA059DA279559F40002BEB4 /* Tests iOS */,
|
||||
5CA059E6279559F40002BEB4 /* Tests macOS */,
|
||||
5CA059CB279559F40002BEB4 /* Products */,
|
||||
5C764E7A279C71D4000C6508 /* Frameworks */,
|
||||
);
|
||||
@@ -363,7 +270,6 @@
|
||||
5C2E260D27A30E2400F70299 /* Views */,
|
||||
5CA059C5279559F40002BEB4 /* Assets.xcassets */,
|
||||
5C764E7D279C7275000C6508 /* SimpleX (iOS)-Bridging-Header.h */,
|
||||
5C764E7E279C7275000C6508 /* SimpleX (macOS)-Bridging-Header.h */,
|
||||
5C764E7F279C7276000C6508 /* dummy.m */,
|
||||
);
|
||||
path = Shared;
|
||||
@@ -373,20 +279,11 @@
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CA059CA279559F40002BEB4 /* SimpleX.app */,
|
||||
5CA059D0279559F40002BEB4 /* SimpleX.app */,
|
||||
5CA059D7279559F40002BEB4 /* Tests iOS.xctest */,
|
||||
5CA059E3279559F40002BEB4 /* Tests macOS.xctest */,
|
||||
);
|
||||
name = Products;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5CA059D1279559F40002BEB4 /* macOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
);
|
||||
path = macOS;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5CA059DA279559F40002BEB4 /* Tests iOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -396,15 +293,6 @@
|
||||
path = "Tests iOS";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5CA059E6279559F40002BEB4 /* Tests macOS */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CA059E7279559F40002BEB4 /* Tests_macOS.swift */,
|
||||
5CA059E9279559F40002BEB4 /* Tests_macOSLaunchTests.swift */,
|
||||
);
|
||||
path = "Tests macOS";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5CB924DD27A8622200ACCCDD /* NewChat */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
@@ -487,23 +375,6 @@
|
||||
productReference = 5CA059CA279559F40002BEB4 /* SimpleX.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
5CA059CF279559F40002BEB4 /* SimpleX (macOS) */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5CA059F6279559F40002BEB4 /* Build configuration list for PBXNativeTarget "SimpleX (macOS)" */;
|
||||
buildPhases = (
|
||||
5CA059CC279559F40002BEB4 /* Sources */,
|
||||
5CA059CD279559F40002BEB4 /* Frameworks */,
|
||||
5CA059CE279559F40002BEB4 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
);
|
||||
name = "SimpleX (macOS)";
|
||||
productName = "SimpleX (macOS)";
|
||||
productReference = 5CA059D0279559F40002BEB4 /* SimpleX.app */;
|
||||
productType = "com.apple.product-type.application";
|
||||
};
|
||||
5CA059D6279559F40002BEB4 /* Tests iOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5CA059F9279559F40002BEB4 /* Build configuration list for PBXNativeTarget "Tests iOS" */;
|
||||
@@ -522,24 +393,6 @@
|
||||
productReference = 5CA059D7279559F40002BEB4 /* Tests iOS.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
5CA059E2279559F40002BEB4 /* Tests macOS */ = {
|
||||
isa = PBXNativeTarget;
|
||||
buildConfigurationList = 5CA059FC279559F40002BEB4 /* Build configuration list for PBXNativeTarget "Tests macOS" */;
|
||||
buildPhases = (
|
||||
5CA059DF279559F40002BEB4 /* Sources */,
|
||||
5CA059E0279559F40002BEB4 /* Frameworks */,
|
||||
5CA059E1279559F40002BEB4 /* Resources */,
|
||||
);
|
||||
buildRules = (
|
||||
);
|
||||
dependencies = (
|
||||
5CA059E5279559F40002BEB4 /* PBXTargetDependency */,
|
||||
);
|
||||
name = "Tests macOS";
|
||||
productName = "Tests macOS";
|
||||
productReference = 5CA059E3279559F40002BEB4 /* Tests macOS.xctest */;
|
||||
productType = "com.apple.product-type.bundle.ui-testing";
|
||||
};
|
||||
/* End PBXNativeTarget section */
|
||||
|
||||
/* Begin PBXProject section */
|
||||
@@ -555,18 +408,10 @@
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
LastSwiftMigration = 1320;
|
||||
};
|
||||
5CA059CF279559F40002BEB4 = {
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
LastSwiftMigration = 1320;
|
||||
};
|
||||
5CA059D6279559F40002BEB4 = {
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
TestTargetID = 5CA059C9279559F40002BEB4;
|
||||
};
|
||||
5CA059E2279559F40002BEB4 = {
|
||||
CreatedOnToolsVersion = 13.2.1;
|
||||
TestTargetID = 5CA059CF279559F40002BEB4;
|
||||
};
|
||||
};
|
||||
};
|
||||
buildConfigurationList = 5CA059C1279559F40002BEB4 /* Build configuration list for PBXProject "SimpleX" */;
|
||||
@@ -576,6 +421,7 @@
|
||||
knownRegions = (
|
||||
en,
|
||||
Base,
|
||||
ru,
|
||||
);
|
||||
mainGroup = 5CA059BD279559F40002BEB4;
|
||||
packageReferences = (
|
||||
@@ -586,9 +432,7 @@
|
||||
projectRoot = "";
|
||||
targets = (
|
||||
5CA059C9279559F40002BEB4 /* SimpleX (iOS) */,
|
||||
5CA059CF279559F40002BEB4 /* SimpleX (macOS) */,
|
||||
5CA059D6279559F40002BEB4 /* Tests iOS */,
|
||||
5CA059E2279559F40002BEB4 /* Tests macOS */,
|
||||
);
|
||||
};
|
||||
/* End PBXProject section */
|
||||
@@ -599,14 +443,8 @@
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CA059EF279559F40002BEB4 /* Assets.xcassets in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CA059CE279559F40002BEB4 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CA059F0279559F40002BEB4 /* Assets.xcassets in Resources */,
|
||||
5CC2C0FC2809BF11000C35E3 /* Localizable.strings in Resources */,
|
||||
5CC2C0FF2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings in Resources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -617,13 +455,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CA059E1279559F40002BEB4 /* Resources */ = {
|
||||
isa = PBXResourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXResourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXSourcesBuildPhase section */
|
||||
@@ -637,6 +468,8 @@
|
||||
5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */,
|
||||
5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */,
|
||||
5C5346A827B59A6A004DF848 /* ChatHelp.swift in Sources */,
|
||||
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */,
|
||||
3CDBCF4827FF621E00354CDD /* ChatItemLinkView.swift in Sources */,
|
||||
5C764E80279C7276000C6508 /* dummy.m in Sources */,
|
||||
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */,
|
||||
5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */,
|
||||
@@ -679,58 +512,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CA059CC279559F40002BEB4 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5C6AD81427A834E300348BD7 /* NewChatButton.swift in Sources */,
|
||||
5CB924D827A8563F00ACCCDD /* SettingsView.swift in Sources */,
|
||||
5CEACCE427DE9246000BD591 /* ComposeView.swift in Sources */,
|
||||
5CB924E227A867BA00ACCCDD /* UserProfile.swift in Sources */,
|
||||
5CE4407A27ADB701007B033A /* EmojiItemView.swift in Sources */,
|
||||
5C5346A927B59A6A004DF848 /* ChatHelp.swift in Sources */,
|
||||
5C764E81279C7276000C6508 /* dummy.m in Sources */,
|
||||
5C7505A927B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */,
|
||||
5C3A88D227DF57800060F1C2 /* FramedItemView.swift in Sources */,
|
||||
5CB924E527A8683A00ACCCDD /* UserAddress.swift in Sources */,
|
||||
640F50E427CF991C001E05C2 /* SMPServers.swift in Sources */,
|
||||
5C063D2827A4564100AEC577 /* ChatPreviewView.swift in Sources */,
|
||||
5C35CFCC27B2E91D00FB6C6D /* NtfManager.swift in Sources */,
|
||||
5C2E261327A30FEA00F70299 /* TerminalView.swift in Sources */,
|
||||
5C9FD96C27A56D4D0075386C /* JSON.swift in Sources */,
|
||||
5C9FD96F27A5D6ED0075386C /* SendMessageView.swift in Sources */,
|
||||
5CC1C99327A6C7F5000D9FF6 /* QRCode.swift in Sources */,
|
||||
5C116CDD27AABE0400E66D01 /* ContactRequestView.swift in Sources */,
|
||||
5CB9250E27A9432000ACCCDD /* ChatListNavLink.swift in Sources */,
|
||||
5CA059EE279559F40002BEB4 /* ContentView.swift in Sources */,
|
||||
5CCD403527A5F6DF00368C90 /* AddContactView.swift in Sources */,
|
||||
5C3A88CF27DF50170060F1C2 /* DetermineWidth.swift in Sources */,
|
||||
5C7505A627B679EE00BE3227 /* NavLinkPlain.swift in Sources */,
|
||||
5C7505A327B65FDB00BE3227 /* CIMetaView.swift in Sources */,
|
||||
5C35CFC927B2782E00FB6C6D /* BGManager.swift in Sources */,
|
||||
5CA05A4D27974EB60002BEB4 /* WelcomeView.swift in Sources */,
|
||||
5C2E261027A30FDC00F70299 /* ChatView.swift in Sources */,
|
||||
5C2E260C27A30CFA00F70299 /* ChatListView.swift in Sources */,
|
||||
5C971E2227AEBF8300C8A3CE /* ChatInfoImage.swift in Sources */,
|
||||
5C5F2B6E27EBC3FE006A9D5F /* ImagePicker.swift in Sources */,
|
||||
5C577F7E27C83AA10006112D /* MarkdownHelp.swift in Sources */,
|
||||
5CA059EC279559F40002BEB4 /* SimpleXApp.swift in Sources */,
|
||||
5CCD403827A5F9A200368C90 /* ConnectContactView.swift in Sources */,
|
||||
5CCD403B27A5F9BE00368C90 /* CreateGroupView.swift in Sources */,
|
||||
5CEACCEE27DEA495000BD591 /* MsgContentView.swift in Sources */,
|
||||
5C764E8A279CBCB3000C6508 /* ChatModel.swift in Sources */,
|
||||
5C971E1E27AEBEF600C8A3CE /* ChatInfoView.swift in Sources */,
|
||||
5CC1C99627A6CF7F000D9FF6 /* ShareSheet.swift in Sources */,
|
||||
5C2E260827A2941F00F70299 /* SimpleXAPI.swift in Sources */,
|
||||
5CB924D527A853F100ACCCDD /* SettingsButton.swift in Sources */,
|
||||
5C5F2B7127EBC704006A9D5F /* ProfileImage.swift in Sources */,
|
||||
64AA1C6D27F3537400AC7277 /* DeletedItemView.swift in Sources */,
|
||||
5CE4407327ADB1D0007B033A /* Emoji.swift in Sources */,
|
||||
5C1A4C1F27A715B700EAD5AD /* ChatItemView.swift in Sources */,
|
||||
64AA1C6A27EE10C800AC7277 /* ContextItemView.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CA059D3279559F40002BEB4 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
@@ -740,15 +521,6 @@
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
5CA059DF279559F40002BEB4 /* Sources */ = {
|
||||
isa = PBXSourcesBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CA059EA279559F40002BEB4 /* Tests_macOSLaunchTests.swift in Sources */,
|
||||
5CA059E8279559F40002BEB4 /* Tests_macOS.swift in Sources */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
/* End PBXSourcesBuildPhase section */
|
||||
|
||||
/* Begin PBXTargetDependency section */
|
||||
@@ -757,13 +529,27 @@
|
||||
target = 5CA059C9279559F40002BEB4 /* SimpleX (iOS) */;
|
||||
targetProxy = 5CA059D8279559F40002BEB4 /* PBXContainerItemProxy */;
|
||||
};
|
||||
5CA059E5279559F40002BEB4 /* PBXTargetDependency */ = {
|
||||
isa = PBXTargetDependency;
|
||||
target = 5CA059CF279559F40002BEB4 /* SimpleX (macOS) */;
|
||||
targetProxy = 5CA059E4279559F40002BEB4 /* PBXContainerItemProxy */;
|
||||
};
|
||||
/* End PBXTargetDependency section */
|
||||
|
||||
/* Begin PBXVariantGroup section */
|
||||
5CC2C0FA2809BF11000C35E3 /* Localizable.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
5CC2C0FB2809BF11000C35E3 /* ru */,
|
||||
);
|
||||
name = Localizable.strings;
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
5CC2C0FD2809BF11000C35E3 /* SimpleX--iOS--InfoPlist.strings */ = {
|
||||
isa = PBXVariantGroup;
|
||||
children = (
|
||||
5CC2C0FE2809BF11000C35E3 /* ru */,
|
||||
);
|
||||
name = "SimpleX--iOS--InfoPlist.strings";
|
||||
sourceTree = "<group>";
|
||||
};
|
||||
/* End PBXVariantGroup section */
|
||||
|
||||
/* Begin XCBuildConfiguration section */
|
||||
5CA059F1279559F40002BEB4 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
@@ -820,6 +606,7 @@
|
||||
MTL_FAST_MATH = YES;
|
||||
ONLY_ACTIVE_ARCH = YES;
|
||||
SWIFT_ACTIVE_COMPILATION_CONDITIONS = DEBUG;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
};
|
||||
name = Debug;
|
||||
@@ -872,6 +659,7 @@
|
||||
MTL_ENABLE_DEBUG_INFO = NO;
|
||||
MTL_FAST_MATH = YES;
|
||||
SWIFT_COMPILATION_MODE = wholemodule;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-O";
|
||||
};
|
||||
name = Release;
|
||||
@@ -956,83 +744,6 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
5CA059F7279559F40002BEB4 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (macOS)Debug.entitlements";
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "SimpleX--macOS--Info.plist";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries",
|
||||
"$(PROJECT_DIR)/Libraries/ios",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.1;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Shared/SimpleX (macOS)-Bridging-Header.h";
|
||||
SWIFT_OPTIMIZATION_LEVEL = "-Onone";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
5CA059F8279559F40002BEB4 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ASSETCATALOG_COMPILER_APPICON_NAME = AppIcon;
|
||||
ASSETCATALOG_COMPILER_GLOBAL_ACCENT_COLOR_NAME = AccentColor;
|
||||
CLANG_ENABLE_MODULES = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = macOS/macOS.entitlements;
|
||||
CODE_SIGN_IDENTITY = "-";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
COMBINE_HIDPI_IMAGES = YES;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_HARDENED_RUNTIME = YES;
|
||||
ENABLE_PREVIEWS = YES;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
INFOPLIST_FILE = "SimpleX--macOS--Info.plist";
|
||||
INFOPLIST_KEY_NSHumanReadableCopyright = "";
|
||||
LD_RUNPATH_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"@executable_path/../Frameworks",
|
||||
);
|
||||
LIBRARY_SEARCH_PATHS = (
|
||||
"$(inherited)",
|
||||
"$(PROJECT_DIR)/Libraries",
|
||||
"$(PROJECT_DIR)/Libraries/ios",
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.1;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = macosx;
|
||||
SWIFT_EMIT_LOC_STRINGS = YES;
|
||||
SWIFT_OBJC_BRIDGING_HEADER = "Shared/SimpleX (macOS)-Bridging-Header.h";
|
||||
SWIFT_VERSION = 5.0;
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
5CA059FA279559F40002BEB4 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
@@ -1074,44 +785,6 @@
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
5CA059FD279559F40002BEB4 /* Debug */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 9767FTRA3G;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.1;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-macOS";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_TARGET_NAME = "SimpleX (macOS)";
|
||||
};
|
||||
name = Debug;
|
||||
};
|
||||
5CA059FE279559F40002BEB4 /* Release */ = {
|
||||
isa = XCBuildConfiguration;
|
||||
buildSettings = {
|
||||
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 1;
|
||||
DEVELOPMENT_TEAM = 9767FTRA3G;
|
||||
GENERATE_INFOPLIST_FILE = YES;
|
||||
MACOSX_DEPLOYMENT_TARGET = 12.1;
|
||||
MARKETING_VERSION = 1.0;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.Tests-macOS";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
SDKROOT = macosx;
|
||||
SWIFT_EMIT_LOC_STRINGS = NO;
|
||||
SWIFT_VERSION = 5.0;
|
||||
TEST_TARGET_NAME = "SimpleX (macOS)";
|
||||
};
|
||||
name = Release;
|
||||
};
|
||||
/* End XCBuildConfiguration section */
|
||||
|
||||
/* Begin XCConfigurationList section */
|
||||
@@ -1133,15 +806,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
5CA059F6279559F40002BEB4 /* Build configuration list for PBXNativeTarget "SimpleX (macOS)" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5CA059F7279559F40002BEB4 /* Debug */,
|
||||
5CA059F8279559F40002BEB4 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
5CA059F9279559F40002BEB4 /* Build configuration list for PBXNativeTarget "Tests iOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
@@ -1151,15 +815,6 @@
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
5CA059FC279559F40002BEB4 /* Build configuration list for PBXNativeTarget "Tests macOS" */ = {
|
||||
isa = XCConfigurationList;
|
||||
buildConfigurations = (
|
||||
5CA059FD279559F40002BEB4 /* Debug */,
|
||||
5CA059FE279559F40002BEB4 /* Release */,
|
||||
);
|
||||
defaultConfigurationIsVisible = 0;
|
||||
defaultConfigurationName = Release;
|
||||
};
|
||||
/* End XCConfigurationList section */
|
||||
|
||||
/* Begin XCRemoteSwiftPackageReference section */
|
||||
|
||||
@@ -0,0 +1,89 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<Scheme
|
||||
LastUpgradeVersion = "1330"
|
||||
version = "1.3">
|
||||
<BuildAction
|
||||
parallelizeBuildables = "YES"
|
||||
buildImplicitDependencies = "YES">
|
||||
<BuildActionEntries>
|
||||
<BuildActionEntry
|
||||
buildForTesting = "YES"
|
||||
buildForRunning = "YES"
|
||||
buildForProfiling = "YES"
|
||||
buildForArchiving = "YES"
|
||||
buildForAnalyzing = "YES">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5CA059C9279559F40002BEB4"
|
||||
BuildableName = "SimpleX.app"
|
||||
BlueprintName = "SimpleX (iOS)"
|
||||
ReferencedContainer = "container:SimpleX.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildActionEntry>
|
||||
</BuildActionEntries>
|
||||
</BuildAction>
|
||||
<TestAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES">
|
||||
<Testables>
|
||||
<TestableReference
|
||||
skipped = "NO">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5CA059D6279559F40002BEB4"
|
||||
BuildableName = "Tests iOS.xctest"
|
||||
BlueprintName = "Tests iOS"
|
||||
ReferencedContainer = "container:SimpleX.xcodeproj">
|
||||
</BuildableReference>
|
||||
</TestableReference>
|
||||
</Testables>
|
||||
</TestAction>
|
||||
<LaunchAction
|
||||
buildConfiguration = "Debug"
|
||||
selectedDebuggerIdentifier = "Xcode.DebuggerFoundation.Debugger.LLDB"
|
||||
selectedLauncherIdentifier = "Xcode.DebuggerFoundation.Launcher.LLDB"
|
||||
launchStyle = "0"
|
||||
useCustomWorkingDirectory = "NO"
|
||||
ignoresPersistentStateOnLaunch = "NO"
|
||||
debugDocumentVersioning = "YES"
|
||||
debugServiceExtension = "internal"
|
||||
allowLocationSimulation = "YES"
|
||||
showNonLocalizedStrings = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5CA059C9279559F40002BEB4"
|
||||
BuildableName = "SimpleX.app"
|
||||
BlueprintName = "SimpleX (iOS)"
|
||||
ReferencedContainer = "container:SimpleX.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</LaunchAction>
|
||||
<ProfileAction
|
||||
buildConfiguration = "Release"
|
||||
shouldUseLaunchSchemeArgsEnv = "YES"
|
||||
savedToolIdentifier = ""
|
||||
useCustomWorkingDirectory = "NO"
|
||||
debugDocumentVersioning = "YES">
|
||||
<BuildableProductRunnable
|
||||
runnableDebuggingMode = "0">
|
||||
<BuildableReference
|
||||
BuildableIdentifier = "primary"
|
||||
BlueprintIdentifier = "5CA059C9279559F40002BEB4"
|
||||
BuildableName = "SimpleX.app"
|
||||
BlueprintName = "SimpleX (iOS)"
|
||||
ReferencedContainer = "container:SimpleX.xcodeproj">
|
||||
</BuildableReference>
|
||||
</BuildableProductRunnable>
|
||||
</ProfileAction>
|
||||
<AnalyzeAction
|
||||
buildConfiguration = "Debug">
|
||||
</AnalyzeAction>
|
||||
<ArchiveAction
|
||||
buildConfiguration = "Release"
|
||||
revealArchiveInOrganizer = "YES">
|
||||
</ArchiveAction>
|
||||
</Scheme>
|
||||
@@ -1,42 +0,0 @@
|
||||
//
|
||||
// Tests_macOS.swift
|
||||
// Tests macOS
|
||||
//
|
||||
// Created by Evgeny Poberezkin on 17/01/2022.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
class Tests_macOS: XCTestCase {
|
||||
|
||||
override func setUpWithError() throws {
|
||||
// Put setup code here. This method is called before the invocation of each test method in the class.
|
||||
|
||||
// In UI tests it is usually best to stop immediately when a failure occurs.
|
||||
continueAfterFailure = false
|
||||
|
||||
// In UI tests it’s important to set the initial state - such as interface orientation - required for your tests before they run. The setUp method is a good place to do this.
|
||||
}
|
||||
|
||||
override func tearDownWithError() throws {
|
||||
// Put teardown code here. This method is called after the invocation of each test method in the class.
|
||||
}
|
||||
|
||||
func testExample() throws {
|
||||
// UI tests must launch the application that they test.
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Use recording to get started writing UI tests.
|
||||
// Use XCTAssert and related functions to verify your tests produce the correct results.
|
||||
}
|
||||
|
||||
func testLaunchPerformance() throws {
|
||||
if #available(macOS 10.15, iOS 13.0, tvOS 13.0, watchOS 7.0, *) {
|
||||
// This measures how long it takes to launch your application.
|
||||
measure(metrics: [XCTApplicationLaunchMetric()]) {
|
||||
XCUIApplication().launch()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -1,32 +0,0 @@
|
||||
//
|
||||
// Tests_macOSLaunchTests.swift
|
||||
// Tests macOS
|
||||
//
|
||||
// Created by Evgeny Poberezkin on 17/01/2022.
|
||||
//
|
||||
|
||||
import XCTest
|
||||
|
||||
class Tests_macOSLaunchTests: XCTestCase {
|
||||
|
||||
override class var runsForEachTargetApplicationUIConfiguration: Bool {
|
||||
true
|
||||
}
|
||||
|
||||
override func setUpWithError() throws {
|
||||
continueAfterFailure = false
|
||||
}
|
||||
|
||||
func testLaunch() throws {
|
||||
let app = XCUIApplication()
|
||||
app.launch()
|
||||
|
||||
// Insert steps here to perform after app launch but before taking a screenshot,
|
||||
// such as logging into a test account or navigating somewhere in the app
|
||||
|
||||
let attachment = XCTAttachment(screenshot: app.screenshot())
|
||||
attachment.name = "Launch Screen"
|
||||
attachment.lifetime = .keepAlways
|
||||
add(attachment)
|
||||
}
|
||||
}
|
||||
@@ -1,10 +0,0 @@
|
||||
<?xml version="1.0" encoding="UTF-8"?>
|
||||
<!DOCTYPE plist PUBLIC "-//Apple//DTD PLIST 1.0//EN" "http://www.apple.com/DTDs/PropertyList-1.0.dtd">
|
||||
<plist version="1.0">
|
||||
<dict>
|
||||
<key>com.apple.security.app-sandbox</key>
|
||||
<true/>
|
||||
<key>com.apple.security.files.user-selected.read-only</key>
|
||||
<true/>
|
||||
</dict>
|
||||
</plist>
|
||||
@@ -0,0 +1,462 @@
|
||||
/* No comment provided by engineer. */
|
||||
" " = " ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
" (" = " (";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
" (can be copied)" = " (можно скопировать)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"_italic_" = "\\_курсив_";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
", " = ", ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
": " = ": ";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
": %@" = ": %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"!1 colored!" = "!1 цвет!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"(shared only with your contacts)" = "(отправляется только вашим контактам)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
")" = ")";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"[Send us email](mailto:chat@simplex.chat)" = "[Отправить email](mailto:chat@simplex.chat)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"**Add new contact**: to create your one-time QR Code for your contact." = "**Добавить новый контакт**: чтобы создать одноразовый QR код или ссылку для вашего контакта.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"**Scan QR code**: to connect to your contact who shows QR code to you." = "**Сканировать QR код**: чтобы соединиться с вашим контактом (который показывает вам QR код).";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"*bold*" = "\\*жирный*";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"#secret#" = "#секрет#";
|
||||
|
||||
/* notification title */
|
||||
"%@ is connected!" = "Установлено соединение с %@!";
|
||||
|
||||
/* notification title */
|
||||
"%@ wants to connect!" = "%@ хочет соединиться!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lld" = "%lld";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"%lldk" = "%lldk";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"`a + b`" = "\\`a + b`";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"~strike~" = "\\~зачеркнуть~";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"💻 desktop: scan displayed QR code from the app, via **Scan QR code**." = "💻 на компьютере: сосканируйте QR код из приложения через **Сканировать QR код**.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"📱 mobile: tap **Open in mobile app**, then tap **Connect** in the app." = "📱 на мобильном: намжите кнопку **Open in mobile app** на веб странице, затем нажмите **Соединиться** в приложении.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"6" = "6";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"above, then:" = "наверху, затем:";
|
||||
|
||||
/* accept contact request via notification */
|
||||
"Accept" = "Принять";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Accept contact" = "Принять запрос";
|
||||
|
||||
/* notification body */
|
||||
"Accept contact request from %@?" = "Принять запрос на соединение от %@?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Add contact" = "Добавить контакт";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"All your contacts will remain connected" = "Все контакты, которые соединились через этот адрес, сохранятся.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"bold" = "жирный";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Cancel" = "Отменить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Chat console" = "Консоль";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Chat with the developers" = "Соединиться с разработчиками";
|
||||
|
||||
/* back button to return to chats list */
|
||||
"Chats" = "Назад";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Choose from library" = "Выбрать из библиотеки";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"colored" = "цвет";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Configure SMP servers" = "Настройка SMP серверов";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Confirm" = "Подтвердить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect" = "Соединиться";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"connect to SimpleX Chat developers." = "соединиться с разработчиками.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via contact link?" = "Соединиться через ссылку-контакт?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connect via invitation link?" = "Соединиться через ссылку-приглашение?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server…" = "Устанавливается соединение с сервером…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting server… (error: %@)" = "Устанавливается соединение с сервером… (ошибка: %@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connecting..." = "Устанавливается соединение…";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection error" = "Ошибка соединения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection request" = "Запрос на соединение";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection request sent!" = "Запрос на соединение отправлен!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Connection timeout" = "Превышено время соединения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Contact already exists" = "Существующий контакт";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Contact and all messages will be deleted - this cannot be undone!" = "Контакт и все сообщения будут удалены - это действие нельзя отменить!";
|
||||
|
||||
/* notification */
|
||||
"Contact is connected" = "Соединение с контактом установлено";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Copy" = "Скопировать";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create" = "Создать";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create address" = "Создать адрес";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create group" = "Создать группу";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Create profile" = "Создать профиль";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete" = "Удалить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete address" = "Удалить адрес";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete address?" = "Удалить адрес?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete contact" = "Удалить контакт";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete contact?" = "Удалить контакт?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete for me" = "Удалить для меня";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete group" = "Удалить группу";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Delete message?" = "Удалить сообщение?";
|
||||
|
||||
/* deleted chat item */
|
||||
"deleted" = "удалено";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Develop" = "Для разработчиков";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Display name" = "Имя профиля";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Edit" = "Редактировать";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Enter one SMP server per line:" = "Введите SMP серверы, каждый на отдельной строке:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error saving SMP servers" = "Ошибка при сохранении SMP серверов";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error: %@" = "Ошибка: %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Error: URL is invalid" = "Ошибка: неверная ссылка";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Full name (optional)" = "Полное имя (не обязательно)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Group deletion is not supported" = "Удаление групп не поддерживается";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Help" = "Помощь";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"How to" = "Информация";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"How to use markdown" = "Как форматировать";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"How to use SimpleX Chat" = "Как использовать SimpleX Chat";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"If you cannot meet in person, you can **scan QR code in the video call**, or your contact can share an invitation link." = "Если вы не можете встретиться лично, вы можете **сосканировать QR код во время видеозвонка**, или ваш контакт может отправить вам ссылку.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"If you cannot meet in person, you can **show QR code in the video call**, or you can share the invitation link via any other channel." = "Если вы не можете встретиться лично, вы можете **показать QR код во время видеозвонка** или отправить ссылку через любой другой канал связи.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"If you received SimpleX Chat invitation link you can open it in your browser:" = "Если вы получили ссылку с приглашением из SimpleX Chat, вы можете открыть её в браузере:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Install [SimpleX Chat for terminal](https://github.com/simplex-chat/simplex-chat)" = "[SimpleX Chat для терминала](https://github.com/simplex-chat/simplex-chat)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Invalid connection link" = "Ошибка в ссылке контакта";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"italic" = "курсив";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Make sure SMP server addresses are in correct format, line separated and are not duplicated." = "Пожалуйста, проверьте, что адреса SMP серверов имеют правильный формат, каждый адрес на отдельной строке и не повторяется.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Markdown in messages" = "Форматирование сообщений";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Message delivery error" = "Ошибка доставки сообщения";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Most likely this contact has deleted the connection with you." = "Скорее всего, этот контакт удалил соединение с вами.";
|
||||
|
||||
/* notification */
|
||||
"New contact request" = "Новый запрос на соединение";
|
||||
|
||||
/* notifications */
|
||||
"New message" = "Новое сообщение";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Notifications are disabled!" = "Уведомления выключены";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Open Settings" = "Открыть Настройки";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Please check that you used the correct link or ask your contact to send you another one." = "Пожалуйста, проверьте, что вы использовали правильную ссылку или попросите, чтобы ваш контакт отправил вам другую ссылку.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Please check your network connection and try again." = "Пожалуйста, проверьте ваше соединение с сетью и попробуйте еще раз.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Profile image" = "Аватар";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Read" = "Прочитано";
|
||||
|
||||
/* to be removed */
|
||||
"receiving files is not supported yet" = "получение файлов не поддерживается";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Reject" = "Отклонить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Reject contact (sender NOT notified)" = "Отклонить (не уведомляя отправителя)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Reject contact request" = "Отклонить запрос";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Reply" = "Ответить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Save" = "Сохранить";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Save (and notify contacts)" = "Сохранить (и уведомить контакты)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Saved SMP servers will be removed" = "Сохраненные SMP серверы будут удалены";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Scan QR code" = "Сканировать QR код";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"secret" = "секрет";
|
||||
|
||||
/* to be removed */
|
||||
"sending files is not supported yet" = "отправка файлов не поддерживается";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Server connected" = "Установлено соединение с сервером";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Settings" = "Настройки";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Share" = "Поделиться";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Share invitation link" = "Поделиться ссылкой";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Share link" = "Поделиться ссылкой";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Show QR code to your contact\nto scan from the app" = "Покажите QR код вашему контакту для сканирования в приложении";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"SMP servers" = "SMP серверы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Start new chat" = "Начать новый разговор";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"strike" = "зачеркнуть";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Take picture" = "Сделать фото";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Tap button " = "Нажмите кнопку";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Thank you for installing SimpleX Chat!" = "Спасибо, что Вы установили SimpleX Chat!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The app can notify you when you receive messages or contact requests - please open settings to enable." = "Приложение может посылать вам уведомления о сообщениях и запросах на соединение - уведомления можно включить в Настройках.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The messaging and application platform 100% private by design!" = "Платформа для сообщений и приложений, которая защищает вашу личную информацию и безопасность.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"The sender will NOT be notified" = "Отправитель не будет уведомлён";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To ask any questions and to receive updates:" = "Задать вопросы и получать уведомления о новых версиях:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To connect via link" = "Соединиться через ссылку";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"To start a new chat" = "Начать новый разговор";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Trying to connect to the server used to receive messages from this contact (error: %@)." = "Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта (ошибка: %@).";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Trying to connect to the server used to receive messages from this contact." = "Устанавливается соединение с сервером, через который вы получаете сообщения от этого контакта.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Unexpected error: %@" = "Неожиданная ошибка: %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Use SimpleX Chat servers?" = "Использовать серверы предосталенные SimpleX Chat?";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Using SimpleX Chat servers." = "Используются серверы, предоставленные SimpleX Chat.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"v%@ (%@)" = "v%@ (%@)";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"wants to connect to you!" = "хочет соединиться с вами!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Welcome %@!" = "Здравствуйте %@!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You" = "Вы";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are already connected to %@ via this link." = "Вы уже соединены с %@ через эту ссылку.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You are connected to the server used to receive messages from this contact." = "Установлено соединение с сервером, через который вы получается сообщения от этого контакта.";
|
||||
|
||||
/* notification body */
|
||||
"You can now send messages to %@" = "Вы теперь можете отправлять сообщения %@";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You can share your address as a link or as a QR code - anybody will be able to connect to you. You won't lose your contacts if you later delete it." = "Вы можете использовать ваш адрес как ссылку или как QR код - кто угодно сможет соединиться с вами. Вы сможете удалить адрес, сохранив контакты, которые через него соединились.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You can use markdown to format messages:" = "Вы можете форматировать сообщения:";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You control your chat!" = "Вы котролируете Ваш чат!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You will be connected when your connection request is accepted, please wait or check later!" = "Соединение будет установлено, когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"You will be connected when your contact's device is online, please wait or check later!" = "Соединение будет установлено, когда ваш контакт будет онлайн. Пожалуйста, подождите или проверьте позже!";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your chat address" = "Ваш SimpleX адрес";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your chat profile" = "Ваш профиль";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your chat profile will be sent to your contact" = "Ваш профиль будет отправлен вашему контакту";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your chats" = "Ваши чаты";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Ваш профиль хранится на вашем устройстве и отправляется только вашим контактам.\nSimpleX серверы не могут получить доступ к вашему профилю.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile will be sent to the contact that you received this link from" = "Ваш профиль будет отправлен контакту, от которого вы получили эту ссылку.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your profile, contacts and messages (once delivered) are only stored locally on your device." = "Ваш профиль, контакты и сообщения (после доставки) хранятся только на вашем устройстве.";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your settings" = "Настройки";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your SimpleX contact address" = "Ваш SimpleX адрес";
|
||||
|
||||
/* No comment provided by engineer. */
|
||||
"Your SMP servers" = "Ваши SMP серверы";
|
||||
|
||||
@@ -0,0 +1,6 @@
|
||||
/* Bundle name */
|
||||
"CFBundleName" = "SimpleX";
|
||||
|
||||
/* Privacy - Camera Usage Description */
|
||||
"NSCameraUsageDescription" = "SimpleX использует камеру для сканирования QR кодов при соединении с другими пользователями";
|
||||
|
||||
@@ -14,6 +14,7 @@ import qualified Data.Text as T
|
||||
import Simplex.Chat
|
||||
import Simplex.Chat.Bot
|
||||
import Simplex.Chat.Controller
|
||||
import Simplex.Chat.Core
|
||||
import Simplex.Chat.Messages
|
||||
import Simplex.Chat.Options
|
||||
import Simplex.Chat.Types
|
||||
@@ -23,7 +24,7 @@ import Text.Read
|
||||
main :: IO ()
|
||||
main = do
|
||||
opts <- welcomeGetOpts
|
||||
simplexChatBot defaultChatConfig opts mySquaringBot
|
||||
simplexChatCore defaultChatConfig opts Nothing mySquaringBot
|
||||
|
||||
welcomeGetOpts :: IO ChatOpts
|
||||
welcomeGetOpts = do
|
||||
@@ -50,5 +51,5 @@ mySquaringBot _user cc = do
|
||||
Just n -> msg <> " * " <> msg <> " = " <> show (n * n)
|
||||
_ -> pure ()
|
||||
where
|
||||
sendMsg Contact {contactId} msg = sendCmd cc $ "/_send @" <> show contactId <> " text " <> msg
|
||||
sendMsg Contact {contactId} msg = sendChatCmd cc $ "/_send @" <> show contactId <> " text " <> msg
|
||||
contactConnected Contact {localDisplayName} = putStrLn $ T.unpack localDisplayName <> " connected"
|
||||
|
||||
@@ -5,6 +5,7 @@ module Main where
|
||||
import Simplex.Chat
|
||||
import Simplex.Chat.Bot
|
||||
import Simplex.Chat.Controller (versionNumber)
|
||||
import Simplex.Chat.Core
|
||||
import Simplex.Chat.Options
|
||||
import System.Directory (getAppUserDataDirectory)
|
||||
import Text.Read
|
||||
@@ -12,7 +13,7 @@ import Text.Read
|
||||
main :: IO ()
|
||||
main = do
|
||||
opts <- welcomeGetOpts
|
||||
simplexChatBot defaultChatConfig opts $
|
||||
simplexChatCore defaultChatConfig opts Nothing $
|
||||
chatBotRepl "Hello! I am a simple squaring bot - if you send me a number, I will calculate its square" $ \msg ->
|
||||
case readMaybe msg :: Maybe Integer of
|
||||
Just n -> msg <> " * " <> msg <> " = " <> show (n * n)
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user