mirror of
https://github.com/simplex-chat/simplex-chat.git
synced 2024-12-17 17:20:21 +01:00
Compare commits
76 Commits
| Author | SHA1 | Date | |
|---|---|---|---|
| d438cec674 | |||
| e4c5dbdb08 | |||
| 5d1a393131 | |||
| 8e3a713a49 | |||
| 7cec79e79b | |||
| e9db7e8e58 | |||
| f53882049f | |||
| 8f4c9a673a | |||
| 02f980e968 | |||
| 412f75219a | |||
| c8c81a840b | |||
| 925cb39397 | |||
| f6ceb89c4b | |||
| a35d392f76 | |||
| 1306c84938 | |||
| 6484e36b82 | |||
| 918be31f2c | |||
| 648884044f | |||
| 505c6cb062 | |||
| 065c8f8861 | |||
| 6c509027f7 | |||
| 4eafdc3723 | |||
| 77f5c678d5 | |||
| 5e71d00ace | |||
| c2169f7813 | |||
| 7ff4b05c90 | |||
| d7f8bff957 | |||
| bda0917787 | |||
| 0eae7f56f3 | |||
| 24e3806b25 | |||
| f606e5742c | |||
| c6af2c285a | |||
| 35b7f2cb1c | |||
| 76a33a3743 | |||
| b6fd5e61b6 | |||
| fe639eed48 | |||
| 638fd3756a | |||
| de81434b36 | |||
| 433b3652fb | |||
| 86fe2212c3 | |||
| 182225a5b1 | |||
| e10c63dc64 | |||
| 29941729d2 | |||
| ee53377070 | |||
| 0deb6a4eb1 | |||
| b84ef0ce35 | |||
| 34dee32def | |||
| 94851f177b | |||
| e560b49d14 | |||
| d8b52ee0d3 | |||
| a9c0f356b4 | |||
| 8a3710482b | |||
| b3a4ea8e57 | |||
| 4be6c69633 | |||
| 005d99ad50 | |||
| 36f86beafd | |||
| 1fa79a2654 | |||
| 72611084d3 | |||
| a5db36469d | |||
| f8e6a78a3b | |||
| 5b0c8e0f76 | |||
| 2968cd0460 | |||
| 8ffb4e500e | |||
| 6133993cfe | |||
| 0d7a32877f | |||
| 18efc28d16 | |||
| 069395c2a0 | |||
| a725d2efac | |||
| 5f9710e4bb | |||
| ea6c5bfb0b | |||
| 14c279d1e0 | |||
| d2de81100d | |||
| 9b28ae6d9e | |||
| ea862a8f34 | |||
| 2bd1a82b7d | |||
| 97a37634ef |
@@ -5,14 +5,20 @@ on:
|
||||
pull_request_target:
|
||||
types: [opened, closed, synchronize]
|
||||
|
||||
permissions:
|
||||
actions: write
|
||||
contents: write
|
||||
pull-requests: write
|
||||
statuses: write
|
||||
|
||||
jobs:
|
||||
CLAssistant:
|
||||
runs-on: ubuntu-latest
|
||||
steps:
|
||||
- name: "CLA Assistant"
|
||||
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request'
|
||||
if: (github.event.comment.body == 'recheck' || github.event.comment.body == 'I have read the CLA Document and I hereby sign the CLA') || github.event_name == 'pull_request_target'
|
||||
# Beta Release
|
||||
uses: cla-assistant/github-action@v2.1.3-beta
|
||||
uses: cla-assistant/github-action@v2.3.0
|
||||
env:
|
||||
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}
|
||||
# the below token should have repo scope and must be manually added by you in the repository's secret
|
||||
@@ -33,4 +39,4 @@ jobs:
|
||||
#custom-pr-sign-comment: 'The signature to be committed in order to sign the CLA'
|
||||
#custom-allsigned-prcomment: 'pull request comment when all contributors has signed, defaults to **CLA Assistant Lite bot** All Contributors have signed the CLA.'
|
||||
#lock-pullrequest-aftermerge: false - if you don't want this bot to automatically lock the pull request after merging (default - true)
|
||||
#use-dco-flag: true - If you are using DCO instead of CLA
|
||||
#use-dco-flag: true - If you are using DCO instead of CLA
|
||||
|
||||
@@ -4,7 +4,7 @@
|
||||
[](https://www.reddit.com/r/SimpleXChat)
|
||||
<a rel="me" href="https://mastodon.social/@simplex"></a>
|
||||
|
||||
| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md) |
|
||||
| 30/03/2023 | EN, [FR](/docs/lang/fr/README.md), [CZ](/docs/lang/cs/README.md), [PL](/docs/lang/pl/README.md) |
|
||||
|
||||
<img src="images/simplex-chat-logo.svg" alt="SimpleX logo" width="100%">
|
||||
|
||||
|
||||
@@ -103,6 +103,7 @@ final class ChatModel: ObservableObject {
|
||||
// tracks keyboard height via subscription in AppDelegate
|
||||
@Published var keyboardHeight: CGFloat = 0
|
||||
@Published var pasteboardHasStrings: Bool = UIPasteboard.general.hasStrings
|
||||
@Published var networkInfo = UserNetworkInfo(networkType: .other, online: true)
|
||||
|
||||
var messageDelivery: Dictionary<Int64, () -> Void> = [:]
|
||||
|
||||
@@ -778,6 +779,24 @@ final class Chat: ObservableObject, Identifiable {
|
||||
|
||||
var viewId: String { get { "\(chatInfo.id) \(created.timeIntervalSince1970)" } }
|
||||
|
||||
func groupFeatureEnabled(_ feature: GroupFeature) -> Bool {
|
||||
if case let .group(groupInfo) = self.chatInfo {
|
||||
let p = groupInfo.fullGroupPreferences
|
||||
return switch feature {
|
||||
case .timedMessages: p.timedMessages.on
|
||||
case .directMessages: p.directMessages.on(for: groupInfo.membership)
|
||||
case .fullDelete: p.fullDelete.on
|
||||
case .reactions: p.reactions.on
|
||||
case .voice: p.voice.on(for: groupInfo.membership)
|
||||
case .files: p.files.on(for: groupInfo.membership)
|
||||
case .simplexLinks: p.simplexLinks.on(for: groupInfo.membership)
|
||||
case .history: p.history.on
|
||||
}
|
||||
} else {
|
||||
return true
|
||||
}
|
||||
}
|
||||
|
||||
public static var sampleData: Chat = Chat(chatInfo: ChatInfo.sampleData.direct, chatItems: [])
|
||||
}
|
||||
|
||||
|
||||
@@ -0,0 +1,73 @@
|
||||
//
|
||||
// NetworkObserver.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by Avently on 05.04.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import Foundation
|
||||
import Network
|
||||
import SimpleXChat
|
||||
|
||||
class NetworkObserver {
|
||||
static let shared = NetworkObserver()
|
||||
private let queue: DispatchQueue = DispatchQueue(label: "chat.simplex.app.NetworkObserver")
|
||||
private var prevInfo: UserNetworkInfo? = nil
|
||||
private var monitor: NWPathMonitor?
|
||||
private let monitorLock: DispatchQueue = DispatchQueue(label: "chat.simplex.app.monitorLock")
|
||||
|
||||
func restartMonitor() {
|
||||
monitorLock.sync {
|
||||
monitor?.cancel()
|
||||
let mon = NWPathMonitor()
|
||||
mon.pathUpdateHandler = { [weak self] path in
|
||||
self?.networkPathChanged(path: path)
|
||||
}
|
||||
mon.start(queue: queue)
|
||||
monitor = mon
|
||||
}
|
||||
}
|
||||
|
||||
private func networkPathChanged(path: NWPath) {
|
||||
let info = UserNetworkInfo(
|
||||
networkType: networkTypeFromPath(path),
|
||||
online: path.status == .satisfied
|
||||
)
|
||||
if (prevInfo != info) {
|
||||
prevInfo = info
|
||||
setNetworkInfo(info)
|
||||
}
|
||||
}
|
||||
|
||||
private func networkTypeFromPath(_ path: NWPath) -> UserNetworkType {
|
||||
if path.usesInterfaceType(.wiredEthernet) {
|
||||
.ethernet
|
||||
} else if path.usesInterfaceType(.wifi) {
|
||||
.wifi
|
||||
} else if path.usesInterfaceType(.cellular) {
|
||||
.cellular
|
||||
} else if path.usesInterfaceType(.other) {
|
||||
.other
|
||||
} else {
|
||||
.none
|
||||
}
|
||||
}
|
||||
|
||||
private static var networkObserver: NetworkObserver? = nil
|
||||
|
||||
private func setNetworkInfo(_ info: UserNetworkInfo) {
|
||||
logger.debug("setNetworkInfo Network changed: \(String(describing: info))")
|
||||
DispatchQueue.main.sync {
|
||||
ChatModel.shared.networkInfo = info
|
||||
}
|
||||
if !hasChatCtrl() { return }
|
||||
self.monitorLock.sync {
|
||||
do {
|
||||
try apiSetNetworkInfo(info)
|
||||
} catch let err {
|
||||
logger.error("setNetworkInfo error: \(responseError(err))")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -353,11 +353,20 @@ func apiGetChatItemInfo(type: ChatType, id: Int64, itemId: Int64) async throws -
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64) async -> ChatItem? {
|
||||
let cmd: ChatCommand = .apiForwardChatItem(toChatType: toChatType, toChatId: toChatId, fromChatType: fromChatType, fromChatId: fromChatId, itemId: itemId)
|
||||
return await processSendMessageCmd(toChatType: toChatType, cmd: cmd)
|
||||
}
|
||||
|
||||
func apiSendMessage(type: ChatType, id: Int64, file: CryptoFile?, quotedItemId: Int64?, msg: MsgContent, live: Bool = false, ttl: Int? = nil) async -> ChatItem? {
|
||||
let chatModel = ChatModel.shared
|
||||
let cmd: ChatCommand = .apiSendMessage(type: type, id: id, file: file, quotedItemId: quotedItemId, msg: msg, live: live, ttl: ttl)
|
||||
return await processSendMessageCmd(toChatType: type, cmd: cmd)
|
||||
}
|
||||
|
||||
private func processSendMessageCmd(toChatType: ChatType, cmd: ChatCommand) async -> ChatItem? {
|
||||
let chatModel = ChatModel.shared
|
||||
let r: ChatResponse
|
||||
if type == .direct {
|
||||
if toChatType == .direct {
|
||||
var cItem: ChatItem? = nil
|
||||
let endTask = beginBGTask({
|
||||
if let cItem = cItem {
|
||||
@@ -397,7 +406,7 @@ func apiCreateChatItem(noteFolderId: Int64, file: CryptoFile?, msg: MsgContent)
|
||||
}
|
||||
|
||||
private func sendMessageErrorAlert(_ r: ChatResponse) {
|
||||
logger.error("apiSendMessage error: \(String(describing: r))")
|
||||
logger.error("send message error: \(String(describing: r))")
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Error sending message",
|
||||
message: "Error: \(String(describing: r))"
|
||||
@@ -534,6 +543,12 @@ func setNetworkConfig(_ cfg: NetCfg, ctrl: chat_ctrl? = nil) throws {
|
||||
throw r
|
||||
}
|
||||
|
||||
func apiSetNetworkInfo(_ networkInfo: UserNetworkInfo) throws {
|
||||
let r = chatSendCmdSync(.apiSetNetworkInfo(networkInfo: networkInfo))
|
||||
if case .cmdOk = r { return }
|
||||
throw r
|
||||
}
|
||||
|
||||
func reconnectAllServers() async throws {
|
||||
try await sendCommandOkResp(.reconnectAllServers)
|
||||
}
|
||||
@@ -1297,6 +1312,7 @@ func initializeChat(start: Bool, confirmStart: Bool = false, dbKey: String? = ni
|
||||
defer { m.ctrlInitInProgress = false }
|
||||
(m.chatDbEncrypted, m.chatDbStatus) = chatMigrateInit(dbKey, confirmMigrations: confirmMigrations)
|
||||
if m.chatDbStatus != .ok { return }
|
||||
NetworkObserver.shared.restartMonitor()
|
||||
// If we migrated successfully means previous re-encryption process on database level finished successfully too
|
||||
if encryptionStartedDefault.get() {
|
||||
encryptionStartedDefault.set(false)
|
||||
@@ -1462,6 +1478,8 @@ class ChatReceiver {
|
||||
private var receiveMessages = true
|
||||
private var _lastMsgTime = Date.now
|
||||
|
||||
var messagesChannel: ((ChatResponse) -> Void)? = nil
|
||||
|
||||
static let shared = ChatReceiver()
|
||||
|
||||
var lastMsgTime: Date { get { _lastMsgTime } }
|
||||
@@ -1479,6 +1497,9 @@ class ChatReceiver {
|
||||
if let msg = await chatRecvMsg() {
|
||||
self._lastMsgTime = .now
|
||||
await processReceivedMsg(msg)
|
||||
if let messagesChannel {
|
||||
messagesChannel(msg)
|
||||
}
|
||||
}
|
||||
_ = try? await Task.sleep(nanoseconds: 7_500_000)
|
||||
}
|
||||
@@ -1791,7 +1812,6 @@ func processReceivedMsg(_ res: ChatResponse) async {
|
||||
}
|
||||
case let .sndFileCompleteXFTP(user, aChatItem, _):
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
Task { cleanupFile(aChatItem) }
|
||||
case let .sndFileError(user, aChatItem, _):
|
||||
if let aChatItem = aChatItem {
|
||||
await chatItemSimpleUpdate(user, aChatItem)
|
||||
|
||||
@@ -9,6 +9,7 @@
|
||||
import SwiftUI
|
||||
import WebKit
|
||||
import SimpleXChat
|
||||
import AVFoundation
|
||||
|
||||
struct ActiveCallView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@@ -21,6 +22,7 @@ struct ActiveCallView: View {
|
||||
@Binding var canConnectCall: Bool
|
||||
@State var prevColorScheme: ColorScheme = .dark
|
||||
@State var pipShown = false
|
||||
@State var wasConnected = false
|
||||
|
||||
var body: some View {
|
||||
ZStack(alignment: .topLeading) {
|
||||
@@ -69,6 +71,11 @@ struct ActiveCallView: View {
|
||||
Task { await m.callCommand.setClient(nil) }
|
||||
AppDelegate.keepScreenOn(false)
|
||||
client?.endCall()
|
||||
CallSoundsPlayer.shared.stop()
|
||||
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient)
|
||||
if (wasConnected) {
|
||||
CallSoundsPlayer.shared.vibrate(long: true)
|
||||
}
|
||||
}
|
||||
.background(m.activeCallViewIsCollapsed ? .clear : .black)
|
||||
// Quite a big delay when opening/closing the view when a scheme changes (globally) this way. It's not needed when CallKit is used since status bar is green with white text on it
|
||||
@@ -103,6 +110,11 @@ struct ActiveCallView: View {
|
||||
call.callState = .invitationSent
|
||||
call.localCapabilities = capabilities
|
||||
}
|
||||
if call.supportsVideo {
|
||||
try? AVAudioSession.sharedInstance().setCategory(.playback, options: .defaultToSpeaker)
|
||||
}
|
||||
CallSoundsPlayer.shared.startConnectingCallSound()
|
||||
activeCallWaitDeliveryReceipt()
|
||||
}
|
||||
case let .offer(offer, iceCandidates, capabilities):
|
||||
Task {
|
||||
@@ -126,6 +138,8 @@ struct ActiveCallView: View {
|
||||
}
|
||||
await MainActor.run {
|
||||
call.callState = .negotiated
|
||||
CallSoundsPlayer.shared.stop()
|
||||
try? AVAudioSession.sharedInstance().setCategory(.soloAmbient)
|
||||
}
|
||||
}
|
||||
case let .ice(iceCandidates):
|
||||
@@ -144,6 +158,10 @@ struct ActiveCallView: View {
|
||||
: CallController.shared.reportIncomingCall(call: call, connectedAt: nil)
|
||||
call.callState = .connected
|
||||
call.connectedAt = .now
|
||||
if !wasConnected {
|
||||
CallSoundsPlayer.shared.vibrate(long: false)
|
||||
wasConnected = true
|
||||
}
|
||||
}
|
||||
if state.connectionState == "closed" {
|
||||
closeCallView(client)
|
||||
@@ -161,6 +179,10 @@ struct ActiveCallView: View {
|
||||
call.callState = .connected
|
||||
call.connectionInfo = connectionInfo
|
||||
call.connectedAt = .now
|
||||
if !wasConnected {
|
||||
CallSoundsPlayer.shared.vibrate(long: false)
|
||||
wasConnected = true
|
||||
}
|
||||
case .ended:
|
||||
closeCallView(client)
|
||||
call.callState = .ended
|
||||
@@ -187,6 +209,22 @@ struct ActiveCallView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func activeCallWaitDeliveryReceipt() {
|
||||
ChatReceiver.shared.messagesChannel = { msg in
|
||||
guard let call = ChatModel.shared.activeCall, call.callState == .invitationSent else {
|
||||
ChatReceiver.shared.messagesChannel = nil
|
||||
return
|
||||
}
|
||||
if case let .chatItemStatusUpdated(_, msg) = msg,
|
||||
msg.chatInfo.id == call.contact.id,
|
||||
case .sndCall = msg.chatItem.content,
|
||||
case .sndRcvd = msg.chatItem.meta.itemStatus {
|
||||
CallSoundsPlayer.shared.startInCallSound()
|
||||
ChatReceiver.shared.messagesChannel = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func closeCallView(_ client: WebRTCClient) {
|
||||
if m.activeCall != nil {
|
||||
m.showCallView = false
|
||||
|
||||
@@ -8,6 +8,7 @@
|
||||
|
||||
import Foundation
|
||||
import AVFoundation
|
||||
import UIKit
|
||||
|
||||
class SoundPlayer {
|
||||
static let shared = SoundPlayer()
|
||||
@@ -43,3 +44,63 @@ class SoundPlayer {
|
||||
audioPlayer = nil
|
||||
}
|
||||
}
|
||||
|
||||
class CallSoundsPlayer {
|
||||
static let shared = CallSoundsPlayer()
|
||||
private var audioPlayer: AVAudioPlayer?
|
||||
private var playerTask: Task = Task {}
|
||||
|
||||
private func start(_ soundName: String, delayMs: Double) {
|
||||
audioPlayer?.stop()
|
||||
playerTask.cancel()
|
||||
logger.debug("start \(soundName)")
|
||||
guard let path = Bundle.main.path(forResource: soundName, ofType: "mp3", inDirectory: "sounds") else {
|
||||
logger.debug("start: file not found")
|
||||
return
|
||||
}
|
||||
do {
|
||||
let player = try AVAudioPlayer(contentsOf: URL(fileURLWithPath: path))
|
||||
if player.prepareToPlay() {
|
||||
audioPlayer = player
|
||||
}
|
||||
} catch {
|
||||
logger.debug("start: AVAudioPlayer error \(error.localizedDescription)")
|
||||
}
|
||||
|
||||
playerTask = Task {
|
||||
while let player = audioPlayer {
|
||||
player.play()
|
||||
do {
|
||||
try await Task.sleep(nanoseconds: UInt64((player.duration * 1_000_000_000) + delayMs * 1_000_000))
|
||||
} catch {
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
func startConnectingCallSound() {
|
||||
start("connecting_call", delayMs: 0)
|
||||
}
|
||||
|
||||
func startInCallSound() {
|
||||
// Taken from https://github.com/TelegramOrg/Telegram-Android
|
||||
// https://github.com/TelegramOrg/Telegram-Android/blob/master/LICENSE
|
||||
start("in_call", delayMs: 1000)
|
||||
}
|
||||
|
||||
func stop() {
|
||||
playerTask.cancel()
|
||||
audioPlayer?.stop()
|
||||
audioPlayer = nil
|
||||
}
|
||||
|
||||
func vibrate(long: Bool) {
|
||||
// iOS just don't want to vibrate more than once after a short period of time, and all 'styles' feel the same
|
||||
if long {
|
||||
AudioServicesPlayAlertSound(kSystemSoundID_Vibrate)
|
||||
} else {
|
||||
UIImpactFeedbackGenerator(style: .heavy).impactOccurred()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -431,17 +431,18 @@ struct RTCIceServer: Codable, Equatable {
|
||||
}
|
||||
|
||||
// the servers are expected in this format:
|
||||
// stun:stun.simplex.im:443?transport=tcp
|
||||
// turn:private:yleob6AVkiNI87hpR94Z@turn.simplex.im:443?transport=tcp
|
||||
// stuns:stun.simplex.im:443?transport=tcp
|
||||
// turns:private2:Hxuq2QxUjnhj96Zq2r4HjqHRj@turn.simplex.im:443?transport=tcp
|
||||
func parseRTCIceServer(_ str: String) -> RTCIceServer? {
|
||||
var s = replaceScheme(str, "stun:")
|
||||
s = replaceScheme(s, "stuns:")
|
||||
s = replaceScheme(s, "turn:")
|
||||
s = replaceScheme(s, "turns:")
|
||||
if let u: URL = URL(string: s),
|
||||
let scheme = u.scheme,
|
||||
let host = u.host,
|
||||
let port = u.port,
|
||||
u.path == "" && (scheme == "stun" || scheme == "turn" || scheme == "turns") {
|
||||
u.path == "" && (scheme == "stun" || scheme == "stuns" || scheme == "turn" || scheme == "turns") {
|
||||
let query = u.query == nil || u.query == "" ? "" : "?" + (u.query ?? "")
|
||||
return RTCIceServer(
|
||||
urls: ["\(scheme):\(host):\(port)\(query)"],
|
||||
|
||||
@@ -49,7 +49,7 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
}
|
||||
|
||||
private let rtcAudioSession = RTCAudioSession.sharedInstance()
|
||||
private let audioQueue = DispatchQueue(label: "audio")
|
||||
private let audioQueue = DispatchQueue(label: "chat.simplex.app.audio")
|
||||
private var sendCallResponse: (WVAPIMessage) async -> Void
|
||||
var activeCall: Binding<Call?>
|
||||
private var localRendererAspectRatio: Binding<CGFloat?>
|
||||
@@ -65,14 +65,14 @@ final class WebRTCClient: NSObject, RTCVideoViewDelegate, RTCFrameEncryptorDeleg
|
||||
self.localRendererAspectRatio = localRendererAspectRatio
|
||||
rtcAudioSession.useManualAudio = CallController.useCallKit()
|
||||
rtcAudioSession.isAudioEnabled = !CallController.useCallKit()
|
||||
logger.debug("WebRTCClient: rtcAudioSession has manual audio \(self.rtcAudioSession.useManualAudio) and audio enabled \(self.rtcAudioSession.isAudioEnabled)}")
|
||||
logger.debug("WebRTCClient: rtcAudioSession has manual audio \(self.rtcAudioSession.useManualAudio) and audio enabled \(self.rtcAudioSession.isAudioEnabled)")
|
||||
super.init()
|
||||
}
|
||||
|
||||
let defaultIceServers: [WebRTC.RTCIceServer] = [
|
||||
WebRTC.RTCIceServer(urlStrings: ["stun:stun.simplex.im:443"]),
|
||||
WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=udp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"),
|
||||
WebRTC.RTCIceServer(urlStrings: ["turn:turn.simplex.im:443?transport=tcp"], username: "private", credential: "yleob6AVkiNI87hpR94Z"),
|
||||
WebRTC.RTCIceServer(urlStrings: ["stuns:stun.simplex.im:443"]),
|
||||
//WebRTC.RTCIceServer(urlStrings: ["turns:turn.simplex.im:443?transport=udp"], username: "private2", credential: "Hxuq2QxUjnhj96Zq2r4HjqHRj"),
|
||||
WebRTC.RTCIceServer(urlStrings: ["turns:turn.simplex.im:443?transport=tcp"], username: "private2", credential: "Hxuq2QxUjnhj96Zq2r4HjqHRj"),
|
||||
]
|
||||
|
||||
func initializeCall(_ iceServers: [WebRTC.RTCIceServer]?, _ mediaType: CallMediaType, _ aesKey: String?, _ relay: Bool?) -> Call {
|
||||
|
||||
@@ -11,6 +11,7 @@ import SimpleXChat
|
||||
|
||||
struct CIChatFeatureView: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@ObservedObject var chat: Chat
|
||||
var chatItem: ChatItem
|
||||
@Binding var revealed: Bool
|
||||
var feature: Feature
|
||||
@@ -18,7 +19,7 @@ struct CIChatFeatureView: View {
|
||||
var iconColor: Color
|
||||
|
||||
var body: some View {
|
||||
if !revealed, let fs = mergedFeautures() {
|
||||
if !revealed, let fs = mergedFeatures() {
|
||||
HStack {
|
||||
ForEach(fs, content: featureIconView)
|
||||
}
|
||||
@@ -47,7 +48,7 @@ struct CIChatFeatureView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func mergedFeautures() -> [FeatureInfo]? {
|
||||
private func mergedFeatures() -> [FeatureInfo]? {
|
||||
var fs: [FeatureInfo] = []
|
||||
var icons: Set<String> = []
|
||||
if var i = m.getChatItemIndex(chatItem) {
|
||||
@@ -67,8 +68,8 @@ struct CIChatFeatureView: View {
|
||||
switch ci.content {
|
||||
case let .rcvChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param)
|
||||
case let .sndChatFeature(feature, enabled, param): FeatureInfo(feature, enabled.iconColor, param)
|
||||
case let .rcvGroupFeature(feature, preference, param): FeatureInfo(feature, preference.enable.iconColor, param)
|
||||
case let .sndGroupFeature(feature, preference, param): FeatureInfo(feature, preference.enable.iconColor, param)
|
||||
case let .rcvGroupFeature(feature, preference, param, role): FeatureInfo(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor, param)
|
||||
case let .sndGroupFeature(feature, preference, param, role): FeatureInfo(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor, param)
|
||||
default: nil
|
||||
}
|
||||
}
|
||||
@@ -103,6 +104,6 @@ struct CIChatFeatureView: View {
|
||||
struct CIChatFeatureView_Previews: PreviewProvider {
|
||||
static var previews: some View {
|
||||
let enabled = FeatureEnabled(forUser: false, forContact: false)
|
||||
CIChatFeatureView(chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), revealed: Binding.constant(true), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor)
|
||||
CIChatFeatureView(chat: Chat.sampleData, chatItem: ChatItem.getChatFeatureSample(.fullDelete, enabled), revealed: Binding.constant(true), feature: ChatFeature.fullDelete, iconColor: enabled.iconColor)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -17,6 +17,7 @@ struct CIEventView: View {
|
||||
.padding(.horizontal, 6)
|
||||
.padding(.vertical, 4)
|
||||
.textSelection(.disabled)
|
||||
.lineLimit(3)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -69,19 +69,12 @@ struct CIFileView: View {
|
||||
return false
|
||||
}
|
||||
|
||||
private func fileSizeValid() -> Bool {
|
||||
if let file = file {
|
||||
return file.fileSize <= getMaxFileSize(file.fileProtocol)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
private func fileAction() {
|
||||
logger.debug("CIFileView fileAction")
|
||||
if let file = file {
|
||||
switch (file.fileStatus) {
|
||||
case .rcvInvitation:
|
||||
if fileSizeValid() {
|
||||
if fileSizeValid(file) {
|
||||
Task {
|
||||
logger.debug("CIFileView fileAction - in .rcvInvitation, in Task")
|
||||
if let user = m.currentUser {
|
||||
@@ -143,7 +136,7 @@ struct CIFileView: View {
|
||||
case .sndCancelled: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
|
||||
case .sndError: fileIcon("doc.fill", innerIcon: "xmark", innerIconSize: 10)
|
||||
case .rcvInvitation:
|
||||
if fileSizeValid() {
|
||||
if fileSizeValid(file) {
|
||||
fileIcon("arrow.down.doc.fill", color: .accentColor)
|
||||
} else {
|
||||
fileIcon("doc.fill", color: .orange, innerIcon: "exclamationmark", innerIconSize: 12)
|
||||
@@ -201,6 +194,13 @@ struct CIFileView: View {
|
||||
}
|
||||
}
|
||||
|
||||
func fileSizeValid(_ file: CIFile?) -> Bool {
|
||||
if let file = file {
|
||||
return file.fileSize <= getMaxFileSize(file.fileProtocol)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
func saveCryptoFile(_ fileSource: CryptoFile) {
|
||||
if let cfArgs = fileSource.cryptoArgs {
|
||||
let url = getAppFilePath(fileSource.filePath)
|
||||
|
||||
@@ -126,7 +126,7 @@ struct CIVideoView: View {
|
||||
if !decryptionInProgress {
|
||||
Button {
|
||||
decrypt(file: file) {
|
||||
if let decrypted = urlDecrypted {
|
||||
if urlDecrypted != nil {
|
||||
videoPlaying = true
|
||||
player?.play()
|
||||
}
|
||||
|
||||
@@ -65,6 +65,8 @@ struct FramedItemView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
} else if let itemForwarded = chatItem.meta.itemForwarded {
|
||||
framedItemHeader(icon: "arrowshape.turn.up.forward", caption: Text(itemForwarded.text(chat.chatInfo.chatType)).italic(), pad: true)
|
||||
}
|
||||
|
||||
ChatItemContentView(chat: chat, chatItem: chatItem, revealed: $revealed, msgContentView: framedMsgContentView)
|
||||
@@ -163,7 +165,7 @@ struct FramedItemView: View {
|
||||
)
|
||||
}
|
||||
|
||||
@ViewBuilder func framedItemHeader(icon: String? = nil, caption: Text) -> some View {
|
||||
@ViewBuilder func framedItemHeader(icon: String? = nil, caption: Text, pad: Bool = false) -> some View {
|
||||
let v = HStack(spacing: 6) {
|
||||
if let icon = icon {
|
||||
Image(systemName: icon)
|
||||
@@ -178,7 +180,7 @@ struct FramedItemView: View {
|
||||
.foregroundColor(.secondary)
|
||||
.padding(.horizontal, 12)
|
||||
.padding(.top, 6)
|
||||
.padding(.bottom, chatItem.quotedItem == nil ? 6 : 0) // TODO think how to regroup
|
||||
.padding(.bottom, pad || (chatItem.quotedItem == nil && chatItem.meta.itemForwarded == nil) ? 6 : 0)
|
||||
.overlay(DetermineWidth())
|
||||
.frame(minWidth: msgWidth, alignment: .leading)
|
||||
.background(chatItemFrameContextColor(chatItem, colorScheme))
|
||||
@@ -353,9 +355,9 @@ private struct MetaColorPreferenceKey: PreferenceKey {
|
||||
|
||||
func onlyImageOrVideo(_ ci: ChatItem) -> Bool {
|
||||
if case let .image(text, _) = ci.content.msgContent {
|
||||
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && text == ""
|
||||
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && ci.meta.itemForwarded == nil && text == ""
|
||||
} else if case let .video(text, _, _) = ci.content.msgContent {
|
||||
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && text == ""
|
||||
return ci.meta.itemDeleted == nil && !ci.meta.isLive && ci.quotedItem == nil && ci.meta.itemForwarded == nil && text == ""
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
@@ -0,0 +1,152 @@
|
||||
//
|
||||
// ChatItemForwardingView.swift
|
||||
// SimpleX (iOS)
|
||||
//
|
||||
// Created by spaced4ndy on 12.04.2024.
|
||||
// Copyright © 2024 SimpleX Chat. All rights reserved.
|
||||
//
|
||||
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
struct ChatItemForwardingView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@Environment(\.dismiss) var dismiss
|
||||
|
||||
var ci: ChatItem
|
||||
var fromChatInfo: ChatInfo
|
||||
@Binding var composeState: ComposeState
|
||||
|
||||
@State private var searchText: String = ""
|
||||
@FocusState private var searchFocused
|
||||
|
||||
var body: some View {
|
||||
NavigationView {
|
||||
forwardListView()
|
||||
.toolbar {
|
||||
ToolbarItem(placement: .navigationBarLeading) {
|
||||
Button("Cancel") {
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
ToolbarItem(placement: .principal) {
|
||||
Text("Forward")
|
||||
.bold()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func forwardListView() -> some View {
|
||||
VStack(alignment: .leading) {
|
||||
let chatsToForwardTo = filterChatsToForwardTo()
|
||||
if !chatsToForwardTo.isEmpty {
|
||||
ScrollView {
|
||||
LazyVStack(alignment: .leading, spacing: 8) {
|
||||
searchFieldView(text: $searchText, focussed: $searchFocused)
|
||||
.padding(.leading, 2)
|
||||
let s = searchText.trimmingCharacters(in: .whitespaces).localizedLowercase
|
||||
let chats = s == "" ? chatsToForwardTo : chatsToForwardTo.filter { filterChatSearched($0, s) }
|
||||
ForEach(chats) { chat in
|
||||
Divider()
|
||||
forwardListNavLinkView(chat)
|
||||
.disabled(chatModel.deletedChats.contains(chat.chatInfo.id))
|
||||
}
|
||||
}
|
||||
.padding(.horizontal)
|
||||
.padding(.vertical, 8)
|
||||
.background(Color(uiColor: .systemBackground))
|
||||
.cornerRadius(12)
|
||||
.padding(.horizontal)
|
||||
}
|
||||
.background(Color(.systemGroupedBackground))
|
||||
} else {
|
||||
emptyList()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func filterChatsToForwardTo() -> [Chat] {
|
||||
var filteredChats = chatModel.chats.filter({ canForwardToChat($0) })
|
||||
if let index = filteredChats.firstIndex(where: { $0.chatInfo.chatType == .local }) {
|
||||
let privateNotes = filteredChats.remove(at: index)
|
||||
filteredChats.insert(privateNotes, at: 0)
|
||||
}
|
||||
return filteredChats
|
||||
}
|
||||
|
||||
private func filterChatSearched(_ chat: Chat, _ searchStr: String) -> Bool {
|
||||
let cInfo = chat.chatInfo
|
||||
return switch cInfo {
|
||||
case let .direct(contact):
|
||||
viewNameContains(cInfo, searchStr) ||
|
||||
contact.profile.displayName.localizedLowercase.contains(searchStr) ||
|
||||
contact.fullName.localizedLowercase.contains(searchStr)
|
||||
default:
|
||||
viewNameContains(cInfo, searchStr)
|
||||
}
|
||||
|
||||
func viewNameContains(_ cInfo: ChatInfo, _ s: String) -> Bool {
|
||||
cInfo.chatViewName.localizedLowercase.contains(s)
|
||||
}
|
||||
}
|
||||
|
||||
private func canForwardToChat(_ chat: Chat) -> Bool {
|
||||
switch chat.chatInfo {
|
||||
case let .direct(contact): contact.sendMsgEnabled && !contact.nextSendGrpInv
|
||||
case let .group(groupInfo): groupInfo.sendMsgEnabled
|
||||
case let .local(noteFolder): noteFolder.sendMsgEnabled
|
||||
case .contactRequest: false
|
||||
case .contactConnection: false
|
||||
case .invalidJSON: false
|
||||
}
|
||||
}
|
||||
|
||||
private func emptyList() -> some View {
|
||||
Text("No filtered chats")
|
||||
.foregroundColor(.secondary)
|
||||
.frame(maxWidth: .infinity)
|
||||
}
|
||||
|
||||
@ViewBuilder private func forwardListNavLinkView(_ chat: Chat) -> some View {
|
||||
Button {
|
||||
dismiss()
|
||||
if chat.id == fromChatInfo.id {
|
||||
composeState = ComposeState(
|
||||
message: composeState.message,
|
||||
preview: composeState.linkPreview != nil ? composeState.preview : .noPreview,
|
||||
contextItem: .forwardingItem(chatItem: ci, fromChatInfo: fromChatInfo)
|
||||
)
|
||||
} else {
|
||||
composeState = ComposeState.init(forwardingItem: ci, fromChatInfo: fromChatInfo)
|
||||
chatModel.chatId = chat.id
|
||||
}
|
||||
} label: {
|
||||
HStack {
|
||||
ChatInfoImage(chat: chat)
|
||||
.frame(width: 30, height: 30)
|
||||
.padding(.trailing, 2)
|
||||
Text(chat.chatInfo.chatViewName)
|
||||
.foregroundColor(.primary)
|
||||
.lineLimit(1)
|
||||
if chat.chatInfo.incognito {
|
||||
Spacer()
|
||||
Image(systemName: "theatermasks")
|
||||
.resizable()
|
||||
.scaledToFit()
|
||||
.frame(width: 22, height: 22)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
#Preview {
|
||||
ChatItemForwardingView(
|
||||
ci: ChatItem.getSample(1, .directSnd, .now, "hello"),
|
||||
fromChatInfo: .direct(contact: Contact.sampleData),
|
||||
composeState: Binding.constant(ComposeState(message: "hello"))
|
||||
)
|
||||
}
|
||||
@@ -11,6 +11,7 @@ import SimpleXChat
|
||||
|
||||
struct ChatItemInfoView: View {
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@Environment(\.dismiss) var dismiss
|
||||
@Environment(\.colorScheme) var colorScheme
|
||||
var ci: ChatItem
|
||||
@Binding var chatItemInfo: ChatItemInfo?
|
||||
@@ -21,6 +22,7 @@ struct ChatItemInfoView: View {
|
||||
enum CIInfoTab {
|
||||
case history
|
||||
case quote
|
||||
case forwarded
|
||||
case delivery
|
||||
}
|
||||
|
||||
@@ -68,9 +70,20 @@ struct ChatItemInfoView: View {
|
||||
if ci.quotedItem != nil {
|
||||
numTabs += 1
|
||||
}
|
||||
if chatItemInfo?.forwardedFromChatItem != nil {
|
||||
numTabs += 1
|
||||
}
|
||||
return numTabs
|
||||
}
|
||||
|
||||
private var local: Bool {
|
||||
switch ci.chatDir {
|
||||
case .localSnd: true
|
||||
case .localRcv: true
|
||||
default: false
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func itemInfoView() -> some View {
|
||||
if numTabs > 1 {
|
||||
TabView(selection: $selection) {
|
||||
@@ -93,6 +106,13 @@ struct ChatItemInfoView: View {
|
||||
}
|
||||
.tag(CIInfoTab.quote)
|
||||
}
|
||||
if let forwardedFromItem = chatItemInfo?.forwardedFromChatItem {
|
||||
forwardedFromTab(forwardedFromItem)
|
||||
.tabItem {
|
||||
Label(local ? "Saved" : "Forwarded", systemImage: "arrowshape.turn.up.forward")
|
||||
}
|
||||
.tag(CIInfoTab.forwarded)
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
if chatItemInfo?.memberDeliveryStatuses != nil {
|
||||
@@ -275,6 +295,75 @@ struct ChatItemInfoView: View {
|
||||
: Color(uiColor: .tertiarySystemGroupedBackground)
|
||||
}
|
||||
|
||||
@ViewBuilder private func forwardedFromTab(_ forwardedFromItem: AChatItem) -> some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
details()
|
||||
Divider().padding(.vertical)
|
||||
Text(local ? "Saved from" : "Forwarded from")
|
||||
.font(.title2)
|
||||
.padding(.bottom, 4)
|
||||
forwardedFromView(forwardedFromItem)
|
||||
}
|
||||
.padding()
|
||||
}
|
||||
.frame(maxHeight: .infinity, alignment: .top)
|
||||
}
|
||||
|
||||
private func forwardedFromView(_ forwardedFromItem: AChatItem) -> some View {
|
||||
VStack(alignment: .leading, spacing: 8) {
|
||||
Button {
|
||||
Task {
|
||||
await MainActor.run {
|
||||
chatModel.chatId = forwardedFromItem.chatInfo.id
|
||||
dismiss()
|
||||
}
|
||||
}
|
||||
} label: {
|
||||
forwardedFromSender(forwardedFromItem)
|
||||
}
|
||||
|
||||
if !local {
|
||||
Divider().padding(.top, 32)
|
||||
Text("Recipient(s) can't see who this message is from.")
|
||||
.font(.caption)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func forwardedFromSender(_ forwardedFromItem: AChatItem) -> some View {
|
||||
HStack {
|
||||
ChatInfoImage(chat: Chat(chatInfo: forwardedFromItem.chatInfo))
|
||||
.frame(width: 48, height: 48)
|
||||
.padding(.trailing, 6)
|
||||
|
||||
if forwardedFromItem.chatItem.chatDir.sent {
|
||||
VStack(alignment: .leading) {
|
||||
Text("you")
|
||||
.italic()
|
||||
.foregroundColor(.primary)
|
||||
Text(forwardedFromItem.chatInfo.chatViewName)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
} else if case let .groupRcv(groupMember) = forwardedFromItem.chatItem.chatDir {
|
||||
VStack(alignment: .leading) {
|
||||
Text(groupMember.chatViewName)
|
||||
.foregroundColor(.primary)
|
||||
.lineLimit(1)
|
||||
Text(forwardedFromItem.chatInfo.chatViewName)
|
||||
.foregroundColor(.secondary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
} else {
|
||||
Text(forwardedFromItem.chatInfo.chatViewName)
|
||||
.foregroundColor(.primary)
|
||||
.lineLimit(1)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder private func deliveryTab(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View {
|
||||
ScrollView {
|
||||
VStack(alignment: .leading, spacing: 16) {
|
||||
|
||||
@@ -46,7 +46,7 @@ struct ChatItemView: View {
|
||||
let ci = chatItem
|
||||
if chatItem.meta.itemDeleted != nil && (!revealed || chatItem.isDeletedContent) {
|
||||
MarkedDeletedItemView(chat: chat, chatItem: chatItem, revealed: $revealed)
|
||||
} else if ci.quotedItem == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive {
|
||||
} else if ci.quotedItem == nil && ci.meta.itemForwarded == nil && ci.meta.itemDeleted == nil && !ci.meta.isLive {
|
||||
if let mc = ci.content.msgContent, mc.isText && isShortEmoji(ci.content.text) {
|
||||
EmojiItemView(chat: chat, chatItem: ci)
|
||||
} else if ci.content.text.isEmpty, case let .voice(_, duration) = ci.content.msgContent {
|
||||
@@ -102,9 +102,9 @@ struct ChatItemContentView<Content: View>: View {
|
||||
case let .rcvChatPreference(feature, allowed, param):
|
||||
CIFeaturePreferenceView(chat: chat, chatItem: chatItem, feature: feature, allowed: allowed, param: param)
|
||||
case let .sndChatPreference(feature, _, _):
|
||||
CIChatFeatureView(chatItem: chatItem, revealed: $revealed, feature: feature, icon: feature.icon, iconColor: .secondary)
|
||||
case let .rcvGroupFeature(feature, preference, _): chatFeatureView(feature, preference.enable.iconColor)
|
||||
case let .sndGroupFeature(feature, preference, _): chatFeatureView(feature, preference.enable.iconColor)
|
||||
CIChatFeatureView(chat: chat, chatItem: chatItem, revealed: $revealed, feature: feature, icon: feature.icon, iconColor: .secondary)
|
||||
case let .rcvGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor)
|
||||
case let .sndGroupFeature(feature, preference, _, role): chatFeatureView(feature, preference.enabled(role, for: chat.chatInfo.groupInfo?.membership).iconColor)
|
||||
case let .rcvChatFeatureRejected(feature): chatFeatureView(feature, .red)
|
||||
case let .rcvGroupFeatureRejected(feature): chatFeatureView(feature, .red)
|
||||
case .sndModerated: deletedItemView()
|
||||
@@ -149,7 +149,7 @@ struct ChatItemContentView<Content: View>: View {
|
||||
}
|
||||
|
||||
private func chatFeatureView(_ feature: Feature, _ iconColor: Color) -> some View {
|
||||
CIChatFeatureView(chatItem: chatItem, revealed: $revealed, feature: feature, iconColor: iconColor)
|
||||
CIChatFeatureView(chat: chat, chatItem: chatItem, revealed: $revealed, feature: feature, iconColor: iconColor)
|
||||
}
|
||||
|
||||
private var mergedGroupEventText: Text? {
|
||||
|
||||
@@ -83,7 +83,11 @@ struct ChatView: View {
|
||||
initChatView()
|
||||
}
|
||||
.onChange(of: chatModel.chatId) { cId in
|
||||
if cId != nil {
|
||||
showChatInfoSheet = false
|
||||
if let cId {
|
||||
if let c = chatModel.getChat(cId) {
|
||||
chat = c
|
||||
}
|
||||
initChatView()
|
||||
} else {
|
||||
dismiss()
|
||||
@@ -179,7 +183,7 @@ struct ChatView: View {
|
||||
.disabled(!contact.ready || !contact.active)
|
||||
}
|
||||
searchButton()
|
||||
toggleNtfsButton(chat)
|
||||
ToggleNtfsButton(chat: chat)
|
||||
.disabled(!contact.ready || !contact.active)
|
||||
} label: {
|
||||
Image(systemName: "ellipsis")
|
||||
@@ -208,7 +212,7 @@ struct ChatView: View {
|
||||
}
|
||||
Menu {
|
||||
searchButton()
|
||||
toggleNtfsButton(chat)
|
||||
ToggleNtfsButton(chat: chat)
|
||||
} label: {
|
||||
Image(systemName: "ellipsis")
|
||||
}
|
||||
@@ -251,7 +255,8 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
if chatModel.draftChatId == cInfo.id, let draft = chatModel.draft {
|
||||
if chatModel.draftChatId == cInfo.id && !composeState.forwarding,
|
||||
let draft = chatModel.draft {
|
||||
composeState = draft
|
||||
}
|
||||
if chat.chatStats.unreadChat {
|
||||
@@ -296,7 +301,7 @@ struct ChatView: View {
|
||||
}
|
||||
|
||||
private func voiceWithoutFrame(_ ci: ChatItem) -> Bool {
|
||||
ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil
|
||||
ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil && ci.meta.itemForwarded == nil
|
||||
}
|
||||
|
||||
private func chatItemsList() -> some View {
|
||||
@@ -342,8 +347,8 @@ struct ChatView: View {
|
||||
.onChange(of: searchText) { _ in
|
||||
loadChat(chat: chat, search: searchText)
|
||||
}
|
||||
.onChange(of: chatModel.chatId) { _ in
|
||||
if let chatId = chatModel.chatId, let c = chatModel.getChat(chatId) {
|
||||
.onChange(of: chatModel.chatId) { chatId in
|
||||
if let chatId, let c = chatModel.getChat(chatId) {
|
||||
chat = c
|
||||
showChatInfoSheet = false
|
||||
loadChat(chat: c)
|
||||
@@ -539,6 +544,7 @@ struct ChatView: View {
|
||||
@State private var revealed = false
|
||||
@State private var showChatItemInfoSheet: Bool = false
|
||||
@State private var chatItemInfo: ChatItemInfo?
|
||||
@State private var showForwardingSheet: Bool = false
|
||||
|
||||
@State private var allowMenu: Bool = true
|
||||
|
||||
@@ -667,7 +673,7 @@ struct ChatView: View {
|
||||
Button("Delete for me", role: .destructive) {
|
||||
deleteMessage(.cidmInternal)
|
||||
}
|
||||
if let di = deletingItem, di.meta.editable && !di.localNote {
|
||||
if let di = deletingItem, di.meta.deletable && !di.localNote {
|
||||
Button(broadcastDeleteButtonText, role: .destructive) {
|
||||
deleteMessage(.cidmBroadcast)
|
||||
}
|
||||
@@ -693,6 +699,14 @@ struct ChatView: View {
|
||||
}) {
|
||||
ChatItemInfoView(ci: ci, chatItemInfo: $chatItemInfo)
|
||||
}
|
||||
.sheet(isPresented: $showForwardingSheet) {
|
||||
if #available(iOS 16.0, *) {
|
||||
ChatItemForwardingView(ci: ci, fromChatInfo: chat.chatInfo, composeState: $composeState)
|
||||
.presentationDetents([.fraction(0.8)])
|
||||
} else {
|
||||
ChatItemForwardingView(ci: ci, fromChatInfo: chat.chatInfo, composeState: $composeState)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func showMemberImage(_ member: GroupMember, _ prevItem: ChatItem?) -> Bool {
|
||||
@@ -771,10 +785,17 @@ struct ChatView: View {
|
||||
} else {
|
||||
menu.append(saveFileAction(fileSource))
|
||||
}
|
||||
} else if let file = ci.file, case .rcvInvitation = file.fileStatus, fileSizeValid(file) {
|
||||
menu.append(downloadFileAction(file))
|
||||
}
|
||||
if ci.meta.editable && !mc.isVoice && !live {
|
||||
menu.append(editAction(ci))
|
||||
}
|
||||
if ci.meta.itemDeleted == nil
|
||||
&& (ci.file == nil || (fileSource != nil && fileExists))
|
||||
&& !ci.isLiveDummy && !live {
|
||||
menu.append(forwardUIAction(ci))
|
||||
}
|
||||
if !ci.isLiveDummy {
|
||||
menu.append(viewInfoUIAction(ci))
|
||||
}
|
||||
@@ -826,6 +847,15 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func forwardUIAction(_ ci: ChatItem) -> UIAction {
|
||||
UIAction(
|
||||
title: NSLocalizedString("Forward", comment: "chat item action"),
|
||||
image: UIImage(systemName: "arrowshape.turn.up.forward")
|
||||
) { _ in
|
||||
showForwardingSheet = true
|
||||
}
|
||||
}
|
||||
|
||||
private func reactionUIMenuPreiOS16(_ rs: [UIAction]) -> UIMenu {
|
||||
UIMenu(
|
||||
title: NSLocalizedString("React…", comment: "chat item menu"),
|
||||
@@ -923,7 +953,21 @@ struct ChatView: View {
|
||||
saveCryptoFile(fileSource)
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
private func downloadFileAction(_ file: CIFile) -> UIAction {
|
||||
UIAction(
|
||||
title: NSLocalizedString("Download", comment: "chat item action"),
|
||||
image: UIImage(systemName: "arrow.down.doc")
|
||||
) { _ in
|
||||
Task {
|
||||
logger.debug("ChatView downloadFileAction, in Task")
|
||||
if let user = m.currentUser {
|
||||
await receiveFile(user: user, fileId: file.fileId)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func editAction(_ ci: ChatItem) -> UIAction {
|
||||
UIAction(
|
||||
title: NSLocalizedString("Edit", comment: "chat item action"),
|
||||
@@ -1172,14 +1216,18 @@ struct ChatView: View {
|
||||
}
|
||||
}
|
||||
|
||||
@ViewBuilder func toggleNtfsButton(_ chat: Chat) -> some View {
|
||||
Button {
|
||||
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled)
|
||||
} label: {
|
||||
if chat.chatInfo.ntfsEnabled {
|
||||
Label("Mute", systemImage: "speaker.slash")
|
||||
} else {
|
||||
Label("Unmute", systemImage: "speaker.wave.2")
|
||||
struct ToggleNtfsButton: View {
|
||||
@ObservedObject var chat: Chat
|
||||
|
||||
var body: some View {
|
||||
Button {
|
||||
toggleNotifications(chat, enableNtfs: !chat.chatInfo.ntfsEnabled)
|
||||
} label: {
|
||||
if chat.chatInfo.ntfsEnabled {
|
||||
Label("Mute", systemImage: "speaker.slash")
|
||||
} else {
|
||||
Label("Unmute", systemImage: "speaker.wave.2")
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -23,6 +23,7 @@ enum ComposeContextItem {
|
||||
case noContextItem
|
||||
case quotedItem(chatItem: ChatItem)
|
||||
case editingItem(chatItem: ChatItem)
|
||||
case forwardingItem(chatItem: ChatItem, fromChatInfo: ChatInfo)
|
||||
}
|
||||
|
||||
enum VoiceMessageRecordingState {
|
||||
@@ -72,6 +73,13 @@ struct ComposeState {
|
||||
}
|
||||
}
|
||||
|
||||
init(forwardingItem: ChatItem, fromChatInfo: ChatInfo) {
|
||||
self.message = ""
|
||||
self.preview = .noPreview
|
||||
self.contextItem = .forwardingItem(chatItem: forwardingItem, fromChatInfo: fromChatInfo)
|
||||
self.voiceMessageRecordingState = .noRecording
|
||||
}
|
||||
|
||||
func copy(
|
||||
message: String? = nil,
|
||||
liveMessage: LiveMessage? = nil,
|
||||
@@ -102,12 +110,19 @@ struct ComposeState {
|
||||
}
|
||||
}
|
||||
|
||||
var forwarding: Bool {
|
||||
switch contextItem {
|
||||
case .forwardingItem: return true
|
||||
default: return false
|
||||
}
|
||||
}
|
||||
|
||||
var sendEnabled: Bool {
|
||||
switch preview {
|
||||
case let .mediaPreviews(media): return !media.isEmpty
|
||||
case .voicePreview: return voiceMessageRecordingState == .finished
|
||||
case .filePreview: return true
|
||||
default: return !message.isEmpty || liveMessage != nil
|
||||
default: return !message.isEmpty || forwarding || liveMessage != nil
|
||||
}
|
||||
}
|
||||
|
||||
@@ -153,7 +168,7 @@ struct ComposeState {
|
||||
}
|
||||
|
||||
var attachmentDisabled: Bool {
|
||||
if editing || liveMessage != nil || inProgress { return true }
|
||||
if editing || forwarding || liveMessage != nil || inProgress { return true }
|
||||
switch preview {
|
||||
case .noPreview: return false
|
||||
case .linkPreview: return false
|
||||
@@ -161,6 +176,16 @@ struct ComposeState {
|
||||
}
|
||||
}
|
||||
|
||||
var attachmentPreview: Bool {
|
||||
switch preview {
|
||||
case .noPreview: false
|
||||
case .linkPreview: false
|
||||
case let .mediaPreviews(mediaPreviews): !mediaPreviews.isEmpty
|
||||
case .voicePreview: false
|
||||
case .filePreview: true
|
||||
}
|
||||
}
|
||||
|
||||
var empty: Bool {
|
||||
message == "" && noPreview
|
||||
}
|
||||
@@ -234,6 +259,7 @@ struct ComposeView: View {
|
||||
@Binding var keyboardVisible: Bool
|
||||
|
||||
@State var linkUrl: URL? = nil
|
||||
@State var hasSimplexLink: Bool = false
|
||||
@State var prevLinkUrl: URL? = nil
|
||||
@State var pendingLinkUrl: URL? = nil
|
||||
@State var cancelledLinks: Set<String> = []
|
||||
@@ -260,6 +286,16 @@ struct ComposeView: View {
|
||||
if chat.chatInfo.contact?.nextSendGrpInv ?? false {
|
||||
ContextInvitingContactMemberView()
|
||||
}
|
||||
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
|
||||
let fileProhibited = composeState.attachmentPreview && !chat.groupFeatureEnabled(.files)
|
||||
let voiceProhibited = composeState.voicePreview && !chat.chatInfo.featureEnabled(.voice)
|
||||
if simplexLinkProhibited {
|
||||
msgNotAllowedView("SimpleX links not allowed", icon: "link")
|
||||
} else if fileProhibited {
|
||||
msgNotAllowedView("Files and media not allowed", icon: "doc")
|
||||
} else if voiceProhibited {
|
||||
msgNotAllowedView("Voice messages not allowed", icon: "mic")
|
||||
}
|
||||
contextItemView()
|
||||
switch (composeState.editing, composeState.preview) {
|
||||
case (true, .filePreview): EmptyView()
|
||||
@@ -278,7 +314,7 @@ struct ComposeView: View {
|
||||
.padding(.bottom, 12)
|
||||
.padding(.leading, 12)
|
||||
if case let .group(g) = chat.chatInfo,
|
||||
!g.fullGroupPreferences.files.on {
|
||||
!g.fullGroupPreferences.files.on(for: g.membership) {
|
||||
b.disabled(true).onTapGesture {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
title: "Files and media prohibited!",
|
||||
@@ -303,6 +339,7 @@ struct ComposeView: View {
|
||||
},
|
||||
nextSendGrpInv: chat.chatInfo.contact?.nextSendGrpInv ?? false,
|
||||
voiceMessageAllowed: chat.chatInfo.featureEnabled(.voice),
|
||||
disableSendButton: simplexLinkProhibited || fileProhibited || voiceProhibited,
|
||||
showEnableVoiceMessagesAlert: chat.chatInfo.showEnableVoiceMessagesAlert,
|
||||
startVoiceMessageRecording: {
|
||||
Task {
|
||||
@@ -337,13 +374,18 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
.onChange(of: composeState.message) { _ in
|
||||
.onChange(of: composeState.message) { msg in
|
||||
if composeState.linkPreviewAllowed {
|
||||
if composeState.message.count > 0 {
|
||||
showLinkPreview(composeState.message)
|
||||
if msg.count > 0 {
|
||||
showLinkPreview(msg)
|
||||
} else {
|
||||
resetLinkPreview()
|
||||
hasSimplexLink = false
|
||||
}
|
||||
} else if msg.count > 0 && !chat.groupFeatureEnabled(.simplexLinks) {
|
||||
(_, hasSimplexLink) = parseMessage(msg)
|
||||
} else {
|
||||
hasSimplexLink = false
|
||||
}
|
||||
}
|
||||
.onChange(of: chat.userCanSend) { canSend in
|
||||
@@ -610,6 +652,18 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func msgNotAllowedView(_ reason: LocalizedStringKey, icon: String) -> some View {
|
||||
HStack {
|
||||
Image(systemName: icon).foregroundColor(.secondary)
|
||||
Text(reason).italic()
|
||||
}
|
||||
.padding(12)
|
||||
.frame(minHeight: 50)
|
||||
.frame(maxWidth: .infinity, alignment: .leading)
|
||||
.background(Color(uiColor: .tertiarySystemGroupedBackground))
|
||||
.padding(.top, 8)
|
||||
}
|
||||
|
||||
@ViewBuilder private func contextItemView() -> some View {
|
||||
switch composeState.contextItem {
|
||||
case .noContextItem:
|
||||
@@ -628,6 +682,14 @@ struct ComposeView: View {
|
||||
contextIcon: "pencil",
|
||||
cancelContextItem: { clearState() }
|
||||
)
|
||||
case let .forwardingItem(chatItem: forwardedItem, _):
|
||||
ContextItemView(
|
||||
chat: chat,
|
||||
contextItem: forwardedItem,
|
||||
contextIcon: "arrowshape.turn.up.forward",
|
||||
cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) },
|
||||
showSender: false
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -649,6 +711,11 @@ struct ComposeView: View {
|
||||
}
|
||||
if chat.chatInfo.contact?.nextSendGrpInv ?? false {
|
||||
await sendMemberContactInvitation()
|
||||
} else if case let .forwardingItem(ci, fromChatInfo) = composeState.contextItem {
|
||||
sent = await forwardItem(ci, fromChatInfo)
|
||||
if !composeState.message.isEmpty {
|
||||
sent = await send(checkLinkPreview(), quoted: sent?.id, live: false, ttl: nil)
|
||||
}
|
||||
} else if case let .editingItem(ci) = composeState.contextItem {
|
||||
sent = await updateMessage(ci, live: live)
|
||||
} else if let liveMessage = liveMessage, liveMessage.sentMsg != nil {
|
||||
@@ -694,7 +761,15 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
}
|
||||
await MainActor.run { clearState(live: live) }
|
||||
await MainActor.run {
|
||||
let wasForwarding = composeState.forwarding
|
||||
clearState(live: live)
|
||||
if wasForwarding,
|
||||
chatModel.draftChatId == chat.chatInfo.id,
|
||||
let draft = chatModel.draft {
|
||||
composeState = draft
|
||||
}
|
||||
}
|
||||
return sent
|
||||
|
||||
func sending() async {
|
||||
@@ -815,10 +890,26 @@ struct ComposeView: View {
|
||||
return nil
|
||||
}
|
||||
|
||||
func forwardItem(_ forwardedItem: ChatItem, _ fromChatInfo: ChatInfo) async -> ChatItem? {
|
||||
if let chatItem = await apiForwardChatItem(
|
||||
toChatType: chat.chatInfo.chatType,
|
||||
toChatId: chat.chatInfo.apiId,
|
||||
fromChatType: fromChatInfo.chatType,
|
||||
fromChatId: fromChatInfo.apiId,
|
||||
itemId: forwardedItem.id
|
||||
) {
|
||||
await MainActor.run {
|
||||
chatModel.addChatItem(chat.chatInfo, chatItem)
|
||||
}
|
||||
return chatItem
|
||||
}
|
||||
return nil
|
||||
}
|
||||
|
||||
func checkLinkPreview() -> MsgContent {
|
||||
switch (composeState.preview) {
|
||||
case let .linkPreview(linkPreview: linkPreview):
|
||||
if let url = parseMessage(msgText),
|
||||
if let url = parseMessage(msgText).url,
|
||||
let linkPreview = linkPreview,
|
||||
url == linkPreview.uri {
|
||||
return .link(text: msgText, preview: linkPreview)
|
||||
@@ -947,7 +1038,7 @@ struct ComposeView: View {
|
||||
|
||||
private func showLinkPreview(_ s: String) {
|
||||
prevLinkUrl = linkUrl
|
||||
linkUrl = parseMessage(s)
|
||||
(linkUrl, hasSimplexLink) = parseMessage(s)
|
||||
if let url = linkUrl {
|
||||
if url != composeState.linkPreview?.uri && url != pendingLinkUrl {
|
||||
pendingLinkUrl = url
|
||||
@@ -964,13 +1055,17 @@ struct ComposeView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func parseMessage(_ msg: String) -> URL? {
|
||||
let parsedMsg = parseSimpleXMarkdown(msg)
|
||||
let uri = parsedMsg?.first(where: { ft in
|
||||
private func parseMessage(_ msg: String) -> (url: URL?, hasSimplexLink: Bool) {
|
||||
guard let parsedMsg = parseSimpleXMarkdown(msg) else { return (nil, false) }
|
||||
let url: URL? = if 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 }
|
||||
}) {
|
||||
URL(string: uri.text)
|
||||
} else {
|
||||
nil
|
||||
}
|
||||
let simplexLink = parsedMsg.contains(where: { ft in ft.format?.isSimplexLink ?? false })
|
||||
return (url, simplexLink)
|
||||
}
|
||||
|
||||
private func isSimplexLink(_ link: String) -> Bool {
|
||||
|
||||
@@ -15,6 +15,7 @@ struct ContextItemView: View {
|
||||
let contextItem: ChatItem
|
||||
let contextIcon: String
|
||||
let cancelContextItem: () -> Void
|
||||
var showSender: Bool = true
|
||||
|
||||
var body: some View {
|
||||
HStack {
|
||||
@@ -23,7 +24,7 @@ struct ContextItemView: View {
|
||||
.aspectRatio(contentMode: .fit)
|
||||
.frame(width: 16, height: 16)
|
||||
.foregroundColor(.secondary)
|
||||
if let sender = contextItem.memberDisplayName {
|
||||
if showSender, let sender = contextItem.memberDisplayName {
|
||||
VStack(alignment: .leading, spacing: 4) {
|
||||
Text(sender).font(.caption).foregroundColor(.secondary)
|
||||
msgContentView(lines: 2)
|
||||
@@ -48,14 +49,26 @@ struct ContextItemView: View {
|
||||
}
|
||||
|
||||
private func msgContentView(lines: Int) -> some View {
|
||||
MsgContentView(
|
||||
chat: chat,
|
||||
text: contextItem.text,
|
||||
formattedText: contextItem.formattedText,
|
||||
showSecrets: false
|
||||
)
|
||||
.multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading)
|
||||
.lineLimit(lines)
|
||||
contextMsgPreview()
|
||||
.multilineTextAlignment(isRightToLeft(contextItem.text) ? .trailing : .leading)
|
||||
.lineLimit(lines)
|
||||
}
|
||||
|
||||
private func contextMsgPreview() -> Text {
|
||||
return attachment() + messageText(contextItem.text, contextItem.formattedText, nil, preview: true, showSecrets: false)
|
||||
|
||||
func attachment() -> Text {
|
||||
switch contextItem.content.msgContent {
|
||||
case .file: return image("doc.fill")
|
||||
case .image: return image("photo")
|
||||
case .voice: return image("play.fill")
|
||||
default: return Text("")
|
||||
}
|
||||
}
|
||||
|
||||
func image(_ s: String) -> Text {
|
||||
Text(Image(systemName: s)).foregroundColor(Color(uiColor: .tertiaryLabel)) + Text(" ")
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
|
||||
@@ -16,7 +16,6 @@ struct NativeTextEditor: UIViewRepresentable {
|
||||
@Binding var disableEditing: Bool
|
||||
@Binding var height: CGFloat
|
||||
@Binding var focused: Bool
|
||||
let alignment: TextAlignment
|
||||
let onImagesAdded: ([UploadContent]) -> Void
|
||||
|
||||
private let minHeight: CGFloat = 37
|
||||
@@ -30,13 +29,16 @@ struct NativeTextEditor: UIViewRepresentable {
|
||||
func makeUIView(context: Context) -> UITextView {
|
||||
let field = CustomUITextField(height: _height)
|
||||
field.text = text
|
||||
field.textAlignment = alignment == .leading ? .left : .right
|
||||
field.textAlignment = alignment(text)
|
||||
field.autocapitalizationType = .sentences
|
||||
field.setOnTextChangedListener { newText, images in
|
||||
if !disableEditing {
|
||||
// Speed up the process of updating layout, reduce jumping content on screen
|
||||
if !isShortEmoji(newText) { updateHeight(field) }
|
||||
text = newText
|
||||
field.textAlignment = alignment(text)
|
||||
updateFont(field)
|
||||
// Speed up the process of updating layout, reduce jumping content on screen
|
||||
updateHeight(field)
|
||||
self.height = field.frame.size.height
|
||||
} else {
|
||||
field.text = text
|
||||
}
|
||||
@@ -53,10 +55,12 @@ struct NativeTextEditor: UIViewRepresentable {
|
||||
}
|
||||
|
||||
func updateUIView(_ field: UITextView, context: Context) {
|
||||
field.text = text
|
||||
field.textAlignment = alignment == .leading ? .left : .right
|
||||
updateFont(field)
|
||||
updateHeight(field)
|
||||
if field.markedTextRange == nil && field.text != text {
|
||||
field.text = text
|
||||
field.textAlignment = alignment(text)
|
||||
updateFont(field)
|
||||
updateHeight(field)
|
||||
}
|
||||
}
|
||||
|
||||
private func updateHeight(_ field: UITextView) {
|
||||
@@ -73,12 +77,19 @@ struct NativeTextEditor: UIViewRepresentable {
|
||||
}
|
||||
|
||||
private func updateFont(_ field: UITextView) {
|
||||
field.font = isShortEmoji(field.text)
|
||||
let newFont = isShortEmoji(field.text)
|
||||
? (field.text.count < 4 ? largeEmojiUIFont : mediumEmojiUIFont)
|
||||
: UIFont.preferredFont(forTextStyle: .body)
|
||||
if field.font != newFont {
|
||||
field.font = newFont
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func alignment(_ text: String) -> NSTextAlignment {
|
||||
isRightToLeft(text) ? .right : .left
|
||||
}
|
||||
|
||||
private class CustomUITextField: UITextView, UITextViewDelegate {
|
||||
var height: Binding<CGFloat>
|
||||
var newHeight: CGFloat = 0
|
||||
@@ -205,7 +216,6 @@ struct NativeTextEditor_Previews: PreviewProvider{
|
||||
disableEditing: Binding.constant(false),
|
||||
height: Binding.constant(100),
|
||||
focused: Binding.constant(false),
|
||||
alignment: TextAlignment.leading,
|
||||
onImagesAdded: { _ in }
|
||||
)
|
||||
.fixedSize(horizontal: false, vertical: true)
|
||||
|
||||
@@ -20,6 +20,7 @@ struct SendMessageView: View {
|
||||
var nextSendGrpInv: Bool = false
|
||||
var showVoiceMessageButton: Bool = true
|
||||
var voiceMessageAllowed: Bool = true
|
||||
var disableSendButton = false
|
||||
var showEnableVoiceMessagesAlert: ChatInfo.ShowEnableVoiceMessagesAlert = .other
|
||||
var startVoiceMessageRecording: (() -> Void)? = nil
|
||||
var finishVoiceMessageRecording: (() -> Void)? = nil
|
||||
@@ -53,13 +54,11 @@ struct SendMessageView: View {
|
||||
.padding(.vertical, 8)
|
||||
.frame(maxWidth: .infinity)
|
||||
} else {
|
||||
let alignment: TextAlignment = isRightToLeft(composeState.message) ? .trailing : .leading
|
||||
NativeTextEditor(
|
||||
text: $composeState.message,
|
||||
disableEditing: $composeState.inProgress,
|
||||
height: $teHeight,
|
||||
focused: $keyboardVisible,
|
||||
alignment: alignment,
|
||||
onImagesAdded: onMediaAdded
|
||||
)
|
||||
.allowsTightening(false)
|
||||
@@ -109,6 +108,7 @@ struct SendMessageView: View {
|
||||
} else if showVoiceMessageButton
|
||||
&& composeState.message.isEmpty
|
||||
&& !composeState.editing
|
||||
&& !composeState.forwarding
|
||||
&& composeState.liveMessage == nil
|
||||
&& ((composeState.noPreview && vmrs == .noRecording)
|
||||
|| (vmrs == .recording && holdingVMR)) {
|
||||
@@ -184,7 +184,8 @@ struct SendMessageView: View {
|
||||
!composeState.sendEnabled ||
|
||||
composeState.inProgress ||
|
||||
(!voiceMessageAllowed && composeState.voicePreview) ||
|
||||
composeState.endLiveDisabled
|
||||
composeState.endLiveDisabled ||
|
||||
disableSendButton
|
||||
)
|
||||
.frame(width: 29, height: 29)
|
||||
.contextMenu{
|
||||
@@ -223,7 +224,8 @@ struct SendMessageView: View {
|
||||
|
||||
@ViewBuilder private func sendButtonContextMenuItems() -> some View {
|
||||
if composeState.liveMessage == nil,
|
||||
!composeState.editing {
|
||||
!composeState.editing,
|
||||
!composeState.forwarding {
|
||||
if case .noContextItem = composeState.contextItem,
|
||||
!composeState.voicePreview,
|
||||
let send = sendLiveMessage,
|
||||
|
||||
@@ -62,7 +62,7 @@ struct GroupChatInfoView: View {
|
||||
NavigationView {
|
||||
let members = chatModel.groupMembers
|
||||
.filter { m in let status = m.wrapped.memberStatus; return status != .memLeft && status != .memRemoved }
|
||||
.sorted { $0.displayName.lowercased() < $1.displayName.lowercased() }
|
||||
.sorted { $0.wrapped.memberRole > $1.wrapped.memberRole }
|
||||
|
||||
List {
|
||||
groupInfoHeader()
|
||||
|
||||
@@ -83,7 +83,7 @@ struct GroupMemberInfoView: View {
|
||||
Section {
|
||||
if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) {
|
||||
knownDirectChatButton(chat)
|
||||
} else if groupInfo.fullGroupPreferences.directMessages.on {
|
||||
} else if groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
|
||||
if let contactId = member.memberContactId {
|
||||
newDirectChatButton(contactId)
|
||||
} else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false {
|
||||
@@ -110,7 +110,7 @@ struct GroupMemberInfoView: View {
|
||||
Label("Share address", systemImage: "square.and.arrow.up")
|
||||
}
|
||||
if let contactId = member.memberContactId {
|
||||
if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on {
|
||||
if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
|
||||
connectViaAddressButton(contactLink)
|
||||
}
|
||||
} else {
|
||||
|
||||
@@ -9,6 +9,12 @@
|
||||
import SwiftUI
|
||||
import SimpleXChat
|
||||
|
||||
private let featureRoles: [(role: GroupMemberRole?, text: LocalizedStringKey)] = [
|
||||
(nil, "all members"),
|
||||
(.admin, "admins"),
|
||||
(.owner, "owners")
|
||||
]
|
||||
|
||||
struct GroupPreferencesView: View {
|
||||
@Environment(\.dismiss) var dismiss: DismissAction
|
||||
@EnvironmentObject var chatModel: ChatModel
|
||||
@@ -24,10 +30,12 @@ struct GroupPreferencesView: View {
|
||||
List {
|
||||
featureSection(.timedMessages, $preferences.timedMessages.enable)
|
||||
featureSection(.fullDelete, $preferences.fullDelete.enable)
|
||||
featureSection(.directMessages, $preferences.directMessages.enable)
|
||||
featureSection(.directMessages, $preferences.directMessages.enable, $preferences.directMessages.role)
|
||||
featureSection(.reactions, $preferences.reactions.enable)
|
||||
featureSection(.voice, $preferences.voice.enable)
|
||||
featureSection(.files, $preferences.files.enable)
|
||||
featureSection(.voice, $preferences.voice.enable, $preferences.voice.role)
|
||||
featureSection(.files, $preferences.files.enable, $preferences.files.role)
|
||||
// TODO enable simplexLinks preference in 5.8
|
||||
// featureSection(.simplexLinks, $preferences.simplexLinks.enable, $preferences.simplexLinks.role)
|
||||
featureSection(.history, $preferences.history.enable)
|
||||
|
||||
if groupInfo.canEdit {
|
||||
@@ -64,7 +72,7 @@ struct GroupPreferencesView: View {
|
||||
}
|
||||
}
|
||||
|
||||
private func featureSection(_ feature: GroupFeature, _ enableFeature: Binding<GroupFeatureEnabled>) -> some View {
|
||||
private func featureSection(_ feature: GroupFeature, _ enableFeature: Binding<GroupFeatureEnabled>, _ enableForRole: Binding<GroupMemberRole?>? = nil) -> some View {
|
||||
Section {
|
||||
let color: Color = enableFeature.wrappedValue == .on ? .green : .secondary
|
||||
let icon = enableFeature.wrappedValue == .on ? feature.iconFilled : feature.icon
|
||||
@@ -87,6 +95,16 @@ struct GroupPreferencesView: View {
|
||||
)
|
||||
.frame(height: 36)
|
||||
}
|
||||
if enableFeature.wrappedValue == .on, let enableForRole {
|
||||
Picker("Enabled for", selection: enableForRole) {
|
||||
ForEach(featureRoles, id: \.role) { fr in
|
||||
Text(fr.text)
|
||||
}
|
||||
}
|
||||
.frame(height: 36)
|
||||
// remove in v5.8
|
||||
.disabled(true)
|
||||
}
|
||||
} else {
|
||||
settingsRow(icon, color: color) {
|
||||
infoRow(Text(feature.text), enableFeature.wrappedValue.text)
|
||||
@@ -94,10 +112,26 @@ struct GroupPreferencesView: View {
|
||||
if timedOn {
|
||||
infoRow("Delete after", timeText(preferences.timedMessages.ttl))
|
||||
}
|
||||
if enableFeature.wrappedValue == .on, let enableForRole {
|
||||
HStack {
|
||||
Text("Enabled for").foregroundColor(.secondary)
|
||||
Spacer()
|
||||
Text(
|
||||
featureRoles.first(where: { fr in fr.role == enableForRole.wrappedValue })?.text
|
||||
?? "all members"
|
||||
)
|
||||
.foregroundColor(.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
} footer: {
|
||||
Text(feature.enableDescription(enableFeature.wrappedValue, groupInfo.canEdit))
|
||||
}
|
||||
.onChange(of: enableFeature.wrappedValue) { enabled in
|
||||
if case .off = enabled {
|
||||
enableForRole?.wrappedValue = nil
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private func savePreferences() {
|
||||
|
||||
@@ -92,7 +92,7 @@ struct ChatListNavLink: View {
|
||||
.swipeActions(edge: .leading, allowsFullSwipe: true) {
|
||||
markReadButton()
|
||||
toggleFavoriteButton()
|
||||
toggleNtfsButton(chat)
|
||||
ToggleNtfsButton(chat: chat)
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||
if !chat.chatItems.isEmpty {
|
||||
@@ -181,7 +181,7 @@ struct ChatListNavLink: View {
|
||||
.swipeActions(edge: .leading, allowsFullSwipe: true) {
|
||||
markReadButton()
|
||||
toggleFavoriteButton()
|
||||
toggleNtfsButton(chat)
|
||||
ToggleNtfsButton(chat: chat)
|
||||
}
|
||||
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
|
||||
if !chat.chatItems.isEmpty {
|
||||
|
||||
@@ -448,6 +448,9 @@ struct MigrateToDevice: View {
|
||||
case .rcvFileError:
|
||||
alert = .error(title: "Download failed", error: "File was deleted or link is invalid")
|
||||
migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath)
|
||||
case .chatError(_, .error(.noRcvFileUser)):
|
||||
alert = .error(title: "Download failed", error: "File was deleted or link is invalid")
|
||||
migrationState = .downloadFailed(totalBytes: totalBytes, link: link, archivePath: archivePath)
|
||||
default:
|
||||
logger.debug("unsupported event: \(msg.responseType)")
|
||||
}
|
||||
|
||||
@@ -51,9 +51,10 @@ struct AdvancedNetworkSettings: View {
|
||||
}
|
||||
.disabled(currentNetCfg == NetCfg.proxyDefaults)
|
||||
|
||||
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 45_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("TCP connection timeout", selection: $netCfg.tcpConnectTimeout, values: [5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 40_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout", selection: $netCfg.tcpTimeout, values: [5_000000, 7_000000, 10_000000, 15_000000, 20_000000, 30_000000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [15_000, 30_000, 45_000, 60_000, 90_000, 120_000], label: secondsLabel)
|
||||
timeoutSettingPicker("Protocol timeout per KB", selection: $netCfg.tcpTimeoutPerKb, values: [2_500, 5_000, 10_000, 15_000, 20_000, 30_000], label: secondsLabel)
|
||||
intSettingPicker("Receiving concurrency", selection: $netCfg.rcvConcurrency, values: [1, 2, 4, 8, 12, 16, 24], label: "")
|
||||
timeoutSettingPicker("PING interval", selection: $netCfg.smpPingInterval, values: [120_000000, 300_000000, 600_000000, 1200_000000, 2400_000000, 3600_000000], label: secondsLabel)
|
||||
intSettingPicker("PING count", selection: $netCfg.smpPingCount, values: [1, 2, 3, 5, 8], label: "")
|
||||
Toggle("Enable TCP keep-alive", isOn: $enableKeepAlive)
|
||||
|
||||
@@ -24,6 +24,7 @@ private enum NetworkAlert: Identifiable {
|
||||
}
|
||||
|
||||
struct NetworkAndServers: View {
|
||||
@EnvironmentObject var m: ChatModel
|
||||
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
|
||||
@State private var cfgLoaded = false
|
||||
@State private var currentNetCfg = NetCfg.defaults
|
||||
@@ -82,6 +83,14 @@ struct NetworkAndServers: View {
|
||||
Text("WebRTC ICE servers")
|
||||
}
|
||||
}
|
||||
|
||||
Section("Network connection") {
|
||||
HStack {
|
||||
Text(m.networkInfo.networkType.text)
|
||||
Spacer()
|
||||
Image(systemName: "circle.fill").foregroundColor(m.networkInfo.online ? .green : .red)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
.onAppear {
|
||||
|
||||
@@ -739,6 +739,10 @@
|
||||
<target>Позволи необратимо изтриване на изпратените съобщения. (24 часа)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Позволи изпращане на файлове и медия.</target>
|
||||
@@ -1079,6 +1083,10 @@
|
||||
<target>Файлът не може да бъде получен</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Промени</target>
|
||||
@@ -2073,6 +2081,10 @@ This cannot be undone!</source>
|
||||
<target>Понижи версията и отвори чата</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Неуспешно изтегляне</target>
|
||||
@@ -2183,6 +2195,10 @@ This cannot be undone!</source>
|
||||
<target>Активирай kод за достъп за самоунищожение</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Криптирай</target>
|
||||
@@ -2708,6 +2724,10 @@ This cannot be undone!</source>
|
||||
<target>Файловете и медията са забранени в тази група.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Файловете и медията са забранени!</target>
|
||||
@@ -2773,6 +2793,18 @@ This cannot be undone!</source>
|
||||
<target>За конзолата</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Намерено настолно устройство</target>
|
||||
@@ -2883,6 +2915,10 @@ This cannot be undone!</source>
|
||||
<target>Членовете на групата могат необратимо да изтриват изпратените съобщения. (24 часа)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Членовете на групата могат да изпращат лични съобщения.</target>
|
||||
@@ -3746,6 +3782,10 @@ This is your link for group %@!</source>
|
||||
<target>Мрежа и сървъри</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Мрежови настройки</target>
|
||||
@@ -3856,6 +3896,10 @@ This is your link for group %@!</source>
|
||||
<target>Няма история</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Няма разрешение за запис на гласово съобщение</target>
|
||||
@@ -4070,6 +4114,10 @@ This is your link for group %@!</source>
|
||||
<target>Или покажи този код</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING бройка</target>
|
||||
@@ -4332,6 +4380,10 @@ Error: %@</source>
|
||||
<target>Забрани реакциите на съобщенията.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Забрани изпращането на лични съобщения до членовете.</target>
|
||||
@@ -4462,6 +4514,10 @@ Error: %@</source>
|
||||
<target>Получаващият адрес ще бъде променен към друг сървър. Промяната на адреса ще завърши, след като подателят е онлайн.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Получаващият се файл ще бъде спрян.</target>
|
||||
@@ -4477,6 +4533,10 @@ Error: %@</source>
|
||||
<target>Скорошна история и подобрен [bot за директория за групи](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPd jdLW3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Получателите виждат актуализации, докато ги въвеждате.</target>
|
||||
@@ -4777,11 +4837,19 @@ Error: %@</source>
|
||||
<target>Запази съобщението при посрещане?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Запазените WebRTC ICE сървъри ще бъдат премахнати</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Запазено съобщение</target>
|
||||
@@ -5205,6 +5273,14 @@ Error: %@</source>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX линкове</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6043,6 +6119,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Гласовите съобщения са забранени в тази група.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Гласовите съобщения са забранени!</target>
|
||||
@@ -6123,6 +6203,14 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Когато споделяте инкогнито профил с някого, този профил ще се използва за групите, в които той ви кани.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>С криптирани файлове и медия.</target>
|
||||
@@ -6605,6 +6693,10 @@ SimpleX сървърите не могат да видят вашия профи
|
||||
<target>админ</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>съгласуване на криптиране за %@…</target>
|
||||
@@ -6615,6 +6707,10 @@ SimpleX сървърите не могат да видят вашия профи
|
||||
<target>съгласуване на криптиране…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>винаги</target>
|
||||
@@ -6945,6 +7041,10 @@ SimpleX сървърите не могат да видят вашия профи
|
||||
<target>събитие се случи</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>групата е изтрита</target>
|
||||
@@ -7152,6 +7252,10 @@ SimpleX сървърите не могат да видят вашия профи
|
||||
<target>собственик</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>peer-to-peer</target>
|
||||
@@ -7202,6 +7306,14 @@ SimpleX сървърите не могат да видят вашия профи
|
||||
<target>ви острани</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>сек.</target>
|
||||
@@ -7342,6 +7454,10 @@ SimpleX сървърите не могат да видят вашия профи
|
||||
<target>да</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>вие сте поканени в групата</target>
|
||||
|
||||
@@ -720,6 +720,10 @@
|
||||
<target>Povolit nevratné smazání odeslaných zpráv. (24 hodin)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Povolit odesílání souborů a médii.</target>
|
||||
@@ -1043,6 +1047,10 @@
|
||||
<target>Nelze přijmout soubor</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Změnit</target>
|
||||
@@ -1999,6 +2007,10 @@ This cannot be undone!</source>
|
||||
<target>Snížit a otevřít chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2104,6 +2116,10 @@ This cannot be undone!</source>
|
||||
<target>Povolit sebedestrukční heslo</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Šifrovat</target>
|
||||
@@ -2611,6 +2627,10 @@ This cannot be undone!</source>
|
||||
<target>Soubory a média jsou zakázány v této skupině.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Soubory a média jsou zakázány!</target>
|
||||
@@ -2674,6 +2694,18 @@ This cannot be undone!</source>
|
||||
<target>Pro konzoli</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2780,6 +2812,10 @@ This cannot be undone!</source>
|
||||
<target>Členové skupiny mohou nevratně mazat odeslané zprávy. (24 hodin)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Členové skupiny mohou posílat přímé zprávy.</target>
|
||||
@@ -3609,6 +3645,10 @@ This is your link for group %@!</source>
|
||||
<target>Síť a servery</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Nastavení sítě</target>
|
||||
@@ -3718,6 +3758,10 @@ This is your link for group %@!</source>
|
||||
<target>Žádná historie</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Nemáte oprávnění nahrávat hlasové zprávy</target>
|
||||
@@ -3923,6 +3967,10 @@ This is your link for group %@!</source>
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>Počet PING</target>
|
||||
@@ -4173,6 +4221,10 @@ Error: %@</source>
|
||||
<target>Zakázat reakce na zprávy.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Zakázat odesílání přímých zpráv členům.</target>
|
||||
@@ -4300,6 +4352,10 @@ Error: %@</source>
|
||||
<target>Přijímací adresa bude změněna na jiný server. Změna adresy bude dokončena po připojení odesílatele.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Příjem souboru bude zastaven.</target>
|
||||
@@ -4314,6 +4370,10 @@ Error: %@</source>
|
||||
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Příjemci uvidí aktualizace během jejich psaní.</target>
|
||||
@@ -4607,11 +4667,19 @@ Error: %@</source>
|
||||
<target>Uložit uvítací zprávu?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Uložené servery WebRTC ICE budou odstraněny</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<note>message info title</note>
|
||||
@@ -5026,6 +5094,14 @@ Error: %@</source>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>Odkazy na SimpleX</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -5828,6 +5904,10 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
|
||||
<target>Hlasové zprávy jsou v této skupině zakázány.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Hlasové zprávy jsou zakázány!</target>
|
||||
@@ -5905,6 +5985,14 @@ Chcete-li se připojit, požádejte svůj kontakt o vytvoření dalšího odkazu
|
||||
<target>Pokud s někým sdílíte inkognito profil, bude tento profil použit pro skupiny, do kterých vás pozve.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -6368,6 +6456,10 @@ Servery SimpleX nevidí váš profil.</target>
|
||||
<target>správce</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>povoluji šifrování pro %@…</target>
|
||||
@@ -6378,6 +6470,10 @@ Servery SimpleX nevidí váš profil.</target>
|
||||
<target>povoluji šifrování…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>vždy</target>
|
||||
@@ -6700,6 +6796,10 @@ Servery SimpleX nevidí váš profil.</target>
|
||||
<source>event happened</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>skupina smazána</target>
|
||||
@@ -6906,6 +7006,10 @@ Servery SimpleX nevidí váš profil.</target>
|
||||
<target>vlastník</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>peer-to-peer</target>
|
||||
@@ -6953,6 +7057,14 @@ Servery SimpleX nevidí váš profil.</target>
|
||||
<target>odstranil vás</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sek</target>
|
||||
@@ -7086,6 +7198,10 @@ Servery SimpleX nevidí váš profil.</target>
|
||||
<target>ano</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>jste pozváni do skupiny</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Unwiederbringliches löschen von gesendeten Nachrichten erlauben. (24 Stunden)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Das Senden von Dateien und Medien erlauben.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>Datei kann nicht empfangen werden</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Ändern</target>
|
||||
@@ -2078,6 +2086,10 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Datenbank herabstufen und den Chat öffnen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Herunterladen fehlgeschlagen</target>
|
||||
@@ -2188,6 +2200,10 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Selbstzerstörungs-Zugangscode aktivieren</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Verschlüsseln</target>
|
||||
@@ -2713,6 +2729,10 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>In dieser Gruppe sind Dateien und Medien nicht erlaubt.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Dateien und Medien sind nicht erlaubt!</target>
|
||||
@@ -2778,6 +2798,18 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Für Konsole</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Gefundener Desktop</target>
|
||||
@@ -2888,6 +2920,10 @@ Das kann nicht rückgängig gemacht werden!</target>
|
||||
<target>Gruppenmitglieder können gesendete Nachrichten unwiederbringlich löschen. (24 Stunden)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Gruppenmitglieder können Direktnachrichten versenden.</target>
|
||||
@@ -3753,6 +3789,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Netzwerk & Server</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Netzwerkeinstellungen</target>
|
||||
@@ -3863,6 +3903,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Kein Nachrichtenverlauf</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Keine Berechtigung für das Aufnehmen von Sprachnachrichten</target>
|
||||
@@ -4077,6 +4121,10 @@ Das ist Ihr Link für die Gruppe %@!</target>
|
||||
<target>Oder diesen QR-Code anzeigen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING-Zähler</target>
|
||||
@@ -4339,6 +4387,10 @@ Fehler: %@</target>
|
||||
<target>Reaktionen auf Nachrichten nicht erlauben.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Das Senden von Direktnachrichten an Gruppenmitglieder nicht erlauben.</target>
|
||||
@@ -4469,6 +4521,10 @@ Fehler: %@</target>
|
||||
<target>Die Empfängeradresse wird auf einen anderen Server geändert. Der Adresswechsel wird abgeschlossen, wenn der Absender wieder online ist.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Der Empfang der Datei wird beendet.</target>
|
||||
@@ -4484,6 +4540,10 @@ Fehler: %@</target>
|
||||
<target>Aktueller Nachrichtenverlauf und verbesserter [Gruppenverzeichnis-Bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Die Empfänger sehen Nachrichtenaktualisierungen, während Sie sie eingeben.</target>
|
||||
@@ -4784,11 +4844,19 @@ Fehler: %@</target>
|
||||
<target>Begrüßungsmeldung speichern?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Gespeicherte WebRTC ICE-Server werden entfernt</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Gespeicherte Nachricht</target>
|
||||
@@ -5212,6 +5280,14 @@ Fehler: %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX-Links</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6126,10 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
|
||||
<target>In dieser Gruppe sind Sprachnachrichten nicht erlaubt.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Sprachnachrichten sind nicht erlaubt!</target>
|
||||
@@ -6130,6 +6210,14 @@ Bitten Sie Ihren Kontakt darum einen weiteren Verbindungs-Link zu erzeugen, um s
|
||||
<target>Wenn Sie ein Inkognito-Profil mit Jemandem teilen, wird dieses Profil auch für die Gruppen verwendet, für die Sie von diesem Kontakt eingeladen werden.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>Mit verschlüsselten Dateien und Medien.</target>
|
||||
@@ -6613,6 +6701,10 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
|
||||
<target>Admin</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>Verschlüsselung von %@ zustimmen…</target>
|
||||
@@ -6623,6 +6715,10 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
|
||||
<target>Verschlüsselung zustimmen…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>Immer</target>
|
||||
@@ -6953,6 +7049,10 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
|
||||
<target>event happened</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>Gruppe gelöscht</target>
|
||||
@@ -7160,6 +7260,10 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
|
||||
<target>Eigentümer</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>Peer-to-Peer</target>
|
||||
@@ -7210,6 +7314,14 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
|
||||
<target>hat Sie aus der Gruppe entfernt</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sek</target>
|
||||
@@ -7350,6 +7462,10 @@ SimpleX-Server können Ihr Profil nicht einsehen.</target>
|
||||
<target>Ja</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>Sie sind zu der Gruppe eingeladen</target>
|
||||
|
||||
@@ -743,6 +743,11 @@
|
||||
<target>Allow to irreversibly delete sent messages. (24 hours)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<target>Allow to send SimpleX links.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Allow to send files and media.</target>
|
||||
@@ -1083,6 +1088,11 @@
|
||||
<target>Cannot receive file</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<target>Cellular</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Change</target>
|
||||
@@ -2078,6 +2088,11 @@ This cannot be undone!</target>
|
||||
<target>Downgrade and open chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<target>Download</target>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Download failed</target>
|
||||
@@ -2188,6 +2203,11 @@ This cannot be undone!</target>
|
||||
<target>Enable self-destruct passcode</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<target>Enabled for</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Encrypt</target>
|
||||
@@ -2713,6 +2733,11 @@ This cannot be undone!</target>
|
||||
<target>Files and media are prohibited in this group.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<target>Files and media not allowed</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Files and media prohibited!</target>
|
||||
@@ -2778,6 +2803,21 @@ This cannot be undone!</target>
|
||||
<target>For console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<target>Forward</target>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<target>Forwarded</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<target>Forwarded from</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Found desktop</target>
|
||||
@@ -2888,6 +2928,11 @@ This cannot be undone!</target>
|
||||
<target>Group members can irreversibly delete sent messages. (24 hours)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<target>Group members can send SimpleX links.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Group members can send direct messages.</target>
|
||||
@@ -3753,6 +3798,11 @@ This is your link for group %@!</target>
|
||||
<target>Network & servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<target>Network connection</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Network settings</target>
|
||||
@@ -3863,6 +3913,11 @@ This is your link for group %@!</target>
|
||||
<target>No history</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<target>No network connection</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>No permission to record voice message</target>
|
||||
@@ -4077,6 +4132,11 @@ This is your link for group %@!</target>
|
||||
<target>Or show this code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<target>Other</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING count</target>
|
||||
@@ -4339,6 +4399,11 @@ Error: %@</target>
|
||||
<target>Prohibit messages reactions.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<target>Prohibit sending SimpleX links.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Prohibit sending direct messages to members.</target>
|
||||
@@ -4469,6 +4534,11 @@ Error: %@</target>
|
||||
<target>Receiving address will be changed to a different server. Address change will complete after sender comes online.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<target>Receiving concurrency</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Receiving file will be stopped.</target>
|
||||
@@ -4484,6 +4554,11 @@ Error: %@</target>
|
||||
<target>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<target>Recipient(s) can't see who this message is from.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Recipients see updates as you type them.</target>
|
||||
@@ -4784,11 +4859,21 @@ Error: %@</target>
|
||||
<target>Save welcome message?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<target>Saved</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Saved WebRTC ICE servers will be removed</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<target>Saved from</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Saved message</target>
|
||||
@@ -5212,6 +5297,16 @@ Error: %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX links</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<target>SimpleX links are prohibited in this group.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<target>SimpleX links not allowed</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6145,11 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Voice messages are prohibited in this group.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<target>Voice messages not allowed</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Voice messages prohibited!</target>
|
||||
@@ -6130,6 +6230,16 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>When you share an incognito profile with somebody, this profile will be used for the groups they invite you to.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<target>WiFi</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<target>Wired ethernet</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>With encrypted files and media.</target>
|
||||
@@ -6613,6 +6723,11 @@ SimpleX servers cannot see your profile.</target>
|
||||
<target>admin</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<target>admins</target>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>agreeing encryption for %@…</target>
|
||||
@@ -6623,6 +6738,11 @@ SimpleX servers cannot see your profile.</target>
|
||||
<target>agreeing encryption…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<target>all members</target>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>always</target>
|
||||
@@ -6953,6 +7073,11 @@ SimpleX servers cannot see your profile.</target>
|
||||
<target>event happened</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<target>forwarded</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>group deleted</target>
|
||||
@@ -7160,6 +7285,11 @@ SimpleX servers cannot see your profile.</target>
|
||||
<target>owner</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<target>owners</target>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>peer-to-peer</target>
|
||||
@@ -7210,6 +7340,16 @@ SimpleX servers cannot see your profile.</target>
|
||||
<target>removed you</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<target>saved</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<target>saved from %@</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sec</target>
|
||||
@@ -7350,6 +7490,11 @@ SimpleX servers cannot see your profile.</target>
|
||||
<target>yes</target>
|
||||
<note>pref value</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 invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>you are invited to group</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Se permite la eliminación irreversible de mensajes. (24 horas)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Se permite enviar archivos y multimedia.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>No se puede recibir el archivo</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Cambiar</target>
|
||||
@@ -2078,6 +2086,10 @@ This cannot be undone!</source>
|
||||
<target>Degradar y abrir Chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Error en la descarga</target>
|
||||
@@ -2188,6 +2200,10 @@ This cannot be undone!</source>
|
||||
<target>Activar código de autodestrucción</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Cifrar</target>
|
||||
@@ -2713,6 +2729,10 @@ This cannot be undone!</source>
|
||||
<target>No se permiten archivos y multimedia en este grupo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>¡Archivos y multimedia no permitidos!</target>
|
||||
@@ -2778,6 +2798,18 @@ This cannot be undone!</source>
|
||||
<target>Para consola</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Ordenador encontrado</target>
|
||||
@@ -2888,6 +2920,10 @@ This cannot be undone!</source>
|
||||
<target>Los miembros del grupo pueden eliminar mensajes de forma irreversible. (24 horas)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Los miembros del grupo pueden enviar mensajes directos.</target>
|
||||
@@ -3753,6 +3789,10 @@ This is your link for group %@!</source>
|
||||
<target>Servidores y Redes</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Configuración de red</target>
|
||||
@@ -3863,6 +3903,10 @@ This is your link for group %@!</source>
|
||||
<target>Sin historial</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Sin permiso para grabar mensajes de voz</target>
|
||||
@@ -4077,6 +4121,10 @@ This is your link for group %@!</source>
|
||||
<target>O mostrar este código</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>Contador PING</target>
|
||||
@@ -4339,6 +4387,10 @@ Error: %@</target>
|
||||
<target>No se permiten reacciones a los mensajes.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>No se permiten mensajes directos entre miembros.</target>
|
||||
@@ -4469,6 +4521,10 @@ Error: %@</target>
|
||||
<target>La dirección de recepción pasará a otro servidor. El cambio se completará cuando el remitente esté en línea.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Se detendrá la recepción del archivo.</target>
|
||||
@@ -4484,6 +4540,10 @@ Error: %@</target>
|
||||
<target>Historial reciente y [bot del directorio](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) mejorados.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Los destinatarios ven actualizarse mientras escribes.</target>
|
||||
@@ -4784,11 +4844,19 @@ Error: %@</target>
|
||||
<target>¿Guardar mensaje de bienvenida?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Los servidores WebRTC ICE guardados serán eliminados</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Mensaje guardado</target>
|
||||
@@ -5212,6 +5280,14 @@ Error: %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>Enlaces SimpleX</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6051,6 +6127,10 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
|
||||
<target>Los mensajes de voz no están permitidos en este grupo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>¡Mensajes de voz no permitidos!</target>
|
||||
@@ -6131,6 +6211,14 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
|
||||
<target>Cuando compartes un perfil incógnito con alguien, este perfil también se usará para los grupos a los que te inviten.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>Con cifrado de archivos y multimedia.</target>
|
||||
@@ -6614,6 +6702,10 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
|
||||
<target>administrador</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>acordando cifrado para %@…</target>
|
||||
@@ -6624,6 +6716,10 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
|
||||
<target>acordando cifrado…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>siempre</target>
|
||||
@@ -6954,6 +7050,10 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
|
||||
<target>evento ocurrido</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>grupo eliminado</target>
|
||||
@@ -7161,6 +7261,10 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
|
||||
<target>propietario</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>p2p</target>
|
||||
@@ -7211,6 +7315,14 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
|
||||
<target>te ha expulsado</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>seg</target>
|
||||
@@ -7351,6 +7463,10 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
|
||||
<target>sí</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>has sido invitado a un grupo</target>
|
||||
|
||||
@@ -715,6 +715,10 @@
|
||||
<target>Salli lähetettyjen viestien peruuttamaton poistaminen. (24 tuntia)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Salli tiedostojen ja median lähettäminen.</target>
|
||||
@@ -1036,6 +1040,10 @@
|
||||
<target>Tiedostoa ei voi vastaanottaa</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Muuta</target>
|
||||
@@ -1992,6 +2000,10 @@ This cannot be undone!</source>
|
||||
<target>Alenna ja avaa keskustelu</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2097,6 +2109,10 @@ This cannot be undone!</source>
|
||||
<target>Ota itsetuhoava pääsykoodi käyttöön</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Salaa</target>
|
||||
@@ -2601,6 +2617,10 @@ This cannot be undone!</source>
|
||||
<target>Tiedostot ja media ovat tässä ryhmässä kiellettyjä.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Tiedostot ja media kielletty!</target>
|
||||
@@ -2664,6 +2684,18 @@ This cannot be undone!</source>
|
||||
<target>Konsoliin</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2770,6 +2802,10 @@ This cannot be undone!</source>
|
||||
<target>Ryhmän jäsenet voivat poistaa lähetetyt viestit peruuttamattomasti. (24 tuntia)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Ryhmän jäsenet voivat lähettää suoraviestejä.</target>
|
||||
@@ -3599,6 +3635,10 @@ This is your link for group %@!</source>
|
||||
<target>Verkko ja palvelimet</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Verkkoasetukset</target>
|
||||
@@ -3707,6 +3747,10 @@ This is your link for group %@!</source>
|
||||
<target>Ei historiaa</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Ei lupaa ääniviestin tallentamiseen</target>
|
||||
@@ -3911,6 +3955,10 @@ This is your link for group %@!</source>
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING-määrä</target>
|
||||
@@ -4161,6 +4209,10 @@ Error: %@</source>
|
||||
<target>Estä viestireaktiot.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Estä suorien viestien lähettäminen jäsenille.</target>
|
||||
@@ -4288,6 +4340,10 @@ Error: %@</source>
|
||||
<target>Vastaanotto-osoite vaihdetaan toiseen palvelimeen. Osoitteenmuutos tehdään sen jälkeen, kun lähettäjä tulee verkkoon.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Tiedoston vastaanotto pysäytetään.</target>
|
||||
@@ -4302,6 +4358,10 @@ Error: %@</source>
|
||||
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Vastaanottajat näkevät päivitykset, kun kirjoitat niitä.</target>
|
||||
@@ -4595,11 +4655,19 @@ Error: %@</source>
|
||||
<target>Tallenna tervetuloviesti?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Tallennetut WebRTC ICE -palvelimet poistetaan</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<note>message info title</note>
|
||||
@@ -5013,6 +5081,14 @@ Error: %@</source>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX-linkit</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -5813,6 +5889,10 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
|
||||
<target>Ääniviestit ovat kiellettyjä tässä ryhmässä.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Ääniviestit kielletty!</target>
|
||||
@@ -5890,6 +5970,14 @@ Jos haluat muodostaa yhteyden, pyydä kontaktiasi luomaan toinen yhteyslinkki ja
|
||||
<target>Kun jaat inkognitoprofiilin jonkun kanssa, tätä profiilia käytetään ryhmissä, joihin tämä sinut kutsuu.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -6353,6 +6441,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
|
||||
<target>ylläpitäjä</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>salauksesta sovitaan %@:lle…</target>
|
||||
@@ -6363,6 +6455,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
|
||||
<target>hyväksyy salausta…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>aina</target>
|
||||
@@ -6685,6 +6781,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
|
||||
<target>tapahtuma tapahtui</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>ryhmä poistettu</target>
|
||||
@@ -6891,6 +6991,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
|
||||
<target>omistaja</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>vertais</target>
|
||||
@@ -6938,6 +7042,14 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
|
||||
<target>poisti sinut</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sek</target>
|
||||
@@ -7070,6 +7182,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
|
||||
<target>kyllä</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>sinut on kutsuttu ryhmään</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Autoriser la suppression irréversible de messages envoyés. (24 heures)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Permet l'envoi de fichiers et de médias.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>Impossible de recevoir le fichier</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Changer</target>
|
||||
@@ -2078,6 +2086,10 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Rétrograder et ouvrir le chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Échec du téléchargement</target>
|
||||
@@ -2188,6 +2200,10 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Activer le code d'autodestruction</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Chiffrer</target>
|
||||
@@ -2713,6 +2729,10 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Les fichiers et les médias sont interdits dans ce groupe.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Fichiers et médias interdits !</target>
|
||||
@@ -2778,6 +2798,18 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Pour la console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Bureau trouvé</target>
|
||||
@@ -2888,6 +2920,10 @@ Cette opération ne peut être annulée !</target>
|
||||
<target>Les membres du groupe peuvent supprimer de manière irréversible les messages envoyés. (24 heures)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Les membres du groupe peuvent envoyer des messages directs.</target>
|
||||
@@ -3753,6 +3789,10 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Réseau et serveurs</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Paramètres réseau</target>
|
||||
@@ -3863,6 +3903,10 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Aucun historique</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Pas l'autorisation d'enregistrer un message vocal</target>
|
||||
@@ -4077,6 +4121,10 @@ Voici votre lien pour le groupe %@ !</target>
|
||||
<target>Ou présenter ce code</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>Nombre de PING</target>
|
||||
@@ -4339,6 +4387,10 @@ Erreur : %@</target>
|
||||
<target>Interdire les réactions aux messages.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Interdire l'envoi de messages directs aux membres.</target>
|
||||
@@ -4469,6 +4521,10 @@ Erreur : %@</target>
|
||||
<target>L'adresse de réception sera changée pour un autre serveur. Le changement d'adresse sera terminé lorsque l'expéditeur sera en ligne.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>La réception du fichier sera interrompue.</target>
|
||||
@@ -4484,6 +4540,10 @@ Erreur : %@</target>
|
||||
<target>Historique récent et amélioration du [bot annuaire](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Les destinataires voient les mises à jour au fur et à mesure que vous leur écrivez.</target>
|
||||
@@ -4784,11 +4844,19 @@ Erreur : %@</target>
|
||||
<target>Enregistrer le message d'accueil ?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Les serveurs WebRTC ICE sauvegardés seront supprimés</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Message enregistré</target>
|
||||
@@ -5212,6 +5280,14 @@ Erreur : %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>Liens SimpleX</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6126,10 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
|
||||
<target>Les messages vocaux sont interdits dans ce groupe.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Messages vocaux interdits !</target>
|
||||
@@ -6130,6 +6210,14 @@ Pour vous connecter, veuillez demander à votre contact de créer un autre lien
|
||||
<target>Lorsque vous partagez un profil incognito avec quelqu'un, ce profil sera utilisé pour les groupes auxquels il vous invite.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>Avec les fichiers et les médias chiffrés.</target>
|
||||
@@ -6613,6 +6701,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
<target>admin</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>négociation du chiffrement avec %@…</target>
|
||||
@@ -6623,6 +6715,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
<target>négociation du chiffrement…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>toujours</target>
|
||||
@@ -6953,6 +7049,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
<target>event happened</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>groupe supprimé</target>
|
||||
@@ -7160,6 +7260,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
<target>propriétaire</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>pair-à-pair</target>
|
||||
@@ -7210,6 +7314,14 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
<target>vous a retiré</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sec</target>
|
||||
@@ -7350,6 +7462,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
|
||||
<target>oui</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>vous êtes invité·e au groupe</target>
|
||||
|
||||
@@ -737,6 +737,10 @@
|
||||
<target>Elküldött üzenetek visszafordíthatatlan törlésének engedélyezése. (24 óra)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Fájlok és médiatartalom küldésének engedélyezése.</target>
|
||||
@@ -1072,6 +1076,10 @@
|
||||
<target>Nem lehet fogadni a fájlt</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Változtatás</target>
|
||||
@@ -2060,6 +2068,10 @@ Ezt nem vonható vissza!</target>
|
||||
<target>Visszatérés a korábbi verzióra és a csevegés megnyitása</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2166,6 +2178,10 @@ Ezt nem vonható vissza!</target>
|
||||
<target>Önmegsemmisítő jelkód engedélyezése</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Titkosít</target>
|
||||
@@ -2684,6 +2700,10 @@ Ezt nem vonható vissza!</target>
|
||||
<target>A fájlok- és a médiatartalom küldése le van tiltva ebben a csoportban.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>A fájlok- és a médiatartalom küldése le van tiltva!</target>
|
||||
@@ -2747,6 +2767,18 @@ Ezt nem vonható vissza!</target>
|
||||
<target>Konzolhoz</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Megtalált számítógép</target>
|
||||
@@ -2857,6 +2889,10 @@ Ezt nem vonható vissza!</target>
|
||||
<target>Csoporttagok visszafordíthatatlanul törölhetik az elküldött üzeneteket. (24 óra)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Csoporttagok küldhetnek közvetlen üzeneteket.</target>
|
||||
@@ -3707,6 +3743,10 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
|
||||
<target>Hálózat és kiszolgálók</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Hálózati beállítások</target>
|
||||
@@ -3817,6 +3857,10 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
|
||||
<target>Nincsenek előzmények</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Nincs engedély a hangüzenet rögzítésére</target>
|
||||
@@ -4028,6 +4072,10 @@ Ez az ön hivatkozása a(z) %@ csoporthoz!</target>
|
||||
<target>Vagy mutassa meg ezt a kódot</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING számláló</target>
|
||||
@@ -4287,6 +4335,10 @@ Hiba: %@</target>
|
||||
<target>Az üzenetreakciók tiltása.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Közvetlen üzenetek küldésének letiltása tagok részére.</target>
|
||||
@@ -4415,6 +4467,10 @@ Hiba: %@</target>
|
||||
<target>A fogadó cím egy másik kiszolgálóra változik. A címváltoztatás a feladó online állapotba kerülése után fejeződik be.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>A fájl fogadása leállt.</target>
|
||||
@@ -4430,6 +4486,10 @@ Hiba: %@</target>
|
||||
<target>Legutóbbi előzmények és továbbfejlesztett [könyvtárbot] (simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2TxW3dfMfxy 3%23%2F%3Fv%3D1-2% 26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gloncbqjek4gloncbqjek.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>A címzettek a beírás közben látják a frissítéseket.</target>
|
||||
@@ -4726,11 +4786,19 @@ Hiba: %@</target>
|
||||
<target>Üdvözlőszöveg mentése?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>A mentett WebRTC ICE kiszolgálók eltávolításra kerülnek</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Mentett üzenet</target>
|
||||
@@ -5152,6 +5220,14 @@ Hiba: %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX hivatkozások</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -5981,6 +6057,10 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola
|
||||
<target>A hangüzenetek küldése le van tiltva ebben a csoportban.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>A hangüzenetek le vannak tilva!</target>
|
||||
@@ -6059,6 +6139,14 @@ A csatlakozáshoz kérje meg ismerősét, hogy hozzon létre egy másik kapcsola
|
||||
<target>Inkognitóprofil megosztása esetén a rendszer azt a profilt fogja használni azokhoz a csoportokhoz, amelyekbe meghívást kapott.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>Titkosított fájlokkal és médiatartalommal.</target>
|
||||
@@ -6540,6 +6628,10 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
|
||||
<target>admin</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>titkosítás jóváhagyása %@ számára…</target>
|
||||
@@ -6550,6 +6642,10 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
|
||||
<target>titkosítás elfogadása…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>mindig</target>
|
||||
@@ -6880,6 +6976,10 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
|
||||
<target>esemény történt</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>a csoport törölve</target>
|
||||
@@ -7087,6 +7187,10 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
|
||||
<target>tulajdonos</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>ponttól-pontig</target>
|
||||
@@ -7136,6 +7240,14 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
|
||||
<target>eltávolítottak</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>mp</target>
|
||||
@@ -7275,6 +7387,10 @@ A SimpleX kiszolgálók nem látjhatják profilját.</target>
|
||||
<target>igen</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>meghívást kapott a csoportba</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Permetti di eliminare irreversibilmente i messaggi inviati. (24 ore)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Consenti l'invio di file e contenuti multimediali.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>Impossibile ricevere il file</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Cambia</target>
|
||||
@@ -2078,6 +2086,10 @@ Non è reversibile!</target>
|
||||
<target>Esegui downgrade e apri chat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Scaricamento fallito</target>
|
||||
@@ -2188,6 +2200,10 @@ Non è reversibile!</target>
|
||||
<target>Attiva il codice di autodistruzione</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Crittografare</target>
|
||||
@@ -2713,6 +2729,10 @@ Non è reversibile!</target>
|
||||
<target>File e contenuti multimediali sono vietati in questo gruppo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>File e contenuti multimediali vietati!</target>
|
||||
@@ -2778,6 +2798,18 @@ Non è reversibile!</target>
|
||||
<target>Per console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Desktop trovato</target>
|
||||
@@ -2888,6 +2920,10 @@ Non è reversibile!</target>
|
||||
<target>I membri del gruppo possono eliminare irreversibilmente i messaggi inviati. (24 ore)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>I membri del gruppo possono inviare messaggi diretti.</target>
|
||||
@@ -3753,6 +3789,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Rete e server</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Impostazioni di rete</target>
|
||||
@@ -3863,6 +3903,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>Nessuna cronologia</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Nessuna autorizzazione per registrare messaggi vocali</target>
|
||||
@@ -4077,6 +4121,10 @@ Questo è il tuo link per il gruppo %@!</target>
|
||||
<target>O mostra questo codice</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>Conteggio PING</target>
|
||||
@@ -4339,6 +4387,10 @@ Errore: %@</target>
|
||||
<target>Proibisci le reazioni ai messaggi.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Proibisci l'invio di messaggi diretti ai membri.</target>
|
||||
@@ -4469,6 +4521,10 @@ Errore: %@</target>
|
||||
<target>L'indirizzo di ricezione verrà cambiato in un server diverso. La modifica dell'indirizzo verrà completata dopo che il mittente sarà in linea.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>La ricezione del file verrà interrotta.</target>
|
||||
@@ -4484,6 +4540,10 @@ Errore: %@</target>
|
||||
<target>Cronologia recente e [bot della directory](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion) migliorato.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>I destinatari vedono gli aggiornamenti mentre li digiti.</target>
|
||||
@@ -4784,11 +4844,19 @@ Errore: %@</target>
|
||||
<target>Salvare il messaggio di benvenuto?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>I server WebRTC ICE salvati verranno rimossi</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Messaggio salvato</target>
|
||||
@@ -5212,6 +5280,14 @@ Errore: %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>Link di SimpleX</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6126,10 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
|
||||
<target>I messaggi vocali sono vietati in questo gruppo.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Messaggi vocali vietati!</target>
|
||||
@@ -6130,6 +6210,14 @@ Per connetterti, chiedi al tuo contatto di creare un altro link di connessione e
|
||||
<target>Quando condividi un profilo in incognito con qualcuno, questo profilo verrà utilizzato per i gruppi a cui ti invitano.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>Con file e multimediali criptati.</target>
|
||||
@@ -6613,6 +6701,10 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
<target>amministratore</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>concordando la crittografia per %@…</target>
|
||||
@@ -6623,6 +6715,10 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
<target>concordando la crittografia…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>sempre</target>
|
||||
@@ -6953,6 +7049,10 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
<target>evento accaduto</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>gruppo eliminato</target>
|
||||
@@ -7160,6 +7260,10 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
<target>proprietario</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>peer-to-peer</target>
|
||||
@@ -7210,6 +7314,14 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
<target>ti ha rimosso/a</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sec</target>
|
||||
@@ -7350,6 +7462,10 @@ I server di SimpleX non possono vedere il tuo profilo.</target>
|
||||
<target>sì</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>sei stato/a invitato/a al gruppo</target>
|
||||
|
||||
@@ -719,6 +719,10 @@
|
||||
<target>送信済みメッセージの永久削除を許可する。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>ファイルやメディアの送信を許可する。</target>
|
||||
@@ -1042,6 +1046,10 @@
|
||||
<target>ファイル受信ができません</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>変更</target>
|
||||
@@ -1998,6 +2006,10 @@ This cannot be undone!</source>
|
||||
<target>ダウングレードしてチャットを開く</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2103,6 +2115,10 @@ This cannot be undone!</source>
|
||||
<target>自己破壊パスコードを有効にする</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>暗号化する</target>
|
||||
@@ -2608,6 +2624,10 @@ This cannot be undone!</source>
|
||||
<target>このグループでは、ファイルとメディアは禁止されています。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>ファイルとメディアは禁止されています!</target>
|
||||
@@ -2671,6 +2691,18 @@ This cannot be undone!</source>
|
||||
<target>コンソール</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2777,6 +2809,10 @@ This cannot be undone!</source>
|
||||
<target>グループのメンバーがメッセージを完全削除することができます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>グループのメンバーがダイレクトメッセージを送信できます。</target>
|
||||
@@ -3605,6 +3641,10 @@ This is your link for group %@!</source>
|
||||
<target>ネットワークとサーバ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>ネットワーク設定</target>
|
||||
@@ -3714,6 +3754,10 @@ This is your link for group %@!</source>
|
||||
<target>履歴はありません</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>音声メッセージを録音する権限がありません</target>
|
||||
@@ -3919,6 +3963,10 @@ This is your link for group %@!</source>
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING回数</target>
|
||||
@@ -4169,6 +4217,10 @@ Error: %@</source>
|
||||
<target>メッセージへのリアクションは禁止されています。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>メンバー間のダイレクトメッセージを使用禁止にする。</target>
|
||||
@@ -4295,6 +4347,10 @@ Error: %@</source>
|
||||
<target>開発中の機能です!相手のクライアントが4.2でなければ機能しません。アドレス変更が完了すると、会話にメッセージが出ます。連絡相手 (またはグループのメンバー) からメッセージを受信できないかをご確認ください。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>ファイルの受信を停止します。</target>
|
||||
@@ -4309,6 +4365,10 @@ Error: %@</source>
|
||||
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>受信者には、入力時に更新内容が表示されます。</target>
|
||||
@@ -4602,11 +4662,19 @@ Error: %@</source>
|
||||
<target>ウェルカムメッセージを保存しますか?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>保存されたWebRTC ICEサーバは削除されます</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<note>message info title</note>
|
||||
@@ -5013,6 +5081,14 @@ Error: %@</source>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleXリンク</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -5813,6 +5889,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>このグループでは音声メッセージが使用禁止です。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>音声メッセージは使用禁止です!</target>
|
||||
@@ -5890,6 +5970,14 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>連絡相手にシークレットモードのプロフィールを共有すると、その連絡相手に招待されたグループでも同じプロフィールが使われます。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -6353,6 +6441,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
<target>管理者</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>%@の暗号化に同意しています…</target>
|
||||
@@ -6363,6 +6455,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
<target>暗号化に同意しています…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>常に</target>
|
||||
@@ -6685,6 +6781,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
<target>イベント発生</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>グループ削除済み</target>
|
||||
@@ -6891,6 +6991,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
<target>オーナー</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>P2P</target>
|
||||
@@ -6938,6 +7042,14 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
<target>あなたを除名しました</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>秒</target>
|
||||
@@ -7070,6 +7182,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
|
||||
<target>はい</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>グループ招待が届きました</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Sta toe om verzonden berichten onomkeerbaar te verwijderen. (24 uur)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Sta toe om bestanden en media te verzenden.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>Kan bestand niet ontvangen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Veranderen</target>
|
||||
@@ -2078,6 +2086,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Downgraden en chat openen</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Download mislukt</target>
|
||||
@@ -2188,6 +2200,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Zelfvernietigings wachtwoord inschakelen</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Versleutelen</target>
|
||||
@@ -2713,6 +2729,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Bestanden en media zijn verboden in deze groep.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Bestanden en media verboden!</target>
|
||||
@@ -2778,6 +2798,18 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Voor console</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Desktop gevonden</target>
|
||||
@@ -2888,6 +2920,10 @@ Dit kan niet ongedaan gemaakt worden!</target>
|
||||
<target>Groepsleden kunnen verzonden berichten onherroepelijk verwijderen. (24 uur)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Groepsleden kunnen directe berichten sturen.</target>
|
||||
@@ -3753,6 +3789,10 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Netwerk & servers</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Netwerk instellingen</target>
|
||||
@@ -3863,6 +3903,10 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Geen geschiedenis</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Geen toestemming om spraakbericht op te nemen</target>
|
||||
@@ -4077,6 +4121,10 @@ Dit is jouw link voor groep %@!</target>
|
||||
<target>Of laat deze code zien</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING count</target>
|
||||
@@ -4339,6 +4387,10 @@ Fout: %@</target>
|
||||
<target>Berichten reacties verbieden.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Verbied het sturen van directe berichten naar leden.</target>
|
||||
@@ -4469,6 +4521,10 @@ Fout: %@</target>
|
||||
<target>Het ontvangstadres wordt gewijzigd naar een andere server. Adres wijziging wordt voltooid nadat de afzender online is.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Het ontvangen van het bestand wordt gestopt.</target>
|
||||
@@ -4484,6 +4540,10 @@ Fout: %@</target>
|
||||
<target>Recente geschiedenis en verbeterde [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Ontvangers zien updates terwijl u ze typt.</target>
|
||||
@@ -4784,11 +4844,19 @@ Fout: %@</target>
|
||||
<target>Welkomst bericht opslaan?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Opgeslagen WebRTC ICE servers worden verwijderd</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Opgeslagen bericht</target>
|
||||
@@ -5212,6 +5280,14 @@ Fout: %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX links</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6126,10 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
|
||||
<target>Spraak berichten zijn verboden in deze groep.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Spraak berichten verboden!</target>
|
||||
@@ -6130,6 +6210,14 @@ Om verbinding te maken, vraagt u uw contact om een andere verbinding link te mak
|
||||
<target>Wanneer je een incognito profiel met iemand deelt, wordt dit profiel gebruikt voor de groepen waarvoor ze je uitnodigen.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>‐Met versleutelde bestanden en media.</target>
|
||||
@@ -6613,6 +6701,10 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
<target>Beheerder</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>versleuteling overeenkomen voor %@…</target>
|
||||
@@ -6623,6 +6715,10 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
<target>versleuteling overeenkomen…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>altijd</target>
|
||||
@@ -6953,6 +7049,10 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
<target>gebeurtenis gebeurd</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>groep verwijderd</target>
|
||||
@@ -7160,6 +7260,10 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
<target>Eigenaar</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>peer-to-peer</target>
|
||||
@@ -7210,6 +7314,14 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
<target>heeft je verwijderd</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sec</target>
|
||||
@@ -7350,6 +7462,10 @@ SimpleX servers kunnen uw profiel niet zien.</target>
|
||||
<target>Ja</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>je bent uitgenodigd voor de groep</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Zezwól na nieodwracalne usunięcie wysłanych wiadomości. (24 godziny)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Pozwól na wysyłanie plików i mediów.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>Nie można odebrać pliku</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Zmień</target>
|
||||
@@ -2078,6 +2086,10 @@ To nie może być cofnięte!</target>
|
||||
<target>Obniż wersję i otwórz czat</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Pobieranie nie udane</target>
|
||||
@@ -2188,6 +2200,10 @@ To nie może być cofnięte!</target>
|
||||
<target>Włącz pin samodestrukcji</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Szyfruj</target>
|
||||
@@ -2713,6 +2729,10 @@ To nie może być cofnięte!</target>
|
||||
<target>Pliki i media są zabronione w tej grupie.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Pliki i media zabronione!</target>
|
||||
@@ -2778,6 +2798,18 @@ To nie może być cofnięte!</target>
|
||||
<target>Dla konsoli</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Znaleziono komputer</target>
|
||||
@@ -2888,6 +2920,10 @@ To nie może być cofnięte!</target>
|
||||
<target>Członkowie grupy mogą nieodwracalnie usuwać wysłane wiadomości. (24 godziny)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Członkowie grupy mogą wysyłać bezpośrednie wiadomości.</target>
|
||||
@@ -3753,6 +3789,10 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Sieć i serwery</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Ustawienia sieci</target>
|
||||
@@ -3863,6 +3903,10 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Brak historii</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Brak uprawnień do nagrywania wiadomości głosowej</target>
|
||||
@@ -4077,6 +4121,10 @@ To jest twój link do grupy %@!</target>
|
||||
<target>Lub pokaż ten kod</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>Liczba PINGÓW</target>
|
||||
@@ -4339,6 +4387,10 @@ Błąd: %@</target>
|
||||
<target>Zabroń reakcje wiadomości.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Zabroń wysyłania bezpośrednich wiadomości do członków.</target>
|
||||
@@ -4469,6 +4521,10 @@ Błąd: %@</target>
|
||||
<target>Adres odbiorczy zostanie zmieniony na inny serwer. Zmiana adresu zostanie zakończona gdy nadawca będzie online.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Odbieranie pliku zostanie przerwane.</target>
|
||||
@@ -4484,6 +4540,10 @@ Błąd: %@</target>
|
||||
<target>Ostania historia i ulepszony [bot adresowy](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Odbiorcy widzą aktualizacje podczas ich wpisywania.</target>
|
||||
@@ -4784,11 +4844,19 @@ Błąd: %@</target>
|
||||
<target>Zapisać wiadomość powitalną?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Zapisane serwery WebRTC ICE zostaną usunięte</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Zachowano wiadomość</target>
|
||||
@@ -5212,6 +5280,14 @@ Błąd: %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>Linki SimpleX</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6126,10 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
|
||||
<target>Wiadomości głosowe są zabronione w tej grupie.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Wiadomości głosowe zabronione!</target>
|
||||
@@ -6130,6 +6210,14 @@ Aby się połączyć, poproś Twój kontakt o utworzenie kolejnego linku połąc
|
||||
<target>Gdy udostępnisz komuś profil incognito, będzie on używany w grupach, do których Cię zaprosi.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>Z zaszyfrowanymi plikami i multimediami.</target>
|
||||
@@ -6613,6 +6701,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
<target>administrator</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>uzgadnianie szyfrowania dla %@…</target>
|
||||
@@ -6623,6 +6715,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
<target>uzgadnianie szyfrowania…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>zawsze</target>
|
||||
@@ -6953,6 +7049,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
<target>nowe wydarzenie</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>grupa usunięta</target>
|
||||
@@ -7160,6 +7260,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
<target>właściciel</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>peer-to-peer</target>
|
||||
@@ -7210,6 +7314,14 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
<target>usunął cię</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sek</target>
|
||||
@@ -7350,6 +7462,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
|
||||
<target>tak</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>jesteś zaproszony do grupy</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Разрешить необратимо удалять отправленные сообщения. (24 часа)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Разрешить посылать файлы и медиа.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>Невозможно получить файл</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Поменять</target>
|
||||
@@ -2078,6 +2086,10 @@ This cannot be undone!</source>
|
||||
<target>Откатить версию и открыть чат</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Ошибка загрузки</target>
|
||||
@@ -2188,6 +2200,10 @@ This cannot be undone!</source>
|
||||
<target>Включить код самоуничтожения</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Зашифровать</target>
|
||||
@@ -2713,6 +2729,10 @@ This cannot be undone!</source>
|
||||
<target>Файлы и медиа запрещены в этой группе.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Файлы и медиа запрещены!</target>
|
||||
@@ -2778,6 +2798,18 @@ This cannot be undone!</source>
|
||||
<target>Для консоли</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Компьютер найден</target>
|
||||
@@ -2888,6 +2920,10 @@ This cannot be undone!</source>
|
||||
<target>Члены группы могут необратимо удалять отправленные сообщения. (24 часа)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Члены группы могут посылать прямые сообщения.</target>
|
||||
@@ -3753,6 +3789,10 @@ This is your link for group %@!</source>
|
||||
<target>Сеть & серверы</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Настройки сети</target>
|
||||
@@ -3863,6 +3903,10 @@ This is your link for group %@!</source>
|
||||
<target>Нет истории</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Нет разрешения для записи голосового сообщения</target>
|
||||
@@ -4077,6 +4121,10 @@ This is your link for group %@!</source>
|
||||
<target>Или покажите этот код</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>Количество PING</target>
|
||||
@@ -4339,6 +4387,10 @@ Error: %@</source>
|
||||
<target>Запретить реакции на сообщения.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Запретить посылать прямые сообщения членам группы.</target>
|
||||
@@ -4469,6 +4521,10 @@ Error: %@</source>
|
||||
<target>Адрес получения сообщений будет перемещён на другой сервер. Изменение адреса завершится после того как отправитель будет онлайн.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Приём файла будет прекращён.</target>
|
||||
@@ -4484,6 +4540,10 @@ Error: %@</source>
|
||||
<target>История сообщений и улучшенный [каталог групп](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Получатели видят их в то время как Вы их набираете.</target>
|
||||
@@ -4784,11 +4844,19 @@ Error: %@</source>
|
||||
<target>Сохранить приветственное сообщение?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Сохраненные WebRTC ICE серверы будут удалены</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Сохраненное сообщение</target>
|
||||
@@ -5212,6 +5280,14 @@ Error: %@</source>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX ссылки</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6126,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Голосовые сообщения запрещены в этой группе.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Голосовые сообщения запрещены!</target>
|
||||
@@ -6130,6 +6210,14 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Когда Вы соединены с контактом инкогнито, тот же самый инкогнито профиль будет использоваться для групп с этим контактом.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>С зашифрованными файлами и медиа.</target>
|
||||
@@ -6613,6 +6701,10 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
<target>админ</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>шифрование согласовывается для %@…</target>
|
||||
@@ -6623,6 +6715,10 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
<target>шифрование согласовывается…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>всегда</target>
|
||||
@@ -6953,6 +7049,10 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
<target>событие произошло</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>группа удалена</target>
|
||||
@@ -7160,6 +7260,10 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
<target>владелец</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>peer-to-peer</target>
|
||||
@@ -7210,6 +7314,14 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
<target>удалил(а) Вас из группы</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>сек</target>
|
||||
@@ -7350,6 +7462,10 @@ SimpleX серверы не могут получить доступ к Ваше
|
||||
<target>да</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>Вы приглашены в группу</target>
|
||||
|
||||
@@ -707,6 +707,10 @@
|
||||
<target>อนุญาตให้ลบข้อความที่ส่งไปแล้วอย่างถาวร</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>อนุญาตให้ส่งไฟล์และสื่อ</target>
|
||||
@@ -1028,6 +1032,10 @@
|
||||
<target>ไม่สามารถรับไฟล์ได้</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>เปลี่ยน</target>
|
||||
@@ -1979,6 +1987,10 @@ This cannot be undone!</source>
|
||||
<target>ปรับลดรุ่นและเปิดแชท</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2084,6 +2096,10 @@ This cannot be undone!</source>
|
||||
<target>เปิดใช้งานรหัสผ่านแบบทําลายตัวเอง</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Encrypt</target>
|
||||
@@ -2586,6 +2602,10 @@ This cannot be undone!</source>
|
||||
<target>ไฟล์และสื่อเป็นสิ่งต้องห้ามในกลุ่มนี้</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>ไฟล์และสื่อต้องห้าม!</target>
|
||||
@@ -2649,6 +2669,18 @@ This cannot be undone!</source>
|
||||
<target>สำหรับคอนโซล</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2755,6 +2787,10 @@ This cannot be undone!</source>
|
||||
<target>สมาชิกกลุ่มสามารถลบข้อความที่ส่งแล้วอย่างถาวร</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>สมาชิกกลุ่มสามารถส่งข้อความโดยตรงได้</target>
|
||||
@@ -3581,6 +3617,10 @@ This is your link for group %@!</source>
|
||||
<target>เครือข่ายและเซิร์ฟเวอร์</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>การตั้งค่าเครือข่าย</target>
|
||||
@@ -3688,6 +3728,10 @@ This is your link for group %@!</source>
|
||||
<target>ไม่มีประวัติ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>ไม่อนุญาตให้บันทึกข้อความเสียง</target>
|
||||
@@ -3892,6 +3936,10 @@ This is your link for group %@!</source>
|
||||
<source>Or show this code</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>จํานวน PING</target>
|
||||
@@ -4142,6 +4190,10 @@ Error: %@</source>
|
||||
<target>ห้ามแสดงปฏิกิริยาต่อข้อความ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>ห้ามส่งข้อความโดยตรงถึงสมาชิก</target>
|
||||
@@ -4268,6 +4320,10 @@ Error: %@</source>
|
||||
<target>ที่อยู่ผู้รับจะถูกเปลี่ยนเป็นเซิร์ฟเวอร์อื่น การเปลี่ยนแปลงที่อยู่จะเสร็จสมบูรณ์หลังจากที่ผู้ส่งออนไลน์</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>การรับไฟล์จะหยุดลง</target>
|
||||
@@ -4282,6 +4338,10 @@ Error: %@</source>
|
||||
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>ผู้รับจะเห็นการอัปเดตเมื่อคุณพิมพ์</target>
|
||||
@@ -4574,11 +4634,19 @@ Error: %@</source>
|
||||
<target>บันทึกข้อความต้อนรับ?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>เซิร์ฟเวอร์ WebRTC ICE ที่บันทึกไว้จะถูกลบออก</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<note>message info title</note>
|
||||
@@ -4989,6 +5057,14 @@ Error: %@</source>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>ลิงก์ SimpleX</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -5785,6 +5861,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>ข้อความเสียงเป็นสิ่งต้องห้ามในกลุ่มนี้</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>ห้ามข้อความเสียง!</target>
|
||||
@@ -5862,6 +5942,14 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>เมื่อคุณแชร์โปรไฟล์ที่ไม่ระบุตัวตนกับใครสักคน โปรไฟล์นี้จะใช้สำหรับกลุ่มที่พวกเขาเชิญคุณ</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -6323,6 +6411,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>ผู้ดูแลระบบ</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>ยอมรับ encryption สำหรับ %@…</target>
|
||||
@@ -6333,6 +6425,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>เห็นด้วยกับการ encryption…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>เสมอ</target>
|
||||
@@ -6653,6 +6749,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<source>event happened</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>ลบกลุ่มแล้ว</target>
|
||||
@@ -6859,6 +6959,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>เจ้าของ</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>เพื่อนต่อเพื่อน</target>
|
||||
@@ -6906,6 +7010,14 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>ลบคุณออกแล้ว</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>วินาที</target>
|
||||
@@ -7038,6 +7150,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>ใช่</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>คุณได้รับเชิญให้เข้าร่วมกลุ่ม</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Gönderilen mesajların kalıcı olarak silinmesine izin ver. (24 saat içinde)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Dosya ve medya göndermeye izin ver.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>Dosya alınamıyor</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Değiştir</target>
|
||||
@@ -2078,6 +2086,10 @@ Bu geri alınamaz!</target>
|
||||
<target>Sürüm düşür ve sohbeti aç</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Yükleme başarısız oldu</target>
|
||||
@@ -2188,6 +2200,10 @@ Bu geri alınamaz!</target>
|
||||
<target>Kendini imha şifresini etkinleştir</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Şifreleme</target>
|
||||
@@ -2713,6 +2729,10 @@ Bu geri alınamaz!</target>
|
||||
<target>Dosyalar ve medya bu grupta yasaklandı.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Dosyalar ve medya yasaklandı!</target>
|
||||
@@ -2778,6 +2798,18 @@ Bu geri alınamaz!</target>
|
||||
<target>Konsol için</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Bilgisayar bulundu</target>
|
||||
@@ -2888,6 +2920,10 @@ Bu geri alınamaz!</target>
|
||||
<target>Grup üyeleri, gönderilen mesajları kalıcı olarak silebilir. (24 saat içinde)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Grup üyeleri doğrudan mesajlar gönderebilir.</target>
|
||||
@@ -3753,6 +3789,10 @@ Bu senin grup için bağlantın %@!</target>
|
||||
<target>Ağ & sunucular</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Ağ ayarları</target>
|
||||
@@ -3863,6 +3903,10 @@ Bu senin grup için bağlantın %@!</target>
|
||||
<target>Geçmiş yok</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Sesli mesaj kaydetmek için izin yok</target>
|
||||
@@ -4077,6 +4121,10 @@ Bu senin grup için bağlantın %@!</target>
|
||||
<target>Veya bu kodu göster</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING sayısı</target>
|
||||
@@ -4339,6 +4387,10 @@ Hata: %@</target>
|
||||
<target>Mesajlarda tepkileri yasakla.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Geri dönülmez mesaj silme işlemini yasakla.</target>
|
||||
@@ -4469,6 +4521,10 @@ Hata: %@</target>
|
||||
<target>Alıcı adresi farklı bir sunucuya değiştirilecektir. Gönderici çevrimiçi olduktan sonra adres değişikliği tamamlanacaktır.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Dosya alımı durdurulacaktır.</target>
|
||||
@@ -4484,6 +4540,10 @@ Hata: %@</target>
|
||||
<target>Yakın geçmiş ve geliştirilmiş [dizin botu](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex. im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Alıcılar yazdığına göre güncellemeleri görecektir.</target>
|
||||
@@ -4784,11 +4844,19 @@ Hata: %@</target>
|
||||
<target>Hoşgeldin mesajı kaydedilsin mi?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Kaydedilmiş WebRTC ICE sunucuları silinecek</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Kaydedilmiş mesaj</target>
|
||||
@@ -5212,6 +5280,14 @@ Hata: %@</target>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX bağlantıları</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6126,10 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
|
||||
<target>Bu grupta sesli mesajlar yasaktır.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Sesli mesajlar yasaktır!</target>
|
||||
@@ -6130,6 +6210,14 @@ Bağlanmak için lütfen kişinizden başka bir bağlantı oluşturmasını iste
|
||||
<target>Biriyle gizli bir profil paylaştığınızda, bu profil sizi davet ettikleri gruplar için kullanılacaktır.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>Şifrelenmiş dosyalar ve medya ile birlikte.</target>
|
||||
@@ -6613,6 +6701,10 @@ SimpleX sunucuları profilinizi göremez.</target>
|
||||
<target>yönetici</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>%@ için şifreleme kabul ediliyor…</target>
|
||||
@@ -6623,6 +6715,10 @@ SimpleX sunucuları profilinizi göremez.</target>
|
||||
<target>şifreleme kabul ediliyor…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>her zaman</target>
|
||||
@@ -6953,6 +7049,10 @@ SimpleX sunucuları profilinizi göremez.</target>
|
||||
<target>etkinlik yaşandı</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>grup silindi</target>
|
||||
@@ -7160,6 +7260,10 @@ SimpleX sunucuları profilinizi göremez.</target>
|
||||
<target>sahip</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>eşler arası</target>
|
||||
@@ -7210,6 +7314,14 @@ SimpleX sunucuları profilinizi göremez.</target>
|
||||
<target>sen kaldırıldın</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>sn</target>
|
||||
@@ -7350,6 +7462,10 @@ SimpleX sunucuları profilinizi göremez.</target>
|
||||
<target>evet</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>gruba davet edildiniz</target>
|
||||
|
||||
@@ -743,6 +743,10 @@
|
||||
<target>Дозволяє безповоротно видаляти надіслані повідомлення. (24 години)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>Дозволяє надсилати файли та медіа.</target>
|
||||
@@ -1083,6 +1087,10 @@
|
||||
<target>Не вдається отримати файл</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>Зміна</target>
|
||||
@@ -2078,6 +2086,10 @@ This cannot be undone!</source>
|
||||
<target>Пониження та відкритий чат</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<target>Не вдалося завантажити</target>
|
||||
@@ -2188,6 +2200,10 @@ This cannot be undone!</source>
|
||||
<target>Увімкнути пароль самознищення</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>Зашифрувати</target>
|
||||
@@ -2713,6 +2729,10 @@ This cannot be undone!</source>
|
||||
<target>Файли та медіа в цій групі заборонені.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>Файли та медіа заборонені!</target>
|
||||
@@ -2778,6 +2798,18 @@ This cannot be undone!</source>
|
||||
<target>Для консолі</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>Знайдено робочий стіл</target>
|
||||
@@ -2888,6 +2920,10 @@ This cannot be undone!</source>
|
||||
<target>Учасники групи можуть безповоротно видаляти надіслані повідомлення. (24 години)</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>Учасники групи можуть надсилати прямі повідомлення.</target>
|
||||
@@ -3753,6 +3789,10 @@ This is your link for group %@!</source>
|
||||
<target>Мережа та сервери</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>Налаштування мережі</target>
|
||||
@@ -3863,6 +3903,10 @@ This is your link for group %@!</source>
|
||||
<target>Немає історії</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>Немає дозволу на запис голосового повідомлення</target>
|
||||
@@ -4077,6 +4121,10 @@ This is your link for group %@!</source>
|
||||
<target>Або покажіть цей код</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>Кількість PING</target>
|
||||
@@ -4339,6 +4387,10 @@ Error: %@</source>
|
||||
<target>Заборонити реакції на повідомлення.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>Заборонити надсилати прямі повідомлення учасникам.</target>
|
||||
@@ -4469,6 +4521,10 @@ Error: %@</source>
|
||||
<target>Адреса отримувача буде змінена на інший сервер. Зміна адреси завершиться після того, як відправник з'явиться в мережі.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>Отримання файлу буде зупинено.</target>
|
||||
@@ -4484,6 +4540,10 @@ Error: %@</source>
|
||||
<target>Нещодавня історія та покращення [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>Одержувачі бачать оновлення, коли ви їх вводите.</target>
|
||||
@@ -4784,11 +4844,19 @@ Error: %@</source>
|
||||
<target>Зберегти вітальне повідомлення?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>Збережені сервери WebRTC ICE буде видалено</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>Збережене повідомлення</target>
|
||||
@@ -5212,6 +5280,14 @@ Error: %@</source>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>Посилання SimpleX</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -6050,6 +6126,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Голосові повідомлення в цій групі заборонені.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>Голосові повідомлення заборонені!</target>
|
||||
@@ -6130,6 +6210,14 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>Коли ви ділитеся з кимось своїм профілем інкогніто, цей профіль буде використовуватися для груп, до яких вас запрошують.</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>З зашифрованими файлами та медіа.</target>
|
||||
@@ -6613,6 +6701,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>адмін</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>узгодження шифрування для %@…</target>
|
||||
@@ -6623,6 +6715,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>узгодження шифрування…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>завжди</target>
|
||||
@@ -6953,6 +7049,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>відбулася подія</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>групу видалено</target>
|
||||
@@ -7160,6 +7260,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>власник</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>одноранговий</target>
|
||||
@@ -7210,6 +7314,14 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>прибрали вас</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>сек</target>
|
||||
@@ -7350,6 +7462,10 @@ SimpleX servers cannot see your profile.</source>
|
||||
<target>так</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>вас запрошують до групи</target>
|
||||
|
||||
@@ -728,6 +728,10 @@
|
||||
<target>允许不可撤回地删除已发送消息。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send SimpleX links." xml:space="preserve">
|
||||
<source>Allow to send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Allow to send files and media." xml:space="preserve">
|
||||
<source>Allow to send files and media.</source>
|
||||
<target>允许发送文件和媒体。</target>
|
||||
@@ -1063,6 +1067,10 @@
|
||||
<target>无法接收文件</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Cellular" xml:space="preserve">
|
||||
<source>Cellular</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Change" xml:space="preserve">
|
||||
<source>Change</source>
|
||||
<target>更改</target>
|
||||
@@ -2039,6 +2047,10 @@ This cannot be undone!</source>
|
||||
<target>降级并打开聊天</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download" xml:space="preserve">
|
||||
<source>Download</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Download failed" xml:space="preserve">
|
||||
<source>Download failed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
@@ -2145,6 +2157,10 @@ This cannot be undone!</source>
|
||||
<target>启用自毁密码</target>
|
||||
<note>set passcode view</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Enabled for" xml:space="preserve">
|
||||
<source>Enabled for</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Encrypt" xml:space="preserve">
|
||||
<source>Encrypt</source>
|
||||
<target>加密</target>
|
||||
@@ -2658,6 +2674,10 @@ This cannot be undone!</source>
|
||||
<target>此群组中禁止文件和媒体。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media not allowed" xml:space="preserve">
|
||||
<source>Files and media not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Files and media prohibited!" xml:space="preserve">
|
||||
<source>Files and media prohibited!</source>
|
||||
<target>禁止文件和媒体!</target>
|
||||
@@ -2721,6 +2741,18 @@ This cannot be undone!</source>
|
||||
<target>用于控制台</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forward" xml:space="preserve">
|
||||
<source>Forward</source>
|
||||
<note>chat item action</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded" xml:space="preserve">
|
||||
<source>Forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Forwarded from" xml:space="preserve">
|
||||
<source>Forwarded from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Found desktop" xml:space="preserve">
|
||||
<source>Found desktop</source>
|
||||
<target>找到了桌面</target>
|
||||
@@ -2830,6 +2862,10 @@ This cannot be undone!</source>
|
||||
<target>群组成员可以不可撤回地删除已发送的消息。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send SimpleX links." xml:space="preserve">
|
||||
<source>Group members can send SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Group members can send direct messages." xml:space="preserve">
|
||||
<source>Group members can send direct messages.</source>
|
||||
<target>群组成员可以私信。</target>
|
||||
@@ -3673,6 +3709,10 @@ This is your link for group %@!</source>
|
||||
<target>网络和服务器</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network connection" xml:space="preserve">
|
||||
<source>Network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Network settings" xml:space="preserve">
|
||||
<source>Network settings</source>
|
||||
<target>网络设置</target>
|
||||
@@ -3783,6 +3823,10 @@ This is your link for group %@!</source>
|
||||
<target>无历史记录</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No network connection" xml:space="preserve">
|
||||
<source>No network connection</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="No permission to record voice message" xml:space="preserve">
|
||||
<source>No permission to record voice message</source>
|
||||
<target>没有录制语音消息的权限</target>
|
||||
@@ -3993,6 +4037,10 @@ This is your link for group %@!</source>
|
||||
<target>或者显示此码</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Other" xml:space="preserve">
|
||||
<source>Other</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="PING count" xml:space="preserve">
|
||||
<source>PING count</source>
|
||||
<target>PING 次数</target>
|
||||
@@ -4248,6 +4296,10 @@ Error: %@</source>
|
||||
<target>禁止消息回应。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending SimpleX links." xml:space="preserve">
|
||||
<source>Prohibit sending SimpleX links.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
|
||||
<source>Prohibit sending direct messages to members.</source>
|
||||
<target>禁止向成员发送私信。</target>
|
||||
@@ -4376,6 +4428,10 @@ Error: %@</source>
|
||||
<target>接收地址将变更到不同的服务器。地址更改将在发件人上线后完成。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving concurrency" xml:space="preserve">
|
||||
<source>Receiving concurrency</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Receiving file will be stopped." xml:space="preserve">
|
||||
<source>Receiving file will be stopped.</source>
|
||||
<target>即将停止接收文件。</target>
|
||||
@@ -4390,6 +4446,10 @@ Error: %@</source>
|
||||
<source>Recent history and improved [directory bot](simplex:/contact#/?v=1-4&smp=smp%3A%2F%2Fu2dS9sG8nMNURyZwqASV4yROM28Er0luVTx5X1CsMrU%3D%40smp4.simplex.im%2FeXSPwqTkKyDO3px4fLf1wx3MvPdjdLW3%23%2F%3Fv%3D1-2%26dh%3DMCowBQYDK2VuAyEAaiv6MkMH44L2TcYrt_CsX3ZvM11WgbMEUn0hkIKTOho%253D%26srv%3Do5vmywmrnaxalvz6wi3zicyftgio6psuvyniis6gco6bp6ekl4cqj4id.onion).</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipient(s) can't see who this message is from." xml:space="preserve">
|
||||
<source>Recipient(s) can't see who this message is from.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Recipients see updates as you type them." xml:space="preserve">
|
||||
<source>Recipients see updates as you type them.</source>
|
||||
<target>对方会在您键入时看到更新。</target>
|
||||
@@ -4686,11 +4746,19 @@ Error: %@</source>
|
||||
<target>保存欢迎信息?</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved" xml:space="preserve">
|
||||
<source>Saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved WebRTC ICE servers will be removed" xml:space="preserve">
|
||||
<source>Saved WebRTC ICE servers will be removed</source>
|
||||
<target>已保存的WebRTC ICE服务器将被删除</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved from" xml:space="preserve">
|
||||
<source>Saved from</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Saved message" xml:space="preserve">
|
||||
<source>Saved message</source>
|
||||
<target>已保存的消息</target>
|
||||
@@ -5112,6 +5180,14 @@ Error: %@</source>
|
||||
<trans-unit id="SimpleX links" xml:space="preserve">
|
||||
<source>SimpleX links</source>
|
||||
<target>SimpleX 链接</target>
|
||||
<note>chat feature</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links are prohibited in this group." xml:space="preserve">
|
||||
<source>SimpleX links are prohibited in this group.</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX links not allowed" xml:space="preserve">
|
||||
<source>SimpleX links not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="SimpleX one-time invitation" xml:space="preserve">
|
||||
@@ -5939,6 +6015,10 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>语音信息在该群组中被禁用。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages not allowed" xml:space="preserve">
|
||||
<source>Voice messages not allowed</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Voice messages prohibited!" xml:space="preserve">
|
||||
<source>Voice messages prohibited!</source>
|
||||
<target>语音消息禁止发送!</target>
|
||||
@@ -6016,6 +6096,14 @@ To connect, please ask your contact to create another connection link and check
|
||||
<target>当您与某人共享隐身聊天资料时,该资料将用于他们邀请您加入的群组。</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="WiFi" xml:space="preserve">
|
||||
<source>WiFi</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="Wired ethernet" xml:space="preserve">
|
||||
<source>Wired ethernet</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="With encrypted files and media." xml:space="preserve">
|
||||
<source>With encrypted files and media.</source>
|
||||
<target>加密的文件和媒体。</target>
|
||||
@@ -6487,6 +6575,10 @@ SimpleX 服务器无法看到您的资料。</target>
|
||||
<target>管理员</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="admins" xml:space="preserve">
|
||||
<source>admins</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="agreeing encryption for %@…" xml:space="preserve">
|
||||
<source>agreeing encryption for %@…</source>
|
||||
<target>正在协商将加密应用于 %@…</target>
|
||||
@@ -6497,6 +6589,10 @@ SimpleX 服务器无法看到您的资料。</target>
|
||||
<target>同意加密…</target>
|
||||
<note>chat item text</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="all members" xml:space="preserve">
|
||||
<source>all members</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="always" xml:space="preserve">
|
||||
<source>always</source>
|
||||
<target>始终</target>
|
||||
@@ -6825,6 +6921,10 @@ SimpleX 服务器无法看到您的资料。</target>
|
||||
<target>发生的事</target>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="forwarded" xml:space="preserve">
|
||||
<source>forwarded</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="group deleted" xml:space="preserve">
|
||||
<source>group deleted</source>
|
||||
<target>群组已删除</target>
|
||||
@@ -7031,6 +7131,10 @@ SimpleX 服务器无法看到您的资料。</target>
|
||||
<target>群主</target>
|
||||
<note>member role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="owners" xml:space="preserve">
|
||||
<source>owners</source>
|
||||
<note>feature role</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="peer-to-peer" xml:space="preserve">
|
||||
<source>peer-to-peer</source>
|
||||
<target>点对点</target>
|
||||
@@ -7080,6 +7184,14 @@ SimpleX 服务器无法看到您的资料。</target>
|
||||
<target>已将您移除</target>
|
||||
<note>rcv group event chat item</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved" xml:space="preserve">
|
||||
<source>saved</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="saved from %@" xml:space="preserve">
|
||||
<source>saved from %@</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="sec" xml:space="preserve">
|
||||
<source>sec</source>
|
||||
<target>秒</target>
|
||||
@@ -7217,6 +7329,10 @@ SimpleX 服务器无法看到您的资料。</target>
|
||||
<target>是</target>
|
||||
<note>pref value</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you" xml:space="preserve">
|
||||
<source>you</source>
|
||||
<note>No comment provided by engineer.</note>
|
||||
</trans-unit>
|
||||
<trans-unit id="you are invited to group" xml:space="preserve">
|
||||
<source>you are invited to group</source>
|
||||
<target>您被邀请加入群组</target>
|
||||
|
||||
@@ -586,9 +586,6 @@ func receivedMsgNtf(_ res: ChatResponse) async -> (String, NSENotification)? {
|
||||
cleanupDirectFile(aChatItem)
|
||||
}
|
||||
return nil
|
||||
case let .sndFileCompleteXFTP(_, aChatItem, _):
|
||||
cleanupFile(aChatItem)
|
||||
return nil
|
||||
case let .callInvitation(invitation):
|
||||
// Do not post it without CallKit support, iOS will stop launching the app without showing CallKit
|
||||
return (
|
||||
|
||||
@@ -29,6 +29,11 @@
|
||||
5C116CDC27AABE0400E66D01 /* ContactRequestView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C116CDB27AABE0400E66D01 /* ContactRequestView.swift */; };
|
||||
5C13730B28156D2700F43030 /* ContactConnectionView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C13730A28156D2700F43030 /* ContactConnectionView.swift */; };
|
||||
5C1A4C1E27A715B700EAD5AD /* ChatItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */; };
|
||||
5C2217842BD4465500A8B0E7 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C22177F2BD4465500A8B0E7 /* libgmpxx.a */; };
|
||||
5C2217852BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217802BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a */; };
|
||||
5C2217862BD4465500A8B0E7 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217812BD4465500A8B0E7 /* libgmp.a */; };
|
||||
5C2217872BD4465500A8B0E7 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217822BD4465500A8B0E7 /* libffi.a */; };
|
||||
5C2217882BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C2217832BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a */; };
|
||||
5C2E260727A2941F00F70299 /* SimpleXAPI.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260627A2941F00F70299 /* SimpleXAPI.swift */; };
|
||||
5C2E260B27A30CFA00F70299 /* ChatListView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260A27A30CFA00F70299 /* ChatListView.swift */; };
|
||||
5C2E260F27A30FDC00F70299 /* ChatView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C2E260E27A30FDC00F70299 /* ChatView.swift */; };
|
||||
@@ -139,11 +144,6 @@
|
||||
5CEACCED27DEA495000BD591 /* MsgContentView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEACCEC27DEA495000BD591 /* MsgContentView.swift */; };
|
||||
5CEBD7462A5C0A8F00665FE2 /* KeyboardPadding.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */; };
|
||||
5CEBD7482A5F115D00665FE2 /* SetDeliveryReceiptsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */; };
|
||||
5CF898622BB984E400EE33B6 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF8985D2BB984E400EE33B6 /* libgmpxx.a */; };
|
||||
5CF898632BB984E400EE33B6 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF8985E2BB984E400EE33B6 /* libffi.a */; };
|
||||
5CF898642BB984E400EE33B6 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF8985F2BB984E400EE33B6 /* libgmp.a */; };
|
||||
5CF898652BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF898602BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a */; };
|
||||
5CF898662BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5CF898612BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a */; };
|
||||
5CF937202B24DE8C00E1D781 /* SharedFileSubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */; };
|
||||
5CF937232B2503D000E1D781 /* NSESubscriber.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CF937212B25034A00E1D781 /* NSESubscriber.swift */; };
|
||||
5CFA59C42860BC6200863A68 /* MigrateToAppGroupView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */; };
|
||||
@@ -172,6 +172,7 @@
|
||||
646BB38E283FDB6D001CE359 /* LocalAuthenticationUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */; };
|
||||
647F090E288EA27B00644C40 /* GroupMemberInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */; };
|
||||
648010AB281ADD15009009B9 /* CIFileView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648010AA281ADD15009009B9 /* CIFileView.swift */; };
|
||||
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */; };
|
||||
649BCDA0280460FD00C3A862 /* ComposeImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCD9F280460FD00C3A862 /* ComposeImageView.swift */; };
|
||||
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
|
||||
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
|
||||
@@ -187,6 +188,7 @@
|
||||
8C69FE7D2B8C7D2700267E38 /* AppSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */; };
|
||||
8C7D949A2B88952700B7B9E1 /* MigrateToDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */; };
|
||||
8C7DF3202B7CDB0A00C886D0 /* MigrateFromDevice.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */; };
|
||||
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; };
|
||||
D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; };
|
||||
D72A9088294BD7A70047C86D /* NativeTextEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = D72A9087294BD7A70047C86D /* NativeTextEditor.swift */; };
|
||||
D741547829AF89AF0022400A /* StoreKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547729AF89AF0022400A /* StoreKit.framework */; };
|
||||
@@ -277,6 +279,11 @@
|
||||
5C13730A28156D2700F43030 /* ContactConnectionView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContactConnectionView.swift; sourceTree = "<group>"; };
|
||||
5C13730C2815740A00F43030 /* DebugJSON.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; path = DebugJSON.playground; sourceTree = "<group>"; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
|
||||
5C1A4C1D27A715B700EAD5AD /* ChatItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemView.swift; sourceTree = "<group>"; };
|
||||
5C22177F2BD4465500A8B0E7 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5C2217802BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5C2217812BD4465500A8B0E7 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5C2217822BD4465500A8B0E7 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5C2217832BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a"; sourceTree = "<group>"; };
|
||||
5C245F3C2B501E98001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
5C245F3D2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = "tr.lproj/SimpleX--iOS--InfoPlist.strings"; sourceTree = "<group>"; };
|
||||
5C245F3E2B501F13001CC39F /* tr */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = tr; path = tr.lproj/InfoPlist.strings; sourceTree = "<group>"; };
|
||||
@@ -431,11 +438,6 @@
|
||||
5CEACCEC27DEA495000BD591 /* MsgContentView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MsgContentView.swift; sourceTree = "<group>"; };
|
||||
5CEBD7452A5C0A8F00665FE2 /* KeyboardPadding.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = KeyboardPadding.swift; sourceTree = "<group>"; };
|
||||
5CEBD7472A5F115D00665FE2 /* SetDeliveryReceiptsView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SetDeliveryReceiptsView.swift; sourceTree = "<group>"; };
|
||||
5CF8985D2BB984E400EE33B6 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
|
||||
5CF8985E2BB984E400EE33B6 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
|
||||
5CF8985F2BB984E400EE33B6 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
|
||||
5CF898602BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a"; sourceTree = "<group>"; };
|
||||
5CF898612BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a"; sourceTree = "<group>"; };
|
||||
5CF9371F2B24DE8C00E1D781 /* SharedFileSubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SharedFileSubscriber.swift; sourceTree = "<group>"; };
|
||||
5CF937212B25034A00E1D781 /* NSESubscriber.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NSESubscriber.swift; sourceTree = "<group>"; };
|
||||
5CFA59C32860BC6200863A68 /* MigrateToAppGroupView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToAppGroupView.swift; sourceTree = "<group>"; };
|
||||
@@ -463,6 +465,7 @@
|
||||
646BB38D283FDB6D001CE359 /* LocalAuthenticationUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = LocalAuthenticationUtils.swift; sourceTree = "<group>"; };
|
||||
647F090D288EA27B00644C40 /* GroupMemberInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GroupMemberInfoView.swift; sourceTree = "<group>"; };
|
||||
648010AA281ADD15009009B9 /* CIFileView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIFileView.swift; sourceTree = "<group>"; };
|
||||
648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemForwardingView.swift; sourceTree = "<group>"; };
|
||||
6493D667280ED77F007A76FB /* en */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = en; path = en.lproj/Localizable.strings; sourceTree = "<group>"; };
|
||||
649BCD9F280460FD00C3A862 /* ComposeImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeImageView.swift; sourceTree = "<group>"; };
|
||||
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
|
||||
@@ -480,6 +483,7 @@
|
||||
8C69FE7C2B8C7D2700267E38 /* AppSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AppSettings.swift; sourceTree = "<group>"; };
|
||||
8C7D94992B88952700B7B9E1 /* MigrateToDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateToDevice.swift; sourceTree = "<group>"; };
|
||||
8C7DF31F2B7CDB0A00C886D0 /* MigrateFromDevice.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MigrateFromDevice.swift; sourceTree = "<group>"; };
|
||||
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NetworkObserver.swift; sourceTree = "<group>"; };
|
||||
D72A9087294BD7A70047C86D /* NativeTextEditor.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NativeTextEditor.swift; sourceTree = "<group>"; };
|
||||
D741547729AF89AF0022400A /* StoreKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = StoreKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/StoreKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
D741547929AF90B00022400A /* PushKit.framework */ = {isa = PBXFileReference; lastKnownFileType = wrapper.framework; name = PushKit.framework; path = Platforms/iPhoneOS.platform/Developer/SDKs/iPhoneOS16.1.sdk/System/Library/Frameworks/PushKit.framework; sourceTree = DEVELOPER_DIR; };
|
||||
@@ -521,13 +525,13 @@
|
||||
isa = PBXFrameworksBuildPhase;
|
||||
buildActionMask = 2147483647;
|
||||
files = (
|
||||
5CF898652BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a in Frameworks */,
|
||||
5CF898622BB984E400EE33B6 /* libgmpxx.a in Frameworks */,
|
||||
5CF898642BB984E400EE33B6 /* libgmp.a in Frameworks */,
|
||||
5C2217842BD4465500A8B0E7 /* libgmpxx.a in Frameworks */,
|
||||
5C2217872BD4465500A8B0E7 /* libffi.a in Frameworks */,
|
||||
5C2217862BD4465500A8B0E7 /* libgmp.a in Frameworks */,
|
||||
5C2217882BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a in Frameworks */,
|
||||
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
|
||||
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
|
||||
5CF898632BB984E400EE33B6 /* libffi.a in Frameworks */,
|
||||
5CF898662BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a in Frameworks */,
|
||||
5C2217852BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a in Frameworks */,
|
||||
);
|
||||
runOnlyForDeploymentPostprocessing = 0;
|
||||
};
|
||||
@@ -583,6 +587,7 @@
|
||||
5CBE6C11294487F7002D9531 /* VerifyCodeView.swift */,
|
||||
5CBE6C132944CC12002D9531 /* ScanCodeView.swift */,
|
||||
64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */,
|
||||
648679AA2BC96A74006456E7 /* ChatItemForwardingView.swift */,
|
||||
);
|
||||
path = Chat;
|
||||
sourceTree = "<group>";
|
||||
@@ -590,11 +595,11 @@
|
||||
5C764E5C279C70B7000C6508 /* Libraries */ = {
|
||||
isa = PBXGroup;
|
||||
children = (
|
||||
5CF8985E2BB984E400EE33B6 /* libffi.a */,
|
||||
5CF8985F2BB984E400EE33B6 /* libgmp.a */,
|
||||
5CF8985D2BB984E400EE33B6 /* libgmpxx.a */,
|
||||
5CF898612BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv-ghc9.6.3.a */,
|
||||
5CF898602BB984E400EE33B6 /* libHSsimplex-chat-5.6.0.4-FOF2McwHkk1EIlP5UNozOv.a */,
|
||||
5C2217822BD4465500A8B0E7 /* libffi.a */,
|
||||
5C2217812BD4465500A8B0E7 /* libgmp.a */,
|
||||
5C22177F2BD4465500A8B0E7 /* libgmpxx.a */,
|
||||
5C2217802BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy-ghc9.6.3.a */,
|
||||
5C2217832BD4465500A8B0E7 /* libHSsimplex-chat-5.7.0.1-4ZWOoOvQtRR3xwBSWKICWy.a */,
|
||||
);
|
||||
path = Libraries;
|
||||
sourceTree = "<group>";
|
||||
@@ -623,6 +628,7 @@
|
||||
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */,
|
||||
5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */,
|
||||
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
|
||||
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */,
|
||||
);
|
||||
path = Model;
|
||||
sourceTree = "<group>";
|
||||
@@ -1158,6 +1164,7 @@
|
||||
5C10D88828EED12E00E58BF0 /* ContactConnectionInfo.swift in Sources */,
|
||||
5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */,
|
||||
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */,
|
||||
648679AB2BC96A74006456E7 /* ChatItemForwardingView.swift in Sources */,
|
||||
64466DC829FC2B3B00E3D48D /* CreateSimpleXAddress.swift in Sources */,
|
||||
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */,
|
||||
5C7505A827B6D34800BE3227 /* ChatInfoToolbar.swift in Sources */,
|
||||
@@ -1176,6 +1183,7 @@
|
||||
5C9A5BDB2871E05400A5B906 /* SetNotificationsMode.swift in Sources */,
|
||||
5CB0BA8E2827126500B3292C /* OnboardingView.swift in Sources */,
|
||||
6442E0BE2880182D00CEC0F9 /* GroupChatInfoView.swift in Sources */,
|
||||
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */,
|
||||
5C2E261227A30FEA00F70299 /* TerminalView.swift in Sources */,
|
||||
5C9FD96E27A5D6ED0075386C /* SendMessageView.swift in Sources */,
|
||||
5CA7DFC329302AF000F7FDDE /* AppSheet.swift in Sources */,
|
||||
@@ -1536,7 +1544,7 @@
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 204;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -1561,7 +1569,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
LLVM_LTO = YES_THIN;
|
||||
MARKETING_VERSION = 5.6;
|
||||
MARKETING_VERSION = 5.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1585,7 +1593,7 @@
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 204;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
DEAD_CODE_STRIPPING = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
@@ -1610,7 +1618,7 @@
|
||||
"@executable_path/Frameworks",
|
||||
);
|
||||
LLVM_LTO = YES;
|
||||
MARKETING_VERSION = 5.6;
|
||||
MARKETING_VERSION = 5.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
|
||||
PRODUCT_NAME = SimpleX;
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1671,7 +1679,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 204;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
GCC_OPTIMIZATION_LEVEL = s;
|
||||
@@ -1686,7 +1694,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LLVM_LTO = YES;
|
||||
MARKETING_VERSION = 5.6;
|
||||
MARKETING_VERSION = 5.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1708,7 +1716,7 @@
|
||||
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
|
||||
CODE_SIGN_IDENTITY = "Apple Development";
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 204;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
ENABLE_BITCODE = NO;
|
||||
ENABLE_CODE_COVERAGE = NO;
|
||||
@@ -1723,7 +1731,7 @@
|
||||
"@executable_path/../../Frameworks",
|
||||
);
|
||||
LLVM_LTO = YES;
|
||||
MARKETING_VERSION = 5.6;
|
||||
MARKETING_VERSION = 5.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
|
||||
PRODUCT_NAME = "$(TARGET_NAME)";
|
||||
PROVISIONING_PROFILE_SPECIFIER = "";
|
||||
@@ -1745,7 +1753,7 @@
|
||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 204;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1771,7 +1779,7 @@
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
LLVM_LTO = YES;
|
||||
MARKETING_VERSION = 5.6;
|
||||
MARKETING_VERSION = 5.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
@@ -1796,7 +1804,7 @@
|
||||
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
|
||||
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
|
||||
CODE_SIGN_STYLE = Automatic;
|
||||
CURRENT_PROJECT_VERSION = 204;
|
||||
CURRENT_PROJECT_VERSION = 208;
|
||||
DEFINES_MODULE = YES;
|
||||
DEVELOPMENT_TEAM = 5NN7GUYB6T;
|
||||
DYLIB_COMPATIBILITY_VERSION = 1;
|
||||
@@ -1822,7 +1830,7 @@
|
||||
"$(PROJECT_DIR)/Libraries/sim",
|
||||
);
|
||||
LLVM_LTO = YES;
|
||||
MARKETING_VERSION = 5.6;
|
||||
MARKETING_VERSION = 5.7;
|
||||
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
|
||||
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
|
||||
SDKROOT = iphoneos;
|
||||
|
||||
@@ -87,6 +87,11 @@ public func chatInitControllerRemovingDatabases() {
|
||||
|
||||
|
||||
public func chatCloseStore() {
|
||||
// Prevent crash when exiting the app with already closed store (for example, after changing a database passpharase)
|
||||
guard hasChatCtrl() else {
|
||||
logger.error("chatCloseStore: already closed, chatCtrl is nil")
|
||||
return
|
||||
}
|
||||
let err = fromCString(chat_close_store(getChatCtrl()))
|
||||
if err != "" {
|
||||
logger.error("chatCloseStore error: \(err)")
|
||||
|
||||
@@ -50,6 +50,7 @@ public enum ChatCommand {
|
||||
case apiDeleteChatItem(type: ChatType, id: Int64, itemId: Int64, mode: CIDeleteMode)
|
||||
case apiDeleteMemberChatItem(groupId: Int64, groupMemberId: Int64, itemId: Int64)
|
||||
case apiChatItemReaction(type: ChatType, id: Int64, itemId: Int64, add: Bool, reaction: MsgReaction)
|
||||
case apiForwardChatItem(toChatType: ChatType, toChatId: Int64, fromChatType: ChatType, fromChatId: Int64, itemId: Int64)
|
||||
case apiGetNtfToken
|
||||
case apiRegisterToken(token: DeviceToken, notificationMode: NotificationsMode)
|
||||
case apiVerifyToken(token: DeviceToken, nonce: String, code: String)
|
||||
@@ -77,6 +78,7 @@ public enum ChatCommand {
|
||||
case apiGetChatItemTTL(userId: Int64)
|
||||
case apiSetNetworkConfig(networkConfig: NetCfg)
|
||||
case apiGetNetworkConfig
|
||||
case apiSetNetworkInfo(networkInfo: UserNetworkInfo)
|
||||
case reconnectAllServers
|
||||
case apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings)
|
||||
case apiSetMemberSettings(groupId: Int64, groupMemberId: Int64, memberSettings: GroupMemberSettings)
|
||||
@@ -194,6 +196,7 @@ public enum ChatCommand {
|
||||
case let .apiDeleteChatItem(type, id, itemId, mode): return "/_delete item \(ref(type, id)) \(itemId) \(mode.rawValue)"
|
||||
case let .apiDeleteMemberChatItem(groupId, groupMemberId, itemId): return "/_delete member item #\(groupId) \(groupMemberId) \(itemId)"
|
||||
case let .apiChatItemReaction(type, id, itemId, add, reaction): return "/_reaction \(ref(type, id)) \(itemId) \(onOff(add)) \(encodeJSON(reaction))"
|
||||
case let .apiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId): return "/_forward \(ref(toChatType, toChatId)) \(ref(fromChatType, fromChatId)) \(itemId)"
|
||||
case .apiGetNtfToken: return "/_ntf get "
|
||||
case let .apiRegisterToken(token, notificationMode): return "/_ntf register \(token.cmdString) \(notificationMode.rawValue)"
|
||||
case let .apiVerifyToken(token, nonce, code): return "/_ntf verify \(token.cmdString) \(nonce) \(code)"
|
||||
@@ -221,6 +224,7 @@ public enum ChatCommand {
|
||||
case let .apiGetChatItemTTL(userId): return "/_ttl \(userId)"
|
||||
case let .apiSetNetworkConfig(networkConfig): return "/_network \(encodeJSON(networkConfig))"
|
||||
case .apiGetNetworkConfig: return "/network"
|
||||
case let .apiSetNetworkInfo(networkInfo): return "/_network info \(encodeJSON(networkInfo))"
|
||||
case .reconnectAllServers: return "/reconnect"
|
||||
case let .apiSetChatSettings(type, id, chatSettings): return "/_settings \(ref(type, id)) \(encodeJSON(chatSettings))"
|
||||
case let .apiSetMemberSettings(groupId, groupMemberId, memberSettings): return "/_member settings #\(groupId) \(groupMemberId) \(encodeJSON(memberSettings))"
|
||||
@@ -341,6 +345,7 @@ public enum ChatCommand {
|
||||
case .apiConnectContactViaAddress: return "apiConnectContactViaAddress"
|
||||
case .apiDeleteMemberChatItem: return "apiDeleteMemberChatItem"
|
||||
case .apiChatItemReaction: return "apiChatItemReaction"
|
||||
case .apiForwardChatItem: return "apiForwardChatItem"
|
||||
case .apiGetNtfToken: return "apiGetNtfToken"
|
||||
case .apiRegisterToken: return "apiRegisterToken"
|
||||
case .apiVerifyToken: return "apiVerifyToken"
|
||||
@@ -368,6 +373,7 @@ public enum ChatCommand {
|
||||
case .apiGetChatItemTTL: return "apiGetChatItemTTL"
|
||||
case .apiSetNetworkConfig: return "apiSetNetworkConfig"
|
||||
case .apiGetNetworkConfig: return "apiGetNetworkConfig"
|
||||
case .apiSetNetworkInfo: return "apiSetNetworkInfo"
|
||||
case .reconnectAllServers: return "reconnectAllServers"
|
||||
case .apiSetChatSettings: return "apiSetChatSettings"
|
||||
case .apiSetMemberSettings: return "apiSetMemberSettings"
|
||||
@@ -1251,6 +1257,7 @@ public struct NetCfg: Codable, Equatable {
|
||||
public var tcpConnectTimeout: Int // microseconds
|
||||
public var tcpTimeout: Int // microseconds
|
||||
public var tcpTimeoutPerKb: Int // microseconds
|
||||
public var rcvConcurrency: Int // pool size
|
||||
public var tcpKeepAlive: KeepAliveOpts?
|
||||
public var smpPingInterval: Int // microseconds
|
||||
public var smpPingCount: Int // times
|
||||
@@ -1259,9 +1266,10 @@ public struct NetCfg: Codable, Equatable {
|
||||
public static let defaults: NetCfg = NetCfg(
|
||||
socksProxy: nil,
|
||||
sessionMode: TransportSessionMode.user,
|
||||
tcpConnectTimeout: 20_000_000,
|
||||
tcpConnectTimeout: 10_000_000,
|
||||
tcpTimeout: 15_000_000,
|
||||
tcpTimeoutPerKb: 45_000,
|
||||
tcpTimeoutPerKb: 10_000,
|
||||
rcvConcurrency: 12,
|
||||
tcpKeepAlive: KeepAliveOpts.defaults,
|
||||
smpPingInterval: 1200_000_000,
|
||||
smpPingCount: 3,
|
||||
@@ -1271,9 +1279,10 @@ public struct NetCfg: Codable, Equatable {
|
||||
public static let proxyDefaults: NetCfg = NetCfg(
|
||||
socksProxy: nil,
|
||||
sessionMode: TransportSessionMode.user,
|
||||
tcpConnectTimeout: 30_000_000,
|
||||
tcpConnectTimeout: 20_000_000,
|
||||
tcpTimeout: 20_000_000,
|
||||
tcpTimeoutPerKb: 60_000,
|
||||
tcpTimeoutPerKb: 15_000,
|
||||
rcvConcurrency: 8,
|
||||
tcpKeepAlive: KeepAliveOpts.defaults,
|
||||
smpPingInterval: 1200_000_000,
|
||||
smpPingCount: 3,
|
||||
@@ -1714,6 +1723,8 @@ public enum ChatErrorType: Decodable {
|
||||
case fallbackToSMPProhibited(fileId: Int64)
|
||||
case inlineFileProhibited(fileId: Int64)
|
||||
case invalidQuote
|
||||
case invalidForward
|
||||
case forwardNoFile
|
||||
case invalidChatItemUpdate
|
||||
case invalidChatItemDelete
|
||||
case hasCurrentCall
|
||||
@@ -2086,3 +2097,31 @@ public enum AppSettingsLockScreenCalls: String, Codable {
|
||||
case show
|
||||
case accept
|
||||
}
|
||||
|
||||
public struct UserNetworkInfo: Codable, Equatable {
|
||||
public let networkType: UserNetworkType
|
||||
public let online: Bool
|
||||
|
||||
public init(networkType: UserNetworkType, online: Bool) {
|
||||
self.networkType = networkType
|
||||
self.online = online
|
||||
}
|
||||
}
|
||||
|
||||
public enum UserNetworkType: String, Codable {
|
||||
case none
|
||||
case cellular
|
||||
case wifi
|
||||
case ethernet
|
||||
case other
|
||||
|
||||
public var text: LocalizedStringKey {
|
||||
switch self {
|
||||
case .none: "No network connection"
|
||||
case .cellular: "Cellular"
|
||||
case .wifi: "WiFi"
|
||||
case .ethernet: "Wired ethernet"
|
||||
case .other: "Other"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -29,6 +29,7 @@ let GROUP_DEFAULT_NETWORK_SESSION_MODE = "networkSessionMode"
|
||||
let GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT = "networkTCPConnectTimeout"
|
||||
let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT = "networkTCPTimeout"
|
||||
let GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb"
|
||||
let GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency"
|
||||
let GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL = "networkSMPPingInterval"
|
||||
let GROUP_DEFAULT_NETWORK_SMP_PING_COUNT = "networkSMPPingCount"
|
||||
let GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE = "networkEnableKeepAlive"
|
||||
@@ -55,6 +56,7 @@ public func registerGroupDefaults() {
|
||||
GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT: NetCfg.defaults.tcpConnectTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT: NetCfg.defaults.tcpTimeout,
|
||||
GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB: NetCfg.defaults.tcpTimeoutPerKb,
|
||||
GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY: NetCfg.defaults.rcvConcurrency,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL: NetCfg.defaults.smpPingInterval,
|
||||
GROUP_DEFAULT_NETWORK_SMP_PING_COUNT: NetCfg.defaults.smpPingCount,
|
||||
GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive,
|
||||
@@ -278,6 +280,7 @@ public func getNetCfg() -> NetCfg {
|
||||
let tcpConnectTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT)
|
||||
let tcpTimeout = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT)
|
||||
let tcpTimeoutPerKb = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB)
|
||||
let rcvConcurrency = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY)
|
||||
let smpPingInterval = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL)
|
||||
let smpPingCount = groupDefaults.integer(forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT)
|
||||
let enableKeepAlive = groupDefaults.bool(forKey: GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE)
|
||||
@@ -297,6 +300,7 @@ public func getNetCfg() -> NetCfg {
|
||||
tcpConnectTimeout: tcpConnectTimeout,
|
||||
tcpTimeout: tcpTimeout,
|
||||
tcpTimeoutPerKb: tcpTimeoutPerKb,
|
||||
rcvConcurrency: rcvConcurrency,
|
||||
tcpKeepAlive: tcpKeepAlive,
|
||||
smpPingInterval: smpPingInterval,
|
||||
smpPingCount: smpPingCount,
|
||||
@@ -310,6 +314,7 @@ public func setNetCfg(_ cfg: NetCfg) {
|
||||
groupDefaults.set(cfg.tcpConnectTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_CONNECT_TIMEOUT)
|
||||
groupDefaults.set(cfg.tcpTimeout, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT)
|
||||
groupDefaults.set(cfg.tcpTimeoutPerKb, forKey: GROUP_DEFAULT_NETWORK_TCP_TIMEOUT_PER_KB)
|
||||
groupDefaults.set(cfg.rcvConcurrency, forKey: GROUP_DEFAULT_NETWORK_RCV_CONCURRENCY)
|
||||
groupDefaults.set(cfg.smpPingInterval, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_INTERVAL)
|
||||
groupDefaults.set(cfg.smpPingCount, forKey: GROUP_DEFAULT_NETWORK_SMP_PING_COUNT)
|
||||
if let tcpKeepAlive = cfg.tcpKeepAlive {
|
||||
|
||||
@@ -543,6 +543,7 @@ public protocol Feature {
|
||||
var iconFilled: String { get }
|
||||
var iconScale: CGFloat { get }
|
||||
var hasParam: Bool { get }
|
||||
var hasRole: Bool { get }
|
||||
var text: String { get }
|
||||
}
|
||||
|
||||
@@ -569,6 +570,8 @@ public enum ChatFeature: String, Decodable, Feature {
|
||||
}
|
||||
}
|
||||
|
||||
public var hasRole: Bool { false }
|
||||
|
||||
public var text: String {
|
||||
switch self {
|
||||
case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature")
|
||||
@@ -694,6 +697,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case reactions
|
||||
case voice
|
||||
case files
|
||||
case simplexLinks
|
||||
case history
|
||||
|
||||
public var id: Self { self }
|
||||
@@ -705,6 +709,19 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
}
|
||||
}
|
||||
|
||||
public var hasRole: Bool {
|
||||
switch self {
|
||||
case .timedMessages: false
|
||||
case .directMessages: true
|
||||
case .fullDelete: false
|
||||
case .reactions: false
|
||||
case .voice: true
|
||||
case .files: true
|
||||
case .simplexLinks: true
|
||||
case .history: false
|
||||
}
|
||||
}
|
||||
|
||||
public var text: String {
|
||||
switch self {
|
||||
case .timedMessages: return NSLocalizedString("Disappearing messages", comment: "chat feature")
|
||||
@@ -713,6 +730,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .reactions: return NSLocalizedString("Message reactions", comment: "chat feature")
|
||||
case .voice: return NSLocalizedString("Voice messages", comment: "chat feature")
|
||||
case .files: return NSLocalizedString("Files and media", comment: "chat feature")
|
||||
case .simplexLinks: return NSLocalizedString("SimpleX links", comment: "chat feature")
|
||||
case .history: return NSLocalizedString("Visible history", comment: "chat feature")
|
||||
}
|
||||
}
|
||||
@@ -725,6 +743,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .reactions: return "face.smiling"
|
||||
case .voice: return "mic"
|
||||
case .files: return "doc"
|
||||
case .simplexLinks: return "link.circle"
|
||||
case .history: return "clock"
|
||||
}
|
||||
}
|
||||
@@ -737,6 +756,7 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .reactions: return "face.smiling.fill"
|
||||
case .voice: return "mic.fill"
|
||||
case .files: return "doc.fill"
|
||||
case .simplexLinks: return "link.circle.fill"
|
||||
case .history: return "clock.fill"
|
||||
}
|
||||
}
|
||||
@@ -781,6 +801,11 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .on: return "Allow to send files and media."
|
||||
case .off: return "Prohibit sending files and media."
|
||||
}
|
||||
case .simplexLinks:
|
||||
switch enabled {
|
||||
case .on: return "Allow to send SimpleX links."
|
||||
case .off: return "Prohibit sending SimpleX links."
|
||||
}
|
||||
case .history:
|
||||
switch enabled {
|
||||
case .on: return "Send up to 100 last messages to new members."
|
||||
@@ -819,6 +844,11 @@ public enum GroupFeature: String, Decodable, Feature {
|
||||
case .on: return "Group members can send files and media."
|
||||
case .off: return "Files and media are prohibited in this group."
|
||||
}
|
||||
case .simplexLinks:
|
||||
switch enabled {
|
||||
case .on: return "Group members can send SimpleX links."
|
||||
case .off: return "SimpleX links are prohibited in this group."
|
||||
}
|
||||
case .history:
|
||||
switch enabled {
|
||||
case .on: return "Up to 100 last messages are sent to new members."
|
||||
@@ -958,20 +988,22 @@ public enum FeatureAllowed: String, Codable, Identifiable {
|
||||
|
||||
public struct FullGroupPreferences: Decodable, Equatable {
|
||||
public var timedMessages: TimedMessagesGroupPreference
|
||||
public var directMessages: GroupPreference
|
||||
public var directMessages: RoleGroupPreference
|
||||
public var fullDelete: GroupPreference
|
||||
public var reactions: GroupPreference
|
||||
public var voice: GroupPreference
|
||||
public var files: GroupPreference
|
||||
public var voice: RoleGroupPreference
|
||||
public var files: RoleGroupPreference
|
||||
public var simplexLinks: RoleGroupPreference
|
||||
public var history: GroupPreference
|
||||
|
||||
public init(
|
||||
timedMessages: TimedMessagesGroupPreference,
|
||||
directMessages: GroupPreference,
|
||||
directMessages: RoleGroupPreference,
|
||||
fullDelete: GroupPreference,
|
||||
reactions: GroupPreference,
|
||||
voice: GroupPreference,
|
||||
files: GroupPreference,
|
||||
voice: RoleGroupPreference,
|
||||
files: RoleGroupPreference,
|
||||
simplexLinks: RoleGroupPreference,
|
||||
history: GroupPreference
|
||||
) {
|
||||
self.timedMessages = timedMessages
|
||||
@@ -980,36 +1012,40 @@ public struct FullGroupPreferences: Decodable, Equatable {
|
||||
self.reactions = reactions
|
||||
self.voice = voice
|
||||
self.files = files
|
||||
self.simplexLinks = simplexLinks
|
||||
self.history = history
|
||||
}
|
||||
|
||||
public static let sampleData = FullGroupPreferences(
|
||||
timedMessages: TimedMessagesGroupPreference(enable: .off),
|
||||
directMessages: GroupPreference(enable: .off),
|
||||
directMessages: RoleGroupPreference(enable: .off, role: nil),
|
||||
fullDelete: GroupPreference(enable: .off),
|
||||
reactions: GroupPreference(enable: .on),
|
||||
voice: GroupPreference(enable: .on),
|
||||
files: GroupPreference(enable: .on),
|
||||
voice: RoleGroupPreference(enable: .on, role: nil),
|
||||
files: RoleGroupPreference(enable: .on, role: nil),
|
||||
simplexLinks: RoleGroupPreference(enable: .on, role: nil),
|
||||
history: GroupPreference(enable: .on)
|
||||
)
|
||||
}
|
||||
|
||||
public struct GroupPreferences: Codable {
|
||||
public var timedMessages: TimedMessagesGroupPreference?
|
||||
public var directMessages: GroupPreference?
|
||||
public var directMessages: RoleGroupPreference?
|
||||
public var fullDelete: GroupPreference?
|
||||
public var reactions: GroupPreference?
|
||||
public var voice: GroupPreference?
|
||||
public var files: GroupPreference?
|
||||
public var voice: RoleGroupPreference?
|
||||
public var files: RoleGroupPreference?
|
||||
public var simplexLinks: RoleGroupPreference?
|
||||
public var history: GroupPreference?
|
||||
|
||||
public init(
|
||||
timedMessages: TimedMessagesGroupPreference? = nil,
|
||||
directMessages: GroupPreference? = nil,
|
||||
directMessages: RoleGroupPreference? = nil,
|
||||
fullDelete: GroupPreference? = nil,
|
||||
reactions: GroupPreference? = nil,
|
||||
voice: GroupPreference? = nil,
|
||||
files: GroupPreference? = nil,
|
||||
voice: RoleGroupPreference? = nil,
|
||||
files: RoleGroupPreference? = nil,
|
||||
simplexLinks: RoleGroupPreference? = nil,
|
||||
history: GroupPreference? = nil
|
||||
) {
|
||||
self.timedMessages = timedMessages
|
||||
@@ -1018,16 +1054,18 @@ public struct GroupPreferences: Codable {
|
||||
self.reactions = reactions
|
||||
self.voice = voice
|
||||
self.files = files
|
||||
self.simplexLinks = simplexLinks
|
||||
self.history = history
|
||||
}
|
||||
|
||||
public static let sampleData = GroupPreferences(
|
||||
timedMessages: TimedMessagesGroupPreference(enable: .off),
|
||||
directMessages: GroupPreference(enable: .off),
|
||||
directMessages: RoleGroupPreference(enable: .off, role: nil),
|
||||
fullDelete: GroupPreference(enable: .off),
|
||||
reactions: GroupPreference(enable: .on),
|
||||
voice: GroupPreference(enable: .on),
|
||||
files: GroupPreference(enable: .on),
|
||||
voice: RoleGroupPreference(enable: .on, role: nil),
|
||||
files: RoleGroupPreference(enable: .on, role: nil),
|
||||
simplexLinks: RoleGroupPreference(enable: .on, role: nil),
|
||||
history: GroupPreference(enable: .on)
|
||||
)
|
||||
}
|
||||
@@ -1040,6 +1078,7 @@ public func toGroupPreferences(_ fullPreferences: FullGroupPreferences) -> Group
|
||||
reactions: fullPreferences.reactions,
|
||||
voice: fullPreferences.voice,
|
||||
files: fullPreferences.files,
|
||||
simplexLinks: fullPreferences.simplexLinks,
|
||||
history: fullPreferences.history
|
||||
)
|
||||
}
|
||||
@@ -1051,11 +1090,37 @@ public struct GroupPreference: Codable, Equatable {
|
||||
enable == .on
|
||||
}
|
||||
|
||||
public func enabled(_ role: GroupMemberRole?, for m: GroupMember?) -> GroupFeatureEnabled {
|
||||
switch enable {
|
||||
case .off: .off
|
||||
case .on:
|
||||
if let role, let m {
|
||||
m.memberRole >= role ? .on : .off
|
||||
} else {
|
||||
.on
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
public init(enable: GroupFeatureEnabled) {
|
||||
self.enable = enable
|
||||
}
|
||||
}
|
||||
|
||||
public struct RoleGroupPreference: Codable, Equatable {
|
||||
public var enable: GroupFeatureEnabled
|
||||
public var role: GroupMemberRole?
|
||||
|
||||
public func on(for m: GroupMember) -> Bool {
|
||||
enable == .on && m.memberRole >= (role ?? .observer)
|
||||
}
|
||||
|
||||
public init(enable: GroupFeatureEnabled, role: GroupMemberRole?) {
|
||||
self.enable = enable
|
||||
self.role = role
|
||||
}
|
||||
}
|
||||
|
||||
public struct TimedMessagesGroupPreference: Codable, Equatable {
|
||||
public var enable: GroupFeatureEnabled
|
||||
public var ttl: Int?
|
||||
@@ -1280,7 +1345,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat {
|
||||
case .timedMessages: return prefs.timedMessages.on
|
||||
case .fullDelete: return prefs.fullDelete.on
|
||||
case .reactions: return prefs.reactions.on
|
||||
case .voice: return prefs.voice.on
|
||||
case .voice: return prefs.voice.on(for: groupInfo.membership)
|
||||
case .calls: return false
|
||||
}
|
||||
case .local:
|
||||
@@ -1323,7 +1388,7 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat {
|
||||
return .other
|
||||
}
|
||||
case let .group(groupInfo):
|
||||
if !groupInfo.fullGroupPreferences.voice.on {
|
||||
if !groupInfo.fullGroupPreferences.voice.on(for: groupInfo.membership) {
|
||||
return .groupOwnerCan
|
||||
} else {
|
||||
return .other
|
||||
@@ -1975,7 +2040,7 @@ public struct GroupMemberIds: Decodable {
|
||||
var groupId: Int64
|
||||
}
|
||||
|
||||
public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Decodable {
|
||||
public enum GroupMemberRole: String, Identifiable, CaseIterable, Comparable, Codable {
|
||||
case observer = "observer"
|
||||
case author = "author"
|
||||
case member = "member"
|
||||
@@ -2372,10 +2437,10 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
public static func getSample (_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, quotedItem: CIQuote? = nil, file: CIFile? = nil, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, editable: Bool = true) -> ChatItem {
|
||||
public static func getSample (_ id: Int64, _ dir: CIDirection, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, quotedItem: CIQuote? = nil, file: CIFile? = nil, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> ChatItem {
|
||||
ChatItem(
|
||||
chatDir: dir,
|
||||
meta: CIMeta.getSample(id, ts, text, status, itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, editable: editable),
|
||||
meta: CIMeta.getSample(id, ts, text, status, itemDeleted: itemDeleted, itemEdited: itemEdited, itemLive: itemLive, deletable: deletable, editable: editable),
|
||||
content: .sndMsgContent(msgContent: .text(text)),
|
||||
quotedItem: quotedItem,
|
||||
file: file
|
||||
@@ -2466,6 +2531,7 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
itemDeleted: nil,
|
||||
itemEdited: false,
|
||||
itemLive: false,
|
||||
deletable: false,
|
||||
editable: false
|
||||
),
|
||||
content: .rcvDeleted(deleteMode: .cidmBroadcast),
|
||||
@@ -2487,6 +2553,7 @@ public struct ChatItem: Identifiable, Decodable {
|
||||
itemDeleted: nil,
|
||||
itemEdited: false,
|
||||
itemLive: true,
|
||||
deletable: false,
|
||||
editable: false
|
||||
),
|
||||
content: .sndMsgContent(msgContent: .text("")),
|
||||
@@ -2546,10 +2613,12 @@ public struct CIMeta: Decodable {
|
||||
public var itemStatus: CIStatus
|
||||
public var createdAt: Date
|
||||
public var updatedAt: Date
|
||||
public var itemForwarded: CIForwardedFrom?
|
||||
public var itemDeleted: CIDeleted?
|
||||
public var itemEdited: Bool
|
||||
public var itemTimed: CITimed?
|
||||
public var itemLive: Bool?
|
||||
public var deletable: Bool
|
||||
public var editable: Bool
|
||||
|
||||
public var timestampText: Text { get { formatTimestampText(itemTs) } }
|
||||
@@ -2566,7 +2635,7 @@ public struct CIMeta: Decodable {
|
||||
itemStatus.statusIcon(metaColor)
|
||||
}
|
||||
|
||||
public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, editable: Bool = true) -> CIMeta {
|
||||
public static func getSample(_ id: Int64, _ ts: Date, _ text: String, _ status: CIStatus = .sndNew, itemDeleted: CIDeleted? = nil, itemEdited: Bool = false, itemLive: Bool = false, deletable: Bool = true, editable: Bool = true) -> CIMeta {
|
||||
CIMeta(
|
||||
itemId: id,
|
||||
itemTs: ts,
|
||||
@@ -2577,6 +2646,7 @@ public struct CIMeta: Decodable {
|
||||
itemDeleted: itemDeleted,
|
||||
itemEdited: itemEdited,
|
||||
itemLive: itemLive,
|
||||
deletable: deletable,
|
||||
editable: editable
|
||||
)
|
||||
}
|
||||
@@ -2592,6 +2662,7 @@ public struct CIMeta: Decodable {
|
||||
itemDeleted: nil,
|
||||
itemEdited: false,
|
||||
itemLive: false,
|
||||
deletable: false,
|
||||
editable: false
|
||||
)
|
||||
}
|
||||
@@ -2713,6 +2784,31 @@ public enum CIDeleted: Decodable {
|
||||
}
|
||||
}
|
||||
|
||||
public enum MsgDirection: String, Decodable {
|
||||
case rcv = "rcv"
|
||||
case snd = "snd"
|
||||
}
|
||||
|
||||
public enum CIForwardedFrom: Decodable {
|
||||
case unknown
|
||||
case contact(chatName: String, msgDir: MsgDirection, contactId: Int64?, chatItemId: Int64?)
|
||||
case group(chatName: String, msgDir: MsgDirection, groupId: Int64?, chatItemId: Int64?)
|
||||
|
||||
var chatName: String {
|
||||
switch self {
|
||||
case .unknown: ""
|
||||
case let .contact(chatName, _, _, _): chatName
|
||||
case let .group(chatName, _, _, _): chatName
|
||||
}
|
||||
}
|
||||
|
||||
public func text(_ chatType: ChatType) -> LocalizedStringKey {
|
||||
chatType == .local
|
||||
? (chatName == "" ? "saved" : "saved from \(chatName)")
|
||||
: "forwarded"
|
||||
}
|
||||
}
|
||||
|
||||
public enum CIDeleteMode: String, Decodable {
|
||||
case cidmBroadcast = "broadcast"
|
||||
case cidmInternal = "internal"
|
||||
@@ -2742,8 +2838,8 @@ public enum CIContent: Decodable, ItemContent {
|
||||
case sndChatFeature(feature: ChatFeature, enabled: FeatureEnabled, param: Int?)
|
||||
case rcvChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?)
|
||||
case sndChatPreference(feature: ChatFeature, allowed: FeatureAllowed, param: Int?)
|
||||
case rcvGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?)
|
||||
case sndGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?)
|
||||
case rcvGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?, memberRole_: GroupMemberRole?)
|
||||
case sndGroupFeature(groupFeature: GroupFeature, preference: GroupPreference, param: Int?, memberRole_: GroupMemberRole?)
|
||||
case rcvChatFeatureRejected(feature: ChatFeature)
|
||||
case rcvGroupFeatureRejected(groupFeature: GroupFeature)
|
||||
case sndModerated
|
||||
@@ -2777,8 +2873,8 @@ public enum CIContent: Decodable, ItemContent {
|
||||
case let .sndChatFeature(feature, enabled, param): return CIContent.featureText(feature, enabled.text, param)
|
||||
case let .rcvChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param)
|
||||
case let .sndChatPreference(feature, allowed, param): return CIContent.preferenceText(feature, allowed, param)
|
||||
case let .rcvGroupFeature(feature, preference, param): return CIContent.featureText(feature, preference.enable.text, param)
|
||||
case let .sndGroupFeature(feature, preference, param): return CIContent.featureText(feature, preference.enable.text, param)
|
||||
case let .rcvGroupFeature(feature, preference, param, role): return CIContent.featureText(feature, preference.enable.text, param, role)
|
||||
case let .sndGroupFeature(feature, preference, param, role): return CIContent.featureText(feature, preference.enable.text, param, role)
|
||||
case let .rcvChatFeatureRejected(feature): return String.localizedStringWithFormat("%@: received, prohibited", feature.text)
|
||||
case let .rcvGroupFeatureRejected(groupFeature): return String.localizedStringWithFormat("%@: received, prohibited", groupFeature.text)
|
||||
case .sndModerated: return NSLocalizedString("moderated", comment: "moderated chat item")
|
||||
@@ -2803,10 +2899,25 @@ public enum CIContent: Decodable, ItemContent {
|
||||
NSLocalizedString("This chat is protected by end-to-end encryption.", comment: "E2EE info chat item")
|
||||
}
|
||||
|
||||
static func featureText(_ feature: Feature, _ enabled: String, _ param: Int?) -> String {
|
||||
feature.hasParam
|
||||
? "\(feature.text): \(timeText(param))"
|
||||
: "\(feature.text): \(enabled)"
|
||||
static func featureText(_ feature: Feature, _ enabled: String, _ param: Int?, _ role: GroupMemberRole? = nil) -> String {
|
||||
(
|
||||
feature.hasParam
|
||||
? "\(feature.text): \(timeText(param))"
|
||||
: "\(feature.text): \(enabled)"
|
||||
) +
|
||||
(
|
||||
feature.hasRole && role != nil
|
||||
? " (\(roleText(role)))"
|
||||
: ""
|
||||
)
|
||||
}
|
||||
|
||||
private static func roleText(_ role: GroupMemberRole?) -> String {
|
||||
switch role {
|
||||
case .owner: NSLocalizedString("owners", comment: "feature role")
|
||||
case .admin: NSLocalizedString("admins", comment: "feature role")
|
||||
default: NSLocalizedString("all members", comment: "feature role")
|
||||
}
|
||||
}
|
||||
|
||||
public static func preferenceText(_ feature: Feature, _ allowed: FeatureAllowed, _ param: Int?) -> String {
|
||||
@@ -3751,6 +3862,7 @@ public enum ChatItemTTL: Hashable, Identifiable, Comparable {
|
||||
public struct ChatItemInfo: Decodable {
|
||||
public var itemVersions: [ChatItemVersion]
|
||||
public var memberDeliveryStatuses: [MemberDeliveryStatus]?
|
||||
public var forwardedFromChatItem: AChatItem?
|
||||
}
|
||||
|
||||
public struct ChatItemVersion: Decodable {
|
||||
|
||||
Binary file not shown.
Binary file not shown.
@@ -48,12 +48,7 @@ android {
|
||||
proguardFiles(getDefaultProguardFile("proguard-android-optimize.txt"), "proguard-rules.pro")
|
||||
}
|
||||
}
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
kotlinOptions {
|
||||
jvmTarget = "11"
|
||||
freeCompilerArgs += "-opt-in=kotlinx.coroutines.DelicateCoroutinesApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.foundation.ExperimentalFoundationApi"
|
||||
freeCompilerArgs += "-opt-in=androidx.compose.ui.text.ExperimentalTextApi"
|
||||
|
||||
@@ -89,11 +89,12 @@ class MainActivity: FragmentActivity() {
|
||||
}
|
||||
|
||||
override fun onBackPressed() {
|
||||
if (
|
||||
onBackPressedDispatcher.hasEnabledCallbacks() // Has something to do in a backstack
|
||||
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.R // Android 11 or above
|
||||
|| isTaskRoot // there are still other tasks after we reach the main (home) activity
|
||||
) {
|
||||
val canFinishActivity = (
|
||||
onBackPressedDispatcher.hasEnabledCallbacks() // Has something to do in a backstack
|
||||
|| Build.VERSION.SDK_INT >= Build.VERSION_CODES.R // Android 11 or above
|
||||
|| isTaskRoot // there are still other tasks after we reach the main (home) activity
|
||||
) && SimplexApp.context.chatModel.sharedContent.value !is SharedContent.Forward
|
||||
if (canFinishActivity) {
|
||||
// https://medium.com/mobile-app-development-publication/the-risk-of-android-strandhogg-security-issue-and-how-it-can-be-mitigated-80d2ddb4af06
|
||||
super.onBackPressed()
|
||||
}
|
||||
@@ -104,9 +105,15 @@ class MainActivity: FragmentActivity() {
|
||||
AppLock.laFailed.value = true
|
||||
}
|
||||
if (!onBackPressedDispatcher.hasEnabledCallbacks()) {
|
||||
val sharedContent = chatModel.sharedContent.value
|
||||
// Drop shared content
|
||||
SimplexApp.context.chatModel.sharedContent.value = null
|
||||
finish()
|
||||
chatModel.sharedContent.value = null
|
||||
if (sharedContent is SharedContent.Forward) {
|
||||
chatModel.chatId.value = sharedContent.fromChatInfo.id
|
||||
}
|
||||
if (canFinishActivity) {
|
||||
finish()
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -16,8 +16,7 @@ import androidx.work.*
|
||||
import chat.simplex.app.model.NtfManager
|
||||
import chat.simplex.app.model.NtfManager.AcceptCallAction
|
||||
import chat.simplex.app.views.call.CallActivity
|
||||
import chat.simplex.common.helpers.APPLICATION_ID
|
||||
import chat.simplex.common.helpers.requiresIgnoringBattery
|
||||
import chat.simplex.common.helpers.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatController.appPrefs
|
||||
import chat.simplex.common.model.ChatModel.updatingChatsMutex
|
||||
@@ -291,6 +290,10 @@ class SimplexApp: Application(), LifecycleEventObserver {
|
||||
activeCallDestroyWebView()
|
||||
}
|
||||
|
||||
override fun androidRestartNetworkObserver() {
|
||||
NetworkObserver.shared.restartNetworkObserver()
|
||||
}
|
||||
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
@Composable
|
||||
override fun androidLockPortraitOrientation() {
|
||||
|
||||
@@ -83,6 +83,30 @@ plugins {
|
||||
id("org.jetbrains.kotlin.plugin.serialization") apply false
|
||||
}
|
||||
|
||||
// https://raymondctc.medium.com/configuring-your-sourcecompatibility-targetcompatibility-and-kotlinoptions-jvmtarget-all-at-once-66bf2198145f
|
||||
val jvmVersion: Provider<String> = providers.gradleProperty("kotlin.jvm.target")
|
||||
|
||||
configure(subprojects) {
|
||||
// Apply compileOptions to subprojects
|
||||
plugins.withType<com.android.build.gradle.BasePlugin>().configureEach {
|
||||
extensions.findByType<com.android.build.gradle.BaseExtension>()?.apply {
|
||||
jvmVersion.map { JavaVersion.toVersion(it) }.orNull?.let {
|
||||
compileOptions {
|
||||
sourceCompatibility = it
|
||||
targetCompatibility = it
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
// Apply kotlinOptions.jvmTarget to subprojects
|
||||
tasks.withType<org.jetbrains.kotlin.gradle.tasks.KotlinCompile>().configureEach {
|
||||
kotlinOptions {
|
||||
if (jvmVersion.isPresent) jvmTarget = jvmVersion.get()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
tasks.register("clean", Delete::class) {
|
||||
delete(rootProject.buildDir)
|
||||
}
|
||||
|
||||
@@ -12,9 +12,7 @@ version = extra["android.version_name"] as String
|
||||
|
||||
kotlin {
|
||||
androidTarget()
|
||||
jvm("desktop") {
|
||||
jvmToolchain(11)
|
||||
}
|
||||
jvm("desktop")
|
||||
applyDefaultHierarchyTemplate()
|
||||
sourceSets {
|
||||
all {
|
||||
@@ -119,10 +117,6 @@ android {
|
||||
}
|
||||
testOptions.targetSdk = 33
|
||||
lint.targetSdk = 33
|
||||
compileOptions {
|
||||
sourceCompatibility = JavaVersion.VERSION_11
|
||||
targetCompatibility = JavaVersion.VERSION_11
|
||||
}
|
||||
val isAndroid = gradle.startParameter.taskNames.find {
|
||||
val lower = it.lowercase()
|
||||
lower.contains("release") || lower.startsWith("assemble") || lower.startsWith("install")
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
<?xml version="1.0" encoding="utf-8"?>
|
||||
<manifest xmlns:android="http://schemas.android.com/apk/res/android" package="chat.simplex.common">
|
||||
<uses-permission android:name="android.permission.VIBRATE" />
|
||||
<uses-permission android:name="android.permission.ACCESS_NETWORK_STATE" />
|
||||
</manifest>
|
||||
|
||||
+104
@@ -0,0 +1,104 @@
|
||||
package chat.simplex.common.helpers
|
||||
|
||||
import android.net.*
|
||||
import android.util.Log
|
||||
import androidx.core.content.getSystemService
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.UserNetworkInfo
|
||||
import chat.simplex.common.model.UserNetworkType
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.withBGApi
|
||||
import kotlinx.coroutines.Job
|
||||
import kotlinx.coroutines.delay
|
||||
|
||||
class NetworkObserver {
|
||||
private var prevInfo: UserNetworkInfo? = null
|
||||
|
||||
// When having both mobile and Wi-Fi networks enabled with Wi-Fi being active, then disabling Wi-Fi, network reports its offline (which is true)
|
||||
// but since it will be online after switching to mobile, there is no need to inform backend about such temporary change.
|
||||
// But if it will not be online after some seconds, report it and apply required measures
|
||||
private var noNetworkJob = Job() as Job
|
||||
private val networkCallback = object: ConnectivityManager.NetworkCallback() {
|
||||
override fun onCapabilitiesChanged(network: Network, networkCapabilities: NetworkCapabilities) = networkCapabilitiesChanged(networkCapabilities)
|
||||
override fun onLost(network: Network) = networkLost()
|
||||
}
|
||||
private val connectivityManager: ConnectivityManager? = androidAppContext.getSystemService()
|
||||
|
||||
fun restartNetworkObserver() {
|
||||
if (connectivityManager == null) {
|
||||
Log.e(TAG, "Connectivity manager is unavailable, network observer is disabled")
|
||||
val info = UserNetworkInfo(
|
||||
networkType = UserNetworkType.OTHER,
|
||||
online = true,
|
||||
)
|
||||
prevInfo = info
|
||||
setNetworkInfo(info)
|
||||
return
|
||||
}
|
||||
try {
|
||||
connectivityManager.unregisterNetworkCallback(networkCallback)
|
||||
} catch (e: Exception) {
|
||||
// do nothing
|
||||
}
|
||||
val initialCapabilities = connectivityManager.getNetworkCapabilities(connectivityManager.activeNetwork)
|
||||
if (initialCapabilities != null) {
|
||||
networkCapabilitiesChanged(initialCapabilities)
|
||||
} else {
|
||||
networkLost()
|
||||
}
|
||||
try {
|
||||
connectivityManager.registerDefaultNetworkCallback(networkCallback)
|
||||
} catch (e: Exception) {
|
||||
Log.e(TAG, "Error registering network callback: ${e.stackTraceToString()}")
|
||||
}
|
||||
}
|
||||
|
||||
private fun networkCapabilitiesChanged(capabilities: NetworkCapabilities) {
|
||||
connectivityManager ?: return
|
||||
val info = UserNetworkInfo(
|
||||
networkType = networkTypeFromCapabilities(capabilities),
|
||||
online = capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_INTERNET) && capabilities.hasCapability(NetworkCapabilities.NET_CAPABILITY_VALIDATED),
|
||||
)
|
||||
if (prevInfo != info) {
|
||||
prevInfo = info
|
||||
setNetworkInfo(info)
|
||||
}
|
||||
}
|
||||
|
||||
private fun networkLost() {
|
||||
Log.d(TAG, "Network changed: lost")
|
||||
val none = UserNetworkInfo(networkType = UserNetworkType.NONE, false)
|
||||
prevInfo = none
|
||||
setNetworkInfo(none)
|
||||
}
|
||||
|
||||
private fun setNetworkInfo(info: UserNetworkInfo) {
|
||||
Log.d(TAG, "Network changed: $info")
|
||||
noNetworkJob.cancel()
|
||||
if (info.online) {
|
||||
withBGApi {
|
||||
if (controller.hasChatCtrl() && controller.apiSetNetworkInfo(info)) {
|
||||
chatModel.networkInfo.value = info
|
||||
}
|
||||
}
|
||||
} else {
|
||||
noNetworkJob = withBGApi {
|
||||
delay(3000)
|
||||
if (controller.hasChatCtrl() && controller.apiSetNetworkInfo(info)) {
|
||||
chatModel.networkInfo.value = info
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
private fun networkTypeFromCapabilities(capabilities: NetworkCapabilities): UserNetworkType = when {
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_ETHERNET) -> UserNetworkType.ETHERNET
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_WIFI) -> UserNetworkType.WIFI
|
||||
capabilities.hasTransport(NetworkCapabilities.TRANSPORT_CELLULAR) -> UserNetworkType.CELLULAR
|
||||
else -> UserNetworkType.OTHER
|
||||
}
|
||||
|
||||
companion object {
|
||||
val shared = NetworkObserver()
|
||||
}
|
||||
}
|
||||
+84
-6
@@ -2,17 +2,16 @@ package chat.simplex.common.helpers
|
||||
|
||||
import android.media.*
|
||||
import android.net.Uri
|
||||
import android.os.VibrationEffect
|
||||
import android.os.Vibrator
|
||||
import android.os.*
|
||||
import androidx.core.content.ContextCompat
|
||||
import chat.simplex.common.R
|
||||
import chat.simplex.common.platform.SoundPlayerInterface
|
||||
import chat.simplex.common.platform.androidAppContext
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.withApi
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
object SoundPlayer: SoundPlayerInterface {
|
||||
private var player: MediaPlayer? = null
|
||||
var playing = false
|
||||
private var playing = false
|
||||
|
||||
override fun start(scope: CoroutineScope, sound: Boolean) {
|
||||
player?.reset()
|
||||
@@ -32,7 +31,7 @@ object SoundPlayer: SoundPlayerInterface {
|
||||
scope.launch {
|
||||
while (playing) {
|
||||
if (sound) player?.start()
|
||||
vibrator?.vibrate(effect)
|
||||
vibrator?.vibrateApiVersionAware(effect)
|
||||
delay(3500)
|
||||
}
|
||||
}
|
||||
@@ -43,3 +42,82 @@ object SoundPlayer: SoundPlayerInterface {
|
||||
player?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
object CallSoundsPlayer: CallSoundsPlayerInterface {
|
||||
private var player: MediaPlayer? = null
|
||||
private var playingJob: Job? = null
|
||||
|
||||
private fun start(soundPath: String, delay: Long, scope: CoroutineScope) {
|
||||
playingJob?.cancel()
|
||||
player?.reset()
|
||||
player = MediaPlayer().apply {
|
||||
setAudioAttributes(
|
||||
AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_VOICE_COMMUNICATION_SIGNALLING)
|
||||
.build()
|
||||
)
|
||||
setDataSource(androidAppContext, Uri.parse(soundPath))
|
||||
prepare()
|
||||
}
|
||||
if (delay < 1000) {
|
||||
player?.isLooping = true
|
||||
player?.start()
|
||||
return
|
||||
}
|
||||
playingJob = scope.launch {
|
||||
while (isActive) {
|
||||
player?.start()
|
||||
delay(delay)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun startConnectingCallSound(scope: CoroutineScope) {
|
||||
// Taken from https://github.com/TelegramOrg/Telegram-Android
|
||||
// https://github.com/TelegramOrg/Telegram-Android/blob/master/LICENSE
|
||||
start("android.resource://" + androidAppContext.packageName + "/" + R.raw.connecting_call, 0, scope)
|
||||
}
|
||||
|
||||
override fun startInCallSound(scope: CoroutineScope) {
|
||||
start("android.resource://" + androidAppContext.packageName + "/" + R.raw.in_call, 2000, scope)
|
||||
}
|
||||
|
||||
override fun vibrate(times: Int) {
|
||||
val vibrator = ContextCompat.getSystemService(androidAppContext, Vibrator::class.java)
|
||||
val effect = VibrationEffect.createOneShot(20, VibrationEffect.DEFAULT_AMPLITUDE)
|
||||
vibrator?.vibrateApiVersionAware(effect)
|
||||
repeat(times - 1) {
|
||||
withApi {
|
||||
delay(50)
|
||||
vibrator?.vibrateApiVersionAware(effect)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
playingJob?.cancel()
|
||||
player?.stop()
|
||||
}
|
||||
}
|
||||
|
||||
private fun Vibrator.vibrateApiVersionAware(effect: VibrationEffect) {
|
||||
if (Build.VERSION.SDK_INT >= 33) {
|
||||
vibrate(
|
||||
effect,
|
||||
VibrationAttributes.Builder()
|
||||
.setUsage(VibrationAttributes.USAGE_ALARM)
|
||||
.build()
|
||||
)
|
||||
} else if (Build.VERSION.SDK_INT >= 29) {
|
||||
vibrate(
|
||||
effect,
|
||||
AudioAttributes.Builder()
|
||||
.setContentType(AudioAttributes.CONTENT_TYPE_SONIFICATION)
|
||||
.setUsage(AudioAttributes.USAGE_ALARM)
|
||||
.build()
|
||||
)
|
||||
} else {
|
||||
vibrate(effect)
|
||||
}
|
||||
}
|
||||
|
||||
+1
@@ -296,6 +296,7 @@ actual object AudioPlayer: AudioPlayerInterface {
|
||||
}
|
||||
|
||||
actual typealias SoundPlayer = chat.simplex.common.helpers.SoundPlayer
|
||||
actual typealias CallSoundsPlayer = chat.simplex.common.helpers.CallSoundsPlayer
|
||||
|
||||
class CryptoMediaSource(val data: ByteArray) : MediaDataSource() {
|
||||
override fun readAt(position: Long, buffer: ByteArray, offset: Int, size: Int): Int {
|
||||
|
||||
+235
@@ -0,0 +1,235 @@
|
||||
package chat.simplex.common.views.call
|
||||
|
||||
import android.content.Context
|
||||
import android.media.*
|
||||
import android.media.AudioManager.OnCommunicationDeviceChangedListener
|
||||
import android.os.Build
|
||||
import androidx.annotation.RequiresApi
|
||||
import androidx.compose.runtime.*
|
||||
import chat.simplex.common.platform.*
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import java.util.concurrent.Executors
|
||||
|
||||
interface CallAudioDeviceManagerInterface {
|
||||
val devices: State<List<AudioDeviceInfo>>
|
||||
val currentDevice: MutableState<AudioDeviceInfo?>
|
||||
fun start()
|
||||
fun stop()
|
||||
// AudioDeviceInfo.AudioDeviceType
|
||||
fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean)
|
||||
// AudioDeviceInfo.AudioDeviceType
|
||||
fun selectDevice(id: Int)
|
||||
|
||||
companion object {
|
||||
fun new(): CallAudioDeviceManagerInterface =
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
PostSCallAudioDeviceManager()
|
||||
} else {
|
||||
PreSCallAudioDeviceManager()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@RequiresApi(Build.VERSION_CODES.S)
|
||||
class PostSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
|
||||
private val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
override val devices: MutableState<List<AudioDeviceInfo>> = mutableStateOf(emptyList())
|
||||
override val currentDevice: MutableState<AudioDeviceInfo?> = mutableStateOf(null)
|
||||
|
||||
private val audioCallback = object: AudioDeviceCallback() {
|
||||
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
|
||||
super.onAudioDevicesAdded(addedDevices)
|
||||
val oldDevices = devices.value
|
||||
devices.value = am.availableCommunicationDevices
|
||||
Log.d(TAG, "Added audio devices2: ${devices.value.map { it.type }}")
|
||||
|
||||
if (devices.value.size - oldDevices.size > 0) {
|
||||
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, false)
|
||||
}
|
||||
}
|
||||
|
||||
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
|
||||
super.onAudioDevicesRemoved(removedDevices)
|
||||
devices.value = am.availableCommunicationDevices
|
||||
}
|
||||
}
|
||||
|
||||
private val listener: OnCommunicationDeviceChangedListener = OnCommunicationDeviceChangedListener { device ->
|
||||
devices.value = am.availableCommunicationDevices
|
||||
currentDevice.value = device
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
am.registerAudioDeviceCallback(audioCallback, null)
|
||||
am.addOnCommunicationDeviceChangedListener(Executors.newSingleThreadExecutor(), listener)
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
am.unregisterAudioDeviceCallback(audioCallback)
|
||||
am.removeOnCommunicationDeviceChangedListener(listener)
|
||||
}
|
||||
|
||||
override fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean) {
|
||||
Log.d(TAG, "selectLastExternalDeviceOrDefault: set audio mode, speaker enabled: $speaker")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
val commDevice = am.communicationDevice
|
||||
if (keepAnyNonEarpiece && commDevice != null && commDevice.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE) {
|
||||
// some external device or speaker selected already, no need to change it
|
||||
return
|
||||
}
|
||||
|
||||
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
val externalDevice = devices.value.lastOrNull { it.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && it.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE }
|
||||
// External device already selected
|
||||
if (externalDevice != null && externalDevice.type == am.communicationDevice?.type) {
|
||||
return
|
||||
}
|
||||
if (externalDevice != null) {
|
||||
am.setCommunicationDevice(externalDevice)
|
||||
} else if (am.communicationDevice?.type != preferredSecondaryDevice) {
|
||||
am.availableCommunicationDevices.firstOrNull { it.type == preferredSecondaryDevice }?.let {
|
||||
am.setCommunicationDevice(it)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
override fun selectDevice(id: Int) {
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
val device = devices.value.lastOrNull { it.id == id }
|
||||
if (device != null && am.communicationDevice?.id != id ) {
|
||||
am.setCommunicationDevice(device)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
class PreSCallAudioDeviceManager: CallAudioDeviceManagerInterface {
|
||||
private val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
override val devices: MutableState<List<AudioDeviceInfo>> = mutableStateOf(emptyList())
|
||||
override val currentDevice: MutableState<AudioDeviceInfo?> = mutableStateOf(null)
|
||||
|
||||
private val audioCallback = object: AudioDeviceCallback() {
|
||||
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
|
||||
super.onAudioDevicesAdded(addedDevices)
|
||||
val wasSize = devices.value.size
|
||||
devices.value += addedDevices.filter { it.hasSupportedType() }
|
||||
val addedCount = devices.value.size - wasSize
|
||||
//if (addedCount > 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
|
||||
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
|
||||
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, false)
|
||||
//}
|
||||
}
|
||||
|
||||
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
|
||||
super.onAudioDevicesRemoved(removedDevices)
|
||||
val wasSize = devices.value.size
|
||||
devices.value = devices.value.filterNot { removedDevices.any { rm -> rm.id == it.id } }
|
||||
//val removedCount = wasSize - devices.value.size
|
||||
//if (devices.value.count { it.hasSupportedType() } == 2 && chatModel.activeCall.value?.callState == CallState.Connected) {
|
||||
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
|
||||
selectLastExternalDeviceOrDefault(chatModel.activeCall.value?.supportsVideo() == true, true)
|
||||
//}
|
||||
}
|
||||
}
|
||||
|
||||
override fun start() {
|
||||
am.registerAudioDeviceCallback(audioCallback, null)
|
||||
}
|
||||
|
||||
override fun stop() {
|
||||
am.unregisterAudioDeviceCallback(audioCallback)
|
||||
}
|
||||
|
||||
override fun selectLastExternalDeviceOrDefault(speaker: Boolean, keepAnyNonEarpiece: Boolean) {
|
||||
Log.d(TAG, "selectLastExternalDeviceOrDefault: set audio mode, speaker enabled: $speaker")
|
||||
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
val externalDevice = devices.value.lastOrNull { it.hasSupportedType() && it.isSource && it.isSink && it.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && it.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE }
|
||||
if (externalDevice != null) {
|
||||
selectDevice(externalDevice.id)
|
||||
} else {
|
||||
am.stopBluetoothSco()
|
||||
am.isWiredHeadsetOn = false
|
||||
am.isSpeakerphoneOn = preferredSecondaryDevice == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
|
||||
am.isBluetoothScoOn = false
|
||||
val newCurrentDevice = devices.value.firstOrNull { it.type == preferredSecondaryDevice }
|
||||
adaptToCurrentlyActiveDevice(newCurrentDevice)
|
||||
}
|
||||
}
|
||||
|
||||
override fun selectDevice(id: Int) {
|
||||
val device = devices.value.lastOrNull { it.id == id }
|
||||
val isExternalDevice = device != null && device.type != AudioDeviceInfo.TYPE_BUILTIN_SPEAKER && device.type != AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
if (isExternalDevice) {
|
||||
am.isSpeakerphoneOn = false
|
||||
if (device?.type == AudioDeviceInfo.TYPE_WIRED_HEADPHONES) {
|
||||
am.isWiredHeadsetOn = true
|
||||
am.stopBluetoothSco()
|
||||
am.isBluetoothScoOn = false
|
||||
} else {
|
||||
am.isWiredHeadsetOn = false
|
||||
am.startBluetoothSco()
|
||||
am.isBluetoothScoOn = true
|
||||
}
|
||||
adaptToCurrentlyActiveDevice(device)
|
||||
} else {
|
||||
am.stopBluetoothSco()
|
||||
am.isWiredHeadsetOn = false
|
||||
am.isSpeakerphoneOn = device?.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER
|
||||
am.isBluetoothScoOn = false
|
||||
adaptToCurrentlyActiveDevice(device)
|
||||
}
|
||||
}
|
||||
|
||||
private fun adaptToCurrentlyActiveDevice(newCurrentDevice: AudioDeviceInfo?) {
|
||||
currentDevice.value = newCurrentDevice
|
||||
}
|
||||
|
||||
private fun AudioDeviceInfo.hasSupportedType(): Boolean = when (type) {
|
||||
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> true
|
||||
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> true
|
||||
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_SCO -> true
|
||||
AudioDeviceInfo.TYPE_BLE_HEADSET -> true
|
||||
AudioDeviceInfo.TYPE_BLE_SPEAKER -> true
|
||||
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> true
|
||||
else -> false
|
||||
}
|
||||
}
|
||||
|
||||
val AudioDeviceInfo.icon: ImageResource
|
||||
get() = when (this.type) {
|
||||
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> MR.images.ic_volume_down
|
||||
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> MR.images.ic_volume_up
|
||||
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
|
||||
AudioDeviceInfo.TYPE_BLE_HEADSET,
|
||||
AudioDeviceInfo.TYPE_BLE_SPEAKER -> MR.images.ic_bluetooth
|
||||
|
||||
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> MR.images.ic_headphones
|
||||
|
||||
AudioDeviceInfo.TYPE_USB_HEADSET, AudioDeviceInfo.TYPE_USB_DEVICE -> MR.images.ic_usb
|
||||
else -> MR.images.ic_brand_awareness_filled
|
||||
}
|
||||
|
||||
val AudioDeviceInfo.name: StringResource?
|
||||
get() = when (this.type) {
|
||||
AudioDeviceInfo.TYPE_BUILTIN_EARPIECE -> MR.strings.audio_device_earpiece
|
||||
AudioDeviceInfo.TYPE_BUILTIN_SPEAKER -> MR.strings.audio_device_speaker
|
||||
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_SCO,
|
||||
AudioDeviceInfo.TYPE_BLUETOOTH_A2DP,
|
||||
AudioDeviceInfo.TYPE_BLE_HEADSET,
|
||||
AudioDeviceInfo.TYPE_BLE_SPEAKER -> null // Use product name instead
|
||||
|
||||
AudioDeviceInfo.TYPE_WIRED_HEADPHONES -> MR.strings.audio_device_wired_headphones
|
||||
|
||||
AudioDeviceInfo.TYPE_USB_HEADSET, AudioDeviceInfo.TYPE_USB_DEVICE -> null // Use product name instead
|
||||
else -> null // Use product name instead
|
||||
}
|
||||
|
||||
+72
-74
@@ -26,8 +26,7 @@ import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.ui.text.style.TextAlign
|
||||
import androidx.compose.ui.text.style.TextOverflow
|
||||
@@ -39,14 +38,14 @@ import androidx.webkit.WebViewAssetLoader
|
||||
import androidx.webkit.WebViewClientCompat
|
||||
import chat.simplex.common.helpers.showAllowPermissionInSettingsAlert
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.model.Contact
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
import com.google.accompanist.permissions.*
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.flow.filterNotNull
|
||||
@@ -69,7 +68,8 @@ fun activeCallDestroyWebView() = withApi {
|
||||
@SuppressLint("SourceLockedOrientationActivity")
|
||||
@Composable
|
||||
actual fun ActiveCallView() {
|
||||
val audioViaBluetooth = rememberSaveable { mutableStateOf(false) }
|
||||
val call = remember { chatModel.activeCall }.value
|
||||
val scope = rememberCoroutineScope()
|
||||
val proximityLock = remember {
|
||||
val pm = (androidAppContext.getSystemService(Context.POWER_SERVICE) as PowerManager)
|
||||
if (pm.isWakeLockLevelSupported(PROXIMITY_SCREEN_OFF_WAKE_LOCK)) {
|
||||
@@ -78,37 +78,23 @@ actual fun ActiveCallView() {
|
||||
null
|
||||
}
|
||||
}
|
||||
DisposableEffect(Unit) {
|
||||
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
var btDeviceCount = 0
|
||||
val audioCallback = object: AudioDeviceCallback() {
|
||||
override fun onAudioDevicesAdded(addedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Added audio devices: ${addedDevices.map { it.type }}")
|
||||
super.onAudioDevicesAdded(addedDevices)
|
||||
val addedCount = addedDevices.count { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
|
||||
btDeviceCount += addedCount
|
||||
audioViaBluetooth.value = btDeviceCount > 0
|
||||
if (addedCount > 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
|
||||
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
|
||||
setCallSound(chatModel.activeCall.value?.soundSpeaker ?: return, audioViaBluetooth)
|
||||
}
|
||||
}
|
||||
override fun onAudioDevicesRemoved(removedDevices: Array<out AudioDeviceInfo>) {
|
||||
Log.d(TAG, "Removed audio devices: ${removedDevices.map { it.type }}")
|
||||
super.onAudioDevicesRemoved(removedDevices)
|
||||
val removedCount = removedDevices.count { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
|
||||
btDeviceCount -= removedCount
|
||||
audioViaBluetooth.value = btDeviceCount > 0
|
||||
if (btDeviceCount == 0 && chatModel.activeCall.value?.callState == CallState.Connected) {
|
||||
// Setting params in Connected state makes sure that Bluetooth will NOT be broken on Android < 12
|
||||
setCallSound(chatModel.activeCall.value?.soundSpeaker ?: return, audioViaBluetooth)
|
||||
}
|
||||
}
|
||||
val wasConnected = rememberSaveable { mutableStateOf(false) }
|
||||
LaunchedEffect(call) {
|
||||
if (call?.callState == CallState.Connected && !wasConnected.value) {
|
||||
CallSoundsPlayer.vibrate(2)
|
||||
wasConnected.value = true
|
||||
}
|
||||
am.registerAudioDeviceCallback(audioCallback, null)
|
||||
}
|
||||
val callAudioDeviceManager = remember { CallAudioDeviceManagerInterface.new() }
|
||||
DisposableEffect(Unit) {
|
||||
callAudioDeviceManager.start()
|
||||
onDispose {
|
||||
CallSoundsPlayer.stop()
|
||||
if (wasConnected.value) {
|
||||
CallSoundsPlayer.vibrate()
|
||||
}
|
||||
dropAudioManagerOverrides()
|
||||
am.unregisterAudioDeviceCallback(audioCallback)
|
||||
callAudioDeviceManager.stop()
|
||||
if (proximityLock?.isHeld == true) {
|
||||
proximityLock.release()
|
||||
}
|
||||
@@ -122,8 +108,6 @@ actual fun ActiveCallView() {
|
||||
if (proximityLock?.isHeld == false) proximityLock.acquire()
|
||||
}
|
||||
}
|
||||
val scope = rememberCoroutineScope()
|
||||
val call = chatModel.activeCall.value
|
||||
Box(Modifier.fillMaxSize()) {
|
||||
WebRTCView(chatModel.callCommand) { apiMsg ->
|
||||
Log.d(TAG, "received from WebRTCView: $apiMsg")
|
||||
@@ -136,6 +120,9 @@ actual fun ActiveCallView() {
|
||||
val callType = CallType(call.localMedia, r.capabilities)
|
||||
chatModel.controller.apiSendCallInvitation(callRh, call.contact, callType)
|
||||
updateActiveCall(call) { it.copy(callState = CallState.InvitationSent, localCapabilities = r.capabilities) }
|
||||
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
|
||||
CallSoundsPlayer.startConnectingCallSound(scope)
|
||||
activeCallWaitDeliveryReceipt(scope)
|
||||
}
|
||||
is WCallResponse.Offer -> withBGApi {
|
||||
chatModel.controller.apiSendCallOffer(callRh, call.contact, r.offer, r.iceCandidates, call.localMedia, r.capabilities)
|
||||
@@ -144,6 +131,7 @@ actual fun ActiveCallView() {
|
||||
is WCallResponse.Answer -> withBGApi {
|
||||
chatModel.controller.apiSendCallAnswer(callRh, call.contact, r.answer, r.iceCandidates)
|
||||
updateActiveCall(call) { it.copy(callState = CallState.Negotiated) }
|
||||
CallSoundsPlayer.stop()
|
||||
}
|
||||
is WCallResponse.Ice -> withBGApi {
|
||||
chatModel.controller.apiSendCallExtraInfo(callRh, call.contact, r.iceCandidates)
|
||||
@@ -153,7 +141,7 @@ actual fun ActiveCallView() {
|
||||
val callStatus = json.decodeFromString<WebRTCCallStatus>("\"${r.state.connectionState}\"")
|
||||
if (callStatus == WebRTCCallStatus.Connected) {
|
||||
updateActiveCall(call) { it.copy(callState = CallState.Connected, connectedAt = Clock.System.now()) }
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
|
||||
}
|
||||
withBGApi { chatModel.controller.apiCallStatus(callRh, call.contact, callStatus) }
|
||||
} catch (e: Throwable) {
|
||||
@@ -162,7 +150,7 @@ actual fun ActiveCallView() {
|
||||
is WCallResponse.Connected -> {
|
||||
updateActiveCall(call) { it.copy(callState = CallState.Connected, connectionInfo = r.connectionInfo) }
|
||||
scope.launch {
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
|
||||
}
|
||||
}
|
||||
is WCallResponse.End -> {
|
||||
@@ -208,7 +196,7 @@ actual fun ActiveCallView() {
|
||||
else -> false
|
||||
}
|
||||
if (call != null && showOverlay) {
|
||||
ActiveCallOverlay(call, chatModel, audioViaBluetooth)
|
||||
ActiveCallOverlay(call, chatModel, callAudioDeviceManager)
|
||||
}
|
||||
}
|
||||
val context = LocalContext.current
|
||||
@@ -234,19 +222,21 @@ actual fun ActiveCallView() {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, audioViaBluetooth: MutableState<Boolean>) {
|
||||
private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, callAudioDeviceManager: CallAudioDeviceManagerInterface) {
|
||||
ActiveCallOverlayLayout(
|
||||
call = call,
|
||||
speakerCanBeEnabled = !audioViaBluetooth.value,
|
||||
devices = remember { callAudioDeviceManager.devices }.value,
|
||||
currentDevice = remember { callAudioDeviceManager.currentDevice },
|
||||
dismiss = { withBGApi { chatModel.callManager.endCall(call) } },
|
||||
toggleAudio = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Audio, enable = !call.audioEnabled)) },
|
||||
selectDevice = { callAudioDeviceManager.selectDevice(it.id) },
|
||||
toggleVideo = { chatModel.callCommand.add(WCallCommand.Media(CallMediaType.Video, enable = !call.videoEnabled)) },
|
||||
toggleSound = {
|
||||
var call = chatModel.activeCall.value
|
||||
if (call != null) {
|
||||
call = call.copy(soundSpeaker = !call.soundSpeaker)
|
||||
chatModel.activeCall.value = call
|
||||
setCallSound(call.soundSpeaker, audioViaBluetooth)
|
||||
callAudioDeviceManager.selectLastExternalDeviceOrDefault(call.soundSpeaker, true)
|
||||
}
|
||||
},
|
||||
flipCamera = { chatModel.callCommand.add(WCallCommand.Camera(call.localCamera.flipped)) }
|
||||
@@ -257,42 +247,18 @@ private fun ActiveCallOverlay(call: Call, chatModel: ChatModel, audioViaBluetoot
|
||||
fun ActiveCallOverlayDisabled(call: Call) {
|
||||
ActiveCallOverlayLayout(
|
||||
call = call,
|
||||
speakerCanBeEnabled = false,
|
||||
devices = emptyList(),
|
||||
currentDevice = remember { mutableStateOf(null) },
|
||||
enabled = false,
|
||||
dismiss = {},
|
||||
toggleAudio = {},
|
||||
selectDevice = {},
|
||||
toggleVideo = {},
|
||||
toggleSound = {},
|
||||
flipCamera = {}
|
||||
)
|
||||
}
|
||||
|
||||
private fun setCallSound(speaker: Boolean, audioViaBluetooth: MutableState<Boolean>) {
|
||||
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
Log.d(TAG, "setCallSound: set audio mode, speaker enabled: $speaker")
|
||||
am.mode = AudioManager.MODE_IN_COMMUNICATION
|
||||
if (Build.VERSION.SDK_INT >= Build.VERSION_CODES.S) {
|
||||
val btDevice = am.availableCommunicationDevices.lastOrNull { it.type == AudioDeviceInfo.TYPE_BLUETOOTH_SCO }
|
||||
val preferredSecondaryDevice = if (speaker) AudioDeviceInfo.TYPE_BUILTIN_SPEAKER else AudioDeviceInfo.TYPE_BUILTIN_EARPIECE
|
||||
if (btDevice != null) {
|
||||
am.setCommunicationDevice(btDevice)
|
||||
} else if (am.communicationDevice?.type != preferredSecondaryDevice) {
|
||||
am.availableCommunicationDevices.firstOrNull { it.type == preferredSecondaryDevice }?.let {
|
||||
am.setCommunicationDevice(it)
|
||||
}
|
||||
}
|
||||
} else {
|
||||
if (audioViaBluetooth.value) {
|
||||
am.isSpeakerphoneOn = false
|
||||
am.startBluetoothSco()
|
||||
} else {
|
||||
am.stopBluetoothSco()
|
||||
am.isSpeakerphoneOn = speaker
|
||||
}
|
||||
am.isBluetoothScoOn = am.isBluetoothScoAvailableOffCall && audioViaBluetooth.value
|
||||
}
|
||||
}
|
||||
|
||||
private fun dropAudioManagerOverrides() {
|
||||
val am = androidAppContext.getSystemService(Context.AUDIO_SERVICE) as AudioManager
|
||||
am.mode = AudioManager.MODE_NORMAL
|
||||
@@ -308,10 +274,12 @@ private fun dropAudioManagerOverrides() {
|
||||
@Composable
|
||||
private fun ActiveCallOverlayLayout(
|
||||
call: Call,
|
||||
speakerCanBeEnabled: Boolean,
|
||||
devices: List<AudioDeviceInfo>,
|
||||
currentDevice: State<AudioDeviceInfo?>,
|
||||
enabled: Boolean = true,
|
||||
dismiss: () -> Unit,
|
||||
toggleAudio: () -> Unit,
|
||||
selectDevice: (AudioDeviceInfo) -> Unit,
|
||||
toggleVideo: () -> Unit,
|
||||
toggleSound: () -> Unit,
|
||||
flipCamera: () -> Unit
|
||||
@@ -324,6 +292,32 @@ private fun ActiveCallOverlayLayout(
|
||||
}
|
||||
}
|
||||
Column(Modifier.padding(horizontal = DEFAULT_PADDING)) {
|
||||
@Composable
|
||||
fun SelectSoundDevice() {
|
||||
if (devices.size == 2 &&
|
||||
devices.all { it.type == AudioDeviceInfo.TYPE_BUILTIN_EARPIECE || it.type == AudioDeviceInfo.TYPE_BUILTIN_SPEAKER } ||
|
||||
currentDevice.value == null ||
|
||||
devices.none { it.id == currentDevice.value?.id }
|
||||
) {
|
||||
ToggleSoundButton(call, enabled, toggleSound)
|
||||
} else {
|
||||
ExposedDropDownSettingWithIcon(
|
||||
devices.map { Triple(it, it.icon, if (it.name != null) generalGetString(it.name!!) else it.productName.toString()) },
|
||||
currentDevice,
|
||||
fontSize = 18.sp,
|
||||
iconSize = 40.dp,
|
||||
listIconSize = 30.dp,
|
||||
iconColor = Color(0xFFFFFFD8),
|
||||
minWidth = 300.dp,
|
||||
onSelected = {
|
||||
if (it != null) {
|
||||
selectDevice(it)
|
||||
}
|
||||
}
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
when (media) {
|
||||
CallMediaType.Video -> {
|
||||
VideoCallInfoView(call)
|
||||
@@ -332,7 +326,7 @@ private fun ActiveCallOverlayLayout(
|
||||
}
|
||||
Row(Modifier.fillMaxWidth().padding(horizontal = 6.dp), horizontalArrangement = Arrangement.SpaceBetween, verticalAlignment = Alignment.CenterVertically) {
|
||||
ToggleAudioButton(call, enabled, toggleAudio)
|
||||
Spacer(Modifier.size(40.dp))
|
||||
SelectSoundDevice()
|
||||
IconButton(onClick = dismiss, enabled = enabled) {
|
||||
Icon(painterResource(MR.images.ic_call_end_filled), stringResource(MR.strings.icon_descr_hang_up), tint = if (enabled) Color.Red else MaterialTheme.colors.secondary, modifier = Modifier.size(64.dp))
|
||||
}
|
||||
@@ -370,7 +364,7 @@ private fun ActiveCallOverlayLayout(
|
||||
}
|
||||
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.CenterEnd) {
|
||||
Box(Modifier.padding(end = 32.dp)) {
|
||||
ToggleSoundButton(call, speakerCanBeEnabled && enabled, toggleSound)
|
||||
SelectSoundDevice()
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -765,9 +759,11 @@ fun PreviewActiveCallOverlayVideo() {
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "tcp")
|
||||
)
|
||||
),
|
||||
speakerCanBeEnabled = true,
|
||||
devices = emptyList(),
|
||||
currentDevice = remember { mutableStateOf(null) },
|
||||
dismiss = {},
|
||||
toggleAudio = {},
|
||||
selectDevice = {},
|
||||
toggleVideo = {},
|
||||
toggleSound = {},
|
||||
flipCamera = {}
|
||||
@@ -792,9 +788,11 @@ fun PreviewActiveCallOverlayAudio() {
|
||||
RTCIceCandidate(RTCIceCandidateType.Host, "udp")
|
||||
)
|
||||
),
|
||||
speakerCanBeEnabled = true,
|
||||
devices = emptyList(),
|
||||
currentDevice = remember { mutableStateOf(null) },
|
||||
dismiss = {},
|
||||
toggleAudio = {},
|
||||
selectDevice = {},
|
||||
toggleVideo = {},
|
||||
toggleSound = {},
|
||||
flipCamera = {}
|
||||
|
||||
+1
-2
@@ -6,7 +6,6 @@ import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import chat.simplex.common.helpers.toUri
|
||||
import chat.simplex.common.model.CIFile
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.views.helpers.ModalManager
|
||||
@@ -15,7 +14,6 @@ import coil.compose.rememberAsyncImagePainter
|
||||
import coil.decode.GifDecoder
|
||||
import coil.decode.ImageDecoderDecoder
|
||||
import coil.request.ImageRequest
|
||||
import java.net.URI
|
||||
|
||||
@Composable
|
||||
actual fun SimpleAndAnimatedImageView(
|
||||
@@ -43,6 +41,7 @@ actual fun SimpleAndAnimatedImageView(
|
||||
}
|
||||
|
||||
private val imageLoader = ImageLoader.Builder(androidAppContext)
|
||||
.networkObserverEnabled(false)
|
||||
.components {
|
||||
if (SDK_INT >= 28) {
|
||||
add(ImageDecoderDecoder.Factory())
|
||||
|
||||
+14
-13
@@ -3,7 +3,7 @@ package chat.simplex.common.views.chat.item
|
||||
import android.os.Build
|
||||
import android.view.View
|
||||
import androidx.compose.foundation.Image
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.graphics.*
|
||||
import androidx.compose.ui.graphics.painter.BitmapPainter
|
||||
@@ -11,8 +11,8 @@ import androidx.compose.ui.layout.ContentScale
|
||||
import androidx.compose.ui.platform.LocalContext
|
||||
import androidx.compose.ui.viewinterop.AndroidView
|
||||
import androidx.core.view.isVisible
|
||||
import chat.simplex.common.helpers.toUri
|
||||
import chat.simplex.common.platform.VideoPlayer
|
||||
import chat.simplex.common.platform.androidAppContext
|
||||
import chat.simplex.res.MR
|
||||
import coil.ImageLoader
|
||||
import coil.compose.rememberAsyncImagePainter
|
||||
@@ -23,21 +23,11 @@ import coil.size.Size
|
||||
import com.google.android.exoplayer2.ui.AspectRatioFrameLayout
|
||||
import com.google.android.exoplayer2.ui.StyledPlayerView
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import java.net.URI
|
||||
|
||||
@Composable
|
||||
actual fun FullScreenImageView(modifier: Modifier, data: ByteArray, imageBitmap: ImageBitmap) {
|
||||
// I'm making a new instance of imageLoader here because if I use one instance in multiple places
|
||||
// I'm using a new private instance of imageLoader here because if I use one instance in multiple places
|
||||
// after end of composition here a GIF from the first instance will be paused automatically which isn't what I want
|
||||
val imageLoader = ImageLoader.Builder(LocalContext.current)
|
||||
.components {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
add(ImageDecoderDecoder.Factory())
|
||||
} else {
|
||||
add(GifDecoder.Factory())
|
||||
}
|
||||
}
|
||||
.build()
|
||||
Image(
|
||||
rememberAsyncImagePainter(
|
||||
ImageRequest.Builder(LocalContext.current).data(data = data).size(Size.ORIGINAL).build(),
|
||||
@@ -73,3 +63,14 @@ actual fun FullScreenVideoView(player: VideoPlayer, modifier: Modifier, close: (
|
||||
modifier
|
||||
)
|
||||
}
|
||||
|
||||
private val imageLoader = ImageLoader.Builder(androidAppContext)
|
||||
.networkObserverEnabled(false)
|
||||
.components {
|
||||
if (Build.VERSION.SDK_INT >= 28) {
|
||||
add(ImageDecoderDecoder.Factory())
|
||||
} else {
|
||||
add(GifDecoder.Factory())
|
||||
}
|
||||
}
|
||||
.build()
|
||||
|
||||
Binary file not shown.
Binary file not shown.
+88
-12
@@ -111,13 +111,14 @@ object ChatModel {
|
||||
var draft = mutableStateOf(null as ComposeState?)
|
||||
var draftChatId = mutableStateOf(null as String?)
|
||||
|
||||
// working with external intents
|
||||
// working with external intents or internal forwarding of chat items
|
||||
val sharedContent = mutableStateOf(null as SharedContent?)
|
||||
|
||||
val filesToDelete = mutableSetOf<File>()
|
||||
val simplexLinkMode by lazy { mutableStateOf(ChatController.appPrefs.simplexLinkMode.get()) }
|
||||
|
||||
val clipboardHasText = mutableStateOf(false)
|
||||
val networkInfo = mutableStateOf(UserNetworkInfo(networkType = UserNetworkType.OTHER, online = true))
|
||||
|
||||
val updatingChatsMutex: Mutex = Mutex()
|
||||
val changingActiveUserMutex: Mutex = Mutex()
|
||||
@@ -804,6 +805,24 @@ data class Chat(
|
||||
|
||||
val id: String get() = chatInfo.id
|
||||
|
||||
fun groupFeatureEnabled(feature: GroupFeature): Boolean =
|
||||
if (chatInfo is ChatInfo.Group) {
|
||||
val groupInfo = chatInfo.groupInfo
|
||||
val p = groupInfo.fullGroupPreferences
|
||||
when (feature) {
|
||||
GroupFeature.TimedMessages -> p.timedMessages.on
|
||||
GroupFeature.DirectMessages -> p.directMessages.on(groupInfo.membership)
|
||||
GroupFeature.FullDelete -> p.fullDelete.on
|
||||
GroupFeature.Reactions -> p.reactions.on
|
||||
GroupFeature.Voice -> p.voice.on(groupInfo.membership)
|
||||
GroupFeature.Files -> p.files.on(groupInfo.membership)
|
||||
GroupFeature.SimplexLinks -> p.simplexLinks.on(groupInfo.membership)
|
||||
GroupFeature.History -> p.history.on
|
||||
}
|
||||
} else {
|
||||
true
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class ChatStats(val unreadCount: Int = 0, val minUnreadItemId: Long = 0, val unreadChat: Boolean = false)
|
||||
|
||||
@@ -1239,7 +1258,7 @@ data class GroupInfo (
|
||||
ChatFeature.TimedMessages -> fullGroupPreferences.timedMessages.on
|
||||
ChatFeature.FullDelete -> fullGroupPreferences.fullDelete.on
|
||||
ChatFeature.Reactions -> fullGroupPreferences.reactions.on
|
||||
ChatFeature.Voice -> fullGroupPreferences.voice.on
|
||||
ChatFeature.Voice -> fullGroupPreferences.voice.on(membership)
|
||||
ChatFeature.Calls -> false
|
||||
}
|
||||
override val timedMessagesTTL: Int? get() = with(fullGroupPreferences.timedMessages) { if (on) ttl else null }
|
||||
@@ -1734,7 +1753,7 @@ data class ChatItem (
|
||||
val allowAddReaction: Boolean get() =
|
||||
meta.itemDeleted == null && !isLiveDummy && (reactions.count { it.userReacted } < 3)
|
||||
|
||||
private val isLiveDummy: Boolean get() = meta.itemId == TEMP_LIVE_CHAT_ITEM_ID
|
||||
val isLiveDummy: Boolean get() = meta.itemId == TEMP_LIVE_CHAT_ITEM_ID
|
||||
|
||||
val encryptedFile: Boolean? = if (file?.fileSource == null) null else file.fileSource.cryptoArgs != null
|
||||
|
||||
@@ -1883,14 +1902,16 @@ data class ChatItem (
|
||||
status: CIStatus = CIStatus.SndNew(),
|
||||
quotedItem: CIQuote? = null,
|
||||
file: CIFile? = null,
|
||||
itemForwarded: CIForwardedFrom? = null,
|
||||
itemDeleted: CIDeleted? = null,
|
||||
itemEdited: Boolean = false,
|
||||
itemTimed: CITimed? = null,
|
||||
deletable: Boolean = true,
|
||||
editable: Boolean = true
|
||||
) =
|
||||
ChatItem(
|
||||
chatDir = dir,
|
||||
meta = CIMeta.getSample(id, ts, text, status, itemDeleted, itemEdited, itemTimed, editable),
|
||||
meta = CIMeta.getSample(id, ts, text, status, itemForwarded, itemDeleted, itemEdited, itemTimed, deletable, editable),
|
||||
content = CIContent.SndMsgContent(msgContent = MsgContent.MCText(text)),
|
||||
quotedItem = quotedItem,
|
||||
reactions = listOf(),
|
||||
@@ -1974,10 +1995,12 @@ data class ChatItem (
|
||||
itemStatus = CIStatus.RcvRead(),
|
||||
createdAt = Clock.System.now(),
|
||||
updatedAt = Clock.System.now(),
|
||||
itemForwarded = null,
|
||||
itemDeleted = null,
|
||||
itemEdited = false,
|
||||
itemTimed = null,
|
||||
itemLive = false,
|
||||
deletable = false,
|
||||
editable = false
|
||||
),
|
||||
content = CIContent.RcvDeleted(deleteMode = CIDeleteMode.cidmBroadcast),
|
||||
@@ -1995,10 +2018,12 @@ data class ChatItem (
|
||||
itemStatus = CIStatus.RcvRead(),
|
||||
createdAt = Clock.System.now(),
|
||||
updatedAt = Clock.System.now(),
|
||||
itemForwarded = null,
|
||||
itemDeleted = null,
|
||||
itemEdited = false,
|
||||
itemTimed = null,
|
||||
itemLive = true,
|
||||
deletable = false,
|
||||
editable = false
|
||||
),
|
||||
content = CIContent.SndMsgContent(MsgContent.MCText("")),
|
||||
@@ -2095,10 +2120,12 @@ data class CIMeta (
|
||||
val itemStatus: CIStatus,
|
||||
val createdAt: Instant,
|
||||
val updatedAt: Instant,
|
||||
val itemForwarded: CIForwardedFrom?,
|
||||
val itemDeleted: CIDeleted?,
|
||||
val itemEdited: Boolean,
|
||||
val itemTimed: CITimed?,
|
||||
val itemLive: Boolean?,
|
||||
val deletable: Boolean,
|
||||
val editable: Boolean
|
||||
) {
|
||||
val timestampText: String get() = getTimestampText(itemTs)
|
||||
@@ -2118,7 +2145,8 @@ data class CIMeta (
|
||||
companion object {
|
||||
fun getSample(
|
||||
id: Long, ts: Instant, text: String, status: CIStatus = CIStatus.SndNew(),
|
||||
itemDeleted: CIDeleted? = null, itemEdited: Boolean = false, itemTimed: CITimed? = null, itemLive: Boolean = false, editable: Boolean = true
|
||||
itemForwarded: CIForwardedFrom? = null, itemDeleted: CIDeleted? = null, itemEdited: Boolean = false,
|
||||
itemTimed: CITimed? = null, itemLive: Boolean = false, deletable: Boolean = true, editable: Boolean = true
|
||||
): CIMeta =
|
||||
CIMeta(
|
||||
itemId = id,
|
||||
@@ -2127,10 +2155,12 @@ data class CIMeta (
|
||||
itemStatus = status,
|
||||
createdAt = ts,
|
||||
updatedAt = ts,
|
||||
itemForwarded = itemForwarded,
|
||||
itemDeleted = itemDeleted,
|
||||
itemEdited = itemEdited,
|
||||
itemTimed = itemTimed,
|
||||
itemLive = itemLive,
|
||||
deletable = deletable,
|
||||
editable = editable
|
||||
)
|
||||
|
||||
@@ -2143,10 +2173,12 @@ data class CIMeta (
|
||||
itemStatus = CIStatus.SndNew(),
|
||||
createdAt = Clock.System.now(),
|
||||
updatedAt = Clock.System.now(),
|
||||
itemForwarded = null,
|
||||
itemDeleted = null,
|
||||
itemEdited = false,
|
||||
itemTimed = null,
|
||||
itemLive = false,
|
||||
deletable = false,
|
||||
editable = false
|
||||
)
|
||||
}
|
||||
@@ -2257,6 +2289,37 @@ sealed class CIDeleted {
|
||||
@Serializable @SerialName("moderated") class Moderated(val deletedTs: Instant?, val byGroupMember: GroupMember): CIDeleted()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class MsgDirection {
|
||||
@SerialName("rcv") Rcv,
|
||||
@SerialName("snd") Snd;
|
||||
}
|
||||
|
||||
@Serializable
|
||||
sealed class CIForwardedFrom {
|
||||
@Serializable @SerialName("unknown") object Unknown: CIForwardedFrom()
|
||||
@Serializable @SerialName("contact") class Contact(override val chatName: String, val msgDir: MsgDirection, val contactId: Long? = null, val chatItemId: Long? = null): CIForwardedFrom()
|
||||
@Serializable @SerialName("group") class Group(override val chatName: String, val msgDir: MsgDirection, val groupId: Long? = null, val chatItemId: Long? = null): CIForwardedFrom()
|
||||
|
||||
open val chatName: String
|
||||
get() = when (this) {
|
||||
Unknown -> ""
|
||||
is Contact -> chatName
|
||||
is Group -> chatName
|
||||
}
|
||||
|
||||
fun text(chatType: ChatType): String =
|
||||
if (chatType == ChatType.Local) {
|
||||
if (chatName.isEmpty()) {
|
||||
generalGetString(MR.strings.saved_description)
|
||||
} else {
|
||||
generalGetString(MR.strings.saved_from_description).format(chatName)
|
||||
}
|
||||
} else {
|
||||
generalGetString(MR.strings.forwarded_description)
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
enum class CIDeleteMode(val deleteMode: String) {
|
||||
@SerialName("internal") cidmInternal("internal"),
|
||||
@@ -2292,8 +2355,8 @@ sealed class CIContent: ItemContent {
|
||||
@Serializable @SerialName("sndChatFeature") class SndChatFeature(val feature: ChatFeature, val enabled: FeatureEnabled, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvChatPreference") class RcvChatPreference(val feature: ChatFeature, val allowed: FeatureAllowed, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndChatPreference") class SndChatPreference(val feature: ChatFeature, val allowed: FeatureAllowed, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvGroupFeature") class RcvGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndGroupFeature") class SndGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvGroupFeature") class RcvGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null, val memberRole_: GroupMemberRole?): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndGroupFeature") class SndGroupFeature(val groupFeature: GroupFeature, val preference: GroupPreference, val param: Int? = null, val memberRole_: GroupMemberRole?): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvChatFeatureRejected") class RcvChatFeatureRejected(val feature: ChatFeature): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("rcvGroupFeatureRejected") class RcvGroupFeatureRejected(val groupFeature: GroupFeature): CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@Serializable @SerialName("sndModerated") object SndModerated: CIContent() { override val msgContent: MsgContent? get() = null }
|
||||
@@ -2325,8 +2388,8 @@ sealed class CIContent: ItemContent {
|
||||
is SndChatFeature -> featureText(feature, enabled.text, param)
|
||||
is RcvChatPreference -> preferenceText(feature, allowed, param)
|
||||
is SndChatPreference -> preferenceText(feature, allowed, param)
|
||||
is RcvGroupFeature -> featureText(groupFeature, preference.enable.text, param)
|
||||
is SndGroupFeature -> featureText(groupFeature, preference.enable.text, param)
|
||||
is RcvGroupFeature -> featureText(groupFeature, preference.enable.text, param, memberRole_)
|
||||
is SndGroupFeature -> featureText(groupFeature, preference.enable.text, param, memberRole_)
|
||||
is RcvChatFeatureRejected -> "${feature.text}: ${generalGetString(MR.strings.feature_received_prohibited)}"
|
||||
is RcvGroupFeatureRejected -> "${groupFeature.text}: ${generalGetString(MR.strings.feature_received_prohibited)}"
|
||||
is SndModerated -> generalGetString(MR.strings.moderated_description)
|
||||
@@ -2363,11 +2426,23 @@ sealed class CIContent: ItemContent {
|
||||
|
||||
private val e2eeInfoNoPQStr: String = generalGetString(MR.strings.e2ee_info_no_pq_short)
|
||||
|
||||
fun featureText(feature: Feature, enabled: String, param: Int?): String =
|
||||
if (feature.hasParam) {
|
||||
fun featureText(feature: Feature, enabled: String, param: Int?, role: GroupMemberRole? = null): String =
|
||||
(if (feature.hasParam) {
|
||||
"${feature.text}: ${timeText(param)}"
|
||||
} else {
|
||||
"${feature.text}: $enabled"
|
||||
}) + (
|
||||
if (feature.hasRole && role != null)
|
||||
" (${roleText(role)})"
|
||||
else
|
||||
""
|
||||
)
|
||||
|
||||
private fun roleText(role: GroupMemberRole?): String =
|
||||
when (role) {
|
||||
GroupMemberRole.Owner -> generalGetString(MR.strings.feature_roles_owners)
|
||||
GroupMemberRole.Admin -> generalGetString(MR.strings.feature_roles_admins)
|
||||
else -> generalGetString(MR.strings.feature_roles_all_members)
|
||||
}
|
||||
|
||||
fun preferenceText(feature: Feature, allowed: FeatureAllowed, param: Int?): String = when {
|
||||
@@ -3251,7 +3326,8 @@ sealed class ChatItemTTL: Comparable<ChatItemTTL?> {
|
||||
@Serializable
|
||||
class ChatItemInfo(
|
||||
val itemVersions: List<ChatItemVersion>,
|
||||
val memberDeliveryStatuses: List<MemberDeliveryStatus>?
|
||||
val memberDeliveryStatuses: List<MemberDeliveryStatus>?,
|
||||
val forwardedFromChatItem: AChatItem?
|
||||
)
|
||||
|
||||
@Serializable
|
||||
|
||||
+128
-19
@@ -20,6 +20,7 @@ import com.charleskorn.kaml.YamlConfiguration
|
||||
import chat.simplex.res.MR
|
||||
import com.russhwolf.settings.Settings
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.coroutines.channels.Channel
|
||||
import kotlinx.coroutines.sync.withLock
|
||||
import kotlinx.datetime.Clock
|
||||
import kotlinx.datetime.Instant
|
||||
@@ -134,6 +135,7 @@ class AppPreferences {
|
||||
val networkTCPConnectTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT, NetCfg.defaults.tcpConnectTimeout, NetCfg.proxyDefaults.tcpConnectTimeout)
|
||||
val networkTCPTimeout = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT, NetCfg.defaults.tcpTimeout, NetCfg.proxyDefaults.tcpTimeout)
|
||||
val networkTCPTimeoutPerKb = mkTimeoutPreference(SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB, NetCfg.defaults.tcpTimeoutPerKb, NetCfg.proxyDefaults.tcpTimeoutPerKb)
|
||||
val networkRcvConcurrency = mkIntPreference(SHARED_PREFS_NETWORK_RCV_CONCURRENCY, NetCfg.defaults.rcvConcurrency)
|
||||
val networkSMPPingInterval = mkLongPreference(SHARED_PREFS_NETWORK_SMP_PING_INTERVAL, NetCfg.defaults.smpPingInterval)
|
||||
val networkSMPPingCount = mkIntPreference(SHARED_PREFS_NETWORK_SMP_PING_COUNT, NetCfg.defaults.smpPingCount)
|
||||
val networkEnableKeepAlive = mkBoolPreference(SHARED_PREFS_NETWORK_ENABLE_KEEP_ALIVE, NetCfg.defaults.enableKeepAlive)
|
||||
@@ -303,6 +305,7 @@ class AppPreferences {
|
||||
private const val SHARED_PREFS_NETWORK_TCP_CONNECT_TIMEOUT = "NetworkTCPConnectTimeout"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT = "NetworkTCPTimeout"
|
||||
private const val SHARED_PREFS_NETWORK_TCP_TIMEOUT_PER_KB = "networkTCPTimeoutPerKb"
|
||||
private const val SHARED_PREFS_NETWORK_RCV_CONCURRENCY = "networkRcvConcurrency"
|
||||
private const val SHARED_PREFS_NETWORK_SMP_PING_INTERVAL = "NetworkSMPPingInterval"
|
||||
private const val SHARED_PREFS_NETWORK_SMP_PING_COUNT = "NetworkSMPPingCount"
|
||||
private const val SHARED_PREFS_NETWORK_ENABLE_KEEP_ALIVE = "NetworkEnableKeepAlive"
|
||||
@@ -350,11 +353,15 @@ object ChatController {
|
||||
var ctrl: ChatCtrl? = -1
|
||||
val appPrefs: AppPreferences by lazy { AppPreferences() }
|
||||
|
||||
val messagesChannel: Channel<APIResponse> = Channel()
|
||||
|
||||
val chatModel = ChatModel
|
||||
private var receiverStarted = false
|
||||
var lastMsgReceivedTimestamp: Long = System.currentTimeMillis()
|
||||
private set
|
||||
|
||||
fun hasChatCtrl() = ctrl != -1L && ctrl != null
|
||||
|
||||
private suspend fun currentUserId(funcName: String): Long = changingActiveUserMutex.withLock {
|
||||
val userId = chatModel.currentUser.value?.userId
|
||||
if (userId == null) {
|
||||
@@ -481,6 +488,7 @@ object ChatController {
|
||||
if (msg != null) {
|
||||
val finishedWithoutTimeout = withTimeoutOrNull(60_000L) {
|
||||
processReceivedMsg(msg)
|
||||
messagesChannel.trySend(msg)
|
||||
}
|
||||
if (finishedWithoutTimeout == null) {
|
||||
Log.e(TAG, "Timeout reached while processing received message: " + msg.resp.responseType)
|
||||
@@ -732,12 +740,16 @@ object ChatController {
|
||||
|
||||
suspend fun apiSendMessage(rh: Long?, type: ChatType, id: Long, file: CryptoFile? = null, quotedItemId: Long? = null, mc: MsgContent, live: Boolean = false, ttl: Int? = null): AChatItem? {
|
||||
val cmd = CC.ApiSendMessage(type, id, file, quotedItemId, mc, live, ttl)
|
||||
return processSendMessageCmd(rh, cmd)
|
||||
}
|
||||
|
||||
private suspend fun processSendMessageCmd(rh: Long?, cmd: CC): AChatItem? {
|
||||
val r = sendCmd(rh, cmd)
|
||||
return when (r) {
|
||||
is CR.NewChatItem -> r.chatItem
|
||||
else -> {
|
||||
if (!(networkErrorAlert(r))) {
|
||||
apiErrorAlert("apiSendMessage", generalGetString(MR.strings.error_sending_message), r)
|
||||
apiErrorAlert("processSendMessageCmd", generalGetString(MR.strings.error_sending_message), r)
|
||||
}
|
||||
null
|
||||
}
|
||||
@@ -765,6 +777,13 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiForwardChatItem(rh: Long?, toChatType: ChatType, toChatId: Long, fromChatType: ChatType, fromChatId: Long, itemId: Long): ChatItem? {
|
||||
val cmd = CC.ApiForwardChatItem(toChatType, toChatId, fromChatType, fromChatId, itemId)
|
||||
return processSendMessageCmd(rh, cmd)?.chatItem
|
||||
}
|
||||
|
||||
|
||||
|
||||
suspend fun apiUpdateChatItem(rh: Long?, type: ChatType, id: Long, itemId: Long, mc: MsgContent, live: Boolean = false): AChatItem? {
|
||||
val r = sendCmd(rh, CC.ApiUpdateChatItem(type, id, itemId, mc, live))
|
||||
if (r is CR.ChatItemUpdated) return r.chatItem
|
||||
@@ -875,6 +894,9 @@ object ChatController {
|
||||
}
|
||||
}
|
||||
|
||||
suspend fun apiSetNetworkInfo(networkInfo: UserNetworkInfo): Boolean =
|
||||
sendCommandOkResp(null, CC.APISetNetworkInfo(networkInfo))
|
||||
|
||||
suspend fun apiSetMemberSettings(rh: Long?, groupId: Long, groupMemberId: Long, memberSettings: GroupMemberSettings): Boolean =
|
||||
sendCommandOkResp(rh, CC.ApiSetMemberSettings(groupId, groupMemberId, memberSettings))
|
||||
|
||||
@@ -1962,7 +1984,6 @@ object ChatController {
|
||||
}
|
||||
is CR.SndFileCompleteXFTP -> {
|
||||
chatItemSimpleUpdate(rhId, r.user, r.chatItem)
|
||||
cleanupFile(r.chatItem)
|
||||
}
|
||||
is CR.SndFileError -> {
|
||||
if (r.chatItem_ != null) {
|
||||
@@ -2119,7 +2140,7 @@ object ChatController {
|
||||
if (active(r.user)) {
|
||||
chatModel.updateContact(rhId, r.contact)
|
||||
}
|
||||
is CR.ChatCmdError -> when {
|
||||
is CR.ChatRespError -> when {
|
||||
r.chatError is ChatError.ChatErrorAgent && r.chatError.agentError is AgentErrorType.CRITICAL -> {
|
||||
chatModel.processedCriticalError.newError(r.chatError.agentError, r.chatError.agentError.offerRestart)
|
||||
}
|
||||
@@ -2286,6 +2307,7 @@ object ChatController {
|
||||
val tcpConnectTimeout = appPrefs.networkTCPConnectTimeout.get()
|
||||
val tcpTimeout = appPrefs.networkTCPTimeout.get()
|
||||
val tcpTimeoutPerKb = appPrefs.networkTCPTimeoutPerKb.get()
|
||||
val rcvConcurrency = appPrefs.networkRcvConcurrency.get()
|
||||
val smpPingInterval = appPrefs.networkSMPPingInterval.get()
|
||||
val smpPingCount = appPrefs.networkSMPPingCount.get()
|
||||
val enableKeepAlive = appPrefs.networkEnableKeepAlive.get()
|
||||
@@ -2305,6 +2327,7 @@ object ChatController {
|
||||
tcpConnectTimeout = tcpConnectTimeout,
|
||||
tcpTimeout = tcpTimeout,
|
||||
tcpTimeoutPerKb = tcpTimeoutPerKb,
|
||||
rcvConcurrency = rcvConcurrency,
|
||||
tcpKeepAlive = tcpKeepAlive,
|
||||
smpPingInterval = smpPingInterval,
|
||||
smpPingCount = smpPingCount
|
||||
@@ -2322,6 +2345,7 @@ object ChatController {
|
||||
appPrefs.networkTCPConnectTimeout.set(cfg.tcpConnectTimeout)
|
||||
appPrefs.networkTCPTimeout.set(cfg.tcpTimeout)
|
||||
appPrefs.networkTCPTimeoutPerKb.set(cfg.tcpTimeoutPerKb)
|
||||
appPrefs.networkRcvConcurrency.set(cfg.rcvConcurrency)
|
||||
appPrefs.networkSMPPingInterval.set(cfg.smpPingInterval)
|
||||
appPrefs.networkSMPPingCount.set(cfg.smpPingCount)
|
||||
if (cfg.tcpKeepAlive != null) {
|
||||
@@ -2387,6 +2411,7 @@ sealed class CC {
|
||||
class ApiDeleteChatItem(val type: ChatType, val id: Long, val itemId: Long, val mode: CIDeleteMode): CC()
|
||||
class ApiDeleteMemberChatItem(val groupId: Long, val groupMemberId: Long, val itemId: Long): CC()
|
||||
class ApiChatItemReaction(val type: ChatType, val id: Long, val itemId: Long, val add: Boolean, val reaction: MsgReaction): CC()
|
||||
class ApiForwardChatItem(val toChatType: ChatType, val toChatId: Long, val fromChatType: ChatType, val fromChatId: Long, val itemId: Long): CC()
|
||||
class ApiNewGroup(val userId: Long, val incognito: Boolean, val groupProfile: GroupProfile): CC()
|
||||
class ApiAddMember(val groupId: Long, val contactId: Long, val memberRole: GroupMemberRole): CC()
|
||||
class ApiJoinGroup(val groupId: Long): CC()
|
||||
@@ -2409,6 +2434,7 @@ sealed class CC {
|
||||
class APIGetChatItemTTL(val userId: Long): CC()
|
||||
class APISetNetworkConfig(val networkConfig: NetCfg): CC()
|
||||
class APIGetNetworkConfig: CC()
|
||||
class APISetNetworkInfo(val networkInfo: UserNetworkInfo): CC()
|
||||
class APISetChatSettings(val type: ChatType, val id: Long, val chatSettings: ChatSettings): CC()
|
||||
class ApiSetMemberSettings(val groupId: Long, val groupMemberId: Long, val memberSettings: GroupMemberSettings): CC()
|
||||
class APIContactInfo(val contactId: Long): CC()
|
||||
@@ -2529,6 +2555,7 @@ sealed class CC {
|
||||
is ApiDeleteChatItem -> "/_delete item ${chatRef(type, id)} $itemId ${mode.deleteMode}"
|
||||
is ApiDeleteMemberChatItem -> "/_delete member item #$groupId $groupMemberId $itemId"
|
||||
is ApiChatItemReaction -> "/_reaction ${chatRef(type, id)} $itemId ${onOff(add)} ${json.encodeToString(reaction)}"
|
||||
is ApiForwardChatItem -> "/_forward ${chatRef(toChatType, toChatId)} ${chatRef(fromChatType, fromChatId)} $itemId"
|
||||
is ApiNewGroup -> "/_group $userId incognito=${onOff(incognito)} ${json.encodeToString(groupProfile)}"
|
||||
is ApiAddMember -> "/_add #$groupId $contactId ${memberRole.memberRole}"
|
||||
is ApiJoinGroup -> "/_join #$groupId"
|
||||
@@ -2551,6 +2578,7 @@ sealed class CC {
|
||||
is APIGetChatItemTTL -> "/_ttl $userId"
|
||||
is APISetNetworkConfig -> "/_network ${json.encodeToString(networkConfig)}"
|
||||
is APIGetNetworkConfig -> "/network"
|
||||
is APISetNetworkInfo -> "/_network info ${json.encodeToString(networkInfo)}"
|
||||
is APISetChatSettings -> "/_settings ${chatRef(type, id)} ${json.encodeToString(chatSettings)}"
|
||||
is ApiSetMemberSettings -> "/_member settings #$groupId $groupMemberId ${json.encodeToString(memberSettings)}"
|
||||
is APIContactInfo -> "/_info @$contactId"
|
||||
@@ -2666,6 +2694,7 @@ sealed class CC {
|
||||
is ApiDeleteChatItem -> "apiDeleteChatItem"
|
||||
is ApiDeleteMemberChatItem -> "apiDeleteMemberChatItem"
|
||||
is ApiChatItemReaction -> "apiChatItemReaction"
|
||||
is ApiForwardChatItem -> "apiForwardChatItem"
|
||||
is ApiNewGroup -> "apiNewGroup"
|
||||
is ApiAddMember -> "apiAddMember"
|
||||
is ApiJoinGroup -> "apiJoinGroup"
|
||||
@@ -2688,6 +2717,7 @@ sealed class CC {
|
||||
is APIGetChatItemTTL -> "apiGetChatItemTTL"
|
||||
is APISetNetworkConfig -> "apiSetNetworkConfig"
|
||||
is APIGetNetworkConfig -> "apiGetNetworkConfig"
|
||||
is APISetNetworkInfo -> "apiSetNetworkInfo"
|
||||
is APISetChatSettings -> "apiSetChatSettings"
|
||||
is ApiSetMemberSettings -> "apiSetMemberSettings"
|
||||
is APIContactInfo -> "apiContactInfo"
|
||||
@@ -3009,6 +3039,7 @@ data class NetCfg(
|
||||
val tcpConnectTimeout: Long, // microseconds
|
||||
val tcpTimeout: Long, // microseconds
|
||||
val tcpTimeoutPerKb: Long, // microseconds
|
||||
val rcvConcurrency: Int, // pool size
|
||||
val tcpKeepAlive: KeepAliveOpts?,
|
||||
val smpPingInterval: Long, // microseconds
|
||||
val smpPingCount: Int,
|
||||
@@ -3033,9 +3064,10 @@ data class NetCfg(
|
||||
hostMode = HostMode.OnionViaSocks,
|
||||
requiredHostMode = false,
|
||||
sessionMode = TransportSessionMode.User,
|
||||
tcpConnectTimeout = 20_000_000,
|
||||
tcpConnectTimeout = 10_000_000,
|
||||
tcpTimeout = 15_000_000,
|
||||
tcpTimeoutPerKb = 45_000,
|
||||
tcpTimeoutPerKb = 10_000,
|
||||
rcvConcurrency = 12,
|
||||
tcpKeepAlive = KeepAliveOpts.defaults,
|
||||
smpPingInterval = 1200_000_000,
|
||||
smpPingCount = 3
|
||||
@@ -3047,9 +3079,10 @@ data class NetCfg(
|
||||
hostMode = HostMode.OnionViaSocks,
|
||||
requiredHostMode = false,
|
||||
sessionMode = TransportSessionMode.User,
|
||||
tcpConnectTimeout = 30_000_000,
|
||||
tcpConnectTimeout = 20_000_000,
|
||||
tcpTimeout = 20_000_000,
|
||||
tcpTimeoutPerKb = 60_000,
|
||||
tcpTimeoutPerKb = 15_000,
|
||||
rcvConcurrency = 8,
|
||||
tcpKeepAlive = KeepAliveOpts.defaults,
|
||||
smpPingInterval = 1200_000_000,
|
||||
smpPingCount = 3
|
||||
@@ -3418,6 +3451,7 @@ interface Feature {
|
||||
@Composable
|
||||
fun iconFilled(): Painter
|
||||
val hasParam: Boolean
|
||||
val hasRole: Boolean
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@@ -3437,6 +3471,7 @@ enum class ChatFeature: Feature {
|
||||
TimedMessages -> true
|
||||
else -> false
|
||||
}
|
||||
override val hasRole: Boolean = false
|
||||
|
||||
override val text: String
|
||||
get() = when(this) {
|
||||
@@ -3537,6 +3572,7 @@ enum class GroupFeature: Feature {
|
||||
@SerialName("reactions") Reactions,
|
||||
@SerialName("voice") Voice,
|
||||
@SerialName("files") Files,
|
||||
@SerialName("simplexLinks") SimplexLinks,
|
||||
@SerialName("history") History;
|
||||
|
||||
override val hasParam: Boolean get() = when(this) {
|
||||
@@ -3544,6 +3580,18 @@ enum class GroupFeature: Feature {
|
||||
else -> false
|
||||
}
|
||||
|
||||
override val hasRole: Boolean
|
||||
get() = when (this) {
|
||||
TimedMessages -> false
|
||||
DirectMessages -> true
|
||||
FullDelete -> false
|
||||
Reactions -> false
|
||||
Voice -> true
|
||||
Files -> true
|
||||
SimplexLinks -> true
|
||||
History -> false
|
||||
}
|
||||
|
||||
override val text: String
|
||||
get() = when(this) {
|
||||
TimedMessages -> generalGetString(MR.strings.timed_messages)
|
||||
@@ -3552,6 +3600,7 @@ enum class GroupFeature: Feature {
|
||||
Reactions -> generalGetString(MR.strings.message_reactions)
|
||||
Voice -> generalGetString(MR.strings.voice_messages)
|
||||
Files -> generalGetString(MR.strings.files_and_media)
|
||||
SimplexLinks -> generalGetString(MR.strings.simplex_links)
|
||||
History -> generalGetString(MR.strings.recent_history)
|
||||
}
|
||||
|
||||
@@ -3563,6 +3612,7 @@ enum class GroupFeature: Feature {
|
||||
Reactions -> painterResource(MR.images.ic_add_reaction)
|
||||
Voice -> painterResource(MR.images.ic_keyboard_voice)
|
||||
Files -> painterResource(MR.images.ic_draft)
|
||||
SimplexLinks -> painterResource(MR.images.ic_link)
|
||||
History -> painterResource(MR.images.ic_schedule)
|
||||
}
|
||||
|
||||
@@ -3574,6 +3624,7 @@ enum class GroupFeature: Feature {
|
||||
Reactions -> painterResource(MR.images.ic_add_reaction_filled)
|
||||
Voice -> painterResource(MR.images.ic_keyboard_voice_filled)
|
||||
Files -> painterResource(MR.images.ic_draft_filled)
|
||||
SimplexLinks -> painterResource(MR.images.ic_link)
|
||||
History -> painterResource(MR.images.ic_schedule_filled)
|
||||
}
|
||||
|
||||
@@ -3604,6 +3655,10 @@ enum class GroupFeature: Feature {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.allow_to_send_files)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.prohibit_sending_files)
|
||||
}
|
||||
SimplexLinks -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.allow_to_send_simplex_links)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.prohibit_sending_simplex_links)
|
||||
}
|
||||
History -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.enable_sending_recent_history)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.disable_sending_recent_history)
|
||||
@@ -3635,6 +3690,10 @@ enum class GroupFeature: Feature {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.group_members_can_send_files)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.files_are_prohibited_in_group)
|
||||
}
|
||||
SimplexLinks -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.group_members_can_send_simplex_links)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.simplex_links_are_prohibited_in_group)
|
||||
}
|
||||
History -> when(enabled) {
|
||||
GroupFeatureEnabled.ON -> generalGetString(MR.strings.recent_history_is_sent_to_new_members)
|
||||
GroupFeatureEnabled.OFF -> generalGetString(MR.strings.recent_history_is_not_sent_to_new_members)
|
||||
@@ -3748,11 +3807,12 @@ enum class FeatureAllowed {
|
||||
@Serializable
|
||||
data class FullGroupPreferences(
|
||||
val timedMessages: TimedMessagesGroupPreference,
|
||||
val directMessages: GroupPreference,
|
||||
val directMessages: RoleGroupPreference,
|
||||
val fullDelete: GroupPreference,
|
||||
val reactions: GroupPreference,
|
||||
val voice: GroupPreference,
|
||||
val files: GroupPreference,
|
||||
val voice: RoleGroupPreference,
|
||||
val files: RoleGroupPreference,
|
||||
val simplexLinks: RoleGroupPreference,
|
||||
val history: GroupPreference,
|
||||
) {
|
||||
fun toGroupPreferences(): GroupPreferences =
|
||||
@@ -3763,17 +3823,19 @@ data class FullGroupPreferences(
|
||||
reactions = reactions,
|
||||
voice = voice,
|
||||
files = files,
|
||||
simplexLinks = simplexLinks,
|
||||
history = history
|
||||
)
|
||||
|
||||
companion object {
|
||||
val sampleData = FullGroupPreferences(
|
||||
timedMessages = TimedMessagesGroupPreference(GroupFeatureEnabled.OFF),
|
||||
directMessages = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
directMessages = RoleGroupPreference(GroupFeatureEnabled.OFF, role = null),
|
||||
fullDelete = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
reactions = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = GroupPreference(GroupFeatureEnabled.ON),
|
||||
files = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
files = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
simplexLinks = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
history = GroupPreference(GroupFeatureEnabled.ON),
|
||||
)
|
||||
}
|
||||
@@ -3782,21 +3844,23 @@ data class FullGroupPreferences(
|
||||
@Serializable
|
||||
data class GroupPreferences(
|
||||
val timedMessages: TimedMessagesGroupPreference? = null,
|
||||
val directMessages: GroupPreference? = null,
|
||||
val directMessages: RoleGroupPreference? = null,
|
||||
val fullDelete: GroupPreference? = null,
|
||||
val reactions: GroupPreference? = null,
|
||||
val voice: GroupPreference? = null,
|
||||
val files: GroupPreference? = null,
|
||||
val voice: RoleGroupPreference? = null,
|
||||
val files: RoleGroupPreference? = null,
|
||||
val simplexLinks: RoleGroupPreference? = null,
|
||||
val history: GroupPreference? = null,
|
||||
) {
|
||||
companion object {
|
||||
val sampleData = GroupPreferences(
|
||||
timedMessages = TimedMessagesGroupPreference(GroupFeatureEnabled.OFF),
|
||||
directMessages = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
directMessages = RoleGroupPreference(GroupFeatureEnabled.OFF, role = null),
|
||||
fullDelete = GroupPreference(GroupFeatureEnabled.OFF),
|
||||
reactions = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = GroupPreference(GroupFeatureEnabled.ON),
|
||||
files = GroupPreference(GroupFeatureEnabled.ON),
|
||||
voice = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
files = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
simplexLinks = RoleGroupPreference(GroupFeatureEnabled.ON, role = null),
|
||||
history = GroupPreference(GroupFeatureEnabled.ON),
|
||||
)
|
||||
}
|
||||
@@ -3807,6 +3871,26 @@ data class GroupPreference(
|
||||
val enable: GroupFeatureEnabled
|
||||
) {
|
||||
val on: Boolean get() = enable == GroupFeatureEnabled.ON
|
||||
|
||||
fun enabled(role: GroupMemberRole?, m: GroupMember?): GroupFeatureEnabled =
|
||||
when (enable) {
|
||||
GroupFeatureEnabled.OFF -> GroupFeatureEnabled.OFF
|
||||
GroupFeatureEnabled.ON ->
|
||||
if (role != null && m != null) {
|
||||
if (m.memberRole >= role) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF
|
||||
} else {
|
||||
GroupFeatureEnabled.ON
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class RoleGroupPreference(
|
||||
val enable: GroupFeatureEnabled,
|
||||
val role: GroupMemberRole? = null,
|
||||
) {
|
||||
fun on(m: GroupMember): Boolean =
|
||||
enable == GroupFeatureEnabled.ON && m.memberRole >= (role ?: GroupMemberRole.Observer)
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@@ -4760,6 +4844,7 @@ sealed class ChatErrorType {
|
||||
is FallbackToSMPProhibited -> "fallbackToSMPProhibited"
|
||||
is InlineFileProhibited -> "inlineFileProhibited"
|
||||
is InvalidQuote -> "invalidQuote"
|
||||
is InvalidForward -> "invalidForward"
|
||||
is InvalidChatItemUpdate -> "invalidChatItemUpdate"
|
||||
is InvalidChatItemDelete -> "invalidChatItemDelete"
|
||||
is HasCurrentCall -> "hasCurrentCall"
|
||||
@@ -4838,6 +4923,7 @@ sealed class ChatErrorType {
|
||||
@Serializable @SerialName("fallbackToSMPProhibited") class FallbackToSMPProhibited(val fileId: Long): ChatErrorType()
|
||||
@Serializable @SerialName("inlineFileProhibited") class InlineFileProhibited(val fileId: Long): ChatErrorType()
|
||||
@Serializable @SerialName("invalidQuote") object InvalidQuote: ChatErrorType()
|
||||
@Serializable @SerialName("invalidForward") object InvalidForward: ChatErrorType()
|
||||
@Serializable @SerialName("invalidChatItemUpdate") object InvalidChatItemUpdate: ChatErrorType()
|
||||
@Serializable @SerialName("invalidChatItemDelete") object InvalidChatItemDelete: ChatErrorType()
|
||||
@Serializable @SerialName("hasCurrentCall") object HasCurrentCall: ChatErrorType()
|
||||
@@ -5527,3 +5613,26 @@ enum class AppSettingsLockScreenCalls {
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Serializable
|
||||
data class UserNetworkInfo(
|
||||
val networkType: UserNetworkType,
|
||||
val online: Boolean,
|
||||
)
|
||||
|
||||
enum class UserNetworkType {
|
||||
@SerialName("none") NONE,
|
||||
@SerialName("cellular") CELLULAR,
|
||||
@SerialName("wifi") WIFI,
|
||||
@SerialName("ethernet") ETHERNET,
|
||||
@SerialName("other") OTHER;
|
||||
|
||||
val text: String
|
||||
get() = when (this) {
|
||||
NONE -> generalGetString(MR.strings.network_type_no_network_connection)
|
||||
CELLULAR -> generalGetString(MR.strings.network_type_cellular)
|
||||
WIFI -> generalGetString(MR.strings.network_type_network_wifi)
|
||||
ETHERNET -> generalGetString(MR.strings.network_type_ethernet)
|
||||
OTHER -> generalGetString(MR.strings.network_type_other)
|
||||
}
|
||||
}
|
||||
|
||||
+1
-1
@@ -9,7 +9,6 @@ import chat.simplex.common.views.helpers.DatabaseUtils.randomDatabasePassword
|
||||
import chat.simplex.common.views.onboarding.OnboardingStage
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import java.io.File
|
||||
import java.nio.ByteBuffer
|
||||
|
||||
@@ -88,6 +87,7 @@ suspend fun initChatController(useKey: String? = null, confirmMigrations: Migrat
|
||||
Log.d(TAG, "Unable to migrate successfully: $res")
|
||||
return
|
||||
}
|
||||
platform.androidRestartNetworkObserver()
|
||||
controller.apiSetTempFolder(coreTmpDir.absolutePath)
|
||||
controller.apiSetFilesFolder(appFilesDir.absolutePath)
|
||||
if (appPlatform.isDesktop) {
|
||||
|
||||
+1
@@ -23,6 +23,7 @@ interface PlatformInterface {
|
||||
fun androidStartCallActivity(acceptCall: Boolean, remoteHostId: Long? = null, chatId: ChatId? = null) {}
|
||||
fun androidPictureInPictureAllowed(): Boolean = true
|
||||
fun androidCallEnded() {}
|
||||
fun androidRestartNetworkObserver() {}
|
||||
@Composable fun androidLockPortraitOrientation() {}
|
||||
suspend fun androidAskToAllowBackgroundCalls(): Boolean = true
|
||||
@Composable fun desktopScrollBarComponents(): Triple<Animatable<Float, AnimationVector1D>, Modifier, MutableState<Job>> = remember { Triple(Animatable(0f), Modifier, mutableStateOf(Job())) }
|
||||
|
||||
+9
@@ -39,4 +39,13 @@ interface SoundPlayerInterface {
|
||||
fun stop()
|
||||
}
|
||||
|
||||
interface CallSoundsPlayerInterface {
|
||||
fun startConnectingCallSound(scope: CoroutineScope)
|
||||
fun startInCallSound(scope: CoroutineScope)
|
||||
fun stop()
|
||||
fun vibrate(times: Int = 1)
|
||||
}
|
||||
|
||||
expect object SoundPlayer: SoundPlayerInterface
|
||||
|
||||
expect object CallSoundsPlayer: CallSoundsPlayerInterface
|
||||
|
||||
+1
@@ -85,6 +85,7 @@ fun TerminalLayout(
|
||||
isDirectChat = false,
|
||||
liveMessageAlertShown = SharedPreference(get = { false }, set = {}),
|
||||
sendMsgEnabled = true,
|
||||
sendButtonEnabled = true,
|
||||
nextSendGrpInv = false,
|
||||
needToAllowVoiceToContact = false,
|
||||
allowedVoiceByPrefs = false,
|
||||
|
||||
+20
@@ -1,6 +1,26 @@
|
||||
package chat.simplex.common.views.call
|
||||
|
||||
import androidx.compose.runtime.Composable
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.platform.*
|
||||
import kotlinx.coroutines.*
|
||||
|
||||
@Composable
|
||||
expect fun ActiveCallView()
|
||||
|
||||
fun activeCallWaitDeliveryReceipt(scope: CoroutineScope) = scope.launch(Dispatchers.Default) {
|
||||
for (apiResp in controller.messagesChannel) {
|
||||
val call = chatModel.activeCall.value
|
||||
if (call == null || call.callState > CallState.InvitationSent) break
|
||||
val msg = apiResp.resp
|
||||
if (apiResp.remoteHostId == call.remoteHostId &&
|
||||
msg is CR.ChatItemStatusUpdated &&
|
||||
msg.chatItem.chatInfo.id == call.contact.id &&
|
||||
msg.chatItem.chatItem.content is CIContent.SndCall &&
|
||||
msg.chatItem.chatItem.meta.itemStatus is CIStatus.SndRcvd) {
|
||||
CallSoundsPlayer.startInCallSound(scope)
|
||||
break
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-3
@@ -187,10 +187,11 @@ data class ConnectionState(
|
||||
)
|
||||
|
||||
// the servers are expected in this format:
|
||||
// stun:stun.simplex.im:443?transport=tcp
|
||||
// turn:private:yleob6AVkiNI87hpR94Z@turn.simplex.im:443?transport=tcp
|
||||
// stuns:stun.simplex.im:443?transport=tcp
|
||||
// turns:private2:Hxuq2QxUjnhj96Zq2r4HjqHRj@turn.simplex.im:443?transport=tcp
|
||||
fun parseRTCIceServer(str: String): RTCIceServer? {
|
||||
var s = replaceScheme(str, "stun:")
|
||||
s = replaceScheme(s, "stuns:")
|
||||
s = replaceScheme(s, "turn:")
|
||||
s = replaceScheme(s, "turns:")
|
||||
val u = runCatching { URI(s) }.getOrNull()
|
||||
@@ -198,7 +199,7 @@ fun parseRTCIceServer(str: String): RTCIceServer? {
|
||||
val scheme = u.scheme
|
||||
val host = u.host
|
||||
val port = u.port
|
||||
if (u.path == "" && (scheme == "stun" || scheme == "turn" || scheme == "turns")) {
|
||||
if (u.path == "" && (scheme == "stun" || scheme == "stuns" || scheme == "turn" || scheme == "turns")) {
|
||||
val userInfo = u.userInfo?.split(":")
|
||||
val query = if (u.query == null || u.query == "") "" else "?${u.query}"
|
||||
return RTCIceServer(
|
||||
|
||||
+109
-11
@@ -13,9 +13,8 @@ import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.platform.LocalClipboardManager
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalUriHandler
|
||||
import androidx.compose.ui.platform.*
|
||||
import androidx.compose.ui.text.AnnotatedString
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
@@ -29,6 +28,7 @@ import chat.simplex.common.views.chat.item.ItemAction
|
||||
import chat.simplex.common.views.chat.item.MarkdownText
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chatlist.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
|
||||
@@ -36,10 +36,11 @@ sealed class CIInfoTab {
|
||||
class Delivery(val memberDeliveryStatuses: List<MemberDeliveryStatus>): CIInfoTab()
|
||||
object History: CIInfoTab()
|
||||
class Quote(val quotedItem: CIQuote): CIInfoTab()
|
||||
class Forwarded(val forwardedFromChatItem: AChatItem): CIInfoTab()
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
|
||||
fun ChatItemInfoView(chatRh: Long?, ci: ChatItem, ciInfo: ChatItemInfo, devTools: Boolean) {
|
||||
val sent = ci.chatDir.sent
|
||||
val appColors = CurrentColors.collectAsState().value.appColors
|
||||
val uriHandler = LocalUriHandler.current
|
||||
@@ -151,6 +152,70 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
}
|
||||
}
|
||||
|
||||
val local = when (ci.chatDir) {
|
||||
is CIDirection.LocalSnd -> true
|
||||
is CIDirection.LocalRcv -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ForwardedFromSender(forwardedFromItem: AChatItem) {
|
||||
@Composable
|
||||
fun ItemText(text: String, fontStyle: FontStyle = FontStyle.Normal, color: Color = MaterialTheme.colors.onBackground) {
|
||||
Text(
|
||||
text,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
style = MaterialTheme.typography.body1,
|
||||
fontStyle = fontStyle,
|
||||
color = color,
|
||||
)
|
||||
}
|
||||
|
||||
Row(verticalAlignment = Alignment.CenterVertically) {
|
||||
ChatInfoImage(forwardedFromItem.chatInfo, size = 57.dp)
|
||||
Column(
|
||||
modifier = Modifier
|
||||
.padding(start = 15.dp)
|
||||
.weight(1F)
|
||||
) {
|
||||
if (forwardedFromItem.chatItem.chatDir.sent) {
|
||||
ItemText(text = stringResource(MR.strings.sender_you_pronoun), fontStyle = FontStyle.Italic)
|
||||
Spacer(Modifier.height(7.dp))
|
||||
ItemText(forwardedFromItem.chatInfo.chatViewName, color = MaterialTheme.colors.secondary)
|
||||
} else if (forwardedFromItem.chatItem.chatDir is CIDirection.GroupRcv) {
|
||||
ItemText(text = forwardedFromItem.chatItem.chatDir.groupMember.chatViewName)
|
||||
Spacer(Modifier.height(7.dp))
|
||||
ItemText(forwardedFromItem.chatInfo.chatViewName, color = MaterialTheme.colors.secondary)
|
||||
} else {
|
||||
ItemText(forwardedFromItem.chatInfo.chatViewName, color = MaterialTheme.colors.onBackground)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ForwardedFromView(forwardedFromItem: AChatItem) {
|
||||
Column {
|
||||
SectionItemView(
|
||||
click = {
|
||||
withBGApi {
|
||||
openChat(chatRh, forwardedFromItem.chatInfo, chatModel)
|
||||
ModalManager.end.closeModals()
|
||||
}
|
||||
},
|
||||
padding = PaddingValues(start = 17.dp, end = DEFAULT_PADDING)
|
||||
) {
|
||||
ForwardedFromSender(forwardedFromItem)
|
||||
}
|
||||
|
||||
if (!local) {
|
||||
Divider(Modifier.padding(start = DEFAULT_PADDING_HALF, top = 41.dp, end = DEFAULT_PADDING_HALF, bottom = DEFAULT_PADDING_HALF))
|
||||
Text(stringResource(MR.strings.recipients_can_not_see_who_message_from), Modifier.padding(horizontal = DEFAULT_PADDING), fontSize = 12.sp, color = MaterialTheme.colors.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun Details() {
|
||||
AppBarTitle(stringResource(if (ci.localNote) MR.strings.saved_message_title else if (sent) MR.strings.sent_message else MR.strings.received_message))
|
||||
@@ -188,7 +253,7 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
// LALAL SCROLLBAR DOESN'T WORK
|
||||
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
|
||||
Details()
|
||||
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
|
||||
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = true)
|
||||
val versions = ciInfo.itemVersions
|
||||
if (versions.isNotEmpty()) {
|
||||
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
|
||||
@@ -213,7 +278,7 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
// LALAL SCROLLBAR DOESN'T WORK
|
||||
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
|
||||
Details()
|
||||
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
|
||||
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = true)
|
||||
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
|
||||
Text(stringResource(MR.strings.in_reply_to), style = MaterialTheme.typography.h2, modifier = Modifier.padding(bottom = DEFAULT_PADDING))
|
||||
QuotedMsgView(qi)
|
||||
@@ -222,6 +287,22 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ForwardedFromTab(forwardedFromItem: AChatItem) {
|
||||
// LALAL SCROLLBAR DOESN'T WORK
|
||||
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
|
||||
Details()
|
||||
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = true)
|
||||
SectionView {
|
||||
Text(stringResource(if (local) MR.strings.saved_from_chat_item_info_title else MR.strings.forwarded_from_chat_item_info_title),
|
||||
style = MaterialTheme.typography.h2,
|
||||
modifier = Modifier.padding(start = DEFAULT_PADDING, end = DEFAULT_PADDING, bottom = DEFAULT_PADDING))
|
||||
ForwardedFromView(forwardedFromItem)
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MemberDeliveryStatusView(member: GroupMember, status: CIStatus) {
|
||||
SectionItemView(
|
||||
@@ -271,7 +352,7 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
// LALAL SCROLLBAR DOESN'T WORK
|
||||
ColumnWithScrollBar(Modifier.fillMaxWidth()) {
|
||||
Details()
|
||||
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = false)
|
||||
SectionDividerSpaced(maxTopPadding = false, maxBottomPadding = true)
|
||||
val mss = membersStatuses(chatModel, memberDeliveryStatuses)
|
||||
if (mss.isNotEmpty()) {
|
||||
SectionView(padding = PaddingValues(horizontal = DEFAULT_PADDING)) {
|
||||
@@ -297,6 +378,7 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
is CIInfoTab.Delivery -> stringResource(MR.strings.delivery)
|
||||
is CIInfoTab.History -> stringResource(MR.strings.edit_history)
|
||||
is CIInfoTab.Quote -> stringResource(MR.strings.in_reply_to)
|
||||
is CIInfoTab.Forwarded -> stringResource(if (local) MR.strings.saved_chat_item_info_tab else MR.strings.forwarded_chat_item_info_tab)
|
||||
}
|
||||
}
|
||||
|
||||
@@ -305,6 +387,7 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
is CIInfoTab.Delivery -> MR.images.ic_double_check
|
||||
is CIInfoTab.History -> MR.images.ic_history
|
||||
is CIInfoTab.Quote -> MR.images.ic_reply
|
||||
is CIInfoTab.Forwarded -> MR.images.ic_forward
|
||||
}
|
||||
}
|
||||
|
||||
@@ -316,6 +399,9 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
if (ci.quotedItem != null) {
|
||||
numTabs += 1
|
||||
}
|
||||
if (ciInfo.forwardedFromChatItem != null) {
|
||||
numTabs += 1
|
||||
}
|
||||
return numTabs
|
||||
}
|
||||
|
||||
@@ -326,11 +412,6 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
.fillMaxHeight(),
|
||||
verticalArrangement = Arrangement.SpaceBetween
|
||||
) {
|
||||
LaunchedEffect(ciInfo) {
|
||||
if (ciInfo.memberDeliveryStatuses != null) {
|
||||
selection.value = CIInfoTab.Delivery(ciInfo.memberDeliveryStatuses)
|
||||
}
|
||||
}
|
||||
Column(Modifier.weight(1f)) {
|
||||
when (val sel = selection.value) {
|
||||
is CIInfoTab.Delivery -> {
|
||||
@@ -344,6 +425,10 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
is CIInfoTab.Quote -> {
|
||||
QuoteTab(sel.quotedItem)
|
||||
}
|
||||
|
||||
is CIInfoTab.Forwarded -> {
|
||||
ForwardedFromTab(sel.forwardedFromChatItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
val availableTabs = mutableListOf<CIInfoTab>()
|
||||
@@ -354,6 +439,19 @@ fun ChatItemInfoView(chatModel: ChatModel, ci: ChatItem, ciInfo: ChatItemInfo, d
|
||||
if (ci.quotedItem != null) {
|
||||
availableTabs.add(CIInfoTab.Quote(ci.quotedItem))
|
||||
}
|
||||
if (ciInfo.forwardedFromChatItem != null) {
|
||||
availableTabs.add(CIInfoTab.Forwarded(ciInfo.forwardedFromChatItem))
|
||||
}
|
||||
if (availableTabs.none { it.javaClass == selection.value.javaClass }) {
|
||||
selection.value = availableTabs.first()
|
||||
}
|
||||
LaunchedEffect(ciInfo) {
|
||||
if (ciInfo.forwardedFromChatItem != null && selection.value is CIInfoTab.Forwarded) {
|
||||
selection.value = CIInfoTab.Forwarded(ciInfo.forwardedFromChatItem)
|
||||
} else if (ciInfo.memberDeliveryStatuses != null) {
|
||||
selection.value = CIInfoTab.Delivery(ciInfo.memberDeliveryStatuses)
|
||||
}
|
||||
}
|
||||
TabRow(
|
||||
selectedTabIndex = availableTabs.indexOfFirst { it::class == selection.value::class },
|
||||
backgroundColor = Color.Transparent,
|
||||
|
||||
+12
-9
@@ -52,9 +52,11 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
|
||||
val user = chatModel.currentUser.value
|
||||
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
|
||||
val composeState = rememberSaveable(saver = ComposeState.saver()) {
|
||||
val draft = chatModel.draft.value
|
||||
val sharedContent = chatModel.sharedContent.value
|
||||
mutableStateOf(
|
||||
if (chatModel.draftChatId.value == chatId && chatModel.draft.value != null) {
|
||||
chatModel.draft.value ?: ComposeState(useLinkPreviews = useLinkPreviews)
|
||||
if (chatModel.draftChatId.value == chatId && draft != null && (sharedContent !is SharedContent.Forward || sharedContent.fromChatInfo.id == chatId)) {
|
||||
draft
|
||||
} else {
|
||||
ComposeState(useLinkPreviews = useLinkPreviews)
|
||||
}
|
||||
@@ -408,7 +410,7 @@ fun ChatView(chatId: String, chatModel: ChatModel, onComposed: suspend (chatId:
|
||||
clipboard.shareText(itemInfoShareText(chatModel, cItem, ciInfo, chatModel.controller.appPrefs.developerTools.get()))
|
||||
}
|
||||
}) { close ->
|
||||
ChatItemInfoView(chatModel, cItem, ciInfo, devTools = chatModel.controller.appPrefs.developerTools.get())
|
||||
ChatItemInfoView(chatRh, cItem, ciInfo, devTools = chatModel.controller.appPrefs.developerTools.get())
|
||||
KeyChangeEffect(chatModel.chatId.value) {
|
||||
close()
|
||||
}
|
||||
@@ -956,13 +958,13 @@ fun BoxWithConstraintsScope.ChatItemsList(
|
||||
tryOrShowError("${cItem.id}ChatItem", error = {
|
||||
CIBrokenComposableView(if (cItem.chatDir.sent) Alignment.CenterEnd else Alignment.CenterStart)
|
||||
}) {
|
||||
ChatItemView(chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools)
|
||||
ChatItemView(chat.remoteHostId, chat.chatInfo, cItem, composeState, provider, useLinkPreviews = useLinkPreviews, linkMode = linkMode, revealed = revealed, range = range, deleteMessage = deleteMessage, deleteMessages = deleteMessages, receiveFile = receiveFile, cancelFile = cancelFile, joinGroup = joinGroup, acceptCall = acceptCall, acceptFeature = acceptFeature, openDirectChat = openDirectChat, updateContactStats = updateContactStats, updateMemberStats = updateMemberStats, syncContactConnection = syncContactConnection, syncMemberConnection = syncMemberConnection, findModelChat = findModelChat, findModelMember = findModelMember, scrollToItem = scrollToItem, setReaction = setReaction, showItemDetails = showItemDetails, developerTools = developerTools)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ChatItemView(cItem: ChatItem, range: IntRange?, prevItem: ChatItem?) {
|
||||
val voiceWithTransparentBack = cItem.content.msgContent is MsgContent.MCVoice && cItem.content.text.isEmpty() && cItem.quotedItem == null
|
||||
val voiceWithTransparentBack = cItem.content.msgContent is MsgContent.MCVoice && cItem.content.text.isEmpty() && cItem.quotedItem == null && cItem.meta.itemForwarded == null
|
||||
if (chat.chatInfo is ChatInfo.Group) {
|
||||
if (cItem.chatDir is CIDirection.GroupRcv) {
|
||||
val member = cItem.chatDir.groupMember
|
||||
@@ -1090,9 +1092,9 @@ private fun ScrollToBottom(chatId: ChatId, listState: LazyListState, chatItems:
|
||||
.collect {
|
||||
try {
|
||||
if (listState.firstVisibleItemIndex == 0 || (listState.firstVisibleItemIndex == 1 && listState.layoutInfo.totalItemsCount == chatItems.size)) {
|
||||
listState.animateScrollToItem(0)
|
||||
if (appPlatform.isAndroid) listState.animateScrollToItem(0) else listState.scrollToItem(0)
|
||||
} else {
|
||||
listState.animateScrollBy(scrollDistance)
|
||||
if (appPlatform.isAndroid) listState.animateScrollBy(scrollDistance) else listState.scrollBy(scrollDistance)
|
||||
}
|
||||
} catch (e: CancellationException) {
|
||||
/**
|
||||
@@ -1392,11 +1394,12 @@ private fun providerForGallery(
|
||||
return null
|
||||
}
|
||||
|
||||
var initialIndex = Int.MAX_VALUE / 2
|
||||
// Pager has a bug with overflowing when total pages is around Int.MAX_VALUE. Using smaller value
|
||||
var initialIndex = 10000 / 2
|
||||
var initialChatId = cItemId
|
||||
return object: ImageGalleryProvider {
|
||||
override val initialIndex: Int = initialIndex
|
||||
override val totalMediaSize = mutableStateOf(Int.MAX_VALUE)
|
||||
override val totalMediaSize = mutableStateOf(10000)
|
||||
override fun getMedia(index: Int): ProviderMedia? {
|
||||
val internalIndex = initialIndex - index
|
||||
val item = item(internalIndex, initialChatId)?.second ?: return null
|
||||
|
||||
+121
-20
@@ -1,6 +1,7 @@
|
||||
@file:UseSerializers(UriSerializer::class)
|
||||
package chat.simplex.common.views.chat
|
||||
|
||||
import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.foundation.shape.CircleShape
|
||||
import androidx.compose.material.*
|
||||
@@ -11,6 +12,8 @@ import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import androidx.compose.ui.draw.clip
|
||||
import androidx.compose.ui.graphics.ImageBitmap
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.ui.text.font.FontStyle
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.unit.dp
|
||||
@@ -18,8 +21,7 @@ import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.model.ChatModel.filesToDelete
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.Indigo
|
||||
import chat.simplex.common.ui.theme.isSystemInDarkTheme
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.res.MR
|
||||
@@ -44,6 +46,7 @@ sealed class ComposeContextItem {
|
||||
@Serializable object NoContextItem: ComposeContextItem()
|
||||
@Serializable class QuotedItem(val chatItem: ChatItem): ComposeContextItem()
|
||||
@Serializable class EditingItem(val chatItem: ChatItem): ComposeContextItem()
|
||||
@Serializable class ForwardingItem(val chatItem: ChatItem, val fromChatInfo: ChatInfo): ComposeContextItem()
|
||||
}
|
||||
|
||||
@Serializable
|
||||
@@ -77,13 +80,18 @@ data class ComposeState(
|
||||
is ComposeContextItem.EditingItem -> true
|
||||
else -> false
|
||||
}
|
||||
val forwarding: Boolean
|
||||
get() = when (contextItem) {
|
||||
is ComposeContextItem.ForwardingItem -> true
|
||||
else -> false
|
||||
}
|
||||
val sendEnabled: () -> Boolean
|
||||
get() = {
|
||||
val hasContent = when (preview) {
|
||||
is ComposePreview.MediaPreview -> true
|
||||
is ComposePreview.VoicePreview -> true
|
||||
is ComposePreview.FilePreview -> true
|
||||
else -> message.isNotEmpty() || liveMessage != null
|
||||
else -> message.isNotEmpty() || forwarding || liveMessage != null
|
||||
}
|
||||
hasContent && !inProgress
|
||||
}
|
||||
@@ -107,7 +115,7 @@ data class ComposeState(
|
||||
|
||||
val attachmentDisabled: Boolean
|
||||
get() {
|
||||
if (editing || liveMessage != null || inProgress) return true
|
||||
if (editing || forwarding || liveMessage != null || inProgress) return true
|
||||
return when (preview) {
|
||||
ComposePreview.NoPreview -> false
|
||||
is ComposePreview.CLinkPreview -> false
|
||||
@@ -115,6 +123,15 @@ data class ComposeState(
|
||||
}
|
||||
}
|
||||
|
||||
val attachmentPreview: Boolean
|
||||
get() = when (preview) {
|
||||
ComposePreview.NoPreview -> false
|
||||
is ComposePreview.CLinkPreview -> false
|
||||
is ComposePreview.MediaPreview -> preview.content.isNotEmpty()
|
||||
is ComposePreview.VoicePreview -> false
|
||||
is ComposePreview.FilePreview -> true
|
||||
}
|
||||
|
||||
val empty: Boolean
|
||||
get() = message.isEmpty() && preview is ComposePreview.NoPreview && contextItem is ComposeContextItem.NoContextItem
|
||||
|
||||
@@ -243,10 +260,23 @@ fun ComposeView(
|
||||
attachmentOption: MutableState<AttachmentOption?>,
|
||||
showChooseAttachment: () -> Unit
|
||||
) {
|
||||
val cancelledLinks = rememberSaveable { mutableSetOf<String>() }
|
||||
fun isSimplexLink(link: String): Boolean =
|
||||
link.startsWith("https://simplex.chat", true) || link.startsWith("http://simplex.chat", true)
|
||||
|
||||
fun parseMessage(msg: String): Pair<String?, Boolean> {
|
||||
if (msg.isBlank()) return null to false
|
||||
val parsedMsg = parseToMarkdown(msg) ?: return null to false
|
||||
val link = parsedMsg.firstOrNull { ft -> ft.format is Format.Uri && !cancelledLinks.contains(ft.text) && !isSimplexLink(ft.text) }
|
||||
val simplexLink = parsedMsg.any { ft -> ft.format is Format.SimplexLink }
|
||||
return link?.text to simplexLink
|
||||
}
|
||||
|
||||
val linkUrl = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
// default value parsed because of draft
|
||||
val hasSimplexLink = rememberSaveable { mutableStateOf(parseMessage(composeState.value.message).second) }
|
||||
val prevLinkUrl = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val pendingLinkUrl = rememberSaveable { mutableStateOf<String?>(null) }
|
||||
val cancelledLinks = rememberSaveable { mutableSetOf<String>() }
|
||||
val useLinkPreviews = chatModel.controller.appPrefs.privacyLinkPreviews.get()
|
||||
val saveLastDraft = chatModel.controller.appPrefs.privacySaveLastDraft.get()
|
||||
val smallFont = MaterialTheme.typography.body1.copy(color = MaterialTheme.colors.onBackground)
|
||||
@@ -255,15 +285,6 @@ fun ComposeView(
|
||||
|
||||
AttachmentSelection(composeState, attachmentOption, composeState::processPickedFile) { uris, text -> CoroutineScope(Dispatchers.IO).launch { composeState.processPickedMedia(uris, text) } }
|
||||
|
||||
fun isSimplexLink(link: String): Boolean =
|
||||
link.startsWith("https://simplex.chat", true) || link.startsWith("http://simplex.chat", true)
|
||||
|
||||
fun parseMessage(msg: String): String? {
|
||||
val parsedMsg = parseToMarkdown(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) {
|
||||
composeState.value = composeState.value.copy(preview = ComposePreview.CLinkPreview(null))
|
||||
@@ -283,7 +304,9 @@ fun ComposeView(
|
||||
|
||||
fun showLinkPreview(s: String) {
|
||||
prevLinkUrl.value = linkUrl.value
|
||||
linkUrl.value = parseMessage(s)
|
||||
val parsed = parseMessage(s)
|
||||
linkUrl.value = parsed.first
|
||||
hasSimplexLink.value = parsed.second
|
||||
val url = linkUrl.value
|
||||
if (url != null) {
|
||||
if (url != composeState.value.linkPreview?.uri && url != pendingLinkUrl.value) {
|
||||
@@ -338,6 +361,7 @@ fun ComposeView(
|
||||
is SharedContent.Media -> shared.uris.map { it.toString() }
|
||||
is SharedContent.File -> listOf(shared.uri.toString())
|
||||
is SharedContent.Text -> emptyList()
|
||||
is SharedContent.Forward -> emptyList()
|
||||
}
|
||||
// When sharing a file and pasting it in SimpleX itself, the file shouldn't be deleted before sending or before leaving the chat after sharing
|
||||
chatModel.filesToDelete.removeAll { file ->
|
||||
@@ -384,10 +408,25 @@ fun ComposeView(
|
||||
composeState.value = composeState.value.copy(inProgress = true)
|
||||
}
|
||||
|
||||
suspend fun forwardItem(rhId: Long?, forwardedItem: ChatItem, fromChatInfo: ChatInfo): ChatItem? {
|
||||
val chatItem = controller.apiForwardChatItem(
|
||||
rh = rhId,
|
||||
toChatType = chat.chatInfo.chatType,
|
||||
toChatId = chat.chatInfo.apiId,
|
||||
fromChatType = fromChatInfo.chatType,
|
||||
fromChatId = fromChatInfo.apiId,
|
||||
itemId = forwardedItem.id
|
||||
)
|
||||
if (chatItem != null) {
|
||||
chatModel.addChatItem(rhId, chat.chatInfo, chatItem)
|
||||
}
|
||||
return chatItem
|
||||
}
|
||||
|
||||
fun checkLinkPreview(): MsgContent {
|
||||
return when (val composePreview = cs.preview) {
|
||||
is ComposePreview.CLinkPreview -> {
|
||||
val url = parseMessage(msgText)
|
||||
val url = parseMessage(msgText).first
|
||||
val lp = composePreview.linkPreview
|
||||
if (lp != null && url == lp.uri) {
|
||||
MsgContent.MCLink(msgText, preview = lp)
|
||||
@@ -443,11 +482,18 @@ fun ComposeView(
|
||||
if (liveMessage != null) composeState.value = cs.copy(liveMessage = null)
|
||||
sending()
|
||||
}
|
||||
clearCurrentDraft()
|
||||
if (!cs.forwarding || chatModel.draft.value?.forwarding == true) {
|
||||
clearCurrentDraft()
|
||||
}
|
||||
|
||||
if (chat.nextSendGrpInv) {
|
||||
sendMemberContactInvitation()
|
||||
sent = null
|
||||
} else if (cs.contextItem is ComposeContextItem.ForwardingItem) {
|
||||
sent = forwardItem(chat.remoteHostId, cs.contextItem.chatItem, cs.contextItem.fromChatInfo)
|
||||
if (cs.message.isNotEmpty()) {
|
||||
sent = send(chat, checkLinkPreview(), quoted = sent?.id, live = false, ttl = null)
|
||||
}
|
||||
} else if (cs.contextItem is ComposeContextItem.EditingItem) {
|
||||
val ei = cs.contextItem.chatItem
|
||||
sent = updateMessage(ei, chat, live)
|
||||
@@ -546,7 +592,15 @@ fun ComposeView(
|
||||
sent = send(chat, MsgContent.MCText(msgText), quotedItemId, null, live, ttl)
|
||||
}
|
||||
}
|
||||
val wasForwarding = cs.forwarding
|
||||
val forwardingFromChatId = (cs.contextItem as? ComposeContextItem.ForwardingItem)?.fromChatInfo?.id
|
||||
clearState(live)
|
||||
val draft = chatModel.draft.value
|
||||
if (wasForwarding && chatModel.draftChatId.value == chat.chatInfo.id && forwardingFromChatId != chat.chatInfo.id && draft != null) {
|
||||
composeState.value = draft
|
||||
} else {
|
||||
clearCurrentDraft()
|
||||
}
|
||||
return sent
|
||||
}
|
||||
|
||||
@@ -563,8 +617,16 @@ fun ComposeView(
|
||||
} else {
|
||||
textStyle.value = smallFont
|
||||
if (composeState.value.linkPreviewAllowed) {
|
||||
if (s.isNotEmpty()) showLinkPreview(s)
|
||||
else resetLinkPreview()
|
||||
if (s.isNotEmpty()) {
|
||||
showLinkPreview(s)
|
||||
} else {
|
||||
resetLinkPreview()
|
||||
hasSimplexLink.value = false
|
||||
}
|
||||
} else if (s.isNotEmpty() && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)) {
|
||||
hasSimplexLink.value = parseMessage(s).second
|
||||
} else {
|
||||
hasSimplexLink.value = false
|
||||
}
|
||||
}
|
||||
}
|
||||
@@ -700,6 +762,16 @@ fun ComposeView(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun MsgNotAllowedView(reason: String, icon: Painter) {
|
||||
val color = CurrentColors.collectAsState().value.appColors.receivedMessage
|
||||
Row(Modifier.padding(top = 5.dp).fillMaxWidth().background(color).padding(horizontal = DEFAULT_PADDING_HALF, vertical = DEFAULT_PADDING_HALF * 1.5f), verticalAlignment = Alignment.CenterVertically) {
|
||||
Icon(icon, null, tint = MaterialTheme.colors.secondary)
|
||||
Spacer(Modifier.width(DEFAULT_PADDING_HALF))
|
||||
Text(reason, fontStyle = FontStyle.Italic)
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun contextItemView() {
|
||||
when (val contextItem = composeState.value.contextItem) {
|
||||
@@ -710,6 +782,9 @@ fun ComposeView(
|
||||
is ComposeContextItem.EditingItem -> ContextItemView(contextItem.chatItem, painterResource(MR.images.ic_edit_filled)) {
|
||||
clearState()
|
||||
}
|
||||
is ComposeContextItem.ForwardingItem -> ContextItemView(contextItem.chatItem, painterResource(MR.images.ic_forward), showSender = false) {
|
||||
composeState.value = composeState.value.copy(contextItem = ComposeContextItem.NoContextItem)
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@@ -729,6 +804,10 @@ fun ComposeView(
|
||||
is SharedContent.Text -> onMessageChange(shared.text)
|
||||
is SharedContent.Media -> composeState.processPickedMedia(shared.uris, shared.text)
|
||||
is SharedContent.File -> composeState.processPickedFile(shared.uri, shared.text)
|
||||
is SharedContent.Forward -> composeState.value = composeState.value.copy(
|
||||
contextItem = ComposeContextItem.ForwardingItem(shared.chatItem, shared.fromChatInfo),
|
||||
preview = if (composeState.value.preview is ComposePreview.CLinkPreview) composeState.value.preview else ComposePreview.NoPreview
|
||||
)
|
||||
null -> {}
|
||||
}
|
||||
chatModel.sharedContent.value = null
|
||||
@@ -743,7 +822,17 @@ fun ComposeView(
|
||||
if (nextSendGrpInv.value) {
|
||||
ComposeContextInvitingContactMemberView()
|
||||
}
|
||||
val simplexLinkProhibited = hasSimplexLink.value && !chat.groupFeatureEnabled(GroupFeature.SimplexLinks)
|
||||
val fileProhibited = composeState.value.attachmentPreview && !chat.groupFeatureEnabled(GroupFeature.Files)
|
||||
val voiceProhibited = composeState.value.preview is ComposePreview.VoicePreview && !chat.chatInfo.featureEnabled(ChatFeature.Voice)
|
||||
if (composeState.value.preview !is ComposePreview.VoicePreview || composeState.value.editing) {
|
||||
if (simplexLinkProhibited) {
|
||||
MsgNotAllowedView(generalGetString(MR.strings.simplex_links_not_allowed), icon = painterResource(MR.images.ic_link))
|
||||
} else if (fileProhibited) {
|
||||
MsgNotAllowedView(generalGetString(MR.strings.files_and_media_not_allowed), icon = painterResource(MR.images.ic_draft))
|
||||
} else if (voiceProhibited) {
|
||||
MsgNotAllowedView(generalGetString(MR.strings.voice_messages_not_allowed), icon = painterResource(MR.images.ic_mic))
|
||||
}
|
||||
contextItemView()
|
||||
when {
|
||||
composeState.value.editing && composeState.value.preview is ComposePreview.VoicePreview -> {}
|
||||
@@ -764,7 +853,7 @@ fun ComposeView(
|
||||
modifier = Modifier.padding(end = 8.dp),
|
||||
verticalAlignment = Alignment.Bottom,
|
||||
) {
|
||||
val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on
|
||||
val isGroupAndProhibitedFiles = chat.chatInfo is ChatInfo.Group && !chat.chatInfo.groupInfo.fullGroupPreferences.files.on(chat.chatInfo.groupInfo.membership)
|
||||
val attachmentClicked = if (isGroupAndProhibitedFiles) {
|
||||
{
|
||||
AlertManager.shared.showAlertMsg(
|
||||
@@ -858,6 +947,17 @@ fun ComposeView(
|
||||
chatModel.removeLiveDummy()
|
||||
CIFile.cachedRemoteFileRequests.clear()
|
||||
}
|
||||
if (appPlatform.isDesktop) {
|
||||
// Don't enable this on Android, it breaks it, This method only works on desktop. For Android there is a `KeyChangeEffect(chatModel.chatId.value)`
|
||||
DisposableEffect(Unit) {
|
||||
onDispose {
|
||||
if (chatModel.sharedContent.value is SharedContent.Forward && saveLastDraft && !composeState.value.empty) {
|
||||
chatModel.draft.value = composeState.value
|
||||
chatModel.draftChatId.value = chat.id
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
val timedMessageAllowed = remember(chat.chatInfo) { chat.chatInfo.featureEnabled(ChatFeature.TimedMessages) }
|
||||
val sendButtonColor =
|
||||
@@ -871,6 +971,7 @@ fun ComposeView(
|
||||
chat.chatInfo is ChatInfo.Direct,
|
||||
liveMessageAlertShown = chatModel.controller.appPrefs.liveMessageAlertShown,
|
||||
sendMsgEnabled = sendMsgEnabled.value,
|
||||
sendButtonEnabled = sendMsgEnabled.value && !(simplexLinkProhibited || fileProhibited || voiceProhibited),
|
||||
nextSendGrpInv = nextSendGrpInv.value,
|
||||
needToAllowVoiceToContact,
|
||||
allowedVoiceByPrefs,
|
||||
|
||||
+41
-7
@@ -4,26 +4,29 @@ import androidx.compose.foundation.background
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.*
|
||||
import androidx.compose.ui.graphics.painter.Painter
|
||||
import androidx.compose.runtime.Composable
|
||||
import androidx.compose.runtime.collectAsState
|
||||
import androidx.compose.ui.Alignment
|
||||
import androidx.compose.ui.Modifier
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.text.TextStyle
|
||||
import androidx.compose.foundation.text.InlineTextContent
|
||||
import androidx.compose.foundation.text.appendInlineContent
|
||||
import androidx.compose.runtime.*
|
||||
import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
import kotlinx.datetime.Clock
|
||||
|
||||
@Composable
|
||||
fun ContextItemView(
|
||||
contextItem: ChatItem,
|
||||
contextIcon: Painter,
|
||||
showSender: Boolean = true,
|
||||
cancelContextItem: () -> Unit
|
||||
) {
|
||||
val sent = contextItem.chatDir.sent
|
||||
@@ -31,16 +34,47 @@ fun ContextItemView(
|
||||
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
|
||||
|
||||
@Composable
|
||||
fun msgContentView(lines: Int) {
|
||||
fun MessageText(attachment: ImageResource?, lines: Int) {
|
||||
val inlineContent: Pair<AnnotatedString.Builder.() -> Unit, Map<String, InlineTextContent>>? = if (attachment != null) {
|
||||
remember(contextItem.id) {
|
||||
val inlineContentBuilder: AnnotatedString.Builder.() -> Unit = {
|
||||
appendInlineContent(id = "attachmentIcon")
|
||||
append(" ")
|
||||
}
|
||||
val inlineContent = mapOf(
|
||||
"attachmentIcon" to InlineTextContent(
|
||||
Placeholder(20.sp, 20.sp, PlaceholderVerticalAlign.TextCenter)
|
||||
) {
|
||||
Icon(painterResource(attachment), null, tint = MaterialTheme.colors.secondary)
|
||||
}
|
||||
)
|
||||
inlineContentBuilder to inlineContent
|
||||
}
|
||||
} else null
|
||||
MarkdownText(
|
||||
contextItem.text, contextItem.formattedText,
|
||||
sender = null,
|
||||
toggleSecrets = false,
|
||||
maxLines = lines,
|
||||
inlineContent = inlineContent,
|
||||
linkMode = SimplexLinkMode.DESCRIPTION,
|
||||
modifier = Modifier.fillMaxWidth(),
|
||||
)
|
||||
}
|
||||
|
||||
fun attachment(): ImageResource? =
|
||||
when (contextItem.content.msgContent) {
|
||||
is MsgContent.MCFile -> MR.images.ic_draft_filled
|
||||
is MsgContent.MCImage -> MR.images.ic_image
|
||||
is MsgContent.MCVoice -> MR.images.ic_play_arrow_filled
|
||||
else -> null
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun ContextMsgPreview(lines: Int) {
|
||||
MessageText(remember(contextItem.id) { attachment() }, lines)
|
||||
}
|
||||
|
||||
Row(
|
||||
Modifier
|
||||
.padding(top = 8.dp)
|
||||
@@ -64,7 +98,7 @@ fun ContextItemView(
|
||||
tint = MaterialTheme.colors.secondary,
|
||||
)
|
||||
val sender = contextItem.memberDisplayName
|
||||
if (sender != null) {
|
||||
if (showSender && sender != null) {
|
||||
Column(
|
||||
horizontalAlignment = Alignment.Start,
|
||||
verticalArrangement = Arrangement.spacedBy(4.dp),
|
||||
@@ -73,10 +107,10 @@ fun ContextItemView(
|
||||
sender,
|
||||
style = TextStyle(fontSize = 13.5.sp, color = CurrentColors.value.colors.secondary)
|
||||
)
|
||||
msgContentView(lines = 2)
|
||||
ContextMsgPreview(lines = 2)
|
||||
}
|
||||
} else {
|
||||
msgContentView(lines = 3)
|
||||
ContextMsgPreview(lines = 3)
|
||||
}
|
||||
}
|
||||
IconButton(onClick = cancelContextItem) {
|
||||
|
||||
+9
-4
@@ -39,6 +39,7 @@ fun SendMsgView(
|
||||
isDirectChat: Boolean,
|
||||
liveMessageAlertShown: SharedPreference<Boolean>,
|
||||
sendMsgEnabled: Boolean,
|
||||
sendButtonEnabled: Boolean,
|
||||
nextSendGrpInv: Boolean,
|
||||
needToAllowVoiceToContact: Boolean,
|
||||
allowedVoiceByPrefs: Boolean,
|
||||
@@ -71,11 +72,12 @@ fun SendMsgView(
|
||||
}
|
||||
}
|
||||
val showVoiceButton = !nextSendGrpInv && cs.message.isEmpty() && showVoiceRecordIcon && !composeState.value.editing &&
|
||||
cs.liveMessage == null && (cs.preview is ComposePreview.NoPreview || recState.value is RecordingState.Started)
|
||||
!composeState.value.forwarding && cs.liveMessage == null && (cs.preview is ComposePreview.NoPreview || recState.value is RecordingState.Started)
|
||||
val showDeleteTextButton = rememberSaveable { mutableStateOf(false) }
|
||||
val sendMsgButtonDisabled = !sendMsgEnabled || !cs.sendEnabled() ||
|
||||
(!allowedVoiceByPrefs && cs.preview is ComposePreview.VoicePreview) ||
|
||||
cs.endLiveDisabled
|
||||
cs.endLiveDisabled ||
|
||||
!sendButtonEnabled
|
||||
PlatformTextField(composeState, sendMsgEnabled, sendMsgButtonDisabled, textStyle, showDeleteTextButton, userIsObserver, onMessageChange, editPrevMessage, onFilesPasted) {
|
||||
if (!cs.inProgress) {
|
||||
sendMessage(null)
|
||||
@@ -155,7 +157,7 @@ fun SendMsgView(
|
||||
fun MenuItems(): List<@Composable () -> Unit> {
|
||||
val menuItems = mutableListOf<@Composable () -> Unit>()
|
||||
|
||||
if (cs.liveMessage == null && !cs.editing && !nextSendGrpInv || sendMsgEnabled) {
|
||||
if (cs.liveMessage == null && !cs.editing && !cs.forwarding && !nextSendGrpInv || sendMsgEnabled) {
|
||||
if (
|
||||
cs.preview !is ComposePreview.VoicePreview &&
|
||||
cs.contextItem is ComposeContextItem.NoContextItem &&
|
||||
@@ -430,7 +432,7 @@ private fun SendMsgButton(
|
||||
.padding(4.dp)
|
||||
.alpha(alpha.value)
|
||||
.clip(CircleShape)
|
||||
.background(if (enabled) sendButtonColor else MaterialTheme.colors.secondary)
|
||||
.background(if (enabled) sendButtonColor else MaterialTheme.colors.secondary.copy(alpha = 0.75f))
|
||||
.padding(3.dp)
|
||||
)
|
||||
}
|
||||
@@ -552,6 +554,7 @@ fun PreviewSendMsgView() {
|
||||
isDirectChat = true,
|
||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||
sendMsgEnabled = true,
|
||||
sendButtonEnabled = true,
|
||||
nextSendGrpInv = false,
|
||||
needToAllowVoiceToContact = false,
|
||||
allowedVoiceByPrefs = true,
|
||||
@@ -586,6 +589,7 @@ fun PreviewSendMsgViewEditing() {
|
||||
isDirectChat = true,
|
||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||
sendMsgEnabled = true,
|
||||
sendButtonEnabled = true,
|
||||
nextSendGrpInv = false,
|
||||
needToAllowVoiceToContact = false,
|
||||
allowedVoiceByPrefs = true,
|
||||
@@ -620,6 +624,7 @@ fun PreviewSendMsgViewInProgress() {
|
||||
isDirectChat = true,
|
||||
liveMessageAlertShown = SharedPreference(get = { true }, set = { }),
|
||||
sendMsgEnabled = true,
|
||||
sendButtonEnabled = true,
|
||||
nextSendGrpInv = false,
|
||||
needToAllowVoiceToContact = false,
|
||||
allowedVoiceByPrefs = true,
|
||||
|
||||
+1
-1
@@ -62,7 +62,7 @@ fun GroupChatInfoView(chatModel: ChatModel, rhId: Long?, chatId: String, groupLi
|
||||
},
|
||||
members = chatModel.groupMembers
|
||||
.filter { it.memberStatus != GroupMemberStatus.MemLeft && it.memberStatus != GroupMemberStatus.MemRemoved }
|
||||
.sortedBy { it.displayName.lowercase() },
|
||||
.sortedByDescending { it.memberRole },
|
||||
developerTools,
|
||||
groupLink,
|
||||
addMembers = {
|
||||
|
||||
+2
-2
@@ -309,7 +309,7 @@ fun GroupMemberInfoLayout(
|
||||
SectionView {
|
||||
if (contactId != null && knownDirectChat(contactId) != null) {
|
||||
OpenChatButton(onClick = { openDirectChat(contactId) })
|
||||
} else if (groupInfo.fullGroupPreferences.directMessages.on) {
|
||||
} else if (groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) {
|
||||
if (contactId != null) {
|
||||
OpenChatButton(onClick = { openDirectChat(contactId) })
|
||||
} else if (member.activeConn?.peerChatVRange?.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) == true) {
|
||||
@@ -335,7 +335,7 @@ fun GroupMemberInfoLayout(
|
||||
val clipboard = LocalClipboardManager.current
|
||||
ShareAddressButton { clipboard.shareText(simplexChatLink(member.contactLink)) }
|
||||
if (contactId != null) {
|
||||
if (knownDirectChat(contactId) == null && !groupInfo.fullGroupPreferences.directMessages.on) {
|
||||
if (knownDirectChat(contactId) == null && !groupInfo.fullGroupPreferences.directMessages.on(groupInfo.membership)) {
|
||||
ConnectViaAddressButton(onClick = { connectViaAddress(member.contactLink) })
|
||||
}
|
||||
} else {
|
||||
|
||||
+57
-18
@@ -6,7 +6,6 @@ import SectionDividerSpaced
|
||||
import SectionItemView
|
||||
import SectionTextFooter
|
||||
import SectionView
|
||||
import androidx.compose.foundation.*
|
||||
import androidx.compose.foundation.layout.*
|
||||
import androidx.compose.material.MaterialTheme
|
||||
import androidx.compose.material.Text
|
||||
@@ -20,13 +19,20 @@ import chat.simplex.common.views.usersettings.PreferenceToggleWithIcon
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
|
||||
private val featureRoles: List<Pair<GroupMemberRole?, String>> = listOf(
|
||||
null to generalGetString(MR.strings.feature_roles_all_members),
|
||||
GroupMemberRole.Admin to generalGetString(MR.strings.feature_roles_admins),
|
||||
GroupMemberRole.Owner to generalGetString(MR.strings.feature_roles_owners)
|
||||
)
|
||||
|
||||
@Composable
|
||||
fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () -> Unit,) {
|
||||
fun GroupPreferencesView(m: ChatModel, rhId: Long?, chatId: String, close: () -> Unit) {
|
||||
val groupInfo = remember { derivedStateOf {
|
||||
val ch = m.getChat(chatId)
|
||||
val g = (ch?.chatInfo as? ChatInfo.Group)?.groupInfo
|
||||
if (g == null || ch?.remoteHostId != rhId) null else g
|
||||
if (g == null || ch.remoteHostId != rhId) null else g
|
||||
}}
|
||||
val gInfo = groupInfo.value ?: return
|
||||
var preferences by rememberSaveable(gInfo, stateSaver = serializableSaver()) { mutableStateOf(gInfo.fullGroupPreferences) }
|
||||
@@ -81,7 +87,7 @@ private fun GroupPreferencesLayout(
|
||||
val onTTLUpdated = { ttl: Int? ->
|
||||
applyPrefs(preferences.copy(timedMessages = preferences.timedMessages.copy(ttl = ttl)))
|
||||
}
|
||||
FeatureSection(GroupFeature.TimedMessages, timedMessages, groupInfo, preferences, onTTLUpdated) { enable ->
|
||||
FeatureSection(GroupFeature.TimedMessages, timedMessages, null, groupInfo, preferences, onTTLUpdated) { enable, _ ->
|
||||
if (enable == GroupFeatureEnabled.ON) {
|
||||
applyPrefs(preferences.copy(timedMessages = TimedMessagesGroupPreference(enable = enable, ttl = preferences.timedMessages.ttl ?: 86400)))
|
||||
} else {
|
||||
@@ -90,33 +96,45 @@ private fun GroupPreferencesLayout(
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowDirectMessages = remember(preferences) { mutableStateOf(preferences.directMessages.enable) }
|
||||
FeatureSection(GroupFeature.DirectMessages, allowDirectMessages, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(directMessages = GroupPreference(enable = it)))
|
||||
val directMessagesRole = remember(preferences) { mutableStateOf(preferences.directMessages.role) }
|
||||
FeatureSection(GroupFeature.DirectMessages, allowDirectMessages, directMessagesRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
|
||||
applyPrefs(preferences.copy(directMessages = RoleGroupPreference(enable = enable, role)))
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowFullDeletion = remember(preferences) { mutableStateOf(preferences.fullDelete.enable) }
|
||||
FeatureSection(GroupFeature.FullDelete, allowFullDeletion, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(fullDelete = GroupPreference(enable = it)))
|
||||
FeatureSection(GroupFeature.FullDelete, allowFullDeletion, null, groupInfo, preferences, onTTLUpdated) { enable, _ ->
|
||||
applyPrefs(preferences.copy(fullDelete = GroupPreference(enable = enable)))
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowReactions = remember(preferences) { mutableStateOf(preferences.reactions.enable) }
|
||||
FeatureSection(GroupFeature.Reactions, allowReactions, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(reactions = GroupPreference(enable = it)))
|
||||
FeatureSection(GroupFeature.Reactions, allowReactions, null, groupInfo, preferences, onTTLUpdated) { enable, _ ->
|
||||
applyPrefs(preferences.copy(reactions = GroupPreference(enable = enable)))
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowVoice = remember(preferences) { mutableStateOf(preferences.voice.enable) }
|
||||
FeatureSection(GroupFeature.Voice, allowVoice, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(voice = GroupPreference(enable = it)))
|
||||
val voiceRole = remember(preferences) { mutableStateOf(preferences.voice.role) }
|
||||
FeatureSection(GroupFeature.Voice, allowVoice, voiceRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
|
||||
applyPrefs(preferences.copy(voice = RoleGroupPreference(enable = enable, role)))
|
||||
}
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val allowFiles = remember(preferences) { mutableStateOf(preferences.files.enable) }
|
||||
FeatureSection(GroupFeature.Files, allowFiles, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(files = GroupPreference(enable = it)))
|
||||
val filesRole = remember(preferences) { mutableStateOf(preferences.files.role) }
|
||||
FeatureSection(GroupFeature.Files, allowFiles, filesRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
|
||||
applyPrefs(preferences.copy(files = RoleGroupPreference(enable = enable, role)))
|
||||
}
|
||||
|
||||
// TODO enable simplexLinks preference in 5.8
|
||||
// SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
// val allowSimplexLinks = remember(preferences) { mutableStateOf(preferences.simplexLinks.enable) }
|
||||
// val simplexLinksRole = remember(preferences) { mutableStateOf(preferences.simplexLinks.role) }
|
||||
// FeatureSection(GroupFeature.SimplexLinks, allowSimplexLinks, simplexLinksRole, groupInfo, preferences, onTTLUpdated) { enable, role ->
|
||||
// applyPrefs(preferences.copy(simplexLinks = RoleGroupPreference(enable = enable, role)))
|
||||
// }
|
||||
|
||||
SectionDividerSpaced(true, maxBottomPadding = false)
|
||||
val enableHistory = remember(preferences) { mutableStateOf(preferences.history.enable) }
|
||||
FeatureSection(GroupFeature.History, enableHistory, groupInfo, preferences, onTTLUpdated) {
|
||||
applyPrefs(preferences.copy(history = GroupPreference(enable = it)))
|
||||
FeatureSection(GroupFeature.History, enableHistory, null, groupInfo, preferences, onTTLUpdated) { enable, _ ->
|
||||
applyPrefs(preferences.copy(history = GroupPreference(enable = enable)))
|
||||
}
|
||||
if (groupInfo.canEdit) {
|
||||
SectionDividerSpaced(maxTopPadding = true, maxBottomPadding = false)
|
||||
@@ -134,10 +152,11 @@ private fun GroupPreferencesLayout(
|
||||
private fun FeatureSection(
|
||||
feature: GroupFeature,
|
||||
enableFeature: State<GroupFeatureEnabled>,
|
||||
enableForRole: State<GroupMemberRole?>? = null,
|
||||
groupInfo: GroupInfo,
|
||||
preferences: FullGroupPreferences,
|
||||
onTTLUpdated: (Int?) -> Unit,
|
||||
onSelected: (GroupFeatureEnabled) -> Unit
|
||||
onSelected: (GroupFeatureEnabled, GroupMemberRole?) -> Unit
|
||||
) {
|
||||
SectionView {
|
||||
val on = enableFeature.value == GroupFeatureEnabled.ON
|
||||
@@ -151,7 +170,7 @@ private fun FeatureSection(
|
||||
iconTint,
|
||||
enableFeature.value == GroupFeatureEnabled.ON,
|
||||
) { checked ->
|
||||
onSelected(if (checked) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF)
|
||||
onSelected(if (checked) GroupFeatureEnabled.ON else GroupFeatureEnabled.OFF, enableForRole?.value)
|
||||
}
|
||||
if (timedOn) {
|
||||
val ttl = rememberSaveable(preferences.timedMessages) { mutableStateOf(preferences.timedMessages.ttl) }
|
||||
@@ -165,6 +184,18 @@ private fun FeatureSection(
|
||||
onSelected = onTTLUpdated
|
||||
)
|
||||
}
|
||||
if (enableFeature.value == GroupFeatureEnabled.ON && enableForRole != null) {
|
||||
ExposedDropDownSettingRow(
|
||||
generalGetString(MR.strings.feature_enabled_for),
|
||||
featureRoles,
|
||||
enableForRole,
|
||||
// remove in v5.8
|
||||
enabled = remember { mutableStateOf(false) },
|
||||
onSelected = { value ->
|
||||
onSelected(enableFeature.value, value)
|
||||
}
|
||||
)
|
||||
}
|
||||
} else {
|
||||
InfoRow(
|
||||
feature.text,
|
||||
@@ -175,6 +206,14 @@ private fun FeatureSection(
|
||||
if (timedOn) {
|
||||
InfoRow(generalGetString(MR.strings.delete_after), timeText(preferences.timedMessages.ttl))
|
||||
}
|
||||
if (enableFeature.value == GroupFeatureEnabled.ON && enableForRole != null) {
|
||||
InfoRow(generalGetString(MR.strings.feature_enabled_for), featureRoles.firstOrNull { it.first == enableForRole.value }?.second ?: generalGetString(MR.strings.feature_roles_all_members), textColor = MaterialTheme.colors.secondary)
|
||||
}
|
||||
}
|
||||
}
|
||||
KeyChangeEffect(enableFeature.value) {
|
||||
if (enableFeature.value == GroupFeatureEnabled.OFF) {
|
||||
onSelected(enableFeature.value, null)
|
||||
}
|
||||
}
|
||||
SectionTextFooter(feature.enableDescription(enableFeature.value, groupInfo.canEdit))
|
||||
|
||||
+7
-6
@@ -16,6 +16,7 @@ import chat.simplex.common.platform.onRightClick
|
||||
|
||||
@Composable
|
||||
fun CIChatFeatureView(
|
||||
chatInfo: ChatInfo,
|
||||
chatItem: ChatItem,
|
||||
feature: Feature,
|
||||
iconColor: Color,
|
||||
@@ -23,7 +24,7 @@ fun CIChatFeatureView(
|
||||
revealed: MutableState<Boolean>,
|
||||
showMenu: MutableState<Boolean>,
|
||||
) {
|
||||
val merged = if (!revealed.value) mergedFeatures(chatItem) else emptyList()
|
||||
val merged = if (!revealed.value) mergedFeatures(chatItem, chatInfo) else emptyList()
|
||||
Box(
|
||||
Modifier
|
||||
.combinedClickable(
|
||||
@@ -70,7 +71,7 @@ private fun Feature.toFeatureInfo(color: Color, param: Int?, type: String): Feat
|
||||
)
|
||||
|
||||
@Composable
|
||||
private fun mergedFeatures(chatItem: ChatItem): List<FeatureInfo>? {
|
||||
private fun mergedFeatures(chatItem: ChatItem, chatInfo: ChatInfo): List<FeatureInfo>? {
|
||||
val m = ChatModel
|
||||
val fs: ArrayList<FeatureInfo> = arrayListOf()
|
||||
val icons: MutableSet<PainterBox> = mutableSetOf()
|
||||
@@ -78,7 +79,7 @@ private fun mergedFeatures(chatItem: ChatItem): List<FeatureInfo>? {
|
||||
if (i != null) {
|
||||
val reversedChatItems = m.chatItems.asReversed()
|
||||
while (i < reversedChatItems.size) {
|
||||
val f = featureInfo(reversedChatItems[i]) ?: break
|
||||
val f = featureInfo(reversedChatItems[i], chatInfo) ?: break
|
||||
if (!icons.contains(f.icon)) {
|
||||
fs.add(0, f)
|
||||
icons.add(f.icon)
|
||||
@@ -90,12 +91,12 @@ private fun mergedFeatures(chatItem: ChatItem): List<FeatureInfo>? {
|
||||
}
|
||||
|
||||
@Composable
|
||||
private fun featureInfo(ci: ChatItem): FeatureInfo? =
|
||||
private fun featureInfo(ci: ChatItem, chatInfo: ChatInfo): FeatureInfo? =
|
||||
when (ci.content) {
|
||||
is CIContent.RcvChatFeature -> ci.content.feature.toFeatureInfo(ci.content.enabled.iconColor, ci.content.param, ci.content.feature.name)
|
||||
is CIContent.SndChatFeature -> ci.content.feature.toFeatureInfo(ci.content.enabled.iconColor, ci.content.param, ci.content.feature.name)
|
||||
is CIContent.RcvGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enable.iconColor, ci.content.param, ci.content.groupFeature.name)
|
||||
is CIContent.SndGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enable.iconColor, ci.content.param, ci.content.groupFeature.name)
|
||||
is CIContent.RcvGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enabled(ci.content.memberRole_, (chatInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, ci.content.param, ci.content.groupFeature.name)
|
||||
is CIContent.SndGroupFeature -> ci.content.groupFeature.toFeatureInfo(ci.content.preference.enabled(ci.content.memberRole_, (chatInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, ci.content.param, ci.content.groupFeature.name)
|
||||
else -> null
|
||||
}
|
||||
|
||||
|
||||
+1
-1
@@ -12,7 +12,7 @@ import chat.simplex.common.ui.theme.*
|
||||
|
||||
@Composable
|
||||
fun CIEventView(text: AnnotatedString) {
|
||||
Text(text, Modifier.padding(horizontal = 6.dp, vertical = 6.dp), style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp))
|
||||
Text(text, Modifier.padding(horizontal = 6.dp, vertical = 6.dp), style = MaterialTheme.typography.body1.copy(lineHeight = 22.sp), maxLines = 3)
|
||||
}
|
||||
@Preview/*(
|
||||
uiMode = Configuration.UI_MODE_NIGHT_YES,
|
||||
|
||||
+4
-9
@@ -59,18 +59,11 @@ fun CIFileView(
|
||||
}
|
||||
}
|
||||
|
||||
fun fileSizeValid(): Boolean {
|
||||
if (file != null) {
|
||||
return file.fileSize <= getMaxFileSize(file.fileProtocol)
|
||||
}
|
||||
return false
|
||||
}
|
||||
|
||||
fun fileAction() {
|
||||
if (file != null) {
|
||||
when {
|
||||
file.fileStatus is CIFileStatus.RcvInvitation -> {
|
||||
if (fileSizeValid()) {
|
||||
if (fileSizeValid(file)) {
|
||||
receiveFile(file.fileId)
|
||||
} else {
|
||||
AlertManager.shared.showAlertMsg(
|
||||
@@ -165,7 +158,7 @@ fun CIFileView(
|
||||
is CIFileStatus.SndCancelled -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.SndError -> fileIcon(innerIcon = painterResource(MR.images.ic_close))
|
||||
is CIFileStatus.RcvInvitation ->
|
||||
if (fileSizeValid())
|
||||
if (fileSizeValid(file))
|
||||
fileIcon(innerIcon = painterResource(MR.images.ic_arrow_downward), color = MaterialTheme.colors.primary)
|
||||
else
|
||||
fileIcon(innerIcon = painterResource(MR.images.ic_priority_high), color = WarningOrange)
|
||||
@@ -216,6 +209,8 @@ fun CIFileView(
|
||||
}
|
||||
}
|
||||
|
||||
fun fileSizeValid(file: CIFile): Boolean = file.fileSize <= getMaxFileSize(file.fileProtocol)
|
||||
|
||||
@Composable
|
||||
fun rememberSaveFileLauncher(ciFile: CIFile?): FileChooserLauncher =
|
||||
rememberFileChooserLauncher(false, ciFile) { to: URI? ->
|
||||
|
||||
+41
-12
@@ -19,6 +19,7 @@ import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.*
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.model.ChatModel.controller
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.*
|
||||
@@ -40,6 +41,7 @@ fun chatEventText(eventText: String, ts: String): AnnotatedString =
|
||||
|
||||
@Composable
|
||||
fun ChatItemView(
|
||||
rhId: Long?,
|
||||
cInfo: ChatInfo,
|
||||
cItem: ChatItem,
|
||||
composeState: MutableState<ComposeState>,
|
||||
@@ -195,12 +197,16 @@ fun ChatItemView(
|
||||
}
|
||||
val clipboard = LocalClipboardManager.current
|
||||
val cachedRemoteReqs = remember { CIFile.cachedRemoteFileRequests }
|
||||
val copyAndShareAllowed = when {
|
||||
cItem.content.text.isNotEmpty() -> true
|
||||
fun fileForwardingAllowed() = when {
|
||||
cItem.file != null && chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file.fileSource] != false && cItem.file.loaded -> true
|
||||
getLoadedFilePath(cItem.file) != null -> true
|
||||
else -> false
|
||||
}
|
||||
val copyAndShareAllowed = when {
|
||||
cItem.content.text.isNotEmpty() -> true
|
||||
fileForwardingAllowed() -> true
|
||||
else -> false
|
||||
}
|
||||
|
||||
if (copyAndShareAllowed) {
|
||||
ItemAction(stringResource(MR.strings.share_verb), painterResource(MR.images.ic_share), onClick = {
|
||||
@@ -227,8 +233,19 @@ fun ChatItemView(
|
||||
showMenu.value = false
|
||||
})
|
||||
}
|
||||
if ((cItem.content.msgContent is MsgContent.MCImage || cItem.content.msgContent is MsgContent.MCVideo || cItem.content.msgContent is MsgContent.MCFile || cItem.content.msgContent is MsgContent.MCVoice) && (getLoadedFilePath(cItem.file) != null || (chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file?.fileSource] != false && cItem.file?.loaded == true))) {
|
||||
if (cItem.file != null && (getLoadedFilePath(cItem.file) != null || (chatModel.connectedToRemote() && cachedRemoteReqs[cItem.file.fileSource] != false && cItem.file.loaded))) {
|
||||
SaveContentItemAction(cItem, saveFileLauncher, showMenu)
|
||||
} else if (cItem.file != null && cItem.file.fileStatus is CIFileStatus.RcvInvitation && fileSizeValid(cItem.file)) {
|
||||
ItemAction(stringResource(MR.strings.download_file), painterResource(MR.images.ic_arrow_downward), onClick = {
|
||||
withBGApi {
|
||||
Log.d(TAG, "ChatItemView downloadFileAction")
|
||||
val user = chatModel.currentUser.value
|
||||
if (user != null) {
|
||||
controller.receiveFile(rhId, user, cItem.file.fileId)
|
||||
}
|
||||
}
|
||||
showMenu.value = false
|
||||
})
|
||||
}
|
||||
if (cItem.meta.editable && cItem.content.msgContent !is MsgContent.MCVoice && !live) {
|
||||
ItemAction(stringResource(MR.strings.edit_verb), painterResource(MR.images.ic_edit_filled), onClick = {
|
||||
@@ -236,6 +253,16 @@ fun ChatItemView(
|
||||
showMenu.value = false
|
||||
})
|
||||
}
|
||||
if (cItem.meta.itemDeleted == null &&
|
||||
(cItem.file == null || fileForwardingAllowed()) &&
|
||||
!cItem.isLiveDummy && !live
|
||||
) {
|
||||
ItemAction(stringResource(MR.strings.forward_chat_item), painterResource(MR.images.ic_forward), onClick = {
|
||||
chatModel.chatId.value = null
|
||||
chatModel.sharedContent.value = SharedContent.Forward(cItem, cInfo)
|
||||
showMenu.value = false
|
||||
})
|
||||
}
|
||||
ItemInfoAction(cInfo, cItem, showItemDetails, showMenu)
|
||||
if (revealed.value) {
|
||||
HideItemAction(revealed, showMenu)
|
||||
@@ -304,7 +331,7 @@ fun ChatItemView(
|
||||
MarkedDeletedItemView(cItem, cInfo.timedMessagesTTL, revealed)
|
||||
MarkedDeletedItemDropdownMenu()
|
||||
} else {
|
||||
if (cItem.quotedItem == null && cItem.meta.itemDeleted == null && !cItem.meta.isLive) {
|
||||
if (cItem.quotedItem == null && cItem.meta.itemForwarded == null && cItem.meta.itemDeleted == null && !cItem.meta.isLive) {
|
||||
if (mc is MsgContent.MCText && isShortEmoji(cItem.content.text)) {
|
||||
EmojiItemView(cItem, cInfo.timedMessagesTTL)
|
||||
} else if (mc is MsgContent.MCVoice && cItem.content.text.isEmpty()) {
|
||||
@@ -442,11 +469,11 @@ fun ChatItemView(
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatFeature -> {
|
||||
CIChatFeatureView(cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndChatFeature -> {
|
||||
CIChatFeatureView(cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, c.enabled.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatPreference -> {
|
||||
@@ -454,23 +481,23 @@ fun ChatItemView(
|
||||
CIFeaturePreferenceView(cItem, ct, c.feature, c.allowed, acceptFeature)
|
||||
}
|
||||
is CIContent.SndChatPreference -> {
|
||||
CIChatFeatureView(cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon, revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, MaterialTheme.colors.secondary, icon = c.feature.icon, revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvGroupFeature -> {
|
||||
CIChatFeatureView(cItem, c.groupFeature, c.preference.enable.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndGroupFeature -> {
|
||||
CIChatFeatureView(cItem, c.groupFeature, c.preference.enable.iconColor, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, c.preference.enabled(c.memberRole_, (cInfo as? ChatInfo.Group)?.groupInfo?.membership).iconColor, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvChatFeatureRejected -> {
|
||||
CIChatFeatureView(cItem, c.feature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.feature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.RcvGroupFeatureRejected -> {
|
||||
CIChatFeatureView(cItem, c.groupFeature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
CIChatFeatureView(cInfo, cItem, c.groupFeature, Color.Red, revealed = revealed, showMenu = showMenu)
|
||||
MsgContentItemDropdownMenu()
|
||||
}
|
||||
is CIContent.SndModerated -> DeletedItem()
|
||||
@@ -724,7 +751,7 @@ fun deleteMessageAlertDialog(chatItem: ChatItem, questionText: String, deleteMes
|
||||
deleteMessage(chatItem.id, CIDeleteMode.cidmInternal)
|
||||
AlertManager.shared.hideAlert()
|
||||
}) { Text(stringResource(MR.strings.for_me_only), color = MaterialTheme.colors.error) }
|
||||
if (chatItem.meta.editable && !chatItem.localNote) {
|
||||
if (chatItem.meta.deletable && !chatItem.localNote) {
|
||||
Spacer(Modifier.padding(horizontal = 4.dp))
|
||||
TextButton(onClick = {
|
||||
deleteMessage(chatItem.id, CIDeleteMode.cidmBroadcast)
|
||||
@@ -782,6 +809,7 @@ expect fun copyItemToClipboard(cItem: ChatItem, clipboard: ClipboardManager)
|
||||
fun PreviewChatItemView() {
|
||||
SimpleXTheme {
|
||||
ChatItemView(
|
||||
rhId = null,
|
||||
ChatInfo.Direct.sampleData,
|
||||
ChatItem.getSampleData(
|
||||
1, CIDirection.DirectSnd(), Clock.System.now(), "hello"
|
||||
@@ -818,6 +846,7 @@ fun PreviewChatItemView() {
|
||||
fun PreviewChatItemViewDeletedContent() {
|
||||
SimpleXTheme {
|
||||
ChatItemView(
|
||||
rhId = null,
|
||||
ChatInfo.Direct.sampleData,
|
||||
ChatItem.getDeletedContentSampleData(),
|
||||
useLinkPreviews = true,
|
||||
|
||||
+9
-4
@@ -87,15 +87,16 @@ fun FramedItemView(
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun FramedItemHeader(caption: String, italic: Boolean, icon: Painter? = null) {
|
||||
fun FramedItemHeader(caption: String, italic: Boolean, icon: Painter? = null, pad: Boolean = false) {
|
||||
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
|
||||
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
|
||||
Row(
|
||||
Modifier
|
||||
.background(if (sent) sentColor.toQuote() else receivedColor.toQuote())
|
||||
.fillMaxWidth()
|
||||
.padding(start = 8.dp, top = 6.dp, end = 12.dp, bottom = if (ci.quotedItem == null) 6.dp else 0.dp),
|
||||
.padding(start = 8.dp, top = 6.dp, end = 12.dp, bottom = if (pad || (ci.quotedItem == null && ci.meta.itemForwarded == null)) 6.dp else 0.dp),
|
||||
horizontalArrangement = Arrangement.spacedBy(4.dp),
|
||||
verticalAlignment = Alignment.CenterVertically
|
||||
) {
|
||||
if (icon != null) {
|
||||
Icon(
|
||||
@@ -184,7 +185,7 @@ fun FramedItemView(
|
||||
}
|
||||
|
||||
val transparentBackground = (ci.content.msgContent is MsgContent.MCImage || ci.content.msgContent is MsgContent.MCVideo) &&
|
||||
!ci.meta.isLive && ci.content.text.isEmpty() && ci.quotedItem == null
|
||||
!ci.meta.isLive && ci.content.text.isEmpty() && ci.quotedItem == null && ci.meta.itemForwarded == null
|
||||
|
||||
val sentColor = CurrentColors.collectAsState().value.appColors.sentMessage
|
||||
val receivedColor = CurrentColors.collectAsState().value.appColors.receivedMessage
|
||||
@@ -219,7 +220,11 @@ fun FramedItemView(
|
||||
} else if (ci.meta.isLive) {
|
||||
FramedItemHeader(stringResource(MR.strings.live), false)
|
||||
}
|
||||
ci.quotedItem?.let { ciQuoteView(it) }
|
||||
if (ci.quotedItem != null) {
|
||||
ciQuoteView(ci.quotedItem)
|
||||
} else if (ci.meta.itemForwarded != null) {
|
||||
FramedItemHeader(ci.meta.itemForwarded.text(chatInfo.chatType), true, painterResource(MR.images.ic_forward), pad = true)
|
||||
}
|
||||
if (ci.file == null && ci.formattedText == null && !ci.meta.isLive && isShortEmoji(ci.content.text)) {
|
||||
Box(Modifier.padding(vertical = 6.dp, horizontal = 12.dp)) {
|
||||
Column(
|
||||
|
||||
+5
-3
@@ -68,7 +68,7 @@ fun MarkdownText (
|
||||
senderBold: Boolean = false,
|
||||
modifier: Modifier = Modifier,
|
||||
linkMode: SimplexLinkMode,
|
||||
inlineContent: Map<String, InlineTextContent>? = null,
|
||||
inlineContent: Pair<AnnotatedString.Builder.() -> Unit, Map<String, InlineTextContent>>? = null,
|
||||
onLinkLongClick: (link: String) -> Unit = {}
|
||||
) {
|
||||
val textLayoutDirection = remember (text) {
|
||||
@@ -119,6 +119,7 @@ fun MarkdownText (
|
||||
}
|
||||
if (formattedText == null) {
|
||||
val annotatedText = buildAnnotatedString {
|
||||
inlineContent?.first?.invoke(this)
|
||||
appendSender(this, sender, senderBold)
|
||||
if (text is String) append(text)
|
||||
else if (text is AnnotatedString) append(text)
|
||||
@@ -127,10 +128,11 @@ fun MarkdownText (
|
||||
}
|
||||
if (meta != null) withStyle(reserveTimestampStyle) { append(reserve) }
|
||||
}
|
||||
Text(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow, inlineContent = inlineContent ?: mapOf())
|
||||
Text(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow, inlineContent = inlineContent?.second ?: mapOf())
|
||||
} else {
|
||||
var hasAnnotations = false
|
||||
val annotatedText = buildAnnotatedString {
|
||||
inlineContent?.first?.invoke(this)
|
||||
appendSender(this, sender, senderBold)
|
||||
for ((i, ft) in formattedText.withIndex()) {
|
||||
if (ft.format == null) append(ft.text)
|
||||
@@ -210,7 +212,7 @@ fun MarkdownText (
|
||||
}
|
||||
)
|
||||
} else {
|
||||
Text(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow)
|
||||
Text(annotatedText, style = style, modifier = modifier, maxLines = maxLines, overflow = overflow, inlineContent = inlineContent?.second ?: mapOf())
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
+4
-5
@@ -90,7 +90,7 @@ fun ChatPreviewView(
|
||||
Icon(painterResource(MR.images.ic_verified_user), null, Modifier.size(19.dp).padding(end = 3.dp, top = 1.dp), tint = MaterialTheme.colors.secondary)
|
||||
}
|
||||
|
||||
fun messageDraft(draft: ComposeState): Pair<AnnotatedString, Map<String, InlineTextContent>> {
|
||||
fun messageDraft(draft: ComposeState): Pair<AnnotatedString.Builder.() -> Unit, Map<String, InlineTextContent>> {
|
||||
fun attachment(): Pair<ImageResource, String?>? =
|
||||
when (draft.preview) {
|
||||
is ComposePreview.FilePreview -> MR.images.ic_draft_filled to draft.preview.fileName
|
||||
@@ -100,7 +100,7 @@ fun ChatPreviewView(
|
||||
}
|
||||
|
||||
val attachment = attachment()
|
||||
val text = buildAnnotatedString {
|
||||
val inlineContentBuilder: AnnotatedString.Builder.() -> Unit = {
|
||||
appendInlineContent(id = "editIcon")
|
||||
append(" ")
|
||||
if (attachment != null) {
|
||||
@@ -110,7 +110,6 @@ fun ChatPreviewView(
|
||||
}
|
||||
append(" ")
|
||||
}
|
||||
append(draft.message)
|
||||
}
|
||||
val inlineContent: Map<String, InlineTextContent> = mapOf(
|
||||
"editIcon" to InlineTextContent(
|
||||
@@ -124,7 +123,7 @@ fun ChatPreviewView(
|
||||
Icon(if (attachment?.first != null) painterResource(attachment.first) else painterResource(MR.images.ic_edit_note), null, tint = MaterialTheme.colors.secondary)
|
||||
}
|
||||
)
|
||||
return text to inlineContent
|
||||
return inlineContentBuilder to inlineContent
|
||||
}
|
||||
|
||||
@Composable
|
||||
@@ -169,7 +168,7 @@ fun ChatPreviewView(
|
||||
if (ci != null) {
|
||||
if (showChatPreviews || (chatModelDraftChatId == chat.id && chatModelDraft != null)) {
|
||||
val (text: CharSequence, inlineTextContent) = when {
|
||||
chatModelDraftChatId == chat.id && chatModelDraft != null -> remember(chatModelDraft) { messageDraft(chatModelDraft) }
|
||||
chatModelDraftChatId == chat.id && chatModelDraft != null -> remember(chatModelDraft) { chatModelDraft.message to messageDraft(chatModelDraft) }
|
||||
ci.meta.itemDeleted == null -> ci.text to null
|
||||
else -> markedDeletedText(ci.meta) to null
|
||||
}
|
||||
|
||||
+18
-9
@@ -13,9 +13,8 @@ import dev.icerock.moko.resources.compose.stringResource
|
||||
import androidx.compose.ui.text.font.FontWeight
|
||||
import androidx.compose.ui.unit.dp
|
||||
import chat.simplex.common.SettingsViewState
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.views.helpers.*
|
||||
import chat.simplex.common.model.Chat
|
||||
import chat.simplex.common.model.ChatModel
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.res.MR
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
@@ -74,7 +73,7 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
|
||||
val navButton: @Composable RowScope.() -> Unit = {
|
||||
when {
|
||||
showSearch -> NavigationButtonBack(hideSearchOnBack)
|
||||
users.size > 1 || chatModel.remoteHosts.isNotEmpty() -> {
|
||||
(users.size > 1 || chatModel.remoteHosts.isNotEmpty()) && remember { chatModel.sharedContent }.value !is SharedContent.Forward -> {
|
||||
val allRead = users
|
||||
.filter { u -> !u.user.activeUser && !u.user.hidden }
|
||||
.all { u -> u.unreadCount == 0 }
|
||||
@@ -82,7 +81,14 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
|
||||
userPickerState.value = AnimatedViewState.VISIBLE
|
||||
}
|
||||
}
|
||||
else -> NavigationButtonBack(onButtonClicked = { chatModel.sharedContent.value = null })
|
||||
else -> NavigationButtonBack(onButtonClicked = {
|
||||
val sharedContent = chatModel.sharedContent.value
|
||||
// Drop shared content
|
||||
chatModel.sharedContent.value = null
|
||||
if (sharedContent is SharedContent.Forward) {
|
||||
chatModel.chatId.value = sharedContent.fromChatInfo.id
|
||||
}
|
||||
})
|
||||
}
|
||||
}
|
||||
if (chatModel.chats.size >= 8) {
|
||||
@@ -118,7 +124,8 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
|
||||
is SharedContent.Text -> stringResource(MR.strings.share_message)
|
||||
is SharedContent.Media -> stringResource(MR.strings.share_image)
|
||||
is SharedContent.File -> stringResource(MR.strings.share_file)
|
||||
else -> stringResource(MR.strings.share_message)
|
||||
is SharedContent.Forward -> stringResource(MR.strings.forward_message)
|
||||
null -> stringResource(MR.strings.share_message)
|
||||
},
|
||||
color = MaterialTheme.colors.onBackground,
|
||||
fontWeight = FontWeight.SemiBold,
|
||||
@@ -135,12 +142,14 @@ private fun ShareListToolbar(chatModel: ChatModel, userPickerState: MutableState
|
||||
|
||||
@Composable
|
||||
private fun ShareList(chatModel: ChatModel, search: String) {
|
||||
val filter: (Chat) -> Boolean = { chat: Chat ->
|
||||
chat.chatInfo.chatViewName.lowercase().contains(search.lowercase())
|
||||
}
|
||||
val chats by remember(search) {
|
||||
derivedStateOf {
|
||||
if (search.isEmpty()) chatModel.chats.toList().filter { it.chatInfo.ready } else chatModel.chats.toList().filter { it.chatInfo.ready }.filter(filter)
|
||||
val sorted = chatModel.chats.toList().sortedByDescending { it.chatInfo is ChatInfo.Local }
|
||||
if (search.isEmpty()) {
|
||||
sorted.filter { it.chatInfo.ready }
|
||||
} else {
|
||||
sorted.filter { it.chatInfo.ready && it.chatInfo.chatViewName.lowercase().contains(search.lowercase()) }
|
||||
}
|
||||
}
|
||||
}
|
||||
LazyColumnWithScrollBar(
|
||||
|
||||
+3
@@ -2,6 +2,8 @@
|
||||
package chat.simplex.common.views.helpers
|
||||
|
||||
import androidx.compose.runtime.saveable.Saver
|
||||
import chat.simplex.common.model.ChatInfo
|
||||
import chat.simplex.common.model.ChatItem
|
||||
import kotlinx.coroutines.flow.MutableStateFlow
|
||||
import kotlinx.serialization.*
|
||||
import kotlinx.serialization.descriptors.*
|
||||
@@ -13,6 +15,7 @@ sealed class SharedContent {
|
||||
data class Text(val text: String): SharedContent()
|
||||
data class Media(val text: String, val uris: List<URI>): SharedContent()
|
||||
data class File(val text: String, val uri: URI): SharedContent()
|
||||
data class Forward(val chatItem: ChatItem, val fromChatInfo: ChatInfo): SharedContent()
|
||||
}
|
||||
|
||||
enum class AnimatedViewState {
|
||||
|
||||
+55
@@ -14,6 +14,7 @@ import androidx.compose.ui.unit.*
|
||||
import chat.simplex.res.MR
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.usersettings.SettingsActionItemWithContent
|
||||
import dev.icerock.moko.resources.ImageResource
|
||||
|
||||
@Composable
|
||||
fun <T> ExposedDropDownSetting(
|
||||
@@ -79,6 +80,60 @@ fun <T> ExposedDropDownSetting(
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> ExposedDropDownSettingWithIcon(
|
||||
values: List<Triple<T, ImageResource, String>>,
|
||||
selection: State<T>,
|
||||
fontSize: TextUnit = 16.sp,
|
||||
iconSize: Dp = 40.dp,
|
||||
listIconSize: Dp = 30.dp,
|
||||
iconColor: Color = MenuTextColor,
|
||||
enabled: State<Boolean> = mutableStateOf(true),
|
||||
minWidth: Dp = 200.dp,
|
||||
onSelected: (T) -> Unit
|
||||
) {
|
||||
val expanded = remember { mutableStateOf(false) }
|
||||
ExposedDropdownMenuBox(
|
||||
expanded = expanded.value,
|
||||
onExpandedChange = {
|
||||
expanded.value = !expanded.value && enabled.value
|
||||
}
|
||||
) {
|
||||
Row(
|
||||
Modifier.padding(start = 10.dp),
|
||||
verticalAlignment = Alignment.CenterVertically,
|
||||
horizontalArrangement = Arrangement.End
|
||||
) {
|
||||
val choice = values.first { it.first == selection.value }
|
||||
Icon(painterResource(choice.second), choice.third, Modifier.size(iconSize), tint = iconColor)
|
||||
}
|
||||
DefaultExposedDropdownMenu(
|
||||
modifier = Modifier.widthIn(min = minWidth),
|
||||
expanded = expanded,
|
||||
) {
|
||||
values.forEach { selectionOption ->
|
||||
DropdownMenuItem(
|
||||
onClick = {
|
||||
onSelected(selectionOption.first)
|
||||
expanded.value = false
|
||||
},
|
||||
contentPadding = PaddingValues(horizontal = DEFAULT_PADDING * 1.5f)
|
||||
) {
|
||||
Icon(painterResource(selectionOption.second), selectionOption.third, Modifier.size(listIconSize))
|
||||
Spacer(Modifier.width(15.dp))
|
||||
Text(
|
||||
selectionOption.third,
|
||||
maxLines = 1,
|
||||
overflow = TextOverflow.Ellipsis,
|
||||
color = MenuTextColor,
|
||||
fontSize = fontSize,
|
||||
)
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun <T> ExposedDropDownSettingRow(
|
||||
title: String,
|
||||
|
||||
+2
-2
@@ -254,12 +254,12 @@ fun TextIconSpaced(extraPadding: Boolean = false) {
|
||||
}
|
||||
|
||||
@Composable
|
||||
fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color? = null) {
|
||||
fun InfoRow(title: String, value: String, icon: Painter? = null, iconTint: Color? = null, textColor: Color = MaterialTheme.colors.onBackground) {
|
||||
SectionItemViewSpaceBetween {
|
||||
Row {
|
||||
val iconSize = with(LocalDensity.current) { 21.sp.toDp() }
|
||||
if (icon != null) Icon(icon, title, Modifier.padding(end = 8.dp).size(iconSize), tint = iconTint ?: MaterialTheme.colors.secondary)
|
||||
Text(title)
|
||||
Text(title, color = textColor)
|
||||
}
|
||||
Text(value, color = MaterialTheme.colors.secondary)
|
||||
}
|
||||
|
||||
-1
@@ -15,7 +15,6 @@ import chat.simplex.res.MR
|
||||
import com.charleskorn.kaml.decodeFromStream
|
||||
import dev.icerock.moko.resources.StringResource
|
||||
import kotlinx.coroutines.*
|
||||
import kotlinx.serialization.decodeFromString
|
||||
import kotlinx.serialization.encodeToString
|
||||
import java.io.*
|
||||
import java.net.URI
|
||||
|
||||
+20
-6
@@ -38,9 +38,7 @@ import chat.simplex.common.views.usersettings.*
|
||||
import chat.simplex.res.MR
|
||||
import dev.icerock.moko.resources.compose.painterResource
|
||||
import dev.icerock.moko.resources.compose.stringResource
|
||||
import kotlinx.coroutines.delay
|
||||
import kotlinx.coroutines.flow.distinctUntilChanged
|
||||
import kotlinx.coroutines.runBlocking
|
||||
|
||||
@Composable
|
||||
fun ConnectMobileView() {
|
||||
@@ -269,12 +267,20 @@ fun AddingMobileDevice(showTitle: Boolean, staleQrCode: MutableState<Boolean>, c
|
||||
var cachedR by remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
|
||||
val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) }
|
||||
val customPort = rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
var userChangedAddress by rememberSaveable { mutableStateOf(false) }
|
||||
var userChangedPort by rememberSaveable { mutableStateOf(false) }
|
||||
val startRemoteHost = suspend {
|
||||
if (customAddress.value != cachedR.address && cachedR != null) {
|
||||
userChangedAddress = true
|
||||
}
|
||||
if (customPort.value != cachedR.port && cachedR != null) {
|
||||
userChangedPort = true
|
||||
}
|
||||
val r = chatModel.controller.startRemoteHost(
|
||||
rhId = null,
|
||||
multicast = controller.appPrefs.offerRemoteMulticast.get(),
|
||||
address = if (customAddress.value?.address != cachedR.address?.address) customAddress.value else cachedR.rh?.bindAddress_,
|
||||
port = if (customPort.value != cachedR.port) customPort.value else cachedR.rh?.bindPort_
|
||||
address = if (customAddress.value != null && userChangedAddress) customAddress.value else cachedR.rh?.bindAddress_,
|
||||
port = if (customPort.value != null && userChangedPort) customPort.value else cachedR.rh?.bindPort_
|
||||
)
|
||||
if (r != null) {
|
||||
cachedR = r
|
||||
@@ -343,12 +349,20 @@ private fun showConnectMobileDevice(rh: RemoteHostInfo, connecting: MutableState
|
||||
var cachedR by remember { mutableStateOf<CR.RemoteHostStarted?>(null) }
|
||||
val customAddress = rememberSaveable { mutableStateOf<RemoteCtrlAddress?>(null) }
|
||||
val customPort = rememberSaveable { mutableStateOf<Int?>(null) }
|
||||
var userChangedAddress by rememberSaveable { mutableStateOf(false) }
|
||||
var userChangedPort by rememberSaveable { mutableStateOf(false) }
|
||||
val startRemoteHost = suspend {
|
||||
if (customAddress.value != cachedR.address && cachedR != null) {
|
||||
userChangedAddress = true
|
||||
}
|
||||
if (customPort.value != cachedR.port && cachedR != null) {
|
||||
userChangedPort = true
|
||||
}
|
||||
val r = chatModel.controller.startRemoteHost(
|
||||
rhId = rh.remoteHostId,
|
||||
multicast = controller.appPrefs.offerRemoteMulticast.get(),
|
||||
address = if (customAddress.value?.address != cachedR.address?.address) customAddress.value else cachedR.rh?.bindAddress_ ?: rh.bindAddress_,
|
||||
port = if (customPort.value != cachedR.port) customPort.value else cachedR.rh?.bindPort_ ?: rh.bindPort_
|
||||
address = if (customAddress.value != null && userChangedAddress) customAddress.value else cachedR.rh?.bindAddress_ ?: rh.bindAddress_,
|
||||
port = if (customPort.value != null && userChangedPort) customPort.value else cachedR.rh?.bindPort_ ?: rh.bindPort_
|
||||
)
|
||||
if (r != null) {
|
||||
cachedR = r
|
||||
|
||||
+14
-2
@@ -34,6 +34,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
val networkTCPConnectTimeout = remember { mutableStateOf(currentCfgVal.tcpConnectTimeout) }
|
||||
val networkTCPTimeout = remember { mutableStateOf(currentCfgVal.tcpTimeout) }
|
||||
val networkTCPTimeoutPerKb = remember { mutableStateOf(currentCfgVal.tcpTimeoutPerKb) }
|
||||
var networkRcvConcurrency = remember { mutableStateOf(currentCfgVal.rcvConcurrency) }
|
||||
val networkSMPPingInterval = remember { mutableStateOf(currentCfgVal.smpPingInterval) }
|
||||
val networkSMPPingCount = remember { mutableStateOf(currentCfgVal.smpPingCount) }
|
||||
val networkEnableKeepAlive = remember { mutableStateOf(currentCfgVal.enableKeepAlive) }
|
||||
@@ -68,6 +69,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
tcpConnectTimeout = networkTCPConnectTimeout.value,
|
||||
tcpTimeout = networkTCPTimeout.value,
|
||||
tcpTimeoutPerKb = networkTCPTimeoutPerKb.value,
|
||||
rcvConcurrency = networkRcvConcurrency.value,
|
||||
tcpKeepAlive = tcpKeepAlive,
|
||||
smpPingInterval = networkSMPPingInterval.value,
|
||||
smpPingCount = networkSMPPingCount.value
|
||||
@@ -78,6 +80,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
networkTCPConnectTimeout.value = cfg.tcpConnectTimeout
|
||||
networkTCPTimeout.value = cfg.tcpTimeout
|
||||
networkTCPTimeoutPerKb.value = cfg.tcpTimeoutPerKb
|
||||
networkRcvConcurrency.value = cfg.rcvConcurrency
|
||||
networkSMPPingInterval.value = cfg.smpPingInterval
|
||||
networkSMPPingCount.value = cfg.smpPingCount
|
||||
networkEnableKeepAlive.value = cfg.enableKeepAlive
|
||||
@@ -110,6 +113,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
networkTCPConnectTimeout,
|
||||
networkTCPTimeout,
|
||||
networkTCPTimeoutPerKb,
|
||||
networkRcvConcurrency,
|
||||
networkSMPPingInterval,
|
||||
networkSMPPingCount,
|
||||
networkEnableKeepAlive,
|
||||
@@ -128,6 +132,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
networkTCPConnectTimeout: MutableState<Long>,
|
||||
networkTCPTimeout: MutableState<Long>,
|
||||
networkTCPTimeoutPerKb: MutableState<Long>,
|
||||
networkRcvConcurrency: MutableState<Int>,
|
||||
networkSMPPingInterval: MutableState<Long>,
|
||||
networkSMPPingCount: MutableState<Int>,
|
||||
networkEnableKeepAlive: MutableState<Boolean>,
|
||||
@@ -154,7 +159,7 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
SectionItemView {
|
||||
TimeoutSettingRow(
|
||||
stringResource(MR.strings.network_option_tcp_connection_timeout), networkTCPConnectTimeout,
|
||||
listOf(7_500000, 10_000000, 15_000000, 20_000000, 30_000_000, 45_000_000), secondsLabel
|
||||
listOf(5_000000, 7_500000, 10_000000, 15_000000, 20_000000, 30_000000, 40_000000), secondsLabel
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
@@ -167,7 +172,13 @@ fun AdvancedNetworkSettingsView(chatModel: ChatModel) {
|
||||
// can't be higher than 130ms to avoid overflow on 32bit systems
|
||||
TimeoutSettingRow(
|
||||
stringResource(MR.strings.network_option_protocol_timeout_per_kb), networkTCPTimeoutPerKb,
|
||||
listOf(15_000, 30_000, 45_000, 60_000, 90_000, 120_000), secondsLabel
|
||||
listOf(2_500, 5_000, 10_000, 15_000, 20_000, 30_000), secondsLabel
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
IntSettingRow(
|
||||
stringResource(MR.strings.network_option_rcv_concurrency), networkRcvConcurrency,
|
||||
listOf(1, 2, 4, 8, 12, 16, 24), ""
|
||||
)
|
||||
}
|
||||
SectionItemView {
|
||||
@@ -418,6 +429,7 @@ fun PreviewAdvancedNetworkSettingsLayout() {
|
||||
networkTCPConnectTimeout = remember { mutableStateOf(10_000000) },
|
||||
networkTCPTimeout = remember { mutableStateOf(10_000000) },
|
||||
networkTCPTimeoutPerKb = remember { mutableStateOf(10_000) },
|
||||
networkRcvConcurrency = remember { mutableStateOf(8) },
|
||||
networkSMPPingInterval = remember { mutableStateOf(10_000000) },
|
||||
networkSMPPingCount = remember { mutableStateOf(3) },
|
||||
networkEnableKeepAlive = remember { mutableStateOf(true) },
|
||||
|
||||
+13
-2
@@ -2,6 +2,7 @@ package chat.simplex.common.views.usersettings
|
||||
|
||||
import SectionBottomSpacer
|
||||
import SectionCustomFooter
|
||||
import SectionDividerSpaced
|
||||
import SectionItemView
|
||||
import SectionItemWithValue
|
||||
import SectionView
|
||||
@@ -21,12 +22,12 @@ import androidx.compose.ui.text.*
|
||||
import androidx.compose.ui.text.font.*
|
||||
import androidx.compose.ui.text.input.*
|
||||
import androidx.compose.desktop.ui.tooling.preview.Preview
|
||||
import androidx.compose.ui.graphics.Color
|
||||
import androidx.compose.ui.platform.LocalDensity
|
||||
import androidx.compose.ui.unit.dp
|
||||
import androidx.compose.ui.unit.sp
|
||||
import chat.simplex.common.model.*
|
||||
import chat.simplex.common.platform.ColumnWithScrollBar
|
||||
import chat.simplex.common.platform.chatModel
|
||||
import chat.simplex.common.platform.*
|
||||
import chat.simplex.common.ui.theme.*
|
||||
import chat.simplex.common.views.chat.item.ClickableText
|
||||
import chat.simplex.common.views.helpers.*
|
||||
@@ -190,6 +191,16 @@ fun NetworkAndServersView() {
|
||||
SectionView(generalGetString(MR.strings.settings_section_title_calls)) {
|
||||
SettingsActionItem(painterResource(MR.images.ic_electrical_services), stringResource(MR.strings.webrtc_ice_servers), { ModalManager.start.showModal { RTCServersView(m) } })
|
||||
}
|
||||
|
||||
if (appPlatform.isAndroid) {
|
||||
SectionDividerSpaced()
|
||||
SectionView(generalGetString(MR.strings.settings_section_title_network_connection).uppercase()) {
|
||||
val info = remember { chatModel.networkInfo }.value
|
||||
SettingsActionItemWithContent(icon = null, info.networkType.text) {
|
||||
Icon(painterResource(MR.images.ic_circle_filled), stringResource(MR.strings.icon_descr_server_status_connected), tint = if (info.online) Color.Green else MaterialTheme.colors.error)
|
||||
}
|
||||
}
|
||||
}
|
||||
SectionBottomSpacer()
|
||||
}
|
||||
}
|
||||
|
||||
Some files were not shown because too many files have changed in this diff Show More
Reference in New Issue
Block a user