Merge branch 'master' into ep/donate-page

This commit is contained in:
M Sarmad Qadeer
2024-07-26 14:36:30 +05:00
246 changed files with 11227 additions and 3002 deletions
+51 -17
View File
@@ -73,6 +73,8 @@ final class ChatModel: ObservableObject {
var chatItemStatuses: Dictionary<Int64, CIStatus> = [:]
@Published var chatToTop: String?
@Published var groupMembers: [GMember] = []
@Published var groupMembersIndexes: Dictionary<Int64, Int> = [:] // groupMemberId to index in groupMembers list
@Published var membersLoaded = false
// items in the terminal view
@Published var showingTerminal = false
@Published var terminalItems: [TerminalItem] = []
@@ -180,8 +182,30 @@ final class ChatModel: ObservableObject {
}
}
func populateGroupMembersIndexes() {
groupMembersIndexes.removeAll()
for (i, member) in groupMembers.enumerated() {
groupMembersIndexes[member.groupMemberId] = i
}
}
func getGroupMember(_ groupMemberId: Int64) -> GMember? {
groupMembers.first { $0.groupMemberId == groupMemberId }
if let i = groupMembersIndexes[groupMemberId] {
return groupMembers[i]
}
return nil
}
func loadGroupMembers(_ groupInfo: GroupInfo, updateView: @escaping () -> Void = {}) async {
let groupMembers = await apiListMembers(groupInfo.groupId)
await MainActor.run {
if chatId == groupInfo.id {
self.groupMembers = groupMembers.map { GMember.init($0) }
self.populateGroupMembersIndexes()
self.membersLoaded = true
updateView()
}
}
}
private func getChatIndex(_ id: String) -> Int? {
@@ -330,6 +354,9 @@ final class ChatModel: ObservableObject {
addChat(Chat(chatInfo: cInfo, chatItems: [cItem]))
res = true
}
if cItem.isDeletedContent || cItem.meta.itemDeleted != nil {
VoiceItemState.stopVoiceInChatView(cInfo, cItem)
}
// update current chat
return chatId == cInfo.id ? _upsertChatItem(cInfo, cItem) : res
}
@@ -379,8 +406,8 @@ final class ChatModel: ObservableObject {
}
func removeChatItem(_ cInfo: ChatInfo, _ cItem: ChatItem) {
if cItem.isRcvNew {
decreaseUnreadCounter(cInfo)
if cItem.isRcvNew, let chatIndex = getChatIndex(cInfo.id) {
decreaseUnreadCounter(chatIndex)
}
// update previews
if let chat = getChat(cInfo.id) {
@@ -396,6 +423,7 @@ final class ChatModel: ObservableObject {
}
}
}
VoiceItemState.stopVoiceInChatView(cInfo, cItem)
}
func nextChatItemData<T>(_ chatItemId: Int64, previous: Bool, map: @escaping (ChatItem) -> T?) -> T? {
@@ -525,13 +553,18 @@ final class ChatModel: ObservableObject {
}
}
func markChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) {
if chatId == cInfo.id, let i = getChatItemIndex(cItem) {
if reversedChatItems[i].isRcvNew {
// update current chat
markChatItemRead_(i)
// update preview
decreaseUnreadCounter(cInfo)
func markChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async {
if chatId == cInfo.id,
let itemIndex = getChatItemIndex(cItem),
let chatIndex = getChatIndex(cInfo.id),
reversedChatItems[itemIndex].isRcvNew {
await MainActor.run {
withTransaction(Transaction()) {
// update current chat
markChatItemRead_(itemIndex)
// update preview
decreaseUnreadCounter(chatIndex)
}
}
}
}
@@ -547,11 +580,9 @@ final class ChatModel: ObservableObject {
}
}
func decreaseUnreadCounter(_ cInfo: ChatInfo) {
if let i = getChatIndex(cInfo.id) {
chats[i].chatStats.unreadCount = chats[i].chatStats.unreadCount - 1
decreaseUnreadCounter(user: currentUser!)
}
func decreaseUnreadCounter(_ chatIndex: Int) {
chats[chatIndex].chatStats.unreadCount = chats[chatIndex].chatStats.unreadCount - 1
decreaseUnreadCounter(user: currentUser!)
}
func increaseUnreadCounter(user: any UserLike) {
@@ -667,14 +698,17 @@ final class ChatModel: ObservableObject {
}
// update current chat
if chatId == groupInfo.id {
if let i = groupMembers.firstIndex(where: { $0.groupMemberId == member.groupMemberId }) {
if let i = groupMembersIndexes[member.groupMemberId] {
withAnimation(.default) {
self.groupMembers[i].wrapped = member
self.groupMembers[i].created = Date.now
}
return false
} else {
withAnimation { groupMembers.append(GMember(member)) }
withAnimation {
groupMembers.append(GMember(member))
groupMembersIndexes[member.groupMemberId] = groupMembers.count - 1
}
return true
}
} else {
+93 -22
View File
@@ -217,7 +217,7 @@ func apiDeleteUser(_ userId: Int64, _ delSMPQueues: Bool, viewPwd: String?) asyn
}
func apiStartChat(ctrl: chat_ctrl? = nil) throws -> Bool {
let r = chatSendCmdSync(.startChat(mainApp: true), ctrl)
let r = chatSendCmdSync(.startChat(mainApp: true, enableSndFiles: true), ctrl)
switch r {
case .chatStarted: return true
case .chatRunning: return false
@@ -397,7 +397,7 @@ private func sendMessageErrorAlert(_ r: ChatResponse) {
logger.error("send message error: \(String(describing: r))")
AlertManager.shared.showAlertMsg(
title: "Error sending message",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
@@ -405,7 +405,7 @@ private func createChatItemErrorAlert(_ r: ChatResponse) {
logger.error("apiCreateChatItem error: \(String(describing: r))")
AlertManager.shared.showAlertMsg(
title: "Error creating message",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
@@ -699,7 +699,7 @@ func apiConnect_(incognito: Bool, connReq: String) async -> ((ConnReqType, Pendi
message: "Please check that you used the correct link or ask your contact to send you another one."
)
return (nil, alert)
case .chatCmdError(_, .errorAgent(.SMP(.AUTH))):
case .chatCmdError(_, .errorAgent(.SMP(_, .AUTH))):
let alert = mkAlert(
title: "Connection error (AUTH)",
message: "Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection."
@@ -732,7 +732,7 @@ private func connectionErrorAlert(_ r: ChatResponse) -> Alert {
} else {
return mkAlert(
title: "Connection error",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
}
@@ -898,7 +898,7 @@ func apiAcceptContactRequest(incognito: Bool, contactReqId: Int64) async -> Cont
let am = AlertManager.shared
if case let .acceptingContactRequest(_, contact) = r { return contact }
if case .chatCmdError(_, .errorAgent(.SMP(.AUTH))) = r {
if case .chatCmdError(_, .errorAgent(.SMP(_, .AUTH))) = r {
am.showAlertMsg(
title: "Connection error (AUTH)",
message: "Sender may have deleted the connection request."
@@ -909,7 +909,7 @@ func apiAcceptContactRequest(incognito: Bool, contactReqId: Int64) async -> Cont
logger.error("apiAcceptContactRequest error: \(String(describing: r))")
am.showAlertMsg(
title: "Error accepting contact request",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
return nil
@@ -935,7 +935,7 @@ func uploadStandaloneFile(user: any UserLike, file: CryptoFile, ctrl: chat_ctrl?
return (fileTransferMeta, nil)
} else {
logger.error("uploadStandaloneFile error: \(String(describing: r))")
return (nil, String(describing: r))
return (nil, responseError(r))
}
}
@@ -945,7 +945,7 @@ func downloadStandaloneFile(user: any UserLike, url: String, file: CryptoFile, c
return (rcvFileTransfer, nil)
} else {
logger.error("downloadStandaloneFile error: \(String(describing: r))")
return (nil, String(describing: r))
return (nil, responseError(r))
}
}
@@ -1025,7 +1025,7 @@ func apiReceiveFile(fileId: Int64, userApprovedRelays: Bool, encrypted: Bool, in
if !auto {
am.showAlertMsg(
title: "Error receiving file",
message: "Error: \(String(describing: r))"
message: "Error: \(responseError(r))"
)
}
}
@@ -1091,27 +1091,77 @@ func deleteRemoteCtrl(_ rcId: Int64) async throws {
try await sendCommandOkResp(.deleteRemoteCtrl(remoteCtrlId: rcId))
}
func networkErrorAlert(_ r: ChatResponse) -> Alert? {
struct ErrorAlert {
var title: LocalizedStringKey
var message: LocalizedStringKey
}
func getNetworkErrorAlert(_ r: ChatResponse) -> ErrorAlert? {
switch r {
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))):
return mkAlert(
title: "Connection timeout",
message: "Please check your network connection with \(serverHostname(addr)) and try again."
)
return ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))):
return mkAlert(
title: "Connection error",
message: "Please check your network connection with \(serverHostname(addr)) and try again."
)
return ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .HOST))):
return ErrorAlert(title: "Connection error", message: "Server address is incompatible with network settings: \(serverHostname(addr)).")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TRANSPORT(.version)))):
return ErrorAlert(title: "Connection error", message: "Server version is incompatible with your app: \(serverHostname(addr)).")
case let .chatCmdError(_, .errorAgent(.SMP(serverAddress, .PROXY(proxyErr)))):
return smpProxyErrorAlert(proxyErr, serverAddress)
case let .chatCmdError(_, .errorAgent(.PROXY(proxyServer, relayServer, .protocolError(.PROXY(proxyErr))))):
return proxyDestinationErrorAlert(proxyErr, proxyServer, relayServer)
default:
return nil
}
}
private func smpProxyErrorAlert(_ proxyErr: ProxyError, _ srvAddr: String) -> ErrorAlert? {
switch proxyErr {
case .BROKER(brokerErr: .TIMEOUT):
return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.")
case .BROKER(brokerErr: .NETWORK):
return ErrorAlert(title: "Private routing error", message: "Error connecting to forwarding server \(serverHostname(srvAddr)). Please try later.")
case .BROKER(brokerErr: .HOST):
return ErrorAlert(title: "Private routing error", message: "Forwarding server address is incompatible with network settings: \(serverHostname(srvAddr)).")
case .BROKER(brokerErr: .TRANSPORT(.version)):
return ErrorAlert(title: "Private routing error", message: "Forwarding server version is incompatible with network settings: \(serverHostname(srvAddr)).")
default:
return nil
}
}
private func proxyDestinationErrorAlert(_ proxyErr: ProxyError, _ proxyServer: String, _ relayServer: String) -> ErrorAlert? {
switch proxyErr {
case .BROKER(brokerErr: .TIMEOUT):
return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.")
case .BROKER(brokerErr: .NETWORK):
return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.")
case .NO_SESSION:
return ErrorAlert(title: "Private routing error", message: "Forwarding server \(serverHostname(proxyServer)) failed to connect to destination server \(serverHostname(relayServer)). Please try later.")
case .BROKER(brokerErr: .HOST):
return ErrorAlert(title: "Private routing error", message: "Destination server address of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)) settings.")
case .BROKER(brokerErr: .TRANSPORT(.version)):
return ErrorAlert(title: "Private routing error", message: "Destination server version of \(serverHostname(relayServer)) is incompatible with forwarding server \(serverHostname(proxyServer)).")
default:
return nil
}
}
func networkErrorAlert(_ r: ChatResponse) -> Alert? {
if let alert = getNetworkErrorAlert(r) {
return mkAlert(title: alert.title, message: alert.message)
} else {
return nil
}
}
func acceptContactRequest(incognito: Bool, contactRequest: UserContactRequest) async {
if let contact = await apiAcceptContactRequest(incognito: incognito, contactReqId: contactRequest.apiId) {
let chat = Chat(chatInfo: ChatInfo.direct(contact: contact), chatItems: [])
DispatchQueue.main.async { ChatModel.shared.replaceChat(contactRequest.id, chat) }
DispatchQueue.main.async {
ChatModel.shared.replaceChat(contactRequest.id, chat)
ChatModel.shared.setContactNetworkStatus(contact, .connected)
}
}
}
@@ -1207,7 +1257,7 @@ func apiMarkChatItemRead(_ cInfo: ChatInfo, _ cItem: ChatItem) async {
do {
logger.debug("apiMarkChatItemRead: \(cItem.id)")
try await apiChatRead(type: cInfo.chatType, id: cInfo.apiId, itemRange: (cItem.id, cItem.id))
await MainActor.run { ChatModel.shared.markChatItemRead(cInfo, cItem) }
await ChatModel.shared.markChatItemRead(cInfo, cItem)
} catch {
logger.error("apiMarkChatItemRead apiChatRead error: \(responseError(error))")
}
@@ -1248,7 +1298,7 @@ func apiJoinGroup(_ groupId: Int64) async throws -> JoinGroupResult {
let r = await chatSendCmd(.apiJoinGroup(groupId: groupId))
switch r {
case let .userAcceptedGroupSent(_, groupInfo, _): return .joined(groupInfo: groupInfo)
case .chatCmdError(_, .errorAgent(.SMP(.AUTH))): return .invitationRemoved
case .chatCmdError(_, .errorAgent(.SMP(_, .AUTH))): return .invitationRemoved
case .chatCmdError(_, .errorStore(.groupNotFound)): return .groupNotFound
default: throw r
}
@@ -1354,6 +1404,14 @@ func apiGetVersion() throws -> CoreVersionInfo {
throw r
}
func getAgentSubsTotal() throws -> (SMPServerSubs, Bool) {
let userId = try currentUserId("getAgentSubsTotal")
let r = chatSendCmdSync(.getAgentSubsTotal(userId: userId), log: false)
if case let .agentSubsTotal(_, subsTotal, hasSession) = r { return (subsTotal, hasSession) }
logger.error("getAgentSubsTotal error: \(String(describing: r))")
throw r
}
func getAgentServersSummary() throws -> PresentedServersSummary {
let userId = try currentUserId("getAgentServersSummary")
let r = chatSendCmdSync(.getAgentServersSummary(userId: userId), log: false)
@@ -1617,6 +1675,19 @@ func processReceivedMsg(_ res: ChatResponse) async {
}
}
}
case let .contactSndReady(user, contact):
if active(user) && contact.directOrUsed {
await MainActor.run {
m.updateContact(contact)
if let conn = contact.activeConn {
m.dismissConnReqView(conn.id)
m.removeChat(conn.id)
}
}
}
await MainActor.run {
m.setContactNetworkStatus(contact, .connected)
}
case let .receivedContactRequest(user, contactRequest):
if active(user) {
let cInfo = ChatInfo.contactRequest(contactRequest: contactRequest)
+1 -2
View File
@@ -102,8 +102,7 @@ extension ThemeWallpaper {
public func importFromString() -> ThemeWallpaper {
if preset == nil, let image {
// Need to save image from string and to save its path
if let data = Data(base64Encoded: dropImagePrefix(image)),
let parsed = UIImage(data: data),
if let parsed = UIImage(base64Encoded: image),
let filename = saveWallpaperFile(image: parsed) {
var copy = self
copy.image = nil
@@ -28,15 +28,17 @@ struct ChatInfoToolbar: View {
color: Color(uiColor: .tertiaryLabel)
)
.padding(.trailing, 4)
VStack {
let t = Text(cInfo.displayName).font(.headline)
(cInfo.contact?.verified == true ? contactVerifiedShield + t : t)
.lineLimit(1)
if cInfo.fullName != "" && cInfo.displayName != cInfo.fullName {
Text(cInfo.fullName).font(.subheadline)
.lineLimit(1)
let t = Text(cInfo.displayName).font(.headline)
(cInfo.contact?.verified == true ? contactVerifiedShield + t : t)
.lineLimit(1)
.if (cInfo.fullName != "" && cInfo.displayName != cInfo.fullName) { v in
VStack(spacing: 0) {
v
Text(cInfo.fullName).font(.subheadline)
.lineLimit(1)
.padding(.top, -2)
}
}
}
}
.foregroundColor(theme.colors.onBackground)
.frame(width: 220)
+13 -11
View File
@@ -155,23 +155,25 @@ struct ChatInfoView: View {
}
Section {
if let code = connectionCode { verifyCodeButton(code) }
contactPreferencesButton()
sendReceiptsOption()
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
synchronizeConnectionButton()
Group {
if let code = connectionCode { verifyCodeButton(code) }
contactPreferencesButton()
sendReceiptsOption()
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
synchronizeConnectionButton()
}
// } else if developerTools {
// synchronizeConnectionButtonForce()
// }
}
.disabled(!contact.ready || !contact.active)
NavigationLink {
ChatWallpaperEditorSheet(chat: chat)
} label: {
Label("Chat theme", systemImage: "photo")
}
// } else if developerTools {
// synchronizeConnectionButtonForce()
// }
}
.disabled(!contact.ready || !contact.active)
if let conn = contact.activeConn {
Section {
@@ -271,7 +273,7 @@ struct ChatInfoView: View {
}
}
.actionSheet(isPresented: $showDeleteContactActionSheet) {
if contact.ready && contact.active {
if contact.sndReady && contact.active {
return ActionSheet(
title: Text("Delete contact?\nThis cannot be undone!"),
buttons: [
@@ -9,6 +9,7 @@ import SwiftUI
class AnimatedImageView: UIView {
var image: UIImage? = nil
var imageView: UIImageView? = nil
var cMode: UIView.ContentMode = .scaleAspectFit
override init(frame: CGRect) {
super.init(frame: frame)
@@ -18,11 +19,12 @@ class AnimatedImageView: UIView {
fatalError("Not implemented")
}
convenience init(image: UIImage) {
convenience init(image: UIImage, contentMode: UIView.ContentMode) {
self.init()
self.image = image
self.cMode = contentMode
imageView = UIImageView(gifImage: image)
imageView!.contentMode = .scaleAspectFit
imageView!.contentMode = contentMode
self.addSubview(imageView!)
}
@@ -35,7 +37,7 @@ class AnimatedImageView: UIView {
if let subview = self.subviews.first as? UIImageView {
if image.imageData != subview.gifImage?.imageData {
imageView = UIImageView(gifImage: image)
imageView!.contentMode = .scaleAspectFit
imageView!.contentMode = contentMode
self.addSubview(imageView!)
subview.removeFromSuperview()
}
@@ -47,13 +49,15 @@ class AnimatedImageView: UIView {
struct SwiftyGif: UIViewRepresentable {
private let image: UIImage
private let contentMode: UIView.ContentMode
init(image: UIImage) {
init(image: UIImage, contentMode: UIView.ContentMode = .scaleAspectFit) {
self.image = image
self.contentMode = contentMode
}
func makeUIView(context: Context) -> AnimatedImageView {
AnimatedImageView(image: image)
AnimatedImageView(image: image, contentMode: contentMode)
}
func updateUIView(_ imageView: AnimatedImageView, context: Context) {
@@ -14,39 +14,45 @@ struct CIFileView: View {
@EnvironmentObject var theme: AppTheme
let file: CIFile?
let edited: Bool
var smallView: Bool = false
var body: some View {
let metaReserve = edited
? " "
: " "
Button(action: fileAction) {
HStack(alignment: .bottom, spacing: 6) {
fileIndicator()
.padding(.top, 5)
.padding(.bottom, 3)
if let file = file {
let prettyFileSize = ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary)
VStack(alignment: .leading, spacing: 2) {
Text(file.fileName)
.lineLimit(1)
.multilineTextAlignment(.leading)
.foregroundColor(theme.colors.onBackground)
Text(prettyFileSize + metaReserve)
.font(.caption)
.lineLimit(1)
.multilineTextAlignment(.leading)
.foregroundColor(theme.colors.secondary)
if smallView {
fileIndicator()
.onTapGesture(perform: fileAction)
} else {
let metaReserve = edited
? " "
: " "
Button(action: fileAction) {
HStack(alignment: .bottom, spacing: 6) {
fileIndicator()
.padding(.top, 5)
.padding(.bottom, 3)
if let file = file {
let prettyFileSize = ByteCountFormatter.string(fromByteCount: file.fileSize, countStyle: .binary)
VStack(alignment: .leading, spacing: 2) {
Text(file.fileName)
.lineLimit(1)
.multilineTextAlignment(.leading)
.foregroundColor(theme.colors.onBackground)
Text(prettyFileSize + metaReserve)
.font(.caption)
.lineLimit(1)
.multilineTextAlignment(.leading)
.foregroundColor(theme.colors.secondary)
}
} else {
Text(metaReserve)
}
} else {
Text(metaReserve)
}
.padding(.top, 4)
.padding(.bottom, 6)
.padding(.leading, 10)
.padding(.trailing, 12)
}
.padding(.top, 4)
.padding(.bottom, 6)
.padding(.leading, 10)
.padding(.trailing, 12)
.disabled(!itemInteractive)
}
.disabled(!itemInteractive)
}
private var itemInteractive: Bool {
@@ -199,17 +205,17 @@ struct CIFileView: View {
Image(systemName: icon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 30, height: 30)
.frame(width: smallView ? 36 : 30, height: smallView ? 36 : 30)
.foregroundColor(color)
if let innerIcon = innerIcon,
let innerIconSize = innerIconSize {
let innerIconSize = innerIconSize, (!smallView || file?.showStatusIconInSmallView == true) {
Image(systemName: innerIcon)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(maxHeight: 16)
.frame(width: innerIconSize, height: innerIconSize)
.foregroundColor(.white)
.padding(.top, 12)
.padding(.top, smallView ? 15 : 12)
}
}
}
@@ -15,22 +15,33 @@ struct CIImageView: View {
var preview: UIImage?
let maxWidth: CGFloat
var imgWidth: CGFloat?
@State private var showFullScreenImage = false
var smallView: Bool = false
@Binding var showFullScreenImage: Bool
@State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0
var body: some View {
let file = chatItem.file
VStack(alignment: .center, spacing: 6) {
if let uiImage = getLoadedImage(file) {
imageView(uiImage)
Group { if smallView { smallViewImageView(uiImage) } else { imageView(uiImage) } }
.fullScreenCover(isPresented: $showFullScreenImage) {
FullScreenMediaView(chatItem: chatItem, image: uiImage, showView: $showFullScreenImage)
}
.if(!smallView) { view in
view.modifier(PrivacyBlur(blurred: $blurred))
}
.onTapGesture { showFullScreenImage = true }
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenImage = false
}
} else if let preview {
imageView(preview)
Group {
if smallView {
smallViewImageView(preview)
} else {
imageView(preview).modifier(PrivacyBlur(blurred: $blurred))
}
}
.onTapGesture {
if let file = file {
switch file.fileStatus {
@@ -83,6 +94,9 @@ struct CIImageView: View {
}
}
}
.onDisappear {
showFullScreenImage = false
}
}
private func imageView(_ img: UIImage) -> some View {
@@ -98,7 +112,26 @@ struct CIImageView: View {
.frame(width: w, height: w * img.size.height / img.size.width)
.scaledToFit()
}
loadingIndicator()
if !blurred || !showDownloadButton(chatItem.file?.fileStatus) {
loadingIndicator()
}
}
}
private func smallViewImageView(_ img: UIImage) -> some View {
ZStack(alignment: .topTrailing) {
if img.imageData == nil {
Image(uiImage: img)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: maxWidth, height: maxWidth)
} else {
SwiftyGif(image: img, contentMode: .scaleAspectFill)
.frame(width: maxWidth, height: maxWidth)
}
if chatItem.file?.showStatusIconInSmallView == true {
loadingIndicator()
}
}
}
@@ -145,4 +178,12 @@ struct CIImageView: View {
.tint(.white)
.padding(8)
}
private func showDownloadButton(_ fileStatus: CIFileStatus?) -> Bool {
switch fileStatus {
case .rcvInvitation: true
case .rcvAborted: true
default: false
}
}
}
@@ -12,14 +12,15 @@ import SimpleXChat
struct CILinkView: View {
@EnvironmentObject var theme: AppTheme
let linkPreview: LinkPreview
@State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0
var body: some View {
VStack(alignment: .center, spacing: 6) {
if let data = Data(base64Encoded: dropImagePrefix(linkPreview.image)),
let uiImage = UIImage(data: data) {
if let uiImage = UIImage(base64Encoded: linkPreview.image) {
Image(uiImage: uiImage)
.resizable()
.scaledToFit()
.modifier(PrivacyBlur(blurred: $blurred))
}
VStack(alignment: .leading, spacing: 6) {
Text(linkPreview.title)
@@ -20,45 +20,44 @@ struct CIVideoView: View {
@State private var videoPlaying: Bool = false
private let maxWidth: CGFloat
private var videoWidth: CGFloat?
private let smallView: Bool
@State private var player: AVPlayer?
@State private var fullPlayer: AVPlayer?
@State private var url: URL?
@State private var urlDecrypted: URL?
@State private var decryptionInProgress: Bool = false
@State private var showFullScreenPlayer = false
@Binding private var showFullScreenPlayer: Bool
@State private var timeObserver: Any? = nil
@State private var fullScreenTimeObserver: Any? = nil
@State private var publisher: AnyCancellable? = nil
private var sizeMultiplier: CGFloat { smallView ? 0.38 : 1 }
@State private var blurred: Bool = UserDefaults.standard.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) > 0
init(chatItem: ChatItem, preview: UIImage?, duration: Int, maxWidth: CGFloat, videoWidth: CGFloat?) {
init(chatItem: ChatItem, preview: UIImage?, duration: Int, maxWidth: CGFloat, videoWidth: CGFloat?, smallView: Bool = false, showFullscreenPlayer: Binding<Bool>) {
self.chatItem = chatItem
self.preview = preview
self._duration = State(initialValue: duration)
self.maxWidth = maxWidth
self.videoWidth = videoWidth
if let url = getLoadedVideo(chatItem.file) {
let decrypted = chatItem.file?.fileSource?.cryptoArgs == nil ? url : chatItem.file?.fileSource?.decryptedGet()
self._urlDecrypted = State(initialValue: decrypted)
if let decrypted = decrypted {
self._player = State(initialValue: VideoPlayerView.getOrCreatePlayer(decrypted, false))
self._fullPlayer = State(initialValue: AVPlayer(url: decrypted))
}
self._url = State(initialValue: url)
}
self.smallView = smallView
self._showFullScreenPlayer = showFullscreenPlayer
}
var body: some View {
let file = chatItem.file
ZStack {
ZStack(alignment: smallView ? .topLeading : .center) {
ZStack(alignment: .topLeading) {
if let file = file, let preview = preview, let player = player, let decrypted = urlDecrypted {
if let file = file, let preview = preview, let decrypted = urlDecrypted, smallView {
smallVideoView(decrypted, file, preview)
} else if let file = file, let preview = preview, let player = player, let decrypted = urlDecrypted {
videoView(player, decrypted, file, preview, duration)
} else if let file = file, let defaultPreview = preview, file.loaded && urlDecrypted == nil, smallView {
smallVideoViewEncrypted(file, defaultPreview)
} else if let file = file, let defaultPreview = preview, file.loaded && urlDecrypted == nil {
videoViewEncrypted(file, defaultPreview, duration)
} else if let preview {
imageView(preview)
.onTapGesture {
if let file = file {
} else if let preview, let file {
Group { if smallView { smallViewImageView(preview, file) } else { imageView(preview) } }
.onTapGesture {
switch file.fileStatus {
case .rcvInvitation, .rcvAborted:
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
@@ -82,21 +81,64 @@ struct CIVideoView: View {
default: ()
}
}
}
}
durationProgress()
if !smallView {
durationProgress()
}
}
if let file = file, showDownloadButton(file.fileStatus) {
Button {
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
} label: {
if !blurred, let file, showDownloadButton(file.fileStatus) {
if !smallView {
Button {
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
} label: {
playPauseIcon("play.fill")
}
} else if !file.showStatusIconInSmallView {
playPauseIcon("play.fill")
.onTapGesture {
receiveFileIfValidSize(file: file, receiveFile: receiveFile)
}
}
}
}
.fullScreenCover(isPresented: $showFullScreenPlayer) {
if let decrypted = urlDecrypted {
fullScreenPlayer(decrypted)
}
}
.onAppear {
setupPlayer(chatItem.file)
}
.onChange(of: chatItem.file) { file in
// ChatItem can be changed in small view on chat list screen
setupPlayer(file)
}
.onDisappear {
showFullScreenPlayer = false
}
}
private func showDownloadButton(_ fileStatus: CIFileStatus) -> Bool {
private func setupPlayer(_ file: CIFile?) {
let newUrl = getLoadedVideo(file)
if newUrl == url {
return
}
url = nil
urlDecrypted = nil
player = nil
fullPlayer = nil
if let newUrl {
let decrypted = file?.fileSource?.cryptoArgs == nil ? newUrl : file?.fileSource?.decryptedGet()
urlDecrypted = decrypted
if let decrypted = decrypted {
player = VideoPlayerView.getOrCreatePlayer(decrypted, false)
fullPlayer = AVPlayer(url: decrypted)
}
url = newUrl
}
}
private func showDownloadButton(_ fileStatus: CIFileStatus?) -> Bool {
switch fileStatus {
case .rcvInvitation: true
case .rcvAborted: true
@@ -109,11 +151,6 @@ struct CIVideoView: View {
ZStack(alignment: .center) {
let canBePlayed = !chatItem.chatDir.sent || file.fileStatus == CIFileStatus.sndComplete || (file.fileStatus == .sndStored && file.fileProtocol == .local)
imageView(defaultPreview)
.fullScreenCover(isPresented: $showFullScreenPlayer) {
if let decrypted = urlDecrypted {
fullScreenPlayer(decrypted)
}
}
.onTapGesture {
decrypt(file: file) {
showFullScreenPlayer = urlDecrypted != nil
@@ -122,20 +159,22 @@ struct CIVideoView: View {
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenPlayer = false
}
if !decryptionInProgress {
Button {
decrypt(file: file) {
if urlDecrypted != nil {
videoPlaying = true
player?.play()
if !blurred {
if !decryptionInProgress {
Button {
decrypt(file: file) {
if urlDecrypted != nil {
videoPlaying = true
player?.play()
}
}
} label: {
playPauseIcon(canBePlayed ? "play.fill" : "play.slash")
}
} label: {
playPauseIcon(canBePlayed ? "play.fill" : "play.slash")
.disabled(!canBePlayed)
} else {
videoDecryptionProgress()
}
.disabled(!canBePlayed)
} else {
videoDecryptionProgress()
}
}
}
@@ -154,9 +193,7 @@ struct CIVideoView: View {
videoPlaying = false
}
}
.fullScreenCover(isPresented: $showFullScreenPlayer) {
fullScreenPlayer(url)
}
.modifier(PrivacyBlur(enabled: !videoPlaying, blurred: $blurred))
.onTapGesture {
switch player.timeControlStatus {
case .playing:
@@ -172,7 +209,7 @@ struct CIVideoView: View {
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenPlayer = false
}
if !videoPlaying {
if !videoPlaying && !blurred {
Button {
m.stopPreviousRecPlay = url
player.play()
@@ -194,14 +231,53 @@ struct CIVideoView: View {
}
}
private func smallVideoViewEncrypted(_ file: CIFile, _ preview: UIImage) -> some View {
return ZStack(alignment: .topLeading) {
let canBePlayed = !chatItem.chatDir.sent || file.fileStatus == CIFileStatus.sndComplete || (file.fileStatus == .sndStored && file.fileProtocol == .local)
smallViewImageView(preview, file)
.onTapGesture {
decrypt(file: file) {
showFullScreenPlayer = urlDecrypted != nil
}
}
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenPlayer = false
}
if file.showStatusIconInSmallView {
// Show nothing
} else if !decryptionInProgress {
playPauseIcon(canBePlayed ? "play.fill" : "play.slash")
} else {
videoDecryptionProgress()
}
}
}
private func smallVideoView(_ url: URL, _ file: CIFile, _ preview: UIImage) -> some View {
return ZStack(alignment: .topLeading) {
smallViewImageView(preview, file)
.onTapGesture {
showFullScreenPlayer = true
}
.onChange(of: m.activeCallViewIsCollapsed) { _ in
showFullScreenPlayer = false
}
if !file.showStatusIconInSmallView {
playPauseIcon("play.fill")
}
}
}
private func playPauseIcon(_ image: String, _ color: Color = .white) -> some View {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 12, height: 12)
.frame(width: smallView ? 12 * sizeMultiplier * 1.6 : 12, height: smallView ? 12 * sizeMultiplier * 1.6 : 12)
.foregroundColor(color)
.padding(.leading, 4)
.frame(width: 40, height: 40)
.padding(.leading, smallView ? 0 : 4)
.frame(width: 40 * sizeMultiplier, height: 40 * sizeMultiplier)
.background(Color.black.opacity(0.35))
.clipShape(Circle())
}
@@ -209,9 +285,9 @@ struct CIVideoView: View {
private func videoDecryptionProgress(_ color: Color = .white) -> some View {
ProgressView()
.progressViewStyle(.circular)
.frame(width: 12, height: 12)
.frame(width: smallView ? 12 * sizeMultiplier : 12, height: smallView ? 12 * sizeMultiplier : 12)
.tint(color)
.frame(width: 40, height: 40)
.frame(width: smallView ? 40 * sizeMultiplier * 0.9 : 40, height: smallView ? 40 * sizeMultiplier * 0.9 : 40)
.background(Color.black.opacity(0.35))
.clipShape(Circle())
}
@@ -247,7 +323,23 @@ struct CIVideoView: View {
.resizable()
.scaledToFit()
.frame(width: w)
fileStatusIcon()
.modifier(PrivacyBlur(blurred: $blurred))
if !blurred || !showDownloadButton(chatItem.file?.fileStatus) {
fileStatusIcon()
}
}
}
private func smallViewImageView(_ img: UIImage, _ file: CIFile) -> some View {
ZStack(alignment: .center) {
Image(uiImage: img)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: maxWidth, height: maxWidth)
if file.showStatusIconInSmallView {
fileStatusIcon()
.allowsHitTesting(false)
}
}
}
@@ -322,7 +414,7 @@ struct CIVideoView: View {
.aspectRatio(contentMode: .fit)
.frame(width: size, height: size)
.foregroundColor(.white)
.padding(padding)
.padding(smallView ? 0 : padding)
}
private func progressView() -> some View {
@@ -330,7 +422,7 @@ struct CIVideoView: View {
.progressViewStyle(.circular)
.frame(width: 16, height: 16)
.tint(.white)
.padding(11)
.padding(smallView ? 0 : 11)
}
private func progressCircle(_ progress: Int64, _ total: Int64) -> some View {
@@ -342,7 +434,7 @@ struct CIVideoView: View {
)
.rotationEffect(.degrees(-90))
.frame(width: 16, height: 16)
.padding([.trailing, .top], 11)
.padding([.trailing, .top], smallView ? 0 : 11)
}
// TODO encrypt: where file size is checked?
@@ -382,7 +474,8 @@ struct CIVideoView: View {
)
.onAppear {
DispatchQueue.main.asyncAfter(deadline: .now()) {
m.stopPreviousRecPlay = url
// Prevent feedback loop - setting `ChatModel`s property causes `onAppear` to be called on iOS17+
if m.stopPreviousRecPlay != url { m.stopPreviousRecPlay = url }
if let player = fullPlayer {
player.play()
var played = false
@@ -419,10 +512,12 @@ struct CIVideoView: View {
urlDecrypted = await file.fileSource?.decryptedGetOrCreate(&ChatModel.shared.filesToDelete)
await MainActor.run {
if let decrypted = urlDecrypted {
player = VideoPlayerView.getOrCreatePlayer(decrypted, false)
if !smallView {
player = VideoPlayerView.getOrCreatePlayer(decrypted, false)
}
fullPlayer = AVPlayer(url: decrypted)
}
decryptionInProgress = true
decryptionInProgress = false
completed?()
}
}
@@ -15,15 +15,26 @@ struct CIVoiceView: View {
var chatItem: ChatItem
let recordingFile: CIFile?
let duration: Int
@Binding var audioPlayer: AudioPlayer?
@Binding var playbackState: VoiceMessagePlaybackState
@Binding var playbackTime: TimeInterval?
@State var audioPlayer: AudioPlayer? = nil
@State var playbackState: VoiceMessagePlaybackState = .noPlayback
@State var playbackTime: TimeInterval? = nil
@Binding var allowMenu: Bool
var smallView: Bool = false
@State private var seek: (TimeInterval) -> Void = { _ in }
var body: some View {
Group {
if chatItem.chatDir.sent {
if smallView {
HStack(spacing: 10) {
player()
playerTime()
.allowsHitTesting(false)
if .playing == playbackState || (playbackTime ?? 0) > 0 || !allowMenu {
playbackSlider()
}
}
} else if chatItem.chatDir.sent {
VStack (alignment: .trailing, spacing: 6) {
HStack {
if .playing == playbackState || (playbackTime ?? 0) > 0 || !allowMenu {
@@ -55,6 +66,7 @@ struct CIVoiceView: View {
private func player() -> some View {
VoiceMessagePlayer(
chat: chat,
chatItem: chatItem,
recordingFile: recordingFile,
recordingTime: TimeInterval(duration),
@@ -63,7 +75,8 @@ struct CIVoiceView: View {
audioPlayer: $audioPlayer,
playbackState: $playbackState,
playbackTime: $playbackTime,
allowMenu: $allowMenu
allowMenu: $allowMenu,
sizeMultiplier: smallView ? voiceMessageSizeBasedOnSquareSize(36) / 56 : 1
)
}
@@ -119,6 +132,7 @@ struct VoiceMessagePlayerTime: View {
}
struct VoiceMessagePlayer: View {
@ObservedObject var chat: Chat
@EnvironmentObject var chatModel: ChatModel
@EnvironmentObject var theme: AppTheme
var chatItem: ChatItem
@@ -130,7 +144,9 @@ struct VoiceMessagePlayer: View {
@Binding var audioPlayer: AudioPlayer?
@Binding var playbackState: VoiceMessagePlaybackState
@Binding var playbackTime: TimeInterval?
@Binding var allowMenu: Bool
var sizeMultiplier: CGFloat
var body: some View {
ZStack {
@@ -190,49 +206,113 @@ struct VoiceMessagePlayer: View {
}
}
.onAppear {
if audioPlayer == nil {
let small = sizeMultiplier != 1
audioPlayer = small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.audioPlayer : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.audioPlayer
playbackState = (small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.playbackState : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.playbackState) ?? .noPlayback
playbackTime = small ? VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)]?.playbackTime : VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)]?.playbackTime
}
seek = { to in audioPlayer?.seek(to) }
audioPlayer?.onTimer = { playbackTime = $0 }
let audioPath: URL? = if let recordingSource = getLoadedFileSource(recordingFile) {
getAppFilePath(recordingSource.filePath)
} else {
nil
}
let chatId = chatModel.chatId
let userId = chatModel.currentUser?.userId
audioPlayer?.onTimer = {
playbackTime = $0
notifyStateChange()
// Manual check here is needed because when this view is not visible, SwiftUI don't react on stopPreviousRecPlay, chatId and current user changes and audio keeps playing when it should stop
if (audioPath != nil && chatModel.stopPreviousRecPlay != audioPath) || chatModel.chatId != chatId || chatModel.currentUser?.userId != userId {
stopPlayback()
}
}
audioPlayer?.onFinishPlayback = {
playbackState = .noPlayback
playbackTime = TimeInterval(0)
notifyStateChange()
}
// One voice message was paused, then scrolled far from it, started to play another one, drop to stopped state
if let audioPath, chatModel.stopPreviousRecPlay != audioPath {
stopPlayback()
}
}
.onChange(of: chatModel.stopPreviousRecPlay) { it in
if let recordingFileName = getLoadedFileSource(recordingFile)?.filePath,
chatModel.stopPreviousRecPlay != getAppFilePath(recordingFileName) {
audioPlayer?.stop()
playbackState = .noPlayback
playbackTime = TimeInterval(0)
stopPlayback()
}
}
.onChange(of: playbackState) { state in
allowMenu = state == .paused || state == .noPlayback
// Notify activeContentPreview in ChatPreviewView that playback is finished
if state == .noPlayback, let recordingFileName = getLoadedFileSource(recordingFile)?.filePath,
chatModel.stopPreviousRecPlay == getAppFilePath(recordingFileName) {
chatModel.stopPreviousRecPlay = nil
}
}
.onChange(of: chatModel.chatId) { _ in
stopPlayback()
}
.onDisappear {
if sizeMultiplier == 1 && chatModel.chatId == nil {
stopPlayback()
}
}
}
@ViewBuilder private func playbackButton() -> some View {
switch playbackState {
case .noPlayback:
Button {
if let recordingSource = getLoadedFileSource(recordingFile) {
startPlayback(recordingSource)
}
} label: {
if sizeMultiplier != 1 {
switch playbackState {
case .noPlayback:
playPauseIcon("play.fill", theme.colors.primary)
}
case .playing:
Button {
audioPlayer?.pause()
playbackState = .paused
} label: {
.onTapGesture {
if let recordingSource = getLoadedFileSource(recordingFile) {
startPlayback(recordingSource)
}
}
case .playing:
playPauseIcon("pause.fill", theme.colors.primary)
}
case .paused:
Button {
audioPlayer?.play()
playbackState = .playing
} label: {
.onTapGesture {
audioPlayer?.pause()
playbackState = .paused
notifyStateChange()
}
case .paused:
playPauseIcon("play.fill", theme.colors.primary)
.onTapGesture {
audioPlayer?.play()
playbackState = .playing
notifyStateChange()
}
}
} else {
switch playbackState {
case .noPlayback:
Button {
if let recordingSource = getLoadedFileSource(recordingFile) {
startPlayback(recordingSource)
}
} label: {
playPauseIcon("play.fill", theme.colors.primary)
}
case .playing:
Button {
audioPlayer?.pause()
playbackState = .paused
notifyStateChange()
} label: {
playPauseIcon("pause.fill", theme.colors.primary)
}
case .paused:
Button {
audioPlayer?.play()
playbackState = .playing
notifyStateChange()
} label: {
playPauseIcon("play.fill", theme.colors.primary)
}
}
}
}
@@ -242,28 +322,49 @@ struct VoiceMessagePlayer: View {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 20, height: 20)
.frame(width: 20 * sizeMultiplier, height: 20 * sizeMultiplier)
.foregroundColor(color)
.padding(.leading, image == "play.fill" ? 4 : 0)
.frame(width: 56, height: 56)
.frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
if recordingTime > 0 {
ProgressCircle(length: recordingTime, progress: $playbackTime)
.frame(width: 53, height: 53) // this + ProgressCircle lineWidth = background circle diameter
.frame(width: 53 * sizeMultiplier, height: 53 * sizeMultiplier) // this + ProgressCircle lineWidth = background circle diameter
}
}
}
private func downloadButton(_ recordingFile: CIFile, _ icon: String) -> some View {
Button {
Task {
if let user = chatModel.currentUser {
await receiveFile(user: user, fileId: recordingFile.fileId)
Group {
if sizeMultiplier != 1 {
playPauseIcon(icon, theme.colors.primary)
.onTapGesture {
Task {
if let user = chatModel.currentUser {
await receiveFile(user: user, fileId: recordingFile.fileId)
}
}
}
} else {
Button {
Task {
if let user = chatModel.currentUser {
await receiveFile(user: user, fileId: recordingFile.fileId)
}
}
} label: {
playPauseIcon(icon, theme.colors.primary)
}
}
} label: {
playPauseIcon(icon, theme.colors.primary)
}
}
func notifyStateChange() {
if sizeMultiplier != 1 {
VoiceItemState.smallView[VoiceItemState.id(chat, chatItem)] = VoiceItemState(audioPlayer: audioPlayer, playbackState: playbackState, playbackTime: playbackTime)
} else {
VoiceItemState.chatView[VoiceItemState.id(chat, chatItem)] = VoiceItemState(audioPlayer: audioPlayer, playbackState: playbackState, playbackTime: playbackTime)
}
}
@@ -288,33 +389,96 @@ struct VoiceMessagePlayer: View {
Image(systemName: image)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: size, height: size)
.frame(width: size * sizeMultiplier, height: size * sizeMultiplier)
.foregroundColor(Color(uiColor: .tertiaryLabel))
.frame(width: 56, height: 56)
.frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
}
private func loadingIcon() -> some View {
ProgressView()
.frame(width: 30, height: 30)
.frame(width: 56, height: 56)
.frame(width: 30 * sizeMultiplier, height: 30 * sizeMultiplier)
.frame(width: 56 * sizeMultiplier, height: 56 * sizeMultiplier)
.background(showBackground ? chatItemFrameColor(chatItem, theme) : .clear)
.clipShape(Circle())
}
private func startPlayback(_ recordingSource: CryptoFile) {
chatModel.stopPreviousRecPlay = getAppFilePath(recordingSource.filePath)
let audioPath = getAppFilePath(recordingSource.filePath)
let chatId = chatModel.chatId
let userId = chatModel.currentUser?.userId
chatModel.stopPreviousRecPlay = audioPath
audioPlayer = AudioPlayer(
onTimer: { playbackTime = $0 },
onTimer: {
playbackTime = $0
notifyStateChange()
// Manual check here is needed because when this view is not visible, SwiftUI don't react on stopPreviousRecPlay, chatId and current user changes and audio keeps playing when it should stop
if chatModel.stopPreviousRecPlay != audioPath || chatModel.chatId != chatId || chatModel.currentUser?.userId != userId {
stopPlayback()
}
},
onFinishPlayback: {
playbackState = .noPlayback
playbackTime = TimeInterval(0)
notifyStateChange()
}
)
audioPlayer?.start(fileSource: recordingSource, at: playbackTime)
playbackState = .playing
notifyStateChange()
}
private func stopPlayback() {
audioPlayer?.stop()
playbackState = .noPlayback
playbackTime = TimeInterval(0)
notifyStateChange()
}
}
func voiceMessageSizeBasedOnSquareSize(_ squareSize: CGFloat) -> CGFloat {
let squareToCircleRatio = 0.935
return squareSize + squareSize * (1 - squareToCircleRatio)
}
class VoiceItemState {
var audioPlayer: AudioPlayer?
var playbackState: VoiceMessagePlaybackState
var playbackTime: TimeInterval?
init(audioPlayer: AudioPlayer? = nil, playbackState: VoiceMessagePlaybackState, playbackTime: TimeInterval? = nil) {
self.audioPlayer = audioPlayer
self.playbackState = playbackState
self.playbackTime = playbackTime
}
static func id(_ chat: Chat, _ chatItem: ChatItem) -> String {
"\(chat.id) \(chatItem.id)"
}
static func id(_ chatInfo: ChatInfo, _ chatItem: ChatItem) -> String {
"\(chatInfo.id) \(chatItem.id)"
}
static func stopVoiceInSmallView(_ chatInfo: ChatInfo, _ chatItem: ChatItem) {
let id = id(chatInfo, chatItem)
if let item = smallView[id] {
item.audioPlayer?.stop()
ChatModel.shared.stopPreviousRecPlay = nil
}
}
static func stopVoiceInChatView(_ chatInfo: ChatInfo, _ chatItem: ChatItem) {
let id = id(chatInfo, chatItem)
if let item = chatView[id] {
item.audioPlayer?.stop()
ChatModel.shared.stopPreviousRecPlay = nil
}
}
static var smallView: [String: VoiceItemState] = [:]
static var chatView: [String: VoiceItemState] = [:]
}
struct CIVoiceView_Previews: PreviewProvider {
@@ -339,15 +503,12 @@ struct CIVoiceView_Previews: PreviewProvider {
chatItem: ChatItem.getVoiceMsgContentSample(),
recordingFile: CIFile.getSample(fileName: "voice.m4a", fileSize: 65536, fileStatus: .rcvComplete),
duration: 30,
audioPlayer: .constant(nil),
playbackState: .constant(.playing),
playbackTime: .constant(TimeInterval(20)),
allowMenu: Binding.constant(true)
)
ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(), revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWtFile, revealed: Binding.constant(false), allowMenu: .constant(true), playbackState: .constant(.noPlayback), playbackTime: .constant(nil))
ChatItemView(chat: Chat.sampleData, chatItem: sentVoiceMessage, revealed: Binding.constant(false), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(), revealed: Binding.constant(false), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: ChatItem.getVoiceMsgContentSample(fileStatus: .rcvTransfer(rcvProgress: 7, rcvTotal: 10)), revealed: Binding.constant(false), allowMenu: .constant(true))
ChatItemView(chat: Chat.sampleData, chatItem: voiceMessageWtFile, revealed: Binding.constant(false), allowMenu: .constant(true))
}
.previewLayout(.fixed(width: 360, height: 360))
}
@@ -13,21 +13,23 @@ import SimpleXChat
struct FramedCIVoiceView: View {
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
let recordingFile: CIFile?
let duration: Int
@State var audioPlayer: AudioPlayer? = nil
@State var playbackState: VoiceMessagePlaybackState = .noPlayback
@State var playbackTime: TimeInterval? = nil
@Binding var allowMenu: Bool
@Binding var audioPlayer: AudioPlayer?
@Binding var playbackState: VoiceMessagePlaybackState
@Binding var playbackTime: TimeInterval?
@State private var seek: (TimeInterval) -> Void = { _ in }
var body: some View {
HStack {
VoiceMessagePlayer(
chat: chat,
chatItem: chatItem,
recordingFile: recordingFile,
recordingTime: TimeInterval(duration),
@@ -36,7 +38,8 @@ struct FramedCIVoiceView: View {
audioPlayer: $audioPlayer,
playbackState: $playbackState,
playbackTime: $playbackTime,
allowMenu: $allowMenu
allowMenu: $allowMenu,
sizeMultiplier: 1
)
VoiceMessagePlayerTime(
recordingTime: TimeInterval(duration),
File diff suppressed because one or more lines are too long
@@ -415,7 +415,7 @@ struct ChatItemInfoView: View {
}
@ViewBuilder private func memberDeliveryStatusesView(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> some View {
VStack(alignment: .leading, spacing: 12) {
LazyVStack(alignment: .leading, spacing: 12) {
let mss = membersStatuses(memberDeliveryStatuses)
if !mss.isEmpty {
ForEach(mss, id: \.0.groupMemberId) { memberStatus in
@@ -428,7 +428,7 @@ struct ChatItemInfoView: View {
}
}
private func membersStatuses(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> [(GroupMember, CIStatus, Bool?)] {
private func membersStatuses(_ memberDeliveryStatuses: [MemberDeliveryStatus]) -> [(GroupMember, GroupSndStatus, Bool?)] {
memberDeliveryStatuses.compactMap({ mds in
if let mem = chatModel.getGroupMember(mds.groupMemberId) {
return (mem.wrapped, mds.memberDeliveryStatus, mds.sentViaProxy)
@@ -438,7 +438,7 @@ struct ChatItemInfoView: View {
})
}
private func memberDeliveryStatusView(_ member: GroupMember, _ status: CIStatus, _ sentViaProxy: Bool?) -> some View {
private func memberDeliveryStatusView(_ member: GroupMember, _ status: GroupSndStatus, _ sentViaProxy: Bool?) -> some View {
HStack{
ProfileImage(imageStr: member.image, size: 30)
.padding(.trailing, 2)
@@ -450,23 +450,19 @@ struct ChatItemInfoView: View {
.foregroundColor(theme.colors.secondary).opacity(0.67)
}
let v = Group {
if let (icon, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary) {
switch status {
case .sndRcvd:
ZStack(alignment: .trailing) {
Image(systemName: icon)
.foregroundColor(statusColor.opacity(0.67))
.padding(.trailing, 6)
Image(systemName: icon)
.foregroundColor(statusColor.opacity(0.67))
}
default:
let (icon, statusColor) = status.statusIcon(theme.colors.secondary, theme.colors.primary)
switch status {
case .rcvd:
ZStack(alignment: .trailing) {
Image(systemName: icon)
.foregroundColor(statusColor)
.foregroundColor(statusColor.opacity(0.67))
.padding(.trailing, 6)
Image(systemName: icon)
.foregroundColor(statusColor.opacity(0.67))
}
} else {
Image(systemName: "ellipsis")
.foregroundColor(Color.secondary)
default:
Image(systemName: icon)
.foregroundColor(statusColor)
}
}
+5 -18
View File
@@ -16,28 +16,20 @@ struct ChatItemView: View {
var maxWidth: CGFloat = .infinity
@Binding var revealed: Bool
@Binding var allowMenu: Bool
@Binding var audioPlayer: AudioPlayer?
@Binding var playbackState: VoiceMessagePlaybackState
@Binding var playbackTime: TimeInterval?
init(
chat: Chat,
chatItem: ChatItem,
showMember: Bool = false,
maxWidth: CGFloat = .infinity,
revealed: Binding<Bool>,
allowMenu: Binding<Bool> = .constant(false),
audioPlayer: Binding<AudioPlayer?> = .constant(nil),
playbackState: Binding<VoiceMessagePlaybackState> = .constant(.noPlayback),
playbackTime: Binding<TimeInterval?> = .constant(nil)
allowMenu: Binding<Bool> = .constant(false)
) {
self.chat = chat
self.chatItem = chatItem
self.maxWidth = maxWidth
_revealed = revealed
_allowMenu = allowMenu
_audioPlayer = audioPlayer
_playbackState = playbackState
_playbackTime = playbackTime
}
var body: some View {
@@ -48,7 +40,7 @@ struct ChatItemView: View {
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 {
CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, audioPlayer: $audioPlayer, playbackState: $playbackState, playbackTime: $playbackTime, allowMenu: $allowMenu)
CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: $allowMenu)
} else if ci.content.msgContent == nil {
ChatItemContentView(chat: chat, chatItem: chatItem, revealed: $revealed, msgContentView: { Text(ci.text) }) // msgContent is unreachable branch in this case
} else {
@@ -68,9 +60,7 @@ struct ChatItemView: View {
default: nil
}
}
.map { dropImagePrefix($0) }
.flatMap { Data(base64Encoded: $0) }
.flatMap { UIImage(data: $0) }
.flatMap { UIImage(base64Encoded: $0) }
let adjustedMaxWidth = {
if let preview, preview.size.width <= preview.size.height {
maxWidth * 0.75
@@ -86,10 +76,7 @@ struct ChatItemView: View {
maxWidth: maxWidth,
imgWidth: adjustedMaxWidth,
videoWidth: adjustedMaxWidth,
allowMenu: $allowMenu,
audioPlayer: $audioPlayer,
playbackState: $playbackState,
playbackTime: $playbackTime
allowMenu: $allowMenu
)
}
}
+38 -53
View File
@@ -37,7 +37,6 @@ struct ChatView: View {
@State private var searchText: String = ""
@FocusState private var searchFocussed
// opening GroupMemberInfoView on member icon
@State private var membersLoaded = false
@State private var selectedMember: GMember? = nil
// opening GroupLinkView on link button (incognito)
@State private var showGroupLinkSheet: Bool = false
@@ -46,9 +45,14 @@ struct ChatView: View {
var body: some View {
if #available(iOS 16.0, *) {
viewBody
let v = viewBody
.scrollDismissesKeyboard(.immediately)
.keyboardPadding()
if (searchMode) {
v.toolbarBackground(.thinMaterial, for: .navigationBar)
} else {
v.toolbarBackground(.visible, for: .navigationBar)
}
} else {
viewBody
}
@@ -82,7 +86,6 @@ struct ChatView: View {
)
.disabled(!cInfo.sendMsgEnabled)
}
.padding(.top, 1)
.navigationTitle(cInfo.chatViewName)
.background(theme.colors.background)
.navigationBarTitleDisplayMode(.inline)
@@ -93,6 +96,7 @@ struct ChatView: View {
}
.onChange(of: chatModel.chatId) { cId in
showChatInfoSheet = false
stopAudioPlayer()
if let cId {
if let c = chatModel.getChat(cId) {
chat = c
@@ -114,6 +118,7 @@ struct ChatView: View {
.environmentObject(scrollModel)
.onDisappear {
VideoPlayerView.players.removeAll()
stopAudioPlayer()
if chatModel.chatId == cInfo.id && !presentationMode.wrappedValue.isPresented {
chatModel.chatId = nil
DispatchQueue.main.asyncAfter(deadline: .now() + 0.35) {
@@ -121,7 +126,8 @@ struct ChatView: View {
chatModel.chatItemStatuses = [:]
chatModel.reversedChatItems = []
chatModel.groupMembers = []
membersLoaded = false
chatModel.groupMembersIndexes.removeAll()
chatModel.membersLoaded = false
}
}
}
@@ -163,7 +169,7 @@ struct ChatView: View {
}
} else if case let .group(groupInfo) = cInfo {
Button {
Task { await loadGroupMembers(groupInfo) { showChatInfoSheet = true } }
Task { await chatModel.loadGroupMembers(groupInfo) { showChatInfoSheet = true } }
} label: {
ChatInfoToolbar(chat: chat)
.tint(theme.colors.primary)
@@ -249,18 +255,7 @@ struct ChatView: View {
}
}
}
private func loadGroupMembers(_ groupInfo: GroupInfo, updateView: @escaping () -> Void = {}) async {
let groupMembers = await apiListMembers(groupInfo.groupId)
await MainActor.run {
if chatModel.chatId == groupInfo.id {
chatModel.groupMembers = groupMembers.map { GMember.init($0) }
membersLoaded = true
updateView()
}
}
}
private func initChatView() {
let cInfo = chat.chatInfo
// This check prevents the call to apiContactInfo after the app is suspended, and the database is closed.
@@ -321,8 +316,9 @@ struct ChatView: View {
}
.padding(.horizontal)
.padding(.vertical, 8)
.background(.thinMaterial)
}
private func voiceWithoutFrame(_ ci: ChatItem) -> Bool {
ci.content.msgContent?.isVoice == true && ci.content.text.count == 0 && ci.quotedItem == nil && ci.meta.itemForwarded == nil
}
@@ -384,7 +380,7 @@ struct ChatView: View {
@ViewBuilder private func connectingText() -> some View {
if case let .direct(contact) = chat.chatInfo,
!contact.ready,
!contact.sndReady,
contact.active,
!contact.nextSendGrpInv {
Text("connecting…")
@@ -475,9 +471,7 @@ struct ChatView: View {
.foregroundColor(theme.colors.primary)
}
.onTapGesture {
if let latestUnreadItem = filtered(chatModel.reversedChatItems).last(where: { $0.isRcvNew }) {
scrollModel.scrollToItem(id: latestUnreadItem.id)
}
scrollModel.scrollToBottom()
}
} else if !counts.isNearBottom {
circleButton {
@@ -532,7 +526,7 @@ struct ChatView: View {
private func addMembersButton() -> some View {
Button {
if case let .group(gInfo) = chat.chatInfo {
Task { await loadGroupMembers(gInfo) { showAddMembersSheet = true } }
Task { await chatModel.loadGroupMembers(gInfo) { showAddMembersSheet = true } }
}
} label: {
Image(systemName: "person.crop.circle.badge.plus")
@@ -597,16 +591,19 @@ struct ChatView: View {
}
}
func stopAudioPlayer() {
VoiceItemState.chatView.values.forEach { $0.audioPlayer?.stop() }
VoiceItemState.chatView = [:]
}
@ViewBuilder private func chatItemView(_ ci: ChatItem, _ maxWidth: CGFloat) -> some View {
ChatItemWithMenu(
chat: chat,
chatItem: ci,
maxWidth: maxWidth,
itemWidth: maxWidth,
composeState: $composeState,
selectedMember: $selectedMember,
revealedChatItem: $revealedChatItem,
chatView: self
revealedChatItem: $revealedChatItem
)
}
@@ -614,13 +611,11 @@ struct ChatView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@ObservedObject var chat: Chat
var chatItem: ChatItem
var maxWidth: CGFloat
@State var itemWidth: CGFloat
let chatItem: ChatItem
let maxWidth: CGFloat
@Binding var composeState: ComposeState
@Binding var selectedMember: GMember?
@Binding var revealedChatItem: ChatItem?
var chatView: ChatView
@State private var deletingItem: ChatItem? = nil
@State private var showDeleteMessage = false
@@ -632,10 +627,6 @@ struct ChatView: View {
@State private var allowMenu: Bool = true
@State private var audioPlayer: AudioPlayer?
@State private var playbackState: VoiceMessagePlaybackState = .noPlayback
@State private var playbackTime: TimeInterval?
var revealed: Bool { chatItem == revealedChatItem }
var body: some View {
@@ -688,7 +679,12 @@ struct ChatView: View {
if prevItem == nil || showMemberImage(member, prevItem) || prevMember != nil {
VStack(alignment: .leading, spacing: 4) {
if ci.content.showMemberName {
Text(memberNames(member, prevMember, memCount))
let t = if memCount == 1 && member.memberRole > .member {
Text(member.memberRole.text + " ").fontWeight(.semibold) + Text(member.displayName)
} else {
Text(memberNames(member, prevMember, memCount))
}
t
.font(.caption)
.foregroundStyle(.secondary)
.lineLimit(2)
@@ -698,11 +694,11 @@ struct ChatView: View {
HStack(alignment: .top, spacing: 8) {
ProfileImage(imageStr: member.memberProfile.image, size: memberImageSize, backgroundColor: theme.colors.background)
.onTapGesture {
if chatView.membersLoaded {
if m.membersLoaded {
selectedMember = m.getGroupMember(member.groupMemberId)
} else {
Task {
await chatView.loadGroupMembers(groupInfo) {
await m.loadGroupMembers(groupInfo) {
selectedMember = m.getGroupMember(member.groupMemberId)
}
}
@@ -714,19 +710,19 @@ struct ChatView: View {
chatItemWithMenu(ci, range, maxWidth)
}
}
.padding(.top, 5)
.padding(.bottom, 5)
.padding(.trailing)
.padding(.leading, 12)
} else {
chatItemWithMenu(ci, range, maxWidth)
.padding(.top, 5)
.padding(.bottom, 5)
.padding(.trailing)
.padding(.leading, memberImageSize + 8 + 12)
}
} else {
chatItemWithMenu(ci, range, maxWidth)
.padding(.horizontal)
.padding(.top, 5)
.padding(.bottom, 5)
}
}
@@ -749,10 +745,7 @@ struct ChatView: View {
chatItem: ci,
maxWidth: maxWidth,
revealed: .constant(revealed),
allowMenu: $allowMenu,
audioPlayer: $audioPlayer,
playbackState: $playbackState,
playbackTime: $playbackTime
allowMenu: $allowMenu
)
.modifier(ChatItemClipped(ci))
.contextMenu { menu(ci, range, live: composeState.liveMessage != nil) }
@@ -779,14 +772,6 @@ struct ChatView: View {
}
.frame(maxWidth: maxWidth, maxHeight: .infinity, alignment: alignment)
.frame(minWidth: 0, maxWidth: .infinity, alignment: alignment)
.onDisappear {
if ci.content.msgContent?.isVoice == true {
allowMenu = true
audioPlayer?.stop()
playbackState = .noPlayback
playbackTime = TimeInterval(0)
}
}
.sheet(isPresented: $showChatItemInfoSheet, onDismiss: {
chatItemInfo = nil
}) {
@@ -1097,7 +1082,7 @@ struct ChatView: View {
chatItemInfo = ciInfo
}
if case let .group(gInfo) = chat.chatInfo {
await chatView.loadGroupMembers(gInfo)
await m.loadGroupMembers(gInfo)
}
} catch let error {
logger.error("apiGetChatItemInfo error: \(responseError(error))")
@@ -32,9 +32,8 @@ struct ComposeFileView: View {
}
.padding(.vertical, 1)
.padding(.trailing, 12)
.frame(height: 50)
.frame(height: 54)
.background(theme.appColors.sentMessage)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
}
@@ -18,10 +18,7 @@ struct ComposeImageView: View {
var body: some View {
HStack(alignment: .center, spacing: 8) {
let imgs: [UIImage] = images.compactMap { image in
if let data = Data(base64Encoded: dropImagePrefix(image)) {
return UIImage(data: data)
}
return nil
UIImage(base64Encoded: image)
}
if imgs.count == 0 {
ProgressView()
@@ -49,8 +46,8 @@ struct ComposeImageView: View {
.padding(.vertical, 1)
.padding(.trailing, 12)
.background(theme.appColors.sentMessage)
.frame(minHeight: 54)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
}
@@ -63,14 +63,13 @@ struct ComposeLinkView: View {
.padding(.vertical, 1)
.padding(.trailing, 12)
.background(theme.appColors.sentMessage)
.frame(minHeight: 54)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
private func linkPreviewView(_ linkPreview: LinkPreview) -> some View {
HStack(alignment: .center, spacing: 8) {
if let data = Data(base64Encoded: dropImagePrefix(linkPreview.image)),
let uiImage = UIImage(data: data) {
if let uiImage = UIImage(base64Encoded: linkPreview.image) {
Image(uiImage: uiImage)
.resizable()
.aspectRatio(contentMode: .fit)
@@ -284,8 +284,10 @@ struct ComposeView: View {
var body: some View {
VStack(spacing: 0) {
Divider()
if chat.chatInfo.contact?.nextSendGrpInv ?? false {
ContextInvitingContactMemberView()
Divider()
}
// preference checks should match checks in forwarding list
let simplexLinkProhibited = hasSimplexLink && !chat.groupFeatureEnabled(.simplexLinks)
@@ -293,10 +295,13 @@ struct ComposeView: View {
let voiceProhibited = composeState.voicePreview && !chat.chatInfo.featureEnabled(.voice)
if simplexLinkProhibited {
msgNotAllowedView("SimpleX links not allowed", icon: "link")
Divider()
} else if fileProhibited {
msgNotAllowedView("Files and media not allowed", icon: "doc")
Divider()
} else if voiceProhibited {
msgNotAllowedView("Voice messages not allowed", icon: "mic")
Divider()
}
contextItemView()
switch (composeState.editing, composeState.preview) {
@@ -359,7 +364,6 @@ struct ComposeView: View {
: theme.colors.primary
)
.padding(.trailing, 12)
.background(theme.colors.background)
.disabled(!chat.userCanSend)
if chat.userIsObserver {
@@ -377,6 +381,7 @@ struct ComposeView: View {
}
}
}
.background(.thinMaterial)
.onChange(of: composeState.message) { msg in
if composeState.linkPreviewAllowed {
if msg.count > 0 {
@@ -625,6 +630,7 @@ struct ComposeView: View {
cancelPreview: cancelLinkPreview,
cancelEnabled: !composeState.inProgress
)
Divider()
case let .mediaPreviews(mediaPreviews: media):
ComposeImageView(
images: media.map { (img, _) in img },
@@ -633,6 +639,7 @@ struct ComposeView: View {
chosenMedia = []
},
cancelEnabled: !composeState.editing && !composeState.inProgress)
Divider()
case let .voicePreview(recordingFileName, _):
ComposeVoiceView(
recordingFileName: recordingFileName,
@@ -645,6 +652,7 @@ struct ComposeView: View {
cancelEnabled: !composeState.editing && !composeState.inProgress,
stopPlayback: $stopPlayback
)
Divider()
case let .filePreview(fileName, _):
ComposeFileView(
fileName: fileName,
@@ -652,6 +660,7 @@ struct ComposeView: View {
composeState = composeState.copy(preview: .noPreview)
},
cancelEnabled: !composeState.editing && !composeState.inProgress)
Divider()
}
}
@@ -661,10 +670,9 @@ struct ComposeView: View {
Text(reason).italic()
}
.padding(12)
.frame(minHeight: 50)
.frame(minHeight: 54)
.frame(maxWidth: .infinity, alignment: .leading)
.background(Color(uiColor: .tertiarySystemGroupedBackground))
.padding(.top, 8)
.background(.thinMaterial)
}
@ViewBuilder private func contextItemView() -> some View {
@@ -678,6 +686,7 @@ struct ComposeView: View {
contextIcon: "arrowshape.turn.up.left",
cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) }
)
Divider()
case let .editingItem(chatItem: editingItem):
ContextItemView(
chat: chat,
@@ -685,6 +694,7 @@ struct ComposeView: View {
contextIcon: "pencil",
cancelContextItem: { clearState() }
)
Divider()
case let .forwardingItem(chatItem: forwardedItem, _):
ContextItemView(
chat: chat,
@@ -693,6 +703,7 @@ struct ComposeView: View {
cancelContextItem: { composeState = composeState.copy(contextItem: .noContextItem) },
showSender: false
)
Divider()
}
}
@@ -849,6 +860,7 @@ struct ComposeView: View {
func sendVideo(_ imageData: (String, UploadContent?), text: String = "", quoted: Int64? = nil, live: Bool = false, ttl: Int?) async -> ChatItem? {
let (image, data) = imageData
if case let .video(_, url, duration) = data, let savedFile = moveTempFileFromURL(url) {
ChatModel.shared.filesToDelete.remove(url)
return await send(.video(text: text, image: image, duration: duration), quoted: quoted, file: savedFile, live: live, ttl: ttl)
}
return nil
@@ -51,8 +51,8 @@ struct ComposeVoiceView: View {
.padding(.vertical, 1)
.frame(height: ComposeVoiceView.previewHeight)
.background(theme.appColors.sentMessage)
.frame(minHeight: 54)
.frame(maxWidth: .infinity)
.padding(.top, 8)
}
private func recordingMode() -> some View {
@@ -18,10 +18,9 @@ struct ContextInvitingContactMemberView: View {
Text("Send direct message to connect")
}
.padding(12)
.frame(minHeight: 50)
.frame(minHeight: 54)
.frame(maxWidth: .infinity, alignment: .leading)
.background(theme.appColors.sentMessage)
.padding(.top, 8)
.background(.thinMaterial)
}
}
@@ -43,10 +43,9 @@ struct ContextItemView: View {
.tint(theme.colors.primary)
}
.padding(12)
.frame(minHeight: 50)
.frame(minHeight: 54)
.frame(maxWidth: .infinity)
.background(chatItemFrameColor(contextItem, theme))
.padding(.top, 8)
}
private func msgContentView(lines: Int) -> some View {
@@ -44,6 +44,7 @@ struct SendMessageView: View {
var body: some View {
ZStack {
let composeShape = RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
HStack(alignment: .bottom) {
ZStack(alignment: .leading) {
if case .voicePreview = composeState.preview {
@@ -84,10 +85,9 @@ struct SendMessageView: View {
}
}
.padding(.vertical, 1)
.overlay(
RoundedRectangle(cornerSize: CGSize(width: 20, height: 20))
.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true)
)
.background(theme.colors.background)
.clipShape(composeShape)
.overlay(composeShape.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true))
}
.onChange(of: composeState.message, perform: { text in updateFont(text) })
.onChange(of: composeState.inProgress) { inProgress in
@@ -207,6 +207,7 @@ struct GroupChatInfoView: View {
let groupMembers = await apiListMembers(groupInfo.groupId)
await MainActor.run {
chatModel.groupMembers = groupMembers.map { GMember.init($0) }
chatModel.populateGroupMembersIndexes()
}
}
}
@@ -232,8 +233,7 @@ struct GroupChatInfoView: View {
let t = Text(member.chatViewName).foregroundColor(member.memberIncognito ? .indigo : theme.colors.onBackground)
(member.verified ? memberVerifiedShield + t : t)
.lineLimit(1)
let s = Text(member.memberStatus.shortText)
(user ? Text ("you: ") + s : s)
(user ? Text ("you: ") + Text(member.memberStatus.shortText) : Text(memberConnStatus(member)))
.lineLimit(1)
.font(.caption)
.foregroundColor(theme.colors.secondary)
@@ -266,6 +266,16 @@ struct GroupChatInfoView: View {
}
}
private func memberConnStatus(_ member: GroupMember) -> LocalizedStringKey {
if member.activeConn?.connDisabled ?? false {
return "disabled"
} else if member.activeConn?.connInactive ?? false {
return "inactive"
} else {
return member.memberStatus.shortText
}
}
@ViewBuilder private func memberInfo(_ member: GroupMember) -> some View {
if member.blocked {
Text("blocked")
@@ -76,157 +76,153 @@ struct GroupMemberInfoView: View {
private func groupMemberInfoView() -> some View {
ZStack {
VStack {
let member = groupMember.wrapped
List {
groupMemberInfoHeader(member)
.listRowBackground(Color.clear)
let member = groupMember.wrapped
List {
groupMemberInfoHeader(member)
.listRowBackground(Color.clear)
if member.memberActive {
Section {
if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) {
knownDirectChatButton(chat)
} 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 {
createMemberContactButton()
}
}
if let code = connectionCode { verifyCodeButton(code) }
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
synchronizeConnectionButton()
}
// } else if developerTools {
// synchronizeConnectionButtonForce()
// }
}
}
if let contactLink = member.contactLink {
Section {
SimpleXLinkQRCode(uri: contactLink)
Button {
showShareSheet(items: [simplexChatLink(contactLink)])
} label: {
Label("Share address", systemImage: "square.and.arrow.up")
}
if member.memberActive {
Section {
if let contactId = member.memberContactId, let chat = knownDirectChat(contactId) {
knownDirectChatButton(chat)
} else if groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
if let contactId = member.memberContactId {
if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
connectViaAddressButton(contactLink)
}
} else {
newDirectChatButton(contactId)
} else if member.activeConn?.peerChatVRange.isCompatibleRange(CREATE_MEMBER_CONTACT_VRANGE) ?? false {
createMemberContactButton()
}
}
if let code = connectionCode { verifyCodeButton(code) }
if let connStats = connectionStats,
connStats.ratchetSyncAllowed {
synchronizeConnectionButton()
}
// } else if developerTools {
// synchronizeConnectionButtonForce()
// }
}
}
if let contactLink = member.contactLink {
Section {
SimpleXLinkQRCode(uri: contactLink)
Button {
showShareSheet(items: [simplexChatLink(contactLink)])
} label: {
Label("Share address", systemImage: "square.and.arrow.up")
}
if let contactId = member.memberContactId {
if knownDirectChat(contactId) == nil && !groupInfo.fullGroupPreferences.directMessages.on(for: groupInfo.membership) {
connectViaAddressButton(contactLink)
}
} header: {
Text("Address")
.foregroundColor(theme.colors.secondary)
} footer: {
Text("You can share this address with your contacts to let them connect with **\(member.displayName)**.")
.foregroundColor(theme.colors.secondary)
}
}
Section(header: Text("Member").foregroundColor(theme.colors.secondary)) {
infoRow("Group", groupInfo.displayName)
if let roles = member.canChangeRoleTo(groupInfo: groupInfo) {
Picker("Change role", selection: $newRole) {
ForEach(roles) { role in
Text(role.text)
}
}
.frame(height: 36)
} else {
infoRow("Role", member.memberRole.text)
connectViaAddressButton(contactLink)
}
} header: {
Text("Address")
.foregroundColor(theme.colors.secondary)
} footer: {
Text("You can share this address with your contacts to let them connect with **\(member.displayName)**.")
.foregroundColor(theme.colors.secondary)
}
}
// TODO invited by - need to get contact by contact id
Section(header: Text("Member").foregroundColor(theme.colors.secondary)) {
infoRow("Group", groupInfo.displayName)
if let roles = member.canChangeRoleTo(groupInfo: groupInfo) {
Picker("Change role", selection: $newRole) {
ForEach(roles) { role in
Text(role.text)
}
}
.frame(height: 36)
} else {
infoRow("Role", member.memberRole.text)
}
}
if let connStats = connectionStats {
Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) {
// TODO network connection status
Button("Change receiving address") {
alert = .switchAddressAlert
}
.disabled(
connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil }
|| connStats.ratchetSyncSendProhibited
)
if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) {
Button("Abort changing address") {
alert = .abortSwitchAddressAlert
}
.disabled(
connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch }
|| connStats.ratchetSyncSendProhibited
)
}
smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }, theme.colors.secondary)
smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }, theme.colors.secondary)
}
}
if groupInfo.membership.memberRole >= .admin {
adminDestructiveSection(member)
} else {
nonAdminBlockSection(member)
}
if developerTools {
Section(header: Text("For console").foregroundColor(theme.colors.secondary)) {
infoRow("Local name", member.localDisplayName)
infoRow("Database ID", "\(member.groupMemberId)")
if let conn = member.activeConn {
let connLevelDesc = conn.connLevel == 0 ? NSLocalizedString("direct", comment: "connection level description") : String.localizedStringWithFormat(NSLocalizedString("indirect (%d)", comment: "connection level description"), conn.connLevel)
infoRow("Connection", connLevelDesc)
}
}
if let connStats = connectionStats {
Section(header: Text("Servers").foregroundColor(theme.colors.secondary)) {
// TODO network connection status
Button("Change receiving address") {
alert = .switchAddressAlert
}
.disabled(
connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil }
|| connStats.ratchetSyncSendProhibited
)
if connStats.rcvQueuesInfo.contains(where: { $0.rcvSwitchStatus != nil }) {
Button("Abort changing address") {
alert = .abortSwitchAddressAlert
}
.disabled(
connStats.rcvQueuesInfo.contains { $0.rcvSwitchStatus != nil && !$0.canAbortSwitch }
|| connStats.ratchetSyncSendProhibited
)
}
smpServers("Receiving via", connStats.rcvQueuesInfo.map { $0.rcvServer }, theme.colors.secondary)
smpServers("Sending via", connStats.sndQueuesInfo.map { $0.sndServer }, theme.colors.secondary)
}
}
if groupInfo.membership.memberRole >= .admin {
adminDestructiveSection(member)
} else {
nonAdminBlockSection(member)
}
if developerTools {
Section(header: Text("For console").foregroundColor(theme.colors.secondary)) {
infoRow("Local name", member.localDisplayName)
infoRow("Database ID", "\(member.groupMemberId)")
Button ("Debug delivery") {
Task {
do {
let info = queueInfoText(try await apiGroupMemberQueueInfo(groupInfo.apiId, member.groupMemberId))
await MainActor.run { alert = .queueInfo(info: info) }
} catch let e {
logger.error("apiContactQueueInfo error: \(responseError(e))")
let a = getErrorAlert(e, "Error")
await MainActor.run { alert = .error(title: a.title, error: a.message) }
}
Button ("Debug delivery") {
Task {
do {
let info = queueInfoText(try await apiGroupMemberQueueInfo(groupInfo.apiId, member.groupMemberId))
await MainActor.run { alert = .queueInfo(info: info) }
} catch let e {
logger.error("apiContactQueueInfo error: \(responseError(e))")
let a = getErrorAlert(e, "Error")
await MainActor.run { alert = .error(title: a.title, error: a.message) }
}
}
}
}
}
.navigationBarHidden(true)
.onAppear {
if #unavailable(iOS 16) {
// this condition prevents re-setting picker
if !justOpened { return }
}
justOpened = false
DispatchQueue.main.async {
newRole = member.memberRole
do {
let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
_ = chatModel.upsertGroupMember(groupInfo, mem)
connectionStats = stats
connectionCode = code
} catch let error {
logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
}
}
.navigationBarHidden(true)
.onAppear {
if #unavailable(iOS 16) {
// this condition prevents re-setting picker
if !justOpened { return }
}
justOpened = false
DispatchQueue.main.async {
newRole = member.memberRole
do {
let (_, stats) = try apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
let (mem, code) = member.memberActive ? try apiGetGroupMemberCode(groupInfo.apiId, member.groupMemberId) : (member, nil)
_ = chatModel.upsertGroupMember(groupInfo, mem)
connectionStats = stats
connectionCode = code
} catch let error {
logger.error("apiGroupMemberInfo or apiGetGroupMemberCode error: \(responseError(error))")
}
}
.onChange(of: newRole) { newRole in
if newRole != member.memberRole {
alert = .changeMemberRoleAlert(mem: member, role: newRole)
}
}
.onChange(of: member.memberRole) { role in
newRole = role
}
.onChange(of: newRole) { newRole in
if newRole != member.memberRole {
alert = .changeMemberRoleAlert(mem: member, role: newRole)
}
}
.onChange(of: member.memberRole) { role in
newRole = role
}
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.alert(item: $alert) { alertItem in
switch(alertItem) {
+2 -1
View File
@@ -129,8 +129,9 @@ struct ReverseList<Item: Identifiable & Hashable & Sendable, Content: View>: UIV
from: nil,
for: nil
)
NotificationCenter.default.post(name: .chatViewWillBeginScrolling, object: nil)
}
/// Scrolls up
func scrollToNextPage() {
tableView.setContentOffset(
@@ -100,7 +100,7 @@ struct ChatListNavLink: View {
clearChatButton()
}
Button {
if contact.ready || !contact.active {
if contact.sndReady || !contact.active {
showDeleteContactActionSheet = true
} else {
AlertManager.shared.showAlert(deletePendingContactAlert(chat, contact))
@@ -114,7 +114,7 @@ struct ChatListNavLink: View {
}
}
.actionSheet(isPresented: $showDeleteContactActionSheet) {
if contact.ready && contact.active {
if contact.sndReady && contact.active {
return ActionSheet(
title: Text("Delete contact?\nThis cannot be undone!"),
buttons: [
@@ -564,18 +564,11 @@ func joinGroup(_ groupId: Int64, _ onComplete: @escaping () async -> Void) {
}
}
struct ErrorAlert {
var title: LocalizedStringKey
var message: LocalizedStringKey
}
func getErrorAlert(_ error: Error, _ title: LocalizedStringKey) -> ErrorAlert {
switch error as? ChatResponse {
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .TIMEOUT))):
return ErrorAlert(title: "Connection timeout", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
case let .chatCmdError(_, .errorAgent(.BROKER(addr, .NETWORK))):
return ErrorAlert(title: "Connection error", message: "Please check your network connection with \(serverHostname(addr)) and try again.")
default:
if let r = error as? ChatResponse,
let alert = getNetworkErrorAlert(r) {
return alert
} else {
return ErrorAlert(title: title, message: "Error: \(responseError(error))")
}
}
@@ -21,6 +21,7 @@ struct ChatListView: View {
@State private var newChatMenuOption: NewChatMenuOption? = nil
@State private var userPickerVisible = false
@State private var showConnectDesktop = false
@AppStorage(DEFAULT_SHOW_UNREAD_AND_FAVORITES) private var showUnreadAndFavorites = false
var body: some View {
@@ -162,6 +163,10 @@ struct ChatListView: View {
chatModel.chatToTop = nil
chatModel.popChat(chatId)
}
stopAudioPlayer()
}
.onChange(of: chatModel.currentUser?.userId) { _ in
stopAudioPlayer()
}
if cs.isEmpty && !chatModel.chats.isEmpty {
Text("No filtered chats").foregroundColor(theme.colors.secondary)
@@ -217,6 +222,11 @@ struct ChatListView: View {
}
}
func stopAudioPlayer() {
VoiceItemState.smallView.values.forEach { $0.audioPlayer?.stop() }
VoiceItemState.smallView = [:]
}
private func filteredChats() -> [Chat] {
if let linkChatId = searchChatFilteredBySimplexLink {
return chatModel.chats.filter { $0.id == linkChatId }
@@ -267,31 +277,25 @@ struct ChatListView: View {
struct SubsStatusIndicator: View {
@State private var subs: SMPServerSubs = SMPServerSubs.newSMPServerSubs
@State private var sess: ServerSessions = ServerSessions.newServerSessions
@State private var hasSess: Bool = false
@State private var timer: Timer? = nil
@State private var timerCounter = 0
@State private var showServersSummary = false
@AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false
// Constants for the intervals
let initialInterval: TimeInterval = 1.0
let regularInterval: TimeInterval = 3.0
let initialPhaseDuration: TimeInterval = 10.0 // Duration for initial phase in seconds
var body: some View {
Button {
showServersSummary = true
} label: {
HStack(spacing: 4) {
SubscriptionStatusIndicatorView(subs: subs, sess: sess)
SubscriptionStatusIndicatorView(subs: subs, hasSess: hasSess)
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: subs, sess: sess)
SubscriptionStatusPercentageView(subs: subs, hasSess: hasSess)
}
}
}
.onAppear {
startInitialTimer()
startTimer()
}
.onDisappear {
stopTimer()
@@ -301,35 +305,24 @@ struct SubsStatusIndicator: View {
}
}
private func startInitialTimer() {
timer = Timer.scheduledTimer(withTimeInterval: initialInterval, repeats: true) { _ in
getServersSummary()
timerCounter += 1
// Switch to the regular timer after the initial phase
if timerCounter * Int(initialInterval) >= Int(initialPhaseDuration) {
switchToRegularTimer()
private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
if AppChatState.shared.value == .active {
getSubsTotal()
}
}
}
func switchToRegularTimer() {
timer?.invalidate()
timer = Timer.scheduledTimer(withTimeInterval: regularInterval, repeats: true) { _ in
getServersSummary()
}
}
func stopTimer() {
timer?.invalidate()
timer = nil
}
private func getServersSummary() {
private func getSubsTotal() {
do {
let summ = try getAgentServersSummary()
(subs, sess) = (summ.allUsersSMP.smpTotals.subs, summ.allUsersSMP.smpTotals.sessions)
(subs, hasSess) = try getAgentSubsTotal()
} catch let error {
logger.error("getAgentServersSummary error: \(responseError(error))")
logger.error("getSubsTotal error: \(responseError(error))")
}
}
}
@@ -16,6 +16,8 @@ struct ChatPreviewView: View {
@Binding var progressByTimeout: Bool
@State var deleting: Bool = false
var darkGreen = Color(red: 0, green: 0.5, blue: 0)
@State private var activeContentPreview: ActiveContentPreview? = nil
@State private var showFullscreenGallery: Bool = false
@AppStorage(DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) private var showChatPreviews = true
@@ -43,11 +45,38 @@ struct ChatPreviewView: View {
.padding(.horizontal, 8)
ZStack(alignment: .topTrailing) {
chatMessagePreview(cItem)
let chat = activeContentPreview?.chat ?? chat
let ci = activeContentPreview?.ci ?? chat.chatItems.last
let mc = ci?.content.msgContent
HStack(alignment: .top) {
let deleted = ci?.isDeletedContent == true || ci?.meta.itemDeleted != nil
let showContentPreview = (showChatPreviews && chatModel.draftChatId != chat.id && !deleted) || activeContentPreview != nil
if let ci, showContentPreview {
chatItemContentPreview(chat, ci)
}
let mcIsVoice = switch mc { case .voice: true; default: false }
if !mcIsVoice || !showContentPreview || mc?.text != "" || chatModel.draftChatId == chat.id {
let hasFilePreview = if case .file = mc { true } else { false }
chatMessagePreview(cItem, hasFilePreview)
} else {
Spacer()
chatInfoIcon(chat).frame(minWidth: 37, alignment: .trailing)
}
}
.onChange(of: chatModel.stopPreviousRecPlay?.path) { _ in
checkActiveContentPreview(chat, ci, mc)
}
.onChange(of: activeContentPreview) { _ in
checkActiveContentPreview(chat, ci, mc)
}
.onChange(of: showFullscreenGallery) { _ in
checkActiveContentPreview(chat, ci, mc)
}
chatStatusImage()
.padding(.top, 26)
.frame(maxWidth: .infinity, alignment: .trailing)
}
.frame(maxWidth: .infinity, alignment: .leading)
.padding(.trailing, 8)
Spacer()
@@ -57,6 +86,33 @@ struct ChatPreviewView: View {
.padding(.bottom, -8)
.onChange(of: chatModel.deletedChats.contains(chat.chatInfo.id)) { contains in
deleting = contains
// Stop voice when deleting the chat
if contains, let ci = activeContentPreview?.ci {
VoiceItemState.stopVoiceInSmallView(chat.chatInfo, ci)
}
}
func checkActiveContentPreview(_ chat: Chat, _ ci: ChatItem?, _ mc: MsgContent?) {
let playing = chatModel.stopPreviousRecPlay
if case .voice = activeContentPreview?.mc, playing == nil {
activeContentPreview = nil
} else if activeContentPreview == nil {
if case .image = mc, let ci, let mc, showFullscreenGallery {
activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc)
}
if case .video = mc, let ci, let mc, showFullscreenGallery {
activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc)
}
if case .voice = mc, let ci, let mc, let fileSource = ci.file?.fileSource, playing?.path.hasSuffix(fileSource.filePath) == true {
activeContentPreview = ActiveContentPreview(chat: chat, ci: ci, mc: mc)
}
} else if case .voice = activeContentPreview?.mc {
if let playing, let fileSource = ci?.file?.fileSource, !playing.path.hasSuffix(fileSource.filePath) {
activeContentPreview = nil
}
} else if !showFullscreenGallery {
activeContentPreview = nil
}
}
}
@@ -113,39 +169,47 @@ struct ChatPreviewView: View {
.kerning(-2)
}
private func chatPreviewLayout(_ text: Text, draft: Bool = false) -> some View {
private func chatPreviewLayout(_ text: Text?, draft: Bool = false, _ hasFilePreview: Bool = false) -> some View {
ZStack(alignment: .topTrailing) {
let t = text
.lineLimit(2)
.multilineTextAlignment(.leading)
.frame(maxWidth: .infinity, alignment: .topLeading)
.padding(.leading, 8)
.padding(.trailing, 36)
.padding(.leading, hasFilePreview ? 0 : 8)
.padding(.trailing, hasFilePreview ? 38 : 36)
.offset(x: hasFilePreview ? -2 : 0)
.fixedSize(horizontal: false, vertical: true)
if !showChatPreviews && !draft {
t.privacySensitive(true).redacted(reason: .privacy)
} else {
t
}
let s = chat.chatStats
if s.unreadCount > 0 || s.unreadChat {
unreadCountText(s.unreadCount)
.font(.caption)
.foregroundColor(.white)
.padding(.horizontal, 4)
.frame(minWidth: 18, minHeight: 18)
.background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? theme.colors.primary : theme.colors.secondary)
.cornerRadius(10)
} else if !chat.chatInfo.ntfsEnabled && chat.chatInfo.chatType != .local {
Image(systemName: "speaker.slash.fill")
.foregroundColor(theme.colors.secondary)
} else if chat.chatInfo.chatSettings?.favorite ?? false {
Image(systemName: "star.fill")
.resizable()
.scaledToFill()
.frame(width: 18, height: 18)
.padding(.trailing, 1)
.foregroundColor(.secondary.opacity(0.65))
}
chatInfoIcon(chat).frame(minWidth: 37, alignment: .trailing)
}
}
@ViewBuilder private func chatInfoIcon(_ chat: Chat) -> some View {
let s = chat.chatStats
if s.unreadCount > 0 || s.unreadChat {
unreadCountText(s.unreadCount)
.font(.caption)
.foregroundColor(.white)
.padding(.horizontal, 4)
.frame(minWidth: 18, minHeight: 18)
.background(chat.chatInfo.ntfsEnabled || chat.chatInfo.chatType == .local ? theme.colors.primary : theme.colors.secondary)
.cornerRadius(10)
} else if !chat.chatInfo.ntfsEnabled && chat.chatInfo.chatType != .local {
Image(systemName: "speaker.slash.fill")
.foregroundColor(theme.colors.secondary)
} else if chat.chatInfo.chatSettings?.favorite ?? false {
Image(systemName: "star.fill")
.resizable()
.scaledToFill()
.frame(width: 18, height: 18)
.padding(.trailing, 1)
.foregroundColor(.secondary.opacity(0.65))
} else {
Color.clear.frame(width: 0)
}
}
@@ -172,7 +236,7 @@ struct ChatPreviewView: View {
func chatItemPreview(_ cItem: ChatItem) -> Text {
let itemText = cItem.meta.itemDeleted == nil ? cItem.text : markedDeletedText()
let itemFormattedText = cItem.meta.itemDeleted == nil ? cItem.formattedText : nil
return messageText(itemText, itemFormattedText, cItem.memberDisplayName, icon: attachment(), preview: true, showSecrets: false, secondaryColor: theme.colors.secondary)
return messageText(itemText, itemFormattedText, cItem.memberDisplayName, icon: nil, preview: true, showSecrets: false, secondaryColor: theme.colors.secondary)
// same texts are in markedDeletedText in MarkedDeletedItemView, but it returns LocalizedStringKey;
// can be refactored into a single function if functions calling these are changed to return same type
@@ -196,18 +260,18 @@ struct ChatPreviewView: View {
}
}
@ViewBuilder private func chatMessagePreview(_ cItem: ChatItem?) -> some View {
@ViewBuilder private func chatMessagePreview(_ cItem: ChatItem?, _ hasFilePreview: Bool = false) -> some View {
if chatModel.draftChatId == chat.id, let draft = chatModel.draft {
chatPreviewLayout(messageDraft(draft), draft: true)
chatPreviewLayout(messageDraft(draft), draft: true, hasFilePreview)
} else if let cItem = cItem {
chatPreviewLayout(itemStatusMark(cItem) + chatItemPreview(cItem))
chatPreviewLayout(itemStatusMark(cItem) + chatItemPreview(cItem), hasFilePreview)
} else {
switch (chat.chatInfo) {
case let .direct(contact):
if contact.activeConn == nil && contact.profile.contactLink != nil {
chatPreviewInfoText("Tap to Connect")
.foregroundColor(theme.colors.primary)
} else if !contact.ready && contact.activeConn != nil {
} else if !contact.sndReady && contact.activeConn != nil {
if contact.nextSendGrpInv {
chatPreviewInfoText("send direct message")
} else if contact.active {
@@ -225,6 +289,54 @@ struct ChatPreviewView: View {
}
}
@ViewBuilder func chatItemContentPreview(_ chat: Chat, _ ci: ChatItem) -> some View {
let mc = ci.content.msgContent
switch mc {
case let .link(_, preview):
smallContentPreview(
ZStack(alignment: .topTrailing) {
Image(uiImage: UIImage(base64Encoded: preview.image) ?? UIImage(systemName: "arrow.up.right")!)
.resizable()
.aspectRatio(contentMode: .fill)
.frame(width: 36, height: 36)
ZStack {
Image(systemName: "arrow.up.right")
.resizable()
.foregroundColor(Color.white)
.font(.system(size: 15, weight: .black))
.frame(width: 8, height: 8)
}
.frame(width: 16, height: 16)
.background(Color.black.opacity(0.25))
.cornerRadius(8)
}
.onTapGesture {
UIApplication.shared.open(preview.uri)
}
)
case let .image(_, image):
smallContentPreview(
CIImageView(chatItem: ci, preview: UIImage(base64Encoded: image), maxWidth: 36, smallView: true, showFullScreenImage: $showFullscreenGallery)
.environmentObject(ReverseListScrollModel<ChatItem>())
)
case let .video(_,image, duration):
smallContentPreview(
CIVideoView(chatItem: ci, preview: UIImage(base64Encoded: image), duration: duration, maxWidth: 36, videoWidth: nil, smallView: true, showFullscreenPlayer: $showFullscreenGallery)
.environmentObject(ReverseListScrollModel<ChatItem>())
)
case let .voice(_, duration):
smallContentPreviewVoice(
CIVoiceView(chat: chat, chatItem: ci, recordingFile: ci.file, duration: duration, allowMenu: Binding.constant(true), smallView: true)
)
case .file:
smallContentPreviewFile(
CIFileView(file: ci.file, edited: ci.meta.itemEdited, smallView: true)
)
default: EmptyView()
}
}
@ViewBuilder private func groupInvitationPreviewText(_ groupInfo: GroupInfo) -> some View {
groupInfo.membership.memberIncognito
? chatPreviewInfoText("join as \(groupInfo.membership.memberProfile.displayName)")
@@ -294,10 +406,50 @@ struct ChatPreviewView: View {
}
}
func smallContentPreview(_ view: some View) -> some View {
ZStack {
view
.frame(width: 36, height: 36)
}
.cornerRadius(8)
.overlay(RoundedRectangle(cornerSize: CGSize(width: 8, height: 8))
.strokeBorder(.secondary, lineWidth: 0.3, antialiased: true))
.padding([.top, .leading], 3)
.offset(x: 6)
}
func smallContentPreviewVoice(_ view: some View) -> some View {
ZStack {
view
.frame(height: voiceMessageSizeBasedOnSquareSize(36))
}
.padding(.leading, 8)
.padding(.top, 6)
}
func smallContentPreviewFile(_ view: some View) -> some View {
ZStack {
view
.frame(width: 36, height: 36)
}
.padding(.top, 2)
.padding(.leading, 5)
}
func unreadCountText(_ n: Int) -> Text {
Text(n > 999 ? "\(n / 1000)k" : n > 0 ? "\(n)" : "")
}
private struct ActiveContentPreview: Equatable {
var chat: Chat
var ci: ChatItem
var mc: MsgContent
static func == (lhs: ActiveContentPreview, rhs: ActiveContentPreview) -> Bool {
lhs.chat.id == rhs.chat.id && lhs.ci.id == rhs.ci.id && lhs.mc == rhs.mc
}
}
struct ChatPreviewView_Previews: PreviewProvider {
static var previews: some View {
Group {
@@ -11,6 +11,7 @@ import SimpleXChat
struct ServersSummaryView: View {
@EnvironmentObject var m: ChatModel
@EnvironmentObject var theme: AppTheme
@State private var serversSummary: PresentedServersSummary? = nil
@State private var selectedUserCategory: PresentedUserCategory = .allUsers
@State private var selectedServerType: PresentedServerType = .smp
@@ -58,11 +59,21 @@ struct ServersSummaryView: View {
private func startTimer() {
timer = Timer.scheduledTimer(withTimeInterval: 1.0, repeats: true) { _ in
getServersSummary()
if AppChatState.shared.value == .active {
getServersSummary()
}
}
}
func stopTimer() {
private func getServersSummary() {
do {
serversSummary = try getAgentServersSummary()
} catch let error {
logger.error("getAgentServersSummary error: \(responseError(error))")
}
}
private func stopTimer() {
timer?.invalidate()
timer = nil
}
@@ -91,8 +102,8 @@ struct ServersSummaryView: View {
Group {
if m.users.filter({ u in u.user.activeUser || !u.user.hidden }).count > 1 {
Picker("User selection", selection: $selectedUserCategory) {
Text("All users").tag(PresentedUserCategory.allUsers)
Text("Current user").tag(PresentedUserCategory.currentUser)
Text("All profiles").tag(PresentedUserCategory.allUsers)
Text("Current profile").tag(PresentedUserCategory.currentUser)
}
.pickerStyle(.segmented)
}
@@ -183,19 +194,22 @@ struct ServersSummaryView: View {
}
} else {
Text("No info, try to reload")
.foregroundColor(theme.colors.secondary)
.background(theme.colors.background)
}
}
private func smpSubsSection(_ totals: SMPTotals) -> some View {
Section {
infoRow("Connections subscribed", numOrDash(totals.subs.ssActive))
infoRow("Active connections", numOrDash(totals.subs.ssActive))
infoRow("Total", numOrDash(totals.subs.total))
Toggle("Show percentage", isOn: $showSubscriptionPercentage)
} header: {
HStack {
Text("Message subscriptions")
SubscriptionStatusIndicatorView(subs: totals.subs, sess: totals.sessions)
Text("Message reception")
SubscriptionStatusIndicatorView(subs: totals.subs, hasSess: totals.sessions.hasSess)
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: totals.subs, sess: totals.sessions)
SubscriptionStatusPercentageView(subs: totals.subs, hasSess: totals.sessions.hasSess)
}
}
}
@@ -273,9 +287,9 @@ struct ServersSummaryView: View {
if let subs = srvSumm.subs {
Spacer()
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: subs, sess: srvSumm.sessionsOrNew)
SubscriptionStatusPercentageView(subs: subs, hasSess: srvSumm.sessionsOrNew.hasSess)
}
SubscriptionStatusIndicatorView(subs: subs, sess: srvSumm.sessionsOrNew)
SubscriptionStatusIndicatorView(subs: subs, hasSess: srvSumm.sessionsOrNew.hasSess)
} else if let sess = srvSumm.sessions {
Spacer()
Image(systemName: "arrow.up.circle")
@@ -389,24 +403,16 @@ struct ServersSummaryView: View {
Text("Reset all statistics")
}
}
private func getServersSummary() {
do {
serversSummary = try getAgentServersSummary()
} catch let error {
logger.error("getAgentServersSummary error: \(responseError(error))")
}
}
}
struct SubscriptionStatusIndicatorView: View {
@EnvironmentObject var m: ChatModel
var subs: SMPServerSubs
var sess: ServerSessions
var hasSess: Bool
var body: some View {
let onionHosts = networkUseOnionHostsGroupDefault.get()
let (color, variableValue, opacity, _) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, sess)
let (color, variableValue, opacity, _) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, hasSess)
if #available(iOS 16.0, *) {
Image(systemName: "dot.radiowaves.up.forward", variableValue: variableValue)
.foregroundColor(color)
@@ -420,18 +426,18 @@ struct SubscriptionStatusIndicatorView: View {
struct SubscriptionStatusPercentageView: View {
@EnvironmentObject var m: ChatModel
var subs: SMPServerSubs
var sess: ServerSessions
var hasSess: Bool
var body: some View {
let onionHosts = networkUseOnionHostsGroupDefault.get()
let (_, _, _, statusPercent) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, sess)
let (_, _, _, statusPercent) = subscriptionStatusColorAndPercentage(m.networkInfo.online, onionHosts, subs, hasSess)
Text(verbatim: "\(Int(floor(statusPercent * 100)))%")
.foregroundColor(.secondary)
.font(.caption)
}
}
func subscriptionStatusColorAndPercentage(_ online: Bool, _ onionHosts: OnionHosts, _ subs: SMPServerSubs, _ sess: ServerSessions) -> (Color, Double, Double, Double) {
func subscriptionStatusColorAndPercentage(_ online: Bool, _ onionHosts: OnionHosts, _ subs: SMPServerSubs, _ hasSess: Bool) -> (Color, Double, Double, Double) {
func roundedToQuarter(_ n: Double) -> Double {
n >= 1 ? 1
: n <= 0 ? 0
@@ -446,12 +452,12 @@ func subscriptionStatusColorAndPercentage(_ online: Bool, _ onionHosts: OnionHos
? (
subs.ssActive == 0
? (
sess.ssConnected == 0 ? noConnColorAndPercent : (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
hasSess ? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) : noConnColorAndPercent
)
: ( // ssActive > 0
sess.ssConnected == 0
? (.orange, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) // This would mean implementation error
: (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
hasSess
? (activeColor, activeSubsRounded, subs.shareOfActive, subs.shareOfActive)
: (.orange, activeSubsRounded, subs.shareOfActive, subs.shareOfActive) // This would mean implementation error
)
)
: noConnColorAndPercent
@@ -497,16 +503,16 @@ struct SMPServerSummaryView: View {
private func smpSubsSection(_ subs: SMPServerSubs) -> some View {
Section {
infoRow("Connections subscribed", numOrDash(subs.ssActive))
infoRow("Active connections", numOrDash(subs.ssActive))
infoRow("Pending", numOrDash(subs.ssPending))
infoRow("Total", numOrDash(subs.total))
reconnectButton()
} header: {
HStack {
Text("Message subscriptions")
SubscriptionStatusIndicatorView(subs: subs, sess: summary.sessionsOrNew)
Text("Message reception")
SubscriptionStatusIndicatorView(subs: subs, hasSess: summary.sessionsOrNew.hasSess)
if showSubscriptionPercentage {
SubscriptionStatusPercentageView(subs: subs, sess: summary.sessionsOrNew)
SubscriptionStatusPercentageView(subs: subs, hasSess: summary.sessionsOrNew.hasSess)
}
}
}
@@ -611,7 +617,7 @@ struct DetailedSMPStatsView: View {
infoRow(Text(verbatim: "NO_MSG errors"), numOrDash(stats._ackNoMsgErrs)).padding(.leading, 24)
infoRow("other errors", numOrDash(stats._ackOtherErrs)).padding(.leading, 24)
}
Section {
Section("Connections") {
infoRow("Created", numOrDash(stats._connCreated))
infoRow("Secured", numOrDash(stats._connCreated))
infoRow("Completed", numOrDash(stats._connCompleted))
@@ -620,8 +626,12 @@ struct DetailedSMPStatsView: View {
infoRowTwoValues("Subscribed", "attempts", stats._connSubscribed, stats._connSubAttempts)
infoRow("Subscriptions ignored", numOrDash(stats._connSubIgnored))
infoRow("Subscription errors", numOrDash(stats._connSubErrs))
}
Section {
infoRowTwoValues("Enabled", "attempts", stats._ntfKey, stats._ntfKeyAttempts)
infoRowTwoValues("Disabled", "attempts", stats._ntfKeyDeleted, stats._ntfKeyDeleteAttempts)
} header: {
Text("Connections")
Text("Connection notifications")
} footer: {
Text("Starting from \(localTimestamp(statsStartedAt)).")
}
@@ -23,8 +23,10 @@ struct ChatViewBackground: ViewModifier {
var image = context.resolve(image)
let rect = CGRectMake(0, 0, size.width, size.height)
func repeatDraw(_ imageScale: CGFloat) {
// Prevent range bounds crash and dividing by zero
if size.height == 0 || size.width == 0 || image.size.height == 0 || image.size.width == 0 { return }
image.shading = .color(tint)
let scale = imageScale * 1.57 // for some reason a wallpaper on iOS looks smaller than on Android
let scale = imageScale * 2.5 // scale wallpaper for iOS
for h in 0 ... Int(size.height / image.size.height / scale) {
for w in 0 ... Int(size.width / image.size.width / scale) {
let rect = CGRectMake(CGFloat(w) * image.size.width * scale, CGFloat(h) * image.size.height * scale, image.size.width * scale, image.size.height * scale)
@@ -21,9 +21,7 @@ struct ProfileImage: View {
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
var body: some View {
if let image = imageStr,
let data = Data(base64Encoded: dropImagePrefix(image)),
let uiImage = UIImage(data: data) {
if let uiImage = UIImage(base64Encoded: imageStr) {
clipProfileImage(Image(uiImage: uiImage), size: size, radius: radius)
} else {
let c = color.asAnotherColorFromSecondaryVariant(theme)
@@ -17,3 +17,37 @@ extension View {
}
}
}
extension Notification.Name {
static let chatViewWillBeginScrolling = Notification.Name("chatWillBeginScrolling")
}
struct PrivacyBlur: ViewModifier {
var enabled: Bool = true
@Binding var blurred: Bool
@AppStorage(DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) private var blurRadius: Int = 0
func body(content: Content) -> some View {
if blurRadius > 0 {
// parallel ifs are necessary here because otherwise some views flicker,
// e.g. when playing video
content
.blur(radius: blurred && enabled ? CGFloat(blurRadius) * 0.5 : 0)
.overlay {
if (blurred && enabled) {
Color.clear.contentShape(Rectangle())
.onTapGesture {
blurred = false
}
}
}
.onReceive(NotificationCenter.default.publisher(for: .chatViewWillBeginScrolling)) { _ in
if !blurred {
blurred = true
}
}
} else {
content
}
}
}
@@ -662,7 +662,7 @@ private struct PassphraseConfirmationView: View {
if case .chatCmdError(_, .errorDatabase(.errorOpen(.errorNotADatabase))) = error as? ChatResponse {
showErrorOnMigrationIfNeeded(.errorNotADatabase(dbFile: ""), $alert)
} else {
alert = .error(title: "Error", error: NSLocalizedString("Error verifying passphrase:", comment: "") + " " + String(String(describing: error)))
alert = .error(title: "Error", error: NSLocalizedString("Error verifying passphrase:", comment: "") + " " + String(responseError(error)))
}
}
}
@@ -194,6 +194,7 @@ struct AddGroupView: View {
let groupMembers = await apiListMembers(gInfo.groupId)
await MainActor.run {
m.groupMembers = groupMembers.map { GMember.init($0) }
m.populateGroupMembersIndexes()
}
}
let c = Chat(chatInfo: .group(groupInfo: gInfo), chatItems: [])
@@ -32,6 +32,7 @@ extension AppSettings {
if let val = privacyShowChatPreviews { def.setValue(val, forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS) }
if let val = privacySaveLastDraft { def.setValue(val, forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT) }
if let val = privacyProtectScreen { def.setValue(val, forKey: DEFAULT_PRIVACY_PROTECT_SCREEN) }
if let val = privacyMediaBlurRadius { def.setValue(val, forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) }
if let val = notificationMode { ChatModel.shared.notificationMode = val.toNotificationsMode() }
if let val = notificationPreviewMode { ntfPreviewModeGroupDefault.set(val) }
if let val = webrtcPolicyRelay { def.setValue(val, forKey: DEFAULT_WEBRTC_POLICY_RELAY) }
@@ -62,6 +63,7 @@ extension AppSettings {
c.privacyShowChatPreviews = def.bool(forKey: DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS)
c.privacySaveLastDraft = def.bool(forKey: DEFAULT_PRIVACY_SAVE_LAST_DRAFT)
c.privacyProtectScreen = def.bool(forKey: DEFAULT_PRIVACY_PROTECT_SCREEN)
c.privacyMediaBlurRadius = def.integer(forKey: DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS)
c.notificationMode = AppSettingsNotificationMode.from(ChatModel.shared.notificationMode)
c.notificationPreviewMode = ntfPreviewModeGroupDefault.get()
c.webrtcPolicyRelay = def.bool(forKey: DEFAULT_WEBRTC_POLICY_RELAY)
@@ -32,7 +32,6 @@ struct NetworkAndServers: View {
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@AppStorage(DEFAULT_SHOW_SENT_VIA_RPOXY) private var showSentViaProxy = false
@AppStorage(DEFAULT_SHOW_SUBSCRIPTION_PERCENTAGE) private var showSubscriptionPercentage = false
@State private var cfgLoaded = false
@State private var currentNetCfg = NetCfg.defaults
@State private var netCfg = NetCfg.defaults
@@ -62,8 +61,6 @@ struct NetworkAndServers: View {
Text("XFTP servers")
}
Toggle("Subscription percentage", isOn: $showSubscriptionPercentage)
Picker("Use .onion hosts", selection: $onionHosts) {
ForEach(OnionHosts.values, id: \.self) { Text($0.text) }
}
@@ -22,6 +22,7 @@ struct PrivacySettings: View {
@AppStorage(DEFAULT_PRIVACY_PROTECT_SCREEN) private var protectScreen = false
@AppStorage(DEFAULT_PERFORM_LA) private var prefPerformLA = false
@State private var currentLAMode = privacyLocalAuthModeDefault.get()
@AppStorage(DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS) private var privacyMediaBlurRadius: Int = 0
@State private var contactReceipts = false
@State private var contactReceiptsReset = false
@State private var contactReceiptsOverrides = 0
@@ -113,6 +114,22 @@ struct PrivacySettings: View {
privacyAcceptImagesGroupDefault.set($0)
}
}
settingsRow("circle.rectangle.filled.pattern.diagonalline", color: theme.colors.secondary) {
Picker("Blur media", selection: $privacyMediaBlurRadius) {
let values = [0, 12, 24, 48] + ([0, 12, 24, 48].contains(privacyMediaBlurRadius) ? [] : [privacyMediaBlurRadius])
ForEach(values, id: \.self) { radius in
let text: String = switch radius {
case 0: NSLocalizedString("Off", comment: "blur media")
case 12: NSLocalizedString("Soft", comment: "blur media")
case 24: NSLocalizedString("Medium", comment: "blur media")
case 48: NSLocalizedString("Strong", comment: "blur media")
default: "\(radius)"
}
Text(text)
}
}
}
.frame(height: 36)
settingsRow("network.badge.shield.half.filled", color: theme.colors.secondary) {
Toggle("Protect IP address", isOn: $askToApproveRelays)
}
@@ -15,7 +15,6 @@ struct ProtocolServerView: View {
let serverProtocol: ServerProtocol
@Binding var server: ServerCfg
@State var serverToEdit: ServerCfg
@State var serverEnabled: Bool
@State private var showTestFailure = false
@State private var testing = false
@State private var testFailure: ProtocolTestFailure?
@@ -113,10 +112,10 @@ struct ProtocolServerView: View {
Spacer()
showTestStatus(server: serverToEdit)
}
Toggle("Use for new connections", isOn: $serverEnabled)
.onChange(of: serverEnabled) { enabled in
serverToEdit.enabled = enabled ? .enabled : .disabled
}
let useForNewDisabled = serverToEdit.tested != true && !serverToEdit.preset
Toggle("Use for new connections", isOn: $serverToEdit.enabled)
.disabled(useForNewDisabled)
.foregroundColor(useForNewDisabled ? theme.colors.secondary : theme.colors.onBackground)
}
}
}
@@ -185,8 +184,7 @@ struct ProtocolServerView_Previews: PreviewProvider {
ProtocolServerView(
serverProtocol: .smp,
server: Binding.constant(ServerCfg.sampleData.custom),
serverToEdit: ServerCfg.sampleData.custom,
serverEnabled: true
serverToEdit: ServerCfg.sampleData.custom
)
}
}
@@ -18,8 +18,9 @@ struct ProtocolServersView: View {
@Environment(\.editMode) private var editMode
let serverProtocol: ServerProtocol
@State private var currServers: [ServerCfg] = []
@State private var presetServers: [String] = []
@State private var servers: [ServerCfg] = []
@State private var presetServers: [ServerCfg] = []
@State private var configuredServers: [ServerCfg] = []
@State private var otherServers: [ServerCfg] = []
@State private var selectedServer: String? = nil
@State private var showAddServer = false
@State private var showScanProtoServer = false
@@ -53,31 +54,53 @@ struct ProtocolServersView: View {
private func protocolServersView() -> some View {
List {
Section {
ForEach($servers) { srv in
protocolServerView(srv)
if !configuredServers.isEmpty {
Section {
ForEach($configuredServers) { srv in
protocolServerView(srv)
}
.onMove { indexSet, offset in
configuredServers.move(fromOffsets: indexSet, toOffset: offset)
}
.onDelete { indexSet in
configuredServers.remove(atOffsets: indexSet)
}
} header: {
Text("Configured \(proto) servers")
.foregroundColor(theme.colors.secondary)
} footer: {
Text("The servers for new connections of your current chat profile **\(m.currentUser?.displayName ?? "")**.")
.foregroundColor(theme.colors.secondary)
.lineLimit(10)
}
.onMove { indexSet, offset in
servers.move(fromOffsets: indexSet, toOffset: offset)
}
if !otherServers.isEmpty {
Section {
ForEach($otherServers) { srv in
protocolServerView(srv)
}
.onMove { indexSet, offset in
otherServers.move(fromOffsets: indexSet, toOffset: offset)
}
.onDelete { indexSet in
otherServers.remove(atOffsets: indexSet)
}
} header: {
Text("Other \(proto) servers")
.foregroundColor(theme.colors.secondary)
}
.onDelete { indexSet in
servers.remove(atOffsets: indexSet)
}
Button("Add server…") {
showAddServer = true
}
} header: {
Text("\(proto) servers")
.foregroundColor(theme.colors.secondary)
} footer: {
Text("The servers for new connections of your current chat profile **\(m.currentUser?.displayName ?? "")**.")
.foregroundColor(theme.colors.secondary)
.lineLimit(10)
}
Section {
Button("Reset") { servers = currServers }
.disabled(servers == currServers || testing)
Button("Add server") {
showAddServer = true
}
}
Section {
Button("Reset") { partitionServers(currServers) }
.disabled(Set(allServers) == Set(currServers) || testing)
Button("Test servers", action: testServers)
.disabled(testing || allServersDisabled)
Button("Save servers", action: saveServers)
@@ -86,17 +109,17 @@ struct ProtocolServersView: View {
}
}
.toolbar { EditButton() }
.confirmationDialog("Add server", isPresented: $showAddServer, titleVisibility: .hidden) {
.confirmationDialog("Add server", isPresented: $showAddServer, titleVisibility: .hidden) {
Button("Enter server manually") {
servers.append(ServerCfg.empty)
selectedServer = servers.last?.id
otherServers.append(ServerCfg.empty)
selectedServer = allServers.last?.id
}
Button("Scan server QR code") { showScanProtoServer = true }
Button("Add preset servers", action: addAllPresets)
.disabled(hasAllPresets())
}
.sheet(isPresented: $showScanProtoServer) {
ScanProtocolServer(servers: $servers)
ScanProtocolServer(servers: $otherServers)
.modifier(ThemedBackground(grouped: true))
}
.modifier(BackButton(disabled: Binding.constant(false)) {
@@ -133,27 +156,39 @@ struct ProtocolServersView: View {
}
.onAppear {
// this condition is needed to prevent re-setting the servers when exiting single server view
if !justOpened { return }
do {
let r = try getUserProtoServers(serverProtocol)
currServers = r.protoServers
presetServers = r.presetServers
servers = currServers
} catch let error {
alert = .error(
title: "Error loading \(proto) servers",
error: "Error: \(responseError(error))"
)
if justOpened {
do {
let r = try getUserProtoServers(serverProtocol)
currServers = r.protoServers
presetServers = r.presetServers
partitionServers(currServers)
} catch let error {
alert = .error(
title: "Error loading \(proto) servers",
error: "Error: \(responseError(error))"
)
}
justOpened = false
} else {
partitionServers(allServers)
}
justOpened = false
}
}
private func partitionServers(_ servers: [ServerCfg]) {
configuredServers = servers.filter { $0.preset || $0.enabled }
otherServers = servers.filter { !($0.preset || $0.enabled) }
}
private var allServers: [ServerCfg] {
configuredServers + otherServers
}
private var saveDisabled: Bool {
servers.isEmpty ||
servers == currServers ||
allServers.isEmpty ||
Set(allServers) == Set(currServers) ||
testing ||
!servers.allSatisfy { srv in
!allServers.allSatisfy { srv in
if let address = parseServerAddress(srv.server) {
return uniqueAddress(srv, address)
}
@@ -163,7 +198,7 @@ struct ProtocolServersView: View {
}
private var allServersDisabled: Bool {
servers.allSatisfy { $0.enabled != .enabled }
allServers.allSatisfy { !$0.enabled }
}
private func protocolServerView(_ server: Binding<ServerCfg>) -> some View {
@@ -172,8 +207,7 @@ struct ProtocolServersView: View {
ProtocolServerView(
serverProtocol: serverProtocol,
server: server,
serverToEdit: srv,
serverEnabled: srv.enabled == .enabled
serverToEdit: srv
)
.navigationBarTitle(srv.preset ? "Preset server" : "Your server")
.modifier(ThemedBackground(grouped: true))
@@ -187,7 +221,7 @@ struct ProtocolServersView: View {
invalidServer()
} else if !uniqueAddress(srv, address) {
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
} else if srv.enabled != .enabled {
} else if !srv.enabled {
Image(systemName: "slash.circle").foregroundColor(theme.colors.secondary)
} else {
showTestStatus(server: srv)
@@ -200,7 +234,7 @@ struct ProtocolServersView: View {
.padding(.trailing, 4)
let v = Text(address?.hostnames.first ?? srv.server).lineLimit(1)
if srv.enabled == .enabled {
if srv.enabled {
v
} else {
v.foregroundColor(theme.colors.secondary)
@@ -227,7 +261,7 @@ struct ProtocolServersView: View {
}
private func uniqueAddress(_ s: ServerCfg, _ address: ServerAddress) -> Bool {
servers.allSatisfy { srv in
allServers.allSatisfy { srv in
address.hostnames.allSatisfy { host in
srv.id == s.id || !srv.server.contains(host)
}
@@ -241,13 +275,13 @@ struct ProtocolServersView: View {
private func addAllPresets() {
for srv in presetServers {
if !hasPreset(srv) {
servers.append(ServerCfg(server: srv, preset: true, tested: nil, enabled: .enabled))
configuredServers.append(srv)
}
}
}
private func hasPreset(_ srv: String) -> Bool {
servers.contains(where: { $0.server == srv })
private func hasPreset(_ srv: ServerCfg) -> Bool {
allServers.contains(where: { $0.server == srv.server })
}
private func testServers() {
@@ -265,19 +299,31 @@ struct ProtocolServersView: View {
}
private func resetTestStatus() {
for i in 0..<servers.count {
if servers[i].enabled == .enabled {
servers[i].tested = nil
for i in 0..<configuredServers.count {
if configuredServers[i].enabled {
configuredServers[i].tested = nil
}
}
for i in 0..<otherServers.count {
if otherServers[i].enabled {
otherServers[i].tested = nil
}
}
}
private func runServersTest() async -> [String: ProtocolTestFailure] {
var fs: [String: ProtocolTestFailure] = [:]
for i in 0..<servers.count {
if servers[i].enabled == .enabled {
if let f = await testServerConnection(server: $servers[i]) {
fs[serverHostname(servers[i].server)] = f
for i in 0..<configuredServers.count {
if configuredServers[i].enabled {
if let f = await testServerConnection(server: $configuredServers[i]) {
fs[serverHostname(configuredServers[i].server)] = f
}
}
}
for i in 0..<otherServers.count {
if otherServers[i].enabled {
if let f = await testServerConnection(server: $otherServers[i]) {
fs[serverHostname(otherServers[i].server)] = f
}
}
}
@@ -287,9 +333,9 @@ struct ProtocolServersView: View {
func saveServers() {
Task {
do {
try await setUserProtoServers(serverProtocol, servers: servers)
try await setUserProtoServers(serverProtocol, servers: allServers)
await MainActor.run {
currServers = servers
currServers = allServers
editMode?.wrappedValue = .inactive
}
} catch let error {
@@ -40,7 +40,7 @@ struct ScanProtocolServer: View {
switch resp {
case let .success(r):
if parseServerAddress(r.string) != nil {
servers.append(ServerCfg(server: r.string, preset: false, tested: nil, enabled: .enabled))
servers.append(ServerCfg(server: r.string, preset: false, tested: nil, enabled: false))
dismiss()
} else {
showAddressError = true
@@ -34,6 +34,7 @@ let DEFAULT_PRIVACY_SHOW_CHAT_PREVIEWS = "privacyShowChatPreviews"
let DEFAULT_PRIVACY_SAVE_LAST_DRAFT = "privacySaveLastDraft"
let DEFAULT_PRIVACY_PROTECT_SCREEN = "privacyProtectScreen"
let DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET = "privacyDeliveryReceiptsSet"
let DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS = "privacyMediaBlurRadius"
let DEFAULT_EXPERIMENTAL_CALLS = "experimentalCalls"
let DEFAULT_CHAT_ARCHIVE_NAME = "chatArchiveName"
let DEFAULT_CHAT_ARCHIVE_TIME = "chatArchiveTime"
@@ -87,6 +88,7 @@ let appDefaults: [String: Any] = [
DEFAULT_PRIVACY_SAVE_LAST_DRAFT: true,
DEFAULT_PRIVACY_PROTECT_SCREEN: false,
DEFAULT_PRIVACY_DELIVERY_RECEIPTS_SET: false,
DEFAULT_PRIVACY_MEDIA_BLUR_RADIUS: 0,
DEFAULT_EXPERIMENTAL_CALLS: false,
DEFAULT_CHAT_V3_DB_MIGRATION: V3DBMigrationState.offer.rawValue,
DEFAULT_DEVELOPER_TOOLS: false,
@@ -367,8 +367,8 @@
<source>Add servers by scanning QR codes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
@@ -127,11 +127,6 @@
<target>%@ е потвърдено</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ сървъри</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ качено</target>
@@ -590,6 +585,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Добавете адрес към вашия профил, така че вашите контакти да могат да го споделят с други хора. Актуализацията на профила ще бъде изпратена до вашите контакти.</target>
@@ -610,16 +609,16 @@
<target>Добави профил</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Добави сървър</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Добави сървъри чрез сканиране на QR кодове.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Добави сървър…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Добави към друго устройство</target>
@@ -710,8 +709,8 @@
<target>Всички нови съобщения от %@ ще бъдат скрити!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1351,6 +1350,10 @@
<target>Конфигурирай ICE сървъри</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Потвърди</target>
@@ -1529,10 +1532,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Контактът позволява</target>
@@ -1706,8 +1705,8 @@ This is your own one-time link!</source>
<target>Текуща парола…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -3857,6 +3856,10 @@ This is your link for group %@!</source>
<target>Член</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Ролята на члена ще бъде променена на "%@". Всички членове на групата ще бъдат уведомени.</target>
@@ -3895,6 +3898,14 @@ This is your link for group %@!</source>
<target>Чернова на съобщение</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
@@ -3914,6 +3925,10 @@ This is your link for group %@!</source>
<target>Реакциите на съобщения са забранени в тази група.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
@@ -3935,10 +3950,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Текст на съобщението</target>
@@ -4206,6 +4217,10 @@ This is your link for group %@!</source>
<target>Няма токен за устройство!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Няма филтрирани чатове</target>
@@ -4453,6 +4468,10 @@ This is your link for group %@!</source>
<target>Други</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4619,6 +4638,10 @@ Error: %@</source>
<target>Моля, съхранявайте паролата на сигурно място, НЯМА да можете да я промените, ако я загубите.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Полски интерфейс</target>
@@ -4685,6 +4708,10 @@ Error: %@</source>
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Профилни и сървърни връзки</target>
@@ -5585,6 +5612,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Сървърът изисква оторизация за създаване на опашки, проверете паролата</target>
@@ -5608,6 +5639,14 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Сървъри</target>
@@ -5744,6 +5783,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Показване на визуализация</target>
@@ -5967,10 +6010,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7025,8 +7064,8 @@ Repeat join request?</source>
<target>Можете да го направите видим за вашите контакти в SimpleX чрез Настройки.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Вече можете да изпращате съобщения до %@</target>
<note>notification body</note>
</trans-unit>
@@ -7433,7 +7472,7 @@ SimpleX сървърите не могат да видят вашия профи
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>блокиран от админ</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7762,6 +7801,10 @@ SimpleX сървърите не могат да видят вашия профи
<target>iOS Keychain ще се използва за сигурно съхраняване на паролата, след като рестартирате приложението или промените паролата - това ще позволи получаването на push известия.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>инкогнито чрез линк с адрес за контакт</target>
@@ -386,8 +386,8 @@
<source>Add servers by scanning QR codes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
@@ -126,11 +126,6 @@
<target>%@ je ověřený</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ servery</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<note>No comment provided by engineer.</note>
@@ -572,6 +567,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Přidejte adresu do svého profilu, aby ji vaše kontakty mohly sdílet s dalšími lidmi. Aktualizace profilu bude zaslána vašim kontaktům.</target>
@@ -591,16 +590,16 @@
<target>Přidat profil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Přidat server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Přidejte servery skenováním QR kódů.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Přidat server…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Přidat do jiného zařízení</target>
@@ -688,8 +687,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1305,6 +1304,10 @@
<target>Konfigurace serverů ICE</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Potvrdit</target>
@@ -1467,10 +1470,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Kontakt povolil</target>
@@ -1636,8 +1635,8 @@ This is your own one-time link!</source>
<target>Aktuální přístupová fráze…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -3714,6 +3713,10 @@ This is your link for group %@!</source>
<target>Člen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Role člena se změní na "%@". Všichni členové skupiny budou upozorněni.</target>
@@ -3752,6 +3755,14 @@ This is your link for group %@!</source>
<target>Návrh zprávy</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
@@ -3771,6 +3782,10 @@ This is your link for group %@!</source>
<target>Reakce na zprávy jsou v této skupině zakázány.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
@@ -3791,10 +3806,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Text zprávy</target>
@@ -4047,6 +4058,10 @@ This is your link for group %@!</source>
<target>Žádný token zařízení!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Žádné filtrované chaty</target>
@@ -4283,6 +4298,10 @@ This is your link for group %@!</source>
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4441,6 +4460,10 @@ Error: %@</source>
<target>Heslo uložte bezpečně, v případě jeho ztráty jej NEBUDE možné změnit.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Polské rozhraní</target>
@@ -4506,6 +4529,10 @@ Error: %@</source>
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profil a připojení k serveru</target>
@@ -5383,6 +5410,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Server vyžaduje autorizaci pro vytváření front, zkontrolujte heslo</target>
@@ -5406,6 +5437,14 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Servery</target>
@@ -5537,6 +5576,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Zobrazení náhledu</target>
@@ -5754,10 +5797,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6758,8 +6797,8 @@ Repeat join request?</source>
<source>You can make it visible to your SimpleX contacts via Settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Nyní můžete posílat zprávy %@</target>
<note>notification body</note>
</trans-unit>
@@ -7152,7 +7191,7 @@ Servery SimpleX nevidí váš profil.</target>
</trans-unit>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7477,6 +7516,10 @@ Servery SimpleX nevidí váš profil.</target>
<target>Klíčenka pro iOS bude použita k bezpečnému uložení přístupové fráze po restartování aplikace nebo změně přístupové fráze umožní příjem oznámení push.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>inkognito přes odkaz na kontaktní adresu</target>
File diff suppressed because it is too large Load Diff
@@ -336,8 +336,8 @@ Available in v5.1</source>
<source>Add servers by scanning QR codes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
@@ -127,11 +127,6 @@
<target>%@ is verified</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ uploaded</target>
@@ -593,6 +588,11 @@
<target>Acknowledgement errors</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<target>Active connections</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</target>
@@ -613,16 +613,16 @@
<target>Add profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Add server</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Add servers by scanning QR codes.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Add server…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Add to another device</target>
@@ -718,9 +718,9 @@
<target>All new messages from %@ will be hidden!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<target>All users</target>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<target>All profiles</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1374,6 +1374,11 @@
<target>Configure ICE servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<target>Configured %@ servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Confirm</target>
@@ -1558,11 +1563,6 @@ This is your own one-time link!</target>
<target>Connections</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<target>Connections subscribed</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Contact allows</target>
@@ -1738,9 +1738,9 @@ This is your own one-time link!</target>
<target>Current passphrase…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<target>Current user</target>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<target>Current profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -3923,6 +3923,11 @@ This is your link for group %@!</target>
<target>Member</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Member inactive</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Member role will be changed to "%@". All group members will be notified.</target>
@@ -3963,6 +3968,16 @@ This is your link for group %@!</target>
<target>Message draft</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<target>Message forwarded</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<target>Message may be delivered later if member becomes active.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<target>Message queue info</target>
@@ -3983,6 +3998,11 @@ This is your link for group %@!</target>
<target>Message reactions are prohibited in this group.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<target>Message reception</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Message routing fallback</target>
@@ -4008,11 +4028,6 @@ This is your link for group %@!</target>
<target>Message status: %@</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<target>Message subscriptions</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Message text</target>
@@ -4283,6 +4298,11 @@ This is your link for group %@!</target>
<target>No device token!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<target>No direct connection yet, message is forwarded by admin.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>No filtered chats</target>
@@ -4532,6 +4552,11 @@ This is your link for group %@!</target>
<target>Other</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</source>
<target>Other %@ servers</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>
@@ -4701,6 +4726,11 @@ Error: %@</target>
<target>Please store passphrase securely, you will NOT be able to change it if you lose it.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<target>Please try later.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Polish interface</target>
@@ -4771,6 +4801,11 @@ Error: %@</target>
<target>Private routing</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<target>Private routing error</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profile and server connections</target>
@@ -5708,6 +5743,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Server address is incompatible with network settings.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<target>Server address is incompatible with network settings: %@.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Server requires authorization to create queues, check password</target>
@@ -5733,6 +5773,16 @@ Enable in *Network &amp; servers* settings.</target>
<target>Server version is incompatible with network settings.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<target>Server version is incompatible with network settings: %@.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<target>Server version is incompatible with your app: %@.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Servers</target>
@@ -5873,6 +5923,11 @@ Enable in *Network &amp; servers* settings.</target>
<target>Show message status</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<target>Show percentage</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Show preview</target>
@@ -6103,11 +6158,6 @@ Enable in *Network &amp; servers* settings.</target>
<target>Subscription errors</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<target>Subscription percentage</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<target>Subscriptions ignored</target>
@@ -7186,9 +7236,9 @@ Repeat join request?</target>
<target>You can make it visible to your SimpleX contacts via Settings.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<target>You can now send messages to %@</target>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>You can now chat with %@</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
@@ -7595,7 +7645,7 @@ SimpleX servers cannot see your profile.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>blocked by admin</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7927,6 +7977,11 @@ SimpleX servers cannot see your profile.</target>
<target>iOS Keychain will be used to securely store passphrase after you restart the app or change passphrase - it will allow receiving push notifications.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<target>inactive</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>incognito via contact address link</target>
@@ -127,11 +127,6 @@
<target>%@ está verificado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>Servidores %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ subido</target>
@@ -590,6 +585,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Añade la dirección a tu perfil para que tus contactos puedan compartirla con otros. La actualización del perfil se enviará a tus contactos.</target>
@@ -610,16 +609,16 @@
<target>Añadir perfil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Añadir servidor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Añadir servidores mediante el escaneo de códigos QR.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Añadir servidor…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Añadir a otro dispositivo</target>
@@ -710,8 +709,8 @@
<target>¡Los mensajes nuevos de %@ estarán ocultos!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1354,6 +1353,10 @@
<target>Configure servidores ICE</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Confirmar</target>
@@ -1507,7 +1510,7 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Connection error (AUTH)" xml:space="preserve">
<source>Connection error (AUTH)</source>
<target>Error conexión (Autenticación)</target>
<target>Error de conexión (Autenticación)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection request sent!" xml:space="preserve">
@@ -1522,7 +1525,7 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Connection timeout" xml:space="preserve">
<source>Connection timeout</source>
<target>Tiempo de conexión expirado</target>
<target>Tiempo de conexión agotado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connection with desktop stopped" xml:space="preserve">
@@ -1533,10 +1536,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>El contacto permite</target>
@@ -1710,8 +1709,8 @@ This is your own one-time link!</source>
<target>Contraseña actual…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1837,6 +1836,7 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<target>Informe debug</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
@@ -3333,7 +3333,7 @@ Error: %2$@</target>
</trans-unit>
<trans-unit id="If you can't meet in person, show QR code in a video call, or share the link." xml:space="preserve">
<source>If you can't meet in person, show QR code in a video call, or share the link.</source>
<target>Si no puedes reunirte en persona, muestra el código QR por videollamada, o comparte el enlace.</target>
<target>Si no puedes reunirte en persona, muestra el código QR por videollamada o comparte el enlace.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="If you enter this passcode when opening the app, all app data will be irreversibly removed!" xml:space="preserve">
@@ -3869,6 +3869,10 @@ This is your link for group %@!</source>
<target>Miembro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>El rol del miembro cambiará a "%@" y se notificará al grupo.</target>
@@ -3908,8 +3912,17 @@ This is your link for group %@!</source>
<target>Borrador de mensaje</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<target>Información cola de mensajes</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
@@ -3927,6 +3940,10 @@ This is your link for group %@!</source>
<target>Las reacciones a los mensajes no están permitidas en este grupo.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Enrutamiento de mensajes alternativo</target>
@@ -3950,10 +3967,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Contacto y texto</target>
@@ -4222,6 +4235,10 @@ This is your link for group %@!</source>
<target>¡Sin dispositivo token!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Sin chats filtrados</target>
@@ -4307,7 +4324,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="One-time invitation link" xml:space="preserve">
<source>One-time invitation link</source>
<target>Enlace único de invitación de un uso</target>
<target>Enlace de invitación de un solo uso</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be required for connection. Requires enabling VPN." xml:space="preserve">
@@ -4451,7 +4468,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Or scan QR code" xml:space="preserve">
<source>Or scan QR code</source>
<target>O escanear código QR</target>
<target>O escanea el código QR</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Or securely share this file link" xml:space="preserve">
@@ -4461,7 +4478,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Or show this code" xml:space="preserve">
<source>Or show this code</source>
<target>O mostrar este código</target>
<target>O muestra este código QR</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other" xml:space="preserve">
@@ -4469,6 +4486,10 @@ This is your link for group %@!</source>
<target>Otro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4531,7 +4552,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Paste the link you received" xml:space="preserve">
<source>Paste the link you received</source>
<target>Pegar el enlace recibido</target>
<target>Pega el enlace recibido</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Pending" xml:space="preserve">
@@ -4545,7 +4566,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Periodically" xml:space="preserve">
<source>Periodically</source>
<target>Periódico</target>
<target>Periódicamente</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Permanent decryption error" xml:space="preserve">
@@ -4635,6 +4656,10 @@ Error: %@</target>
<target>Guarda la contraseña de forma segura, NO podrás cambiarla si la pierdes.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Interfaz en polaco</target>
@@ -4704,6 +4729,10 @@ Error: %@</target>
<target>Enrutamiento privado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Datos del perfil y conexiones</target>
@@ -4812,12 +4841,12 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Protocol timeout" xml:space="preserve">
<source>Protocol timeout</source>
<target>Tiempo de espera del protocolo</target>
<target>Timeout protocolo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Protocol timeout per KB" xml:space="preserve">
<source>Protocol timeout per KB</source>
<target>Límite de espera del protocolo por KB</target>
<target>Timeout protocolo por KB</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Proxied" xml:space="preserve">
@@ -4860,32 +4889,32 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Read more" xml:space="preserve">
<source>Read more</source>
<target>Saber más</target>
<target>Conoce más</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." xml:space="preserve">
<source>Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</source>
<target>Saber más en el [Manual del Usuario](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
<target>Conoce más en el [Manual del Usuario](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." xml:space="preserve">
<source>Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</source>
<target>Saber más en [Guía de Usuario](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</target>
<target>Conoce más en la [Guía del Usuario](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." xml:space="preserve">
<source>Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</source>
<target>Saber más en el [Manual del Usuario](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
<target>Conoce más en el [Manual del Usuario](https://simplex.chat/docs/guide/readme.html#connect-to-friends).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Read more in our GitHub repository." xml:space="preserve">
<source>Read more in our GitHub repository.</source>
<target>Saber más en nuestro repositorio GitHub.</target>
<target>Conoce más en nuestro repositorio GitHub.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." xml:space="preserve">
<source>Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme).</source>
<target>Saber más en nuestro [repositorio GitHub](https://github.com/simplex-chat/simplex-chat#readme).</target>
<target>Conoce más en nuestro [repositorio GitHub](https://github.com/simplex-chat/simplex-chat#readme).</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Receipts are disabled" xml:space="preserve">
@@ -5611,6 +5640,10 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
<target>La dirección del servidor es incompatible con la configuración de la red.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>El servidor requiere autorización para crear colas, comprueba la contraseña</target>
@@ -5635,6 +5668,14 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
<target>La versión del servidor es incompatible con la configuración de red.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Servidores</target>
@@ -5739,7 +5780,7 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Share this 1-time invite link" xml:space="preserve">
<source>Share this 1-time invite link</source>
<target>Compartir este enlace de un uso</target>
<target>Comparte este enlace de un solo uso</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Share with contacts" xml:space="preserve">
@@ -5772,6 +5813,10 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
<target>Estado del mensaje</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Mostrar vista previa</target>
@@ -5996,10 +6041,6 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6021,7 +6062,7 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="TCP connection timeout" xml:space="preserve">
<source>TCP connection timeout</source>
<target>Tiempo de espera de la conexión TCP agotado</target>
<target>Timeout de la conexión TCP</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="TCP_KEEPCNT" xml:space="preserve">
@@ -6071,7 +6112,7 @@ Actívalo en ajustes de *Servidores y Redes*.</target>
</trans-unit>
<trans-unit id="Tap to paste link" xml:space="preserve">
<source>Tap to paste link</source>
<target>Pulsa para pegar enlace</target>
<target>Pulsa para pegar el enlacePulsa para pegar enlace</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to scan" xml:space="preserve">
@@ -6339,7 +6380,7 @@ Puede ocurrir por algún bug o cuando la conexión está comprometida.</target>
</trans-unit>
<trans-unit id="To protect your IP address, private routing uses your SMP servers to deliver messages." xml:space="preserve">
<source>To protect your IP address, private routing uses your SMP servers to deliver messages.</source>
<target>Para proteger tu dirección IP, el enrutamiento privado usa tus servidores SMP para enviar mensajes.</target>
<target>Para proteger tu dirección IP, el enrutamiento privado usa tu lista de servidores SMP para enviar mensajes.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To protect your information, turn on SimpleX Lock.&#10;You will be prompted to complete authentication before this feature is enabled." xml:space="preserve">
@@ -6500,9 +6541,8 @@ Se te pedirá que completes la autenticación antes de activar esta función.</t
<trans-unit id="Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.&#10;To connect, please ask your contact to create another connection link and check that you have a stable network connection." xml:space="preserve">
<source>Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.
To connect, please ask your contact to create another connection link and check that you have a stable network connection.</source>
<target>A menos que tu contacto haya eliminado la conexión o
que este enlace ya se haya usado, podría ser un error. Por favor, notifícalo.
Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueba que tienes buena conexión de red.</target>
<target>A menos que tu contacto haya eliminado la conexión o el enlace haya sido usado, podría ser un error. Por favor, notifícalo.
Para conectarte pide a tu contacto que cree otro enlace y comprueba la conexión de red.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unlink" xml:space="preserve">
@@ -6654,12 +6694,12 @@ Para conectarte, pide a tu contacto que cree otro enlace de conexión y comprueb
</trans-unit>
<trans-unit id="Use private routing with unknown servers when IP address is not protected." xml:space="preserve">
<source>Use private routing with unknown servers when IP address is not protected.</source>
<target>Usar enrutamiento privado con servidores desconocidos cuando la dirección IP no está protegida.</target>
<target>Usar enrutamiento privado con servidores desconocidos cuando tu dirección IP no está protegida.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use private routing with unknown servers." xml:space="preserve">
<source>Use private routing with unknown servers.</source>
<target>Usar enrutamiento privado con servidores desconocidos.</target>
<target>Usar enrutamiento privado con servidores de retransmisión desconocidos.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use server" xml:space="preserve">
@@ -7063,8 +7103,8 @@ Repeat join request?</source>
<target>Puedes hacerlo visible para tus contactos de SimpleX en Configuración.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Ya puedes enviar mensajes a %@</target>
<note>notification body</note>
</trans-unit>
@@ -7202,7 +7242,7 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="You will be required to authenticate when you start or resume the app after 30 seconds in background." xml:space="preserve">
<source>You will be required to authenticate when you start or resume the app after 30 seconds in background.</source>
<target>Se te pedirá identificarte cuándo inicies o continues usando la aplicación tras 30 segundos en segundo plano.</target>
<target>Se te pedirá autenticarte cuando inicies la aplicación o sigas usándola tras 30 segundos en segundo plano.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You will connect to all group members." xml:space="preserve">
@@ -7335,8 +7375,8 @@ Puedes cancelarla y eliminar el contacto (e intentarlo más tarde con un enlace
<trans-unit id="Your profile is stored on your device and shared only with your contacts.&#10;SimpleX servers cannot see your profile." xml:space="preserve">
<source>Your profile is stored on your device and shared only with your contacts.
SimpleX servers cannot see your profile.</source>
<target>Tu perfil se almacena en tu dispositivo y sólo se comparte con tus contactos.
Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos.
Los servidores SimpleX no pueden ver tu perfil.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your profile, contacts and delivered messages are stored on your device." xml:space="preserve">
@@ -7471,7 +7511,7 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>bloqueado por administrador</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7800,6 +7840,10 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<target>iOS Keychain se usará para almacenar la contraseña de forma segura después de reiniciar la aplicación o cambiar la contraseña. Esto permitirá recibir notificaciones automáticas.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>en modo incógnito mediante enlace de dirección del contacto</target>
@@ -8084,6 +8128,9 @@ Los servidores de SimpleX no pueden ver tu perfil.</target>
<source>server queue info: %1$@
last received msg: %2$@</source>
<target>información cola del servidor: %1$@
último mensaje recibido: %2$@</target>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
@@ -8128,7 +8175,7 @@ last received msg: %2$@</source>
</trans-unit>
<trans-unit id="unknown relays" xml:space="preserve">
<source>unknown relays</source>
<target>servidor de retransmisión desconocido</target>
<target>con servidores desconocidos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="unknown status" xml:space="preserve">
@@ -8138,7 +8185,7 @@ last received msg: %2$@</source>
</trans-unit>
<trans-unit id="unprotected" xml:space="preserve">
<source>unprotected</source>
<target>desprotegido</target>
<target>con IP desprotegida</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="updated group profile" xml:space="preserve">
@@ -124,11 +124,6 @@
<target>%@ on vahvistettu</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ palvelimet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<note>No comment provided by engineer.</note>
@@ -567,6 +562,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Lisää osoite profiiliisi, jotta kontaktisi voivat jakaa sen muiden kanssa. Profiilipäivitys lähetetään kontakteillesi.</target>
@@ -586,16 +585,16 @@
<target>Lisää profiili</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Lisää palvelin</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Lisää palvelimia skannaamalla QR-koodeja.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Lisää palvelin…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Lisää toiseen laitteeseen</target>
@@ -683,8 +682,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1298,6 +1297,10 @@
<target>Määritä ICE-palvelimet</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Vahvista</target>
@@ -1460,10 +1463,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Kontakti sallii</target>
@@ -1629,8 +1628,8 @@ This is your own one-time link!</source>
<target>Nykyinen tunnuslause…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -3704,6 +3703,10 @@ This is your link for group %@!</source>
<target>Jäsen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Jäsenen rooli muuttuu muotoon "%@". Kaikille ryhmän jäsenille ilmoitetaan asiasta.</target>
@@ -3742,6 +3745,14 @@ This is your link for group %@!</source>
<target>Viestiluonnos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
@@ -3761,6 +3772,10 @@ This is your link for group %@!</source>
<target>Viestireaktiot ovat kiellettyjä tässä ryhmässä.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
@@ -3781,10 +3796,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Viestin teksti</target>
@@ -4036,6 +4047,10 @@ This is your link for group %@!</source>
<target>Ei laitetunnusta!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Ei suodatettuja keskusteluja</target>
@@ -4271,6 +4286,10 @@ This is your link for group %@!</source>
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4429,6 +4448,10 @@ Error: %@</source>
<target>Säilytä tunnuslause turvallisesti, ET voi muuttaa sitä, jos kadotat sen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Puolalainen käyttöliittymä</target>
@@ -4494,6 +4517,10 @@ Error: %@</source>
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profiili- ja palvelinyhteydet</target>
@@ -5370,6 +5397,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Palvelin vaatii valtuutuksen jonojen luomiseen, tarkista salasana</target>
@@ -5393,6 +5424,14 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Palvelimet</target>
@@ -5524,6 +5563,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Näytä esikatselu</target>
@@ -5740,10 +5783,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6743,8 +6782,8 @@ Repeat join request?</source>
<source>You can make it visible to your SimpleX contacts via Settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Voit nyt lähettää viestejä %@:lle</target>
<note>notification body</note>
</trans-unit>
@@ -7137,7 +7176,7 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
</trans-unit>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7462,6 +7501,10 @@ SimpleX-palvelimet eivät näe profiiliasi.</target>
<target>iOS-Avainnippua käytetään tunnuslauseen turvalliseen tallentamiseen sen muuttamisen tai sovelluksen uudelleen käynnistämisen jälkeen - se mahdollistaa push-ilmoitusten vastaanottamisen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>incognito kontaktilinkin kautta</target>
@@ -127,11 +127,6 @@
<target>%@ est vérifié·e</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>Serveurs %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ envoyé</target>
@@ -590,6 +585,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Ajoutez une adresse à votre profil, afin que vos contacts puissent la partager avec d'autres personnes. La mise à jour du profil sera envoyée à vos contacts.</target>
@@ -610,16 +609,16 @@
<target>Ajouter un profil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Ajouter un serveur</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Ajoutez des serveurs en scannant des codes QR.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Ajouter un serveur…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Ajouter à un autre appareil</target>
@@ -710,8 +709,8 @@
<target>Tous les nouveaux messages de %@ seront cachés !</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1354,6 +1353,10 @@
<target>Configurer les serveurs ICE</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Confirmer</target>
@@ -1533,10 +1536,6 @@ Il s'agit de votre propre lien unique !</target>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Votre contact autorise</target>
@@ -1710,8 +1709,8 @@ Il s'agit de votre propre lien unique !</target>
<target>Phrase secrète actuelle…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1837,6 +1836,7 @@ Il s'agit de votre propre lien unique !</target>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<target>Livraison de débogage</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
@@ -3869,6 +3869,10 @@ Voici votre lien pour le groupe %@ !</target>
<target>Membre</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Le rôle du membre sera changé pour "%@". Tous les membres du groupe en seront informés.</target>
@@ -3908,8 +3912,17 @@ Voici votre lien pour le groupe %@ !</target>
<target>Brouillon de message</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<target>Informations sur la file d'attente des messages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
@@ -3927,6 +3940,10 @@ Voici votre lien pour le groupe %@ !</target>
<target>Les réactions aux messages sont interdites dans ce groupe.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Rabattement du routage des messages</target>
@@ -3950,10 +3967,6 @@ Voici votre lien pour le groupe %@ !</target>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Texte du message</target>
@@ -4222,6 +4235,10 @@ Voici votre lien pour le groupe %@ !</target>
<target>Pas de token d'appareil!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Aucune discussion filtrés</target>
@@ -4469,6 +4486,10 @@ Voici votre lien pour le groupe %@ !</target>
<target>Autres</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4635,6 +4656,10 @@ Erreur: %@</target>
<target>Veuillez conserver votre phrase secrète en lieu sûr, vous NE pourrez PAS la changer si vous la perdez.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Interface en polonais</target>
@@ -4704,6 +4729,10 @@ Erreur: %@</target>
<target>Routage privé</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profil et connexions au serveur</target>
@@ -5611,6 +5640,10 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
<target>L'adresse du serveur est incompatible avec les paramètres du réseau.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Le serveur requiert une autorisation pour créer des files d'attente, vérifiez le mot de passe</target>
@@ -5635,6 +5668,14 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
<target>La version du serveur est incompatible avec les paramètres du réseau.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Serveurs</target>
@@ -5772,6 +5813,10 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
<target>Afficher le statut du message</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Afficher l'aperçu</target>
@@ -5996,10 +6041,6 @@ Activez-le dans les paramètres *Réseau et serveurs*.</target>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7062,8 +7103,8 @@ Répéter la demande d'adhésion ?</target>
<target>Vous pouvez le rendre visible à vos contacts SimpleX via Paramètres.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Vous pouvez maintenant envoyer des messages à %@</target>
<note>notification body</note>
</trans-unit>
@@ -7470,7 +7511,7 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>bloqué par l'administrateur</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7799,6 +7840,10 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<target>La keychain d'iOS sera utilisée pour stocker en toute sécurité la phrase secrète après le redémarrage de l'app ou la modification de la phrase secrète - il permettra de recevoir les notifications push.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>mode incognito via le lien d'adresse du contact</target>
@@ -8083,6 +8128,9 @@ Les serveurs SimpleX ne peuvent pas voir votre profil.</target>
<source>server queue info: %1$@
last received msg: %2$@</source>
<target>info sur la file d'attente du serveur: %1$@
dernier message reçu: %2$@</target>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
@@ -403,9 +403,9 @@ Available in v5.1</source>
<target state="translated">הוספת שרתים על ידי סריקת קוד QR.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">הוסף שרת</target>
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">הוסף שרת</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve" approved="no">
@@ -300,8 +300,8 @@
<source>Add servers by scanning QR codes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
@@ -367,8 +367,8 @@
<source>Add servers by scanning QR codes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
File diff suppressed because it is too large Load Diff
File diff suppressed because it is too large Load Diff
@@ -127,11 +127,6 @@
<target>%@ は検証されています</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ サーバー</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ アップロード済</target>
@@ -584,6 +579,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>プロフィールにアドレスを追加し、連絡先があなたのアドレスを他の人と共有できるようにします。プロフィールの更新は連絡先に送信されます。</target>
@@ -603,16 +602,16 @@
<target>プロフィールを追加</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>サーバを追加</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>QRコードでサーバを追加する。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>サーバを追加…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>別の端末に追加</target>
@@ -700,8 +699,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1322,6 +1321,10 @@
<target>ICEサーバを設定</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>確認</target>
@@ -1484,10 +1487,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>連絡先の許可</target>
@@ -1653,8 +1652,8 @@ This is your own one-time link!</source>
<target>現在の暗証フレーズ…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -3729,6 +3728,10 @@ This is your link for group %@!</source>
<target>メンバー</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>メンバーの役割が "%@" に変更されます。 グループメンバー全員に通知されます。</target>
@@ -3766,6 +3769,14 @@ This is your link for group %@!</source>
<target>メッセージの下書き</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
@@ -3785,6 +3796,10 @@ This is your link for group %@!</source>
<target>このグループではメッセージへのリアクションは禁止されています。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
@@ -3805,10 +3820,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>メッセージ内容</target>
@@ -4061,6 +4072,10 @@ This is your link for group %@!</source>
<target>デバイストークンがありません!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>フィルタされたチャットはありません</target>
@@ -4297,6 +4312,10 @@ This is your link for group %@!</source>
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4455,6 +4474,10 @@ Error: %@</source>
<target>パスフレーズを失くさないように保管してください。失くすと変更できなくなります。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>ポーランド語UI</target>
@@ -4520,6 +4543,10 @@ Error: %@</source>
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>プロフィールとサーバ接続</target>
@@ -5388,6 +5415,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>キューを作成するにはサーバーの認証が必要です。パスワードを確認してください</target>
@@ -5411,6 +5442,14 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>サーバ</target>
@@ -5542,6 +5581,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>プレビューを表示</target>
@@ -5759,10 +5802,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6761,8 +6800,8 @@ Repeat join request?</source>
<source>You can make it visible to your SimpleX contacts via Settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>%@ にメッセージを送信できるようになりました</target>
<note>notification body</note>
</trans-unit>
@@ -7155,7 +7194,7 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
</trans-unit>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7480,6 +7519,10 @@ SimpleX サーバーはあなたのプロファイルを参照できません。
<target>iOS キーチェーンは、アプリを再起動するかパスフレーズを変更した後にパスフレーズを安全に保存するために使用され、プッシュ通知を受信できるようになります。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>連絡先リンク経由でシークレットモード</target>
@@ -5,9 +5,11 @@
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="&#10;" xml:space="preserve">
<trans-unit id="&#10;" xml:space="preserve" approved="no">
<source>
</source>
<target state="translated">
</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=" " xml:space="preserve">
@@ -300,8 +302,8 @@
<source>Add servers by scanning QR codes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
@@ -484,53 +486,62 @@
<source>Can't delete user profile!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<trans-unit id="Can't invite contact!" xml:space="preserve" approved="no">
<source>Can't invite contact!</source>
<target state="translated">주소를 초대할 수 없습니다.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contacts!" xml:space="preserve">
<source>Can't invite contacts!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<trans-unit id="Cancel" xml:space="preserve" approved="no">
<source>Cancel</source>
<target state="translated">취소</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cannot access keychain to save database password" xml:space="preserve">
<trans-unit id="Cannot access keychain to save database password" xml:space="preserve" approved="no">
<source>Cannot access keychain to save database password</source>
<target state="translated">데이터베이스 암호를 저장하는 키체인에 접근 할 수 없습니다</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cannot receive file" xml:space="preserve">
<trans-unit id="Cannot receive file" xml:space="preserve" approved="no">
<source>Cannot receive file</source>
<target state="translated">파일을 받을 수 없습니다</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change" xml:space="preserve">
<trans-unit id="Change" xml:space="preserve" approved="no">
<source>Change</source>
<target state="translated">변경</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change database passphrase?" xml:space="preserve">
<source>Change database passphrase?</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change member role?" xml:space="preserve">
<trans-unit id="Change member role?" xml:space="preserve" approved="no">
<source>Change member role?</source>
<target state="translated">멤버 역할을 변경하시겠습니까?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change receiving address" xml:space="preserve">
<trans-unit id="Change receiving address" xml:space="preserve" approved="no">
<source>Change receiving address</source>
<target state="translated">수신 주소 변경</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change receiving address?" xml:space="preserve" approved="no">
<source>Change receiving address?</source>
<target state="translated">修改接收地址?</target>
<target state="translated">수신 주소를 변경하시겠습니까?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change role" xml:space="preserve">
<trans-unit id="Change role" xml:space="preserve" approved="no">
<source>Change role</source>
<target state="translated">역할 변경</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat archive" xml:space="preserve">
<trans-unit id="Chat archive" xml:space="preserve" approved="no">
<source>Chat archive</source>
<target state="translated">채팅 기록 보관함</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat console" xml:space="preserve">
@@ -545,8 +556,9 @@
<source>Chat database deleted</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat database imported" xml:space="preserve">
<trans-unit id="Chat database imported" xml:space="preserve" approved="no">
<source>Chat database imported</source>
<target state="translated">채팅 데이터베이스를 가져옴</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chat is running" xml:space="preserve">
@@ -2397,24 +2409,29 @@ We will be adding server redundancy to prevent lost messages.</source>
<source>Send live message</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send notifications" xml:space="preserve">
<trans-unit id="Send notifications" xml:space="preserve" approved="no">
<source>Send notifications</source>
<target state="translated">알림 전송</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send notifications:" xml:space="preserve">
<trans-unit id="Send notifications:" xml:space="preserve" approved="no">
<source>Send notifications:</source>
<target state="translated">알림 전송:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send questions and ideas" xml:space="preserve">
<trans-unit id="Send questions and ideas" xml:space="preserve" approved="no">
<source>Send questions and ideas</source>
<target state="translated">질문이나 아이디어 보내기</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send them from gallery or custom keyboards." xml:space="preserve">
<trans-unit id="Send them from gallery or custom keyboards." xml:space="preserve" approved="no">
<source>Send them from gallery or custom keyboards.</source>
<target state="needs-translation">갤러리 또는 사용자 정의 키보드에서 그들을 보내십시오.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sender cancelled file transfer." xml:space="preserve">
<trans-unit id="Sender cancelled file transfer." xml:space="preserve" approved="no">
<source>Sender cancelled file transfer.</source>
<target state="translated">상대방이 파일 전송을 취소했습니다.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sender may have deleted the connection request." xml:space="preserve">
@@ -3755,6 +3772,26 @@ SimpleX servers cannot see your profile.</source>
<source>\~strike~</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Change passcode" xml:space="preserve" approved="no">
<source>Change passcode</source>
<target state="translated">패스코드 변경</target>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Cellular" xml:space="preserve" approved="no">
<source>Cellular</source>
<target state="translated">셀룰러</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send messages directly when your or destination server does not support private routing." xml:space="preserve" approved="no">
<source>Send messages directly when your or destination server does not support private routing.</source>
<target state="needs-translation">이 서버 또는 도착 서버가 비밀 라우팅을 지원하지 않을 때 직통 메시지 보내기.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send up to 100 last messages to new members." xml:space="preserve" approved="no">
<source>Send up to 100 last messages to new members.</source>
<target state="translated">새로운 멤버에게 최대 100개의 마지막 메시지 보내기.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="ko" datatype="plaintext">
@@ -3778,8 +3815,9 @@ SimpleX servers cannot see your profile.</source>
<source>SimpleX needs microphone access for audio and video calls, and to record voice messages.</source>
<note>Privacy - Microphone Usage Description</note>
</trans-unit>
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve">
<trans-unit id="NSPhotoLibraryAddUsageDescription" xml:space="preserve" approved="no">
<source>SimpleX needs access to Photo Library for saving captured and received media</source>
<target state="needs-translation">SimpleX는 캡처 및 수신 된 미디어를 저장하기 위해 사진 라이브러리에 접근이 필요합니다</target>
<note>Privacy - Photo Library Additions Usage Description</note>
</trans-unit>
</body>
@@ -3793,8 +3831,9 @@ SimpleX servers cannot see your profile.</source>
<source>SimpleX NSE</source>
<note>Bundle display name</note>
</trans-unit>
<trans-unit id="CFBundleName" xml:space="preserve">
<trans-unit id="CFBundleName" xml:space="preserve" approved="no">
<source>SimpleX NSE</source>
<target state="translated">SimpleX NSE</target>
<note>Bundle name</note>
</trans-unit>
<trans-unit id="NSHumanReadableCopyright" xml:space="preserve">
@@ -329,9 +329,9 @@
<target state="translated">Pridėti serverius skenuojant QR kodus.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">Pridėti serverį</target>
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">Pridėti serverį</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
@@ -367,8 +367,8 @@
<source>Add servers by scanning QR codes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
@@ -127,11 +127,6 @@
<target>%@ is geverifieerd</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ servers</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ geüpload</target>
@@ -590,6 +585,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Voeg een adres toe aan uw profiel, zodat uw contacten het met andere mensen kunnen delen. Profiel update wordt naar uw contacten verzonden.</target>
@@ -610,16 +609,16 @@
<target>Profiel toevoegen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Server toevoegen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Servers toevoegen door QR-codes te scannen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Server toevoegen…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Toevoegen aan een ander apparaat</target>
@@ -710,8 +709,8 @@
<target>Alle nieuwe berichten van %@ worden verborgen!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1354,6 +1353,10 @@
<target>ICE servers configureren</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Bevestigen</target>
@@ -1533,10 +1536,6 @@ Dit is uw eigen eenmalige link!</target>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Contact maakt het mogelijk</target>
@@ -1710,8 +1709,8 @@ Dit is uw eigen eenmalige link!</target>
<target>Huidige wachtwoord…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1837,6 +1836,7 @@ Dit is uw eigen eenmalige link!</target>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<target>Foutopsporing bezorging</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
@@ -3869,6 +3869,10 @@ Dit is jouw link voor groep %@!</target>
<target>Lid</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>De rol van lid wordt gewijzigd in "%@". Alle groepsleden worden op de hoogte gebracht.</target>
@@ -3908,8 +3912,17 @@ Dit is jouw link voor groep %@!</target>
<target>Concept bericht</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<target>Informatie over berichtenwachtrij</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
@@ -3927,6 +3940,10 @@ Dit is jouw link voor groep %@!</target>
<target>Reacties op berichten zijn verboden in deze groep.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Terugval op berichtroutering</target>
@@ -3950,10 +3967,6 @@ Dit is jouw link voor groep %@!</target>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Bericht tekst</target>
@@ -4222,6 +4235,10 @@ Dit is jouw link voor groep %@!</target>
<target>Geen apparaattoken!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Geen gefilterde gesprekken</target>
@@ -4469,6 +4486,10 @@ Dit is jouw link voor groep %@!</target>
<target>Ander</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4635,6 +4656,10 @@ Fout: %@</target>
<target>Bewaar het wachtwoord veilig, u kunt deze NIET wijzigen als u het kwijtraakt.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Poolse interface</target>
@@ -4704,6 +4729,10 @@ Fout: %@</target>
<target>Privéroutering</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profiel- en serververbindingen</target>
@@ -5611,6 +5640,10 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
<target>Serveradres is niet compatibel met netwerkinstellingen.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Server vereist autorisatie om wachtrijen te maken, controleer wachtwoord</target>
@@ -5635,6 +5668,14 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
<target>Serverversie is incompatibel met netwerkinstellingen.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Servers</target>
@@ -5772,6 +5813,10 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
<target>Toon berichtstatus</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Toon voorbeeld</target>
@@ -5996,10 +6041,6 @@ Schakel dit in in *Netwerk en servers*-instellingen.</target>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7062,8 +7103,8 @@ Deelnameverzoek herhalen?</target>
<target>Je kunt het via Instellingen zichtbaar maken voor je SimpleX contacten.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Je kunt nu berichten sturen naar %@</target>
<note>notification body</note>
</trans-unit>
@@ -7470,7 +7511,7 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>geblokkeerd door beheerder</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7799,6 +7840,10 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<target>iOS-keychain wordt gebruikt om het wachtwoord veilig op te slaan nadat u de app opnieuw hebt opgestart of het wachtwoord hebt gewijzigd, hiermee kunt u push meldingen ontvangen.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>incognito via contact adres link</target>
@@ -8083,6 +8128,9 @@ SimpleX servers kunnen uw profiel niet zien.</target>
<source>server queue info: %1$@
last received msg: %2$@</source>
<target>informatie over serverwachtrij: %1$@
laatst ontvangen bericht: %2$@</target>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
@@ -127,11 +127,6 @@
<target>%@ jest zweryfikowany</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ serwery</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ wgrane</target>
@@ -590,6 +585,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Dodaj adres do swojego profilu, aby Twoje kontakty mogły go udostępnić innym osobom. Aktualizacja profilu zostanie wysłana do Twoich kontaktów.</target>
@@ -610,16 +609,16 @@
<target>Dodaj profil</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Dodaj serwer</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Dodaj serwery, skanując kody QR.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Dodaj serwer…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Dodaj do innego urządzenia</target>
@@ -710,8 +709,8 @@
<target>Wszystkie nowe wiadomości z %@ zostaną ukryte!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1354,6 +1353,10 @@
<target>Skonfiguruj serwery ICE</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Potwierdź</target>
@@ -1533,10 +1536,6 @@ To jest twój jednorazowy link!</target>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Kontakt pozwala</target>
@@ -1710,8 +1709,8 @@ To jest twój jednorazowy link!</target>
<target>Obecne hasło…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1837,6 +1836,7 @@ To jest twój jednorazowy link!</target>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<target>Dostarczenie debugowania</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
@@ -3869,6 +3869,10 @@ To jest twój link do grupy %@!</target>
<target>Członek</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Rola członka grupy zostanie zmieniona na "%@". Wszyscy członkowie grupy zostaną powiadomieni.</target>
@@ -3908,8 +3912,17 @@ To jest twój link do grupy %@!</target>
<target>Wersja robocza wiadomości</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<target>Informacje kolejki wiadomości</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
@@ -3927,6 +3940,10 @@ To jest twój link do grupy %@!</target>
<target>Reakcje wiadomości są zabronione w tej grupie.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Rezerwowe trasowania wiadomości</target>
@@ -3950,10 +3967,6 @@ To jest twój link do grupy %@!</target>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Tekst wiadomości</target>
@@ -4222,6 +4235,10 @@ To jest twój link do grupy %@!</target>
<target>Brak tokenu urządzenia!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Brak filtrowanych czatów</target>
@@ -4469,6 +4486,10 @@ To jest twój link do grupy %@!</target>
<target>Inne</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4635,6 +4656,10 @@ Błąd: %@</target>
<target>Przechowuj kod dostępu w bezpieczny sposób, w przypadku jego utraty NIE będzie można go zmienić.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Polski interfejs</target>
@@ -4704,6 +4729,10 @@ Błąd: %@</target>
<target>Prywatne trasowanie</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profil i połączenia z serwerem</target>
@@ -5611,6 +5640,10 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
<target>Adres serwera jest niekompatybilny z ustawieniami sieciowymi.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Serwer wymaga autoryzacji do tworzenia kolejek, sprawdź hasło</target>
@@ -5635,6 +5668,14 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
<target>Wersja serwera jest niekompatybilna z ustawieniami sieciowymi.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Serwery</target>
@@ -5772,6 +5813,10 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
<target>Pokaż status wiadomości</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Pokaż podgląd</target>
@@ -5996,10 +6041,6 @@ Włącz w ustawianiach *Sieć i serwery* .</target>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7062,8 +7103,8 @@ Powtórzyć prośbę dołączenia?</target>
<target>Możesz ustawić go jako widoczny dla swoich kontaktów SimpleX w Ustawieniach.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Możesz teraz wysyłać wiadomości do %@</target>
<note>notification body</note>
</trans-unit>
@@ -7470,7 +7511,7 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>zablokowany przez admina</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7799,6 +7840,10 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<target>iOS Keychain będzie używany do bezpiecznego przechowywania hasła po ponownym uruchomieniu aplikacji lub zmianie hasła - pozwoli to na otrzymywanie powiadomień push.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>incognito poprzez link adresu kontaktowego</target>
@@ -8083,6 +8128,9 @@ Serwery SimpleX nie mogą zobaczyć Twojego profilu.</target>
<source>server queue info: %1$@
last received msg: %2$@</source>
<target>Informacje kolejki serwera: %1$@
ostatnia otrzymana wiadomość: %2$@</target>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
@@ -374,9 +374,9 @@
<target state="translated">Adicione servidores escaneando o QR code.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">Adicionar servidor</target>
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">Adicionar servidor</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve" approved="no">
@@ -5,9 +5,11 @@
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="14.2" build-num="14C18"/>
</header>
<body>
<trans-unit id="&#10;" xml:space="preserve">
<trans-unit id="&#10;" xml:space="preserve" approved="no">
<source>
</source>
<target state="needs-translation">
</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="&#10;Available in v5.1" xml:space="preserve">
@@ -50,16 +52,19 @@ Available in v5.1</source>
<target state="translated">#secreto#</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@" xml:space="preserve">
<trans-unit id="%@" xml:space="preserve" approved="no">
<source>%@</source>
<target state="needs-translation">%@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ %@" xml:space="preserve">
<trans-unit id="%@ %@" xml:space="preserve" approved="no">
<source>%@ %@</source>
<target state="needs-translation">%@ %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ / %@" xml:space="preserve">
<trans-unit id="%@ / %@" xml:space="preserve" approved="no">
<source>%@ / %@</source>
<target state="needs-translation">%@ / %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ is connected!" xml:space="preserve" approved="no">
@@ -117,12 +122,14 @@ Available in v5.1</source>
<target state="translated">%d mensagem(s) ignorada(s)</target>
<note>integrity error chat item</note>
</trans-unit>
<trans-unit id="%lld" xml:space="preserve">
<trans-unit id="%lld" xml:space="preserve" approved="no">
<source>%lld</source>
<target state="needs-translation">%lld</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld %@" xml:space="preserve">
<trans-unit id="%lld %@" xml:space="preserve" approved="no">
<source>%lld %@</source>
<target state="needs-translation">%lld %@</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lld contact(s) selected" xml:space="preserve" approved="no">
@@ -155,24 +162,29 @@ Available in v5.1</source>
<target state="translated">%lld segundos</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lldd" xml:space="preserve">
<trans-unit id="%lldd" xml:space="preserve" approved="no">
<source>%lldd</source>
<target state="needs-translation">%lldd</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lldh" xml:space="preserve">
<trans-unit id="%lldh" xml:space="preserve" approved="no">
<source>%lldh</source>
<target state="needs-translation">%lldh</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lldk" xml:space="preserve">
<trans-unit id="%lldk" xml:space="preserve" approved="no">
<source>%lldk</source>
<target state="needs-translation">%lldk</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lldm" xml:space="preserve">
<trans-unit id="%lldm" xml:space="preserve" approved="no">
<source>%lldm</source>
<target state="needs-translation">%lldm</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%lldmth" xml:space="preserve">
<trans-unit id="%lldmth" xml:space="preserve" approved="no">
<source>%lldmth</source>
<target state="needs-translation">%lldmth</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%llds" xml:space="preserve">
@@ -193,8 +205,9 @@ Available in v5.1</source>
<target state="translated">%u mensagens ignoradas.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="(" xml:space="preserve">
<trans-unit id="(" xml:space="preserve" approved="no">
<source>(</source>
<target state="needs-translation">(</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=")" xml:space="preserve">
@@ -359,8 +372,8 @@ Available in v5.1</source>
<source>Add servers by scanning QR codes.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
@@ -4540,6 +4553,31 @@ SimpleX servers cannot see your profile.</source>
<target state="translated">Confirmar envio</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ downloaded" xml:space="preserve" approved="no">
<source>%@ downloaded</source>
<target state="translated">%@ baixado</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="# %@" xml:space="preserve" approved="no">
<source># %@</source>
<target state="needs-translation"># %@</target>
<note>copied message info title, # &lt;title&gt;</note>
</trans-unit>
<trans-unit id="%@:" xml:space="preserve" approved="no">
<source>%@:</source>
<target state="needs-translation">%@:</target>
<note>copied message info</note>
</trans-unit>
<trans-unit id="%@ (current)" xml:space="preserve" approved="no">
<source>%@ (current)</source>
<target state="translated">%@(atual)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ (current):" xml:space="preserve" approved="no">
<source>%@ (current):</source>
<target state="translated">%@ (atual):</target>
<note>copied message info</note>
</trans-unit>
</body>
</file>
<file original="en.lproj/SimpleX--iOS--InfoPlist.strings" source-language="en" target-language="pt" datatype="plaintext">
@@ -127,11 +127,6 @@
<target>%@ подтверждён</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ серверы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ загружено</target>
@@ -590,6 +585,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Добавьте адрес в свой профиль, чтобы Ваши контакты могли поделиться им. Профиль будет отправлен Вашим контактам.</target>
@@ -610,16 +609,16 @@
<target>Добавить профиль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Добавить сервер</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Добавить серверы через QR код.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Добавить сервер…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Добавить на другое устройство</target>
@@ -710,8 +709,8 @@
<target>Все новые сообщения от %@ будут скрыты!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1354,6 +1353,10 @@
<target>Настройка ICE серверов</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Подтвердить</target>
@@ -1533,10 +1536,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Контакт разрешает</target>
@@ -1710,8 +1709,8 @@ This is your own one-time link!</source>
<target>Текущий пароль…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -3869,6 +3868,10 @@ This is your link for group %@!</source>
<target>Член группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Роль члена группы будет изменена на "%@". Все члены группы получат сообщение.</target>
@@ -3908,6 +3911,14 @@ This is your link for group %@!</source>
<target>Черновик сообщения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
@@ -3927,6 +3938,10 @@ This is your link for group %@!</source>
<target>Реакции на сообщения запрещены в этой группе.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Прямая доставка сообщений</target>
@@ -3950,10 +3965,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Текст сообщения</target>
@@ -4222,6 +4233,10 @@ This is your link for group %@!</source>
<target>Отсутствует токен устройства!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Нет отфильтрованных разговоров</target>
@@ -4469,6 +4484,10 @@ This is your link for group %@!</source>
<target>Другaя сеть</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4635,6 +4654,10 @@ Error: %@</source>
<target>Пожалуйста, надежно сохраните пароль, Вы НЕ сможете его поменять, если потеряете.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Польский интерфейс</target>
@@ -4704,6 +4727,10 @@ Error: %@</source>
<target>Конфиденциальная доставка</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Профиль и соединения на сервере</target>
@@ -5611,6 +5638,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Адрес сервера несовместим с настройками сети.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Сервер требует авторизации для создания очередей, проверьте пароль</target>
@@ -5635,6 +5666,14 @@ Enable in *Network &amp; servers* settings.</source>
<target>Версия сервера несовместима с настройками сети.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Серверы</target>
@@ -5772,6 +5811,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Показать статус сообщения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Показывать уведомления</target>
@@ -5996,10 +6039,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7062,9 +7101,9 @@ Repeat join request?</source>
<target>Вы можете сделать его видимым для ваших контактов в SimpleX через Настройки.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<target>Вы теперь можете отправлять сообщения %@</target>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Вы теперь можете общаться с %@</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="You can set lock screen notification preview via settings." xml:space="preserve">
@@ -7470,7 +7509,7 @@ SimpleX серверы не могут получить доступ к Ваше
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>заблокировано администратором</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7799,6 +7838,10 @@ SimpleX серверы не могут получить доступ к Ваше
<target>Пароль базы данных будет безопасно сохранен в iOS Keychain после запуска чата или изменения пароля - это позволит получать мгновенные уведомления.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>инкогнито через ссылку-контакт</target>
@@ -120,11 +120,6 @@
<target>%@ ได้รับการตรวจสอบแล้ว</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ เซิร์ฟเวอร์</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<note>No comment provided by engineer.</note>
@@ -559,6 +554,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>เพิ่มที่อยู่ลงในโปรไฟล์ของคุณ เพื่อให้ผู้ติดต่อของคุณสามารถแชร์กับผู้อื่นได้ การอัปเดตโปรไฟล์จะถูกส่งไปยังผู้ติดต่อของคุณ</target>
@@ -578,16 +577,16 @@
<target>เพิ่มโปรไฟล์</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>เพิ่มเซิร์ฟเวอร์</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>เพิ่มเซิร์ฟเวอร์โดยการสแกนรหัสคิวอาร์โค้ด</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>เพิ่มเซิร์ฟเวอร์…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>เพิ่มเข้าไปในอุปกรณ์อื่น</target>
@@ -675,8 +674,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1290,6 +1289,10 @@
<target>กำหนดค่าเซิร์ฟเวอร์ ICE</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>ยืนยัน</target>
@@ -1450,10 +1453,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>ผู้ติดต่ออนุญาต</target>
@@ -1618,8 +1617,8 @@ This is your own one-time link!</source>
<target>รหัสผ่านปัจจุบัน…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -3687,6 +3686,10 @@ This is your link for group %@!</source>
<target>สมาชิก</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>บทบาทของสมาชิกจะถูกเปลี่ยนเป็น "%@" สมาชิกกลุ่มทั้งหมดจะได้รับแจ้ง</target>
@@ -3725,6 +3728,14 @@ This is your link for group %@!</source>
<target>ร่างข้อความ</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
@@ -3744,6 +3755,10 @@ This is your link for group %@!</source>
<target>ปฏิกิริยาบนข้อความเป็นสิ่งต้องห้ามในกลุ่มนี้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
@@ -3764,10 +3779,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>ข้อความ</target>
@@ -4017,6 +4028,10 @@ This is your link for group %@!</source>
<target>ไม่มีโทเค็นอุปกรณ์!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>ไม่มีการกรองการแชท</target>
@@ -4252,6 +4267,10 @@ This is your link for group %@!</source>
<source>Other</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4410,6 +4429,10 @@ Error: %@</source>
<target>โปรดจัดเก็บรหัสผ่านอย่างปลอดภัย คุณจะไม่สามารถเปลี่ยนรหัสผ่านได้หากคุณทำรหัสผ่านหาย</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>อินเตอร์เฟซภาษาโปแลนด์</target>
@@ -4475,6 +4498,10 @@ Error: %@</source>
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>การเชื่อมต่อโปรไฟล์และเซิร์ฟเวอร์</target>
@@ -5347,6 +5374,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>เซิร์ฟเวอร์ต้องการการอนุญาตในการสร้างคิว โปรดตรวจสอบรหัสผ่าน</target>
@@ -5370,6 +5401,14 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>เซิร์ฟเวอร์</target>
@@ -5500,6 +5539,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>แสดงตัวอย่าง</target>
@@ -5715,10 +5758,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6715,8 +6754,8 @@ Repeat join request?</source>
<source>You can make it visible to your SimpleX contacts via Settings.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>ตอนนี้คุณสามารถส่งข้อความถึง %@</target>
<note>notification body</note>
</trans-unit>
@@ -7107,7 +7146,7 @@ SimpleX servers cannot see your profile.</source>
</trans-unit>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7430,6 +7469,10 @@ SimpleX servers cannot see your profile.</source>
<target>iOS Keychain จะใช้เพื่อจัดเก็บรหัสผ่านอย่างปลอดภัยหลังจากที่คุณรีสตาร์ทแอปหรือเปลี่ยนรหัสผ่าน ซึ่งจะช่วยให้รับการแจ้งเตือนแบบทันทีได้</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>ไม่ระบุตัวตนผ่านลิงค์ที่อยู่ติดต่อ</target>
@@ -127,11 +127,6 @@
<target>%@ onaylandı</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ sunucuları</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ yüklendi</target>
@@ -590,6 +585,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Kişilerinizin başkalarıyla paylaşabilmesi için profilinize adres ekleyin. Profil güncellemesi kişilerinize gönderilecek.</target>
@@ -610,16 +609,16 @@
<target>Profil ekle</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Sunucu ekle</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Karekod taratarak sunucuları ekleyin.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Sunucu ekle…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Başka bir cihaza ekle</target>
@@ -710,8 +709,8 @@
<target>%@ 'den gelen bütün yeni mesajlar saklı olacak!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1354,6 +1353,10 @@
<target>ICE sunucularını ayarla</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Onayla</target>
@@ -1533,10 +1536,6 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Kişi izin veriyor</target>
@@ -1710,8 +1709,8 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
<target>Şu anki parola…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1837,6 +1836,7 @@ Bu senin kendi tek kullanımlık bağlantın!</target>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<target>Hata ayıklama teslimatı</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
@@ -3869,6 +3869,10 @@ Bu senin grup için bağlantın %@!</target>
<target>Kişi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Üye rolü "%@" olarak değiştirilecektir. Ve tüm grup üyeleri bilgilendirilecektir.</target>
@@ -3908,8 +3912,17 @@ Bu senin grup için bağlantın %@!</target>
<target>Mesaj taslağı</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<target>Mesaj kuyruğu bilgisi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
@@ -3927,6 +3940,10 @@ Bu senin grup için bağlantın %@!</target>
<target>Mesaj tepkileri bu grupta yasaklandı.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Mesaj yönlendirme yedeklemesi</target>
@@ -3950,10 +3967,6 @@ Bu senin grup için bağlantın %@!</target>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Mesaj yazısı</target>
@@ -4222,6 +4235,10 @@ Bu senin grup için bağlantın %@!</target>
<target>Cihaz tokeni yok!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Filtrelenmiş sohbetler yok</target>
@@ -4469,6 +4486,10 @@ Bu senin grup için bağlantın %@!</target>
<target>Diğer</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4635,6 +4656,10 @@ Hata: %@</target>
<target>Lütfen parolayı güvenli bir şekilde saklayın, kaybederseniz parolayı DEĞİŞTİREMEZSİNİZ.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Lehçe arayüz</target>
@@ -4704,6 +4729,10 @@ Hata: %@</target>
<target>Gizli yönlendirme</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>Profil ve sunucu bağlantıları</target>
@@ -5611,6 +5640,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Sunucu adresi ağ ayarlarıyla uyumlu değil.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Sunucunun sıra oluşturması için yetki gereklidir, şifreyi kontrol edin</target>
@@ -5635,6 +5668,14 @@ Enable in *Network &amp; servers* settings.</source>
<target>Sunucu sürümü ağ ayarlarıyla uyumlu değil.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Sunucular</target>
@@ -5772,6 +5813,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Mesaj durumunu göster</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Ön gösterimi göser</target>
@@ -5996,10 +6041,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7062,8 +7103,8 @@ Katılma isteği tekrarlansın mı?</target>
<target>Ayarlardan SimpleX kişilerinize görünür yapabilirsiniz.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Artık %@ adresine mesaj gönderebilirsin</target>
<note>notification body</note>
</trans-unit>
@@ -7470,7 +7511,7 @@ SimpleX sunucuları profilinizi göremez.</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>yönetici tarafından engellendi</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7799,6 +7840,10 @@ SimpleX sunucuları profilinizi göremez.</target>
<target>iOS Anahtar Zinciri, uygulamayı yeniden başlattıktan veya parolayı değiştirdikten sonra parolayı güvenli bir şekilde saklamak için kullanılacaktır - anlık bildirimlerin alınmasına izin verecektir.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>kişi bağlantı linki aracılığıyla gizli</target>
@@ -8083,6 +8128,9 @@ SimpleX sunucuları profilinizi göremez.</target>
<source>server queue info: %1$@
last received msg: %2$@</source>
<target>sunucu kuyruk bilgisi: %1$@
son alınan msj: %2$@</target>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
@@ -127,11 +127,6 @@
<target>%@ перевірено</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ сервери</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<target>%@ завантажено</target>
@@ -590,6 +585,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>Додайте адресу до свого профілю, щоб ваші контакти могли поділитися нею з іншими людьми. Повідомлення про оновлення профілю буде надіслано вашим контактам.</target>
@@ -610,16 +609,16 @@
<target>Додати профіль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>Додати сервер</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>Додайте сервери, відсканувавши QR-код.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>Додати сервер…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>Додати до іншого пристрою</target>
@@ -710,8 +709,8 @@
<target>Всі нові повідомлення від %@ будуть приховані!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1354,6 +1353,10 @@
<target>Налаштування серверів ICE</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>Підтвердити</target>
@@ -1533,10 +1536,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>Контакт дозволяє</target>
@@ -1710,8 +1709,8 @@ This is your own one-time link!</source>
<target>Поточна парольна фраза…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -1837,6 +1836,7 @@ This is your own one-time link!</source>
</trans-unit>
<trans-unit id="Debug delivery" xml:space="preserve">
<source>Debug delivery</source>
<target>Доставка налагодження</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Decentralized" xml:space="preserve">
@@ -3869,6 +3869,10 @@ This is your link for group %@!</source>
<target>Учасник</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Роль учасника буде змінено на "%@". Всі учасники групи будуть повідомлені про це.</target>
@@ -3908,8 +3912,17 @@ This is your link for group %@!</source>
<target>Чернетка повідомлення</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<target>Інформація про чергу повідомлень</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reactions" xml:space="preserve">
@@ -3927,6 +3940,10 @@ This is your link for group %@!</source>
<target>Реакції на повідомлення в цій групі заборонені.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<target>Запасний варіант маршрутизації повідомлень</target>
@@ -3950,10 +3967,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>Текст повідомлення</target>
@@ -4222,6 +4235,10 @@ This is your link for group %@!</source>
<target>Токен пристрою відсутній!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>Немає фільтрованих чатів</target>
@@ -4469,6 +4486,10 @@ This is your link for group %@!</source>
<target>Інше</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4635,6 +4656,10 @@ Error: %@</source>
<target>Будь ласка, зберігайте пароль надійно, ви НЕ зможете змінити його, якщо втратите.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>Польський інтерфейс</target>
@@ -4704,6 +4729,10 @@ Error: %@</source>
<target>Приватна маршрутизація</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>З'єднання профілю та сервера</target>
@@ -5611,6 +5640,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Адреса сервера несумісна з налаштуваннями мережі.</target>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>Сервер вимагає авторизації для створення черг, перевірте пароль</target>
@@ -5635,6 +5668,14 @@ Enable in *Network &amp; servers* settings.</source>
<target>Серверна версія несумісна з мережевими налаштуваннями.</target>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>Сервери</target>
@@ -5772,6 +5813,10 @@ Enable in *Network &amp; servers* settings.</source>
<target>Показати статус повідомлення</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>Показати попередній перегляд</target>
@@ -5996,10 +6041,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -7062,8 +7103,8 @@ Repeat join request?</source>
<target>Ви можете зробити його видимим для ваших контактів у SimpleX за допомогою налаштувань.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>Тепер ви можете надсилати повідомлення на адресу %@</target>
<note>notification body</note>
</trans-unit>
@@ -7470,7 +7511,7 @@ SimpleX servers cannot see your profile.</source>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>заблоковано адміністратором</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7799,6 +7840,10 @@ SimpleX servers cannot see your profile.</source>
<target>Пароль бази даних буде безпечно збережено в iOS Keychain після запуску чату або зміни пароля - це дасть змогу отримувати миттєві повідомлення.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>інкогніто за посиланням на контактну адресу</target>
@@ -8083,6 +8128,9 @@ SimpleX servers cannot see your profile.</source>
<source>server queue info: %1$@
last received msg: %2$@</source>
<target>інформація про чергу на сервері: %1$@
останнє отримане повідомлення: %2$@</target>
<note>queue info</note>
</trans-unit>
<trans-unit id="set new contact address" xml:space="preserve">
@@ -126,11 +126,6 @@
<target>%@ 已认证</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ servers" xml:space="preserve">
<source>%@ servers</source>
<target>%@ 服务器</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="%@ uploaded" xml:space="preserve">
<source>%@ uploaded</source>
<note>No comment provided by engineer.</note>
@@ -578,6 +573,10 @@
<source>Acknowledgement errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Active connections" xml:space="preserve">
<source>Active connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." xml:space="preserve">
<source>Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts.</source>
<target>将地址添加到您的个人资料,以便您的联系人可以与其他人共享。个人资料更新将发送给您的联系人。</target>
@@ -598,16 +597,16 @@
<target>添加个人资料</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve">
<source>Add server</source>
<target>添加服务器</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add servers by scanning QR codes." xml:space="preserve">
<source>Add servers by scanning QR codes.</source>
<target>扫描二维码来添加服务器。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server…" xml:space="preserve">
<source>Add server…</source>
<target>添加服务器…</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve">
<source>Add to another device</source>
<target>添加另一设备</target>
@@ -697,8 +696,8 @@
<source>All new messages from %@ will be hidden!</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All users" xml:space="preserve">
<source>All users</source>
<trans-unit id="All profiles" xml:space="preserve">
<source>All profiles</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1337,6 +1336,10 @@
<target>配置 ICE 服务器</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configured %@ servers" xml:space="preserve">
<source>Configured %@ servers</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Confirm" xml:space="preserve">
<source>Confirm</source>
<target>确认</target>
@@ -1509,10 +1512,6 @@ This is your own one-time link!</source>
<source>Connections</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Connections subscribed" xml:space="preserve">
<source>Connections subscribed</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Contact allows" xml:space="preserve">
<source>Contact allows</source>
<target>联系人允许</target>
@@ -1684,8 +1683,8 @@ This is your own one-time link!</source>
<target>现有密码……</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Current user" xml:space="preserve">
<source>Current user</source>
<trans-unit id="Current profile" xml:space="preserve">
<source>Current profile</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Currently maximum supported file size is %@." xml:space="preserve">
@@ -3819,6 +3818,10 @@ This is your link for group %@!</source>
<target>成员</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>成员角色将更改为 "%@"。所有群成员将收到通知。</target>
@@ -3857,6 +3860,14 @@ This is your link for group %@!</source>
<target>消息草稿</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message forwarded" xml:space="preserve">
<source>Message forwarded</source>
<note>item status text</note>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
<source>Message queue info</source>
<note>No comment provided by engineer.</note>
@@ -3876,6 +3887,10 @@ This is your link for group %@!</source>
<target>该群组禁用了消息回应。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message reception" xml:space="preserve">
<source>Message reception</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message routing fallback" xml:space="preserve">
<source>Message routing fallback</source>
<note>No comment provided by engineer.</note>
@@ -3897,10 +3912,6 @@ This is your link for group %@!</source>
<source>Message status: %@</source>
<note>copied message info</note>
</trans-unit>
<trans-unit id="Message subscriptions" xml:space="preserve">
<source>Message subscriptions</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Message text" xml:space="preserve">
<source>Message text</source>
<target>消息正文</target>
@@ -4165,6 +4176,10 @@ This is your link for group %@!</source>
<target>无设备令牌!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No direct connection yet, message is forwarded by admin." xml:space="preserve">
<source>No direct connection yet, message is forwarded by admin.</source>
<note>item status description</note>
</trans-unit>
<trans-unit id="No filtered chats" xml:space="preserve">
<source>No filtered chats</source>
<target>无过滤聊天</target>
@@ -4410,6 +4425,10 @@ This is your link for group %@!</source>
<target>其他</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Other %@ servers" xml:space="preserve">
<source>Other %@ servers</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>
@@ -4573,6 +4592,10 @@ Error: %@</source>
<target>请安全地保存密码,如果您丢失了密码,您将无法更改它。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Please try later." xml:space="preserve">
<source>Please try later.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Polish interface" xml:space="preserve">
<source>Polish interface</source>
<target>波兰语界面</target>
@@ -4639,6 +4662,10 @@ Error: %@</source>
<source>Private routing</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Private routing error" xml:space="preserve">
<source>Private routing error</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Profile and server connections" xml:space="preserve">
<source>Profile and server connections</source>
<target>资料和服务器连接</target>
@@ -5535,6 +5562,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server address is incompatible with network settings.</source>
<note>srv error text.</note>
</trans-unit>
<trans-unit id="Server address is incompatible with network settings: %@." xml:space="preserve">
<source>Server address is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server requires authorization to create queues, check password" xml:space="preserve">
<source>Server requires authorization to create queues, check password</source>
<target>服务器需要授权才能创建队列,检查密码</target>
@@ -5558,6 +5589,14 @@ Enable in *Network &amp; servers* settings.</source>
<source>Server version is incompatible with network settings.</source>
<note>srv error text</note>
</trans-unit>
<trans-unit id="Server version is incompatible with network settings: %@." xml:space="preserve">
<source>Server version is incompatible with network settings: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Server version is incompatible with your app: %@." xml:space="preserve">
<source>Server version is incompatible with your app: %@.</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Servers" xml:space="preserve">
<source>Servers</source>
<target>服务器</target>
@@ -5694,6 +5733,10 @@ Enable in *Network &amp; servers* settings.</source>
<source>Show message status</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show percentage" xml:space="preserve">
<source>Show percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show preview" xml:space="preserve">
<source>Show preview</source>
<target>显示预览</target>
@@ -5917,10 +5960,6 @@ Enable in *Network &amp; servers* settings.</source>
<source>Subscription errors</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscription percentage" xml:space="preserve">
<source>Subscription percentage</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Subscriptions ignored" xml:space="preserve">
<source>Subscriptions ignored</source>
<note>No comment provided by engineer.</note>
@@ -6965,8 +7004,8 @@ Repeat join request?</source>
<target>你可以通过设置让它对你的 SimpleX 联系人可见。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
<source>You can now send messages to %@</source>
<trans-unit id="You can now chat with %@" xml:space="preserve">
<source>You can now chat with %@</source>
<target>您现在可以给 %@ 发送消息</target>
<note>notification body</note>
</trans-unit>
@@ -7368,7 +7407,7 @@ SimpleX 服务器无法看到您的资料。</target>
<trans-unit id="blocked by admin" xml:space="preserve">
<source>blocked by admin</source>
<target>由管理员封禁</target>
<note>blocked chat item</note>
<note>marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="bold" xml:space="preserve">
<source>bold</source>
@@ -7696,6 +7735,10 @@ SimpleX 服务器无法看到您的资料。</target>
<target>在您重启应用或改变密码后,iOS钥匙串将被用来安全地存储密码——它将允许接收推送通知。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="inactive" xml:space="preserve">
<source>inactive</source>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>通过联系人地址链接隐身聊天</target>
@@ -358,9 +358,9 @@
<target state="translated">使用二維碼掃描以新增伺服器。</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">新增伺服器</target>
<trans-unit id="Add server" xml:space="preserve" approved="no">
<source>Add server</source>
<target state="translated">新增伺服器</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add to another device" xml:space="preserve" approved="no">
+12 -14
View File
@@ -119,7 +119,7 @@ class NotificationService: UNNotificationServiceExtension {
var threadId: UUID? = NSEThreads.shared.newThread()
var notificationInfo: NtfMessages?
var receiveEntityId: String?
var expectedMessages: Set<String> = []
var expectedMessage: String?
// return true if the message is taken - it prevents sending it to another NotificationService instance for processing
var shouldProcessNtf = false
var appSubscriber: AppSubscriber?
@@ -191,7 +191,7 @@ class NotificationService: UNNotificationServiceExtension {
let dbStatus = startChat()
if case .ok = dbStatus,
let ntfInfo = apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo) {
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessages.count))")
logger.debug("NotificationService: receiveNtfMessages: apiGetNtfMessage \(String(describing: ntfInfo.ntfMessage_ == nil ? 0 : 1))")
if let connEntity = ntfInfo.connEntity_ {
setBestAttemptNtf(
ntfInfo.ntfsEnabled
@@ -201,7 +201,7 @@ class NotificationService: UNNotificationServiceExtension {
if let id = connEntity.id, ntfInfo.msgTs != nil {
notificationInfo = ntfInfo
receiveEntityId = id
expectedMessages = Set(ntfInfo.ntfMessages.map { $0.msgId })
expectedMessage = ntfInfo.ntfMessage_.flatMap { $0.msgId }
shouldProcessNtf = true
return
}
@@ -224,12 +224,10 @@ class NotificationService: UNNotificationServiceExtension {
self.setBestAttemptNtf(.empty)
}
if case let .msgInfo(info) = ntf {
let found = expectedMessages.remove(info.msgId)
if found != nil {
logger.debug("NotificationService processNtf: msgInfo, last: \(self.expectedMessages.isEmpty)")
if expectedMessages.isEmpty {
self.deliverBestAttemptNtf()
}
if info.msgId == expectedMessage {
expectedMessage = nil
logger.debug("NotificationService processNtf: msgInfo")
self.deliverBestAttemptNtf()
return true
} else if info.msgTs > msgTs {
logger.debug("NotificationService processNtf: unexpected msgInfo, let other instance to process it, stopping this one")
@@ -636,7 +634,7 @@ func apiGetActiveUser() -> User? {
}
func apiStartChat() throws -> Bool {
let r = sendSimpleXCmd(.startChat(mainApp: false))
let r = sendSimpleXCmd(.startChat(mainApp: false, enableSndFiles: false))
switch r {
case .chatStarted: return true
case .chatRunning: return false
@@ -677,9 +675,9 @@ func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
return nil
}
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
if case let .ntfMessages(user, connEntity_, msgTs, ntfMessages) = r, let user = user {
logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessages.count)")
return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessages: ntfMessages)
if case let .ntfMessages(user, connEntity_, msgTs, ntfMessage_) = r, let user = user {
logger.debug("apiGetNtfMessage response ntfMessages: \(ntfMessage_ == nil ? 0 : 1)")
return NtfMessages(user: user, connEntity_: connEntity_, msgTs: msgTs, ntfMessage_: ntfMessage_)
} else if case let .chatCmdError(_, error) = r {
logger.debug("apiGetNtfMessage error response: \(String.init(describing: error))")
} else {
@@ -726,7 +724,7 @@ struct NtfMessages {
var user: User
var connEntity_: ConnectionEntity?
var msgTs: Date?
var ntfMessages: [NtfMsgInfo]
var ntfMessage_: NtfMsgInfo?
var ntfsEnabled: Bool {
user.showNotifications && (connEntity_?.ntfsEnabled ?? false)
+55 -39
View File
@@ -100,7 +100,6 @@
5CB924D727A8563F00ACCCDD /* SettingsView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924D627A8563F00ACCCDD /* SettingsView.swift */; };
5CB924E127A867BA00ACCCDD /* UserProfile.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB924E027A867BA00ACCCDD /* UserProfile.swift */; };
5CB9250D27A9432000ACCCDD /* ChatListNavLink.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CB9250C27A9432000ACCCDD /* ChatListNavLink.swift */; };
5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */; };
5CBD285C29575B8E00EC2CF4 /* WhatsNewView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD285B29575B8E00EC2CF4 /* WhatsNewView.swift */; };
5CBE6C12294487F7002D9531 /* VerifyCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBE6C11294487F7002D9531 /* VerifyCodeView.swift */; };
5CBE6C142944CC12002D9531 /* ScanCodeView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBE6C132944CC12002D9531 /* ScanCodeView.swift */; };
@@ -171,6 +170,11 @@
649BCDA22805D6EF00C3A862 /* CIImageView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 649BCDA12805D6EF00C3A862 /* CIImageView.swift */; };
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
64BAC45E2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC4592C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a */; };
64BAC45F2C495205008D3995 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC45A2C495205008D3995 /* libffi.a */; };
64BAC4602C495205008D3995 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC45B2C495205008D3995 /* libgmpxx.a */; };
64BAC4612C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC45C2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a */; };
64BAC4622C495205008D3995 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64BAC45D2C495205008D3995 /* libgmp.a */; };
64C06EB52A0A4A7C00792D4D /* ChatItemInfoView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */; };
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
@@ -195,6 +199,8 @@
8C9BC2652C240D5200875A27 /* ThemeModeEditor.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8C9BC2642C240D5100875A27 /* ThemeModeEditor.swift */; };
8CC4ED902BD7B8530078AEE8 /* CallAudioDeviceManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC4ED8F2BD7B8530078AEE8 /* CallAudioDeviceManager.swift */; };
8CC956EE2BC0041000412A11 /* NetworkObserver.swift in Sources */ = {isa = PBXBuildFile; fileRef = 8CC956ED2BC0041000412A11 /* NetworkObserver.swift */; };
CE38A29A2C3FCA54005ED185 /* ImageUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5CBD2859295711D700EC2CF4 /* ImageUtils.swift */; };
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = CE38A29B2C3FCD72005ED185 /* SwiftyGif */; };
CE984D4B2C36C5D500E3AEFF /* ChatItemClipShape.swift in Sources */ = {isa = PBXBuildFile; fileRef = CE984D4A2C36C5D500E3AEFF /* ChatItemClipShape.swift */; };
CEEA861D2C2ABCB50084E1EA /* ReverseList.swift in Sources */ = {isa = PBXBuildFile; fileRef = CEEA861C2C2ABCB50084E1EA /* ReverseList.swift */; };
D7197A1829AE89660055C05A /* WebRTC in Frameworks */ = {isa = PBXBuildFile; productRef = D7197A1729AE89660055C05A /* WebRTC */; };
@@ -203,11 +209,7 @@
D741547A29AF90B00022400A /* PushKit.framework in Frameworks */ = {isa = PBXBuildFile; fileRef = D741547929AF90B00022400A /* PushKit.framework */; };
D77B92DC2952372200A5A1CC /* SwiftyGif in Frameworks */ = {isa = PBXBuildFile; productRef = D77B92DB2952372200A5A1CC /* SwiftyGif */; };
D7F0E33929964E7E0068AF69 /* LZString in Frameworks */ = {isa = PBXBuildFile; productRef = D7F0E33829964E7E0068AF69 /* LZString */; };
E52FF8DA2C34676700BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E52FF8D52C34676600BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7-ghc9.6.3.a */; };
E52FF8DB2C34676700BF81EB /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E52FF8D62C34676600BF81EB /* libgmpxx.a */; };
E52FF8DC2C34676700BF81EB /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E52FF8D72C34676700BF81EB /* libffi.a */; };
E52FF8DD2C34676700BF81EB /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E52FF8D82C34676700BF81EB /* libgmp.a */; };
E52FF8DE2C34676700BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = E52FF8D92C34676700BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7.a */; };
E50581062C3DDD9D009C3F71 /* Yams in Frameworks */ = {isa = PBXBuildFile; productRef = E50581052C3DDD9D009C3F71 /* Yams */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -478,6 +480,11 @@
649BCDA12805D6EF00C3A862 /* CIImageView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIImageView.swift; sourceTree = "<group>"; };
64AA1C6827EE10C800AC7277 /* ContextItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextItemView.swift; sourceTree = "<group>"; };
64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = "<group>"; };
64BAC4592C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a"; sourceTree = "<group>"; };
64BAC45A2C495205008D3995 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
64BAC45B2C495205008D3995 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
64BAC45C2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a"; sourceTree = "<group>"; };
64BAC45D2C495205008D3995 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
64C06EB42A0A4A7C00792D4D /* ChatItemInfoView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatItemInfoView.swift; sourceTree = "<group>"; };
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = "<group>"; };
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
@@ -508,11 +515,6 @@
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; };
D7AA2C3429A936B400737B40 /* MediaEncryption.playground */ = {isa = PBXFileReference; lastKnownFileType = file.playground; name = MediaEncryption.playground; path = Shared/MediaEncryption.playground; sourceTree = SOURCE_ROOT; xcLanguageSpecificationIdentifier = xcode.lang.swift; };
E52FF8D52C34676600BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7-ghc9.6.3.a"; sourceTree = "<group>"; };
E52FF8D62C34676600BF81EB /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
E52FF8D72C34676700BF81EB /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
E52FF8D82C34676700BF81EB /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
E52FF8D92C34676700BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7.a"; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -551,13 +553,15 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
E52FF8DE2C34676700BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7.a in Frameworks */,
E52FF8DB2C34676700BF81EB /* libgmpxx.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
64BAC4622C495205008D3995 /* libgmp.a in Frameworks */,
64BAC45F2C495205008D3995 /* libffi.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
E52FF8DC2C34676700BF81EB /* libffi.a in Frameworks */,
E52FF8DD2C34676700BF81EB /* libgmp.a in Frameworks */,
E52FF8DA2C34676700BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7-ghc9.6.3.a in Frameworks */,
E50581062C3DDD9D009C3F71 /* Yams in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
64BAC4612C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a in Frameworks */,
64BAC45E2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a in Frameworks */,
64BAC4602C495205008D3995 /* libgmpxx.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -624,11 +628,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
E52FF8D72C34676700BF81EB /* libffi.a */,
E52FF8D82C34676700BF81EB /* libgmp.a */,
E52FF8D62C34676600BF81EB /* libgmpxx.a */,
E52FF8D52C34676600BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7-ghc9.6.3.a */,
E52FF8D92C34676700BF81EB /* libHSsimplex-chat-5.8.2.0-D50x9PQRTdqAAO9AQK2dA7.a */,
64BAC45A2C495205008D3995 /* libffi.a */,
64BAC45D2C495205008D3995 /* libgmp.a */,
64BAC45B2C495205008D3995 /* libgmpxx.a */,
64BAC45C2C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU-ghc9.6.3.a */,
64BAC4592C495205008D3995 /* libHSsimplex-chat-6.0.0.1-J5MWx9pYOGnDBWRfMkQxFU.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -656,7 +660,6 @@
5CF937212B25034A00E1D781 /* NSESubscriber.swift */,
5CB346E82869E8BA001FD2EF /* PushEnvironment.swift */,
5C93293E2928E0FD0090FFF9 /* AudioRecPlay.swift */,
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
8CC956ED2BC0041000412A11 /* NetworkObserver.swift */,
);
path = Model;
@@ -856,6 +859,7 @@
5C9FD96A27A56D4D0075386C /* JSON.swift */,
5CDCAD7D2818941F00503DA2 /* API.swift */,
5CDCAD80281A7E2700503DA2 /* Notifications.swift */,
5CBD2859295711D700EC2CF4 /* ImageUtils.swift */,
64DAE1502809D9F5000DA960 /* FileUtils.swift */,
5C9D81182AA7A4F1001D49FD /* CryptoFile.swift */,
5C00168028C4FE760094D739 /* KeyChain.swift */,
@@ -1065,6 +1069,8 @@
);
name = SimpleXChat;
packageProductDependencies = (
E50581052C3DDD9D009C3F71 /* Yams */,
CE38A29B2C3FCD72005ED185 /* SwiftyGif */,
);
productName = SimpleXChat;
productReference = 5CE2BA682845308900EC33A6 /* SimpleXChat.framework */;
@@ -1202,7 +1208,6 @@
6432857C2925443C00FBE5C8 /* GroupPreferencesView.swift in Sources */,
5C93293129239BED0090FFF9 /* ProtocolServerView.swift in Sources */,
5C9CC7AD28C55D7800BEF955 /* DatabaseEncryptionView.swift in Sources */,
5CBD285A295711D700EC2CF4 /* ImageUtils.swift in Sources */,
8C74C3EC2C1B92A900039E77 /* Theme.swift in Sources */,
6419EC562AB8BC8B004A607A /* ContextInvitingContactMemberView.swift in Sources */,
5CE4407927ADB701007B033A /* EmojiItemView.swift in Sources */,
@@ -1373,6 +1378,7 @@
5CE2BA90284533A300EC33A6 /* JSON.swift in Sources */,
5CE2BA8B284533A300EC33A6 /* ChatTypes.swift in Sources */,
5CE2BA8F284533A300EC33A6 /* APITypes.swift in Sources */,
CE38A29A2C3FCA54005ED185 /* ImageUtils.swift in Sources */,
5C9D811A2AA8727A001D49FD /* CryptoFile.swift in Sources */,
5CE2BA8C284533A300EC33A6 /* AppGroup.swift in Sources */,
8C74C3E52C1B900600039E77 /* ThemeTypes.swift in Sources */,
@@ -1612,7 +1618,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 226;
CURRENT_PROJECT_VERSION = 228;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -1637,7 +1643,7 @@
"@executable_path/Frameworks",
);
LLVM_LTO = YES_THIN;
MARKETING_VERSION = 5.8.2;
MARKETING_VERSION = 6.0;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1661,7 +1667,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 226;
CURRENT_PROJECT_VERSION = 228;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -1686,7 +1692,7 @@
"@executable_path/Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.2;
MARKETING_VERSION = 6.0;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.app;
PRODUCT_NAME = SimpleX;
SDKROOT = iphoneos;
@@ -1747,7 +1753,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 226;
CURRENT_PROJECT_VERSION = 228;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s;
@@ -1762,7 +1768,7 @@
"@executable_path/../../Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.2;
MARKETING_VERSION = 6.0;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1784,7 +1790,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 226;
CURRENT_PROJECT_VERSION = 228;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO;
@@ -1799,7 +1805,7 @@
"@executable_path/../../Frameworks",
);
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.2;
MARKETING_VERSION = 6.0;
PRODUCT_BUNDLE_IDENTIFIER = "chat.simplex.app.SimpleX-NSE";
PRODUCT_NAME = "$(TARGET_NAME)";
PROVISIONING_PROFILE_SPECIFIER = "";
@@ -1821,7 +1827,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 226;
CURRENT_PROJECT_VERSION = 228;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1847,7 +1853,7 @@
"$(PROJECT_DIR)/Libraries/sim",
);
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.2;
MARKETING_VERSION = 6.0;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -1872,7 +1878,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 226;
CURRENT_PROJECT_VERSION = 228;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -1898,7 +1904,7 @@
"$(PROJECT_DIR)/Libraries/sim",
);
LLVM_LTO = YES;
MARKETING_VERSION = 5.8.2;
MARKETING_VERSION = 6.0;
PRODUCT_BUNDLE_IDENTIFIER = chat.simplex.SimpleXChat;
PRODUCT_NAME = "$(TARGET_NAME:c99extidentifier)";
SDKROOT = iphoneos;
@@ -1971,8 +1977,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/twostraws/CodeScanner";
requirement = {
kind = upToNextMajorVersion;
minimumVersion = 2.0.0;
kind = exactVersion;
version = 2.1.1;
};
};
8C73C1162C21E17B00892670 /* XCRemoteSwiftPackageReference "Yams" */ = {
@@ -1995,8 +2001,8 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/kirualex/SwiftyGif";
requirement = {
branch = master;
kind = branch;
kind = revision;
revision = 5e8619335d394901379c9add5c4c1c2f420b3800;
};
};
D7F0E33729964E7D0068AF69 /* XCRemoteSwiftPackageReference "lzstring-swift" */ = {
@@ -2020,6 +2026,11 @@
package = 8C73C1162C21E17B00892670 /* XCRemoteSwiftPackageReference "Yams" */;
productName = Yams;
};
CE38A29B2C3FCD72005ED185 /* SwiftyGif */ = {
isa = XCSwiftPackageProductDependency;
package = D77B92DA2952372200A5A1CC /* XCRemoteSwiftPackageReference "SwiftyGif" */;
productName = SwiftyGif;
};
D7197A1729AE89660055C05A /* WebRTC */ = {
isa = XCSwiftPackageProductDependency;
package = D7197A1629AE89660055C05A /* XCRemoteSwiftPackageReference "WebRTC" */;
@@ -2035,6 +2046,11 @@
package = D7F0E33729964E7D0068AF69 /* XCRemoteSwiftPackageReference "lzstring-swift" */;
productName = LZString;
};
E50581052C3DDD9D009C3F71 /* Yams */ = {
isa = XCSwiftPackageProductDependency;
package = 8C73C1162C21E17B00892670 /* XCRemoteSwiftPackageReference "Yams" */;
productName = Yams;
};
/* End XCSwiftPackageProductDependency section */
};
rootObject = 5CA059BE279559F40002BEB4 /* Project object */;
+15 -7
View File
@@ -205,7 +205,7 @@ public func chatResponse(_ s: String) -> ChatResponse {
if let chatData = try? parseChatData(jChat) {
return chatData
}
return ChatData.invalidJSON(prettyJSON(jChat) ?? "")
return ChatData.invalidJSON(serializeJSON(jChat, options: .prettyPrinted) ?? "")
}
return .apiChats(user: user, chats: chats)
}
@@ -218,15 +218,15 @@ public func chatResponse(_ s: String) -> ChatResponse {
}
} else if type == "chatCmdError" {
if let jError = jResp["chatCmdError"] as? NSDictionary {
return .chatCmdError(user_: decodeUser_(jError), chatError: .invalidJSON(json: prettyJSON(jError) ?? ""))
return .chatCmdError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
}
} else if type == "chatError" {
if let jError = jResp["chatError"] as? NSDictionary {
return .chatError(user_: decodeUser_(jError), chatError: .invalidJSON(json: prettyJSON(jError) ?? ""))
return .chatError(user_: decodeUser_(jError), chatError: .invalidJSON(json: errorJson(jError) ?? ""))
}
}
}
json = prettyJSON(j)
json = serializeJSON(j, options: .prettyPrinted)
}
return ChatResponse.response(type: type ?? "invalid", json: json ?? s)
}
@@ -239,6 +239,14 @@ private func decodeUser_(_ jDict: NSDictionary) -> UserRef? {
}
}
private func errorJson(_ jDict: NSDictionary) -> String? {
if let chatError = jDict["chatError"] {
serializeJSON(chatError)
} else {
serializeJSON(jDict)
}
}
func parseChatData(_ jChat: Any) throws -> ChatData {
let jChatDict = jChat as! NSDictionary
let chatInfo: ChatInfo = try decodeObject(jChatDict["chatInfo"]!)
@@ -251,7 +259,7 @@ func parseChatData(_ jChat: Any) throws -> ChatData {
return ChatItem.invalidJSON(
chatDir: decodeProperty(jCI, "chatDir"),
meta: decodeProperty(jCI, "meta"),
json: prettyJSON(jCI) ?? ""
json: serializeJSON(jCI, options: .prettyPrinted) ?? ""
)
}
return ChatData(chatInfo: chatInfo, chatItems: chatItems, chatStats: chatStats)
@@ -268,8 +276,8 @@ func decodeProperty<T: Decodable>(_ obj: Any, _ prop: NSString) -> T? {
return nil
}
func prettyJSON(_ obj: Any) -> String? {
if let d = try? JSONSerialization.data(withJSONObject: obj, options: .prettyPrinted) {
func serializeJSON(_ obj: Any, options: JSONSerialization.WritingOptions = []) -> String? {
if let d = try? JSONSerialization.data(withJSONObject: obj, options: options) {
return String(decoding: d, as: UTF8.self)
}
return nil
+66 -37
View File
@@ -26,7 +26,7 @@ public enum ChatCommand {
case apiMuteUser(userId: Int64)
case apiUnmuteUser(userId: Int64)
case apiDeleteUser(userId: Int64, delSMPQueues: Bool, viewPwd: String?)
case startChat(mainApp: Bool)
case startChat(mainApp: Bool, enableSndFiles: Bool)
case apiStopChat
case apiActivateChat(restoreChat: Bool)
case apiSuspendChat(timeoutMicroseconds: Int)
@@ -146,6 +146,7 @@ public enum ChatCommand {
case apiStandaloneFileInfo(url: String)
// misc
case showVersion
case getAgentSubsTotal(userId: Int64)
case getAgentServersSummary(userId: Int64)
case resetAgentServersStats
case string(String)
@@ -171,7 +172,7 @@ public enum ChatCommand {
case let .apiMuteUser(userId): return "/_mute user \(userId)"
case let .apiUnmuteUser(userId): return "/_unmute user \(userId)"
case let .apiDeleteUser(userId, delSMPQueues, viewPwd): return "/_delete user \(userId) del_smp=\(onOff(delSMPQueues))\(maybePwd(viewPwd))"
case let .startChat(mainApp): return "/_start main=\(onOff(mainApp))"
case let .startChat(mainApp, enableSndFiles): return "/_start main=\(onOff(mainApp)) snd_files=\(onOff(enableSndFiles))"
case .apiStopChat: return "/_stop"
case let .apiActivateChat(restore): return "/_app activate restore=\(onOff(restore))"
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
@@ -309,6 +310,7 @@ public enum ChatCommand {
case let .apiDownloadStandaloneFile(userId, link, file): return "/_download \(userId) \(link) \(file.filePath)"
case let .apiStandaloneFileInfo(link): return "/_download info \(link)"
case .showVersion: return "/version"
case let .getAgentSubsTotal(userId): return "/get subs total \(userId)"
case let .getAgentServersSummary(userId): return "/get servers summary \(userId)"
case .resetAgentServersStats: return "/reset servers stats"
case let .string(str): return str
@@ -447,6 +449,7 @@ public enum ChatCommand {
case .apiDownloadStandaloneFile: return "apiDownloadStandaloneFile"
case .apiStandaloneFileInfo: return "apiStandaloneFileInfo"
case .showVersion: return "showVersion"
case .getAgentSubsTotal: return "getAgentSubsTotal"
case .getAgentServersSummary: return "getAgentServersSummary"
case .resetAgentServersStats: return "resetAgentServersStats"
case .string: return "console command"
@@ -575,6 +578,7 @@ public enum ChatResponse: Decodable, Error {
case userContactLinkDeleted(user: User)
case contactConnected(user: UserRef, contact: Contact, userCustomProfile: Profile?)
case contactConnecting(user: UserRef, contact: Contact)
case contactSndReady(user: UserRef, contact: Contact)
case receivedContactRequest(user: UserRef, contactRequest: UserContactRequest)
case acceptingContactRequest(user: UserRef, contact: Contact)
case contactRequestRejected(user: UserRef)
@@ -661,7 +665,7 @@ public enum ChatResponse: Decodable, Error {
case callInvitations(callInvitations: [RcvCallInvitation])
case ntfTokenStatus(status: NtfTknStatus)
case ntfToken(token: DeviceToken, status: NtfTknStatus, ntfMode: NotificationsMode, ntfServer: String)
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessages: [NtfMsgInfo])
case ntfMessages(user_: User?, connEntity_: ConnectionEntity?, msgTs: Date?, ntfMessage_: NtfMsgInfo?)
case ntfMessage(user: UserRef, connEntity: ConnectionEntity, ntfMessage: NtfMsgInfo)
case contactConnectionDeleted(user: UserRef, connection: PendingContactConnection)
case contactDisabled(user: UserRef, contact: Contact)
@@ -677,6 +681,7 @@ public enum ChatResponse: Decodable, Error {
// misc
case versionInfo(versionInfo: CoreVersionInfo, chatMigrations: [UpMigration], agentMigrations: [UpMigration])
case cmdOk(user: UserRef?)
case agentSubsTotal(user: UserRef, subsTotal: SMPServerSubs, hasSession: Bool)
case agentServersSummary(user: UserRef, serversSummary: PresentedServersSummary)
case agentSubsSummary(user: UserRef, subsSummary: SMPServerSubs)
case chatCmdError(user_: UserRef?, chatError: ChatError)
@@ -742,6 +747,7 @@ public enum ChatResponse: Decodable, Error {
case .userContactLinkDeleted: return "userContactLinkDeleted"
case .contactConnected: return "contactConnected"
case .contactConnecting: return "contactConnecting"
case .contactSndReady: return "contactSndReady"
case .receivedContactRequest: return "receivedContactRequest"
case .acceptingContactRequest: return "acceptingContactRequest"
case .contactRequestRejected: return "contactRequestRejected"
@@ -837,6 +843,7 @@ public enum ChatResponse: Decodable, Error {
case .contactPQEnabled: return "contactPQEnabled"
case .versionInfo: return "versionInfo"
case .cmdOk: return "cmdOk"
case .agentSubsTotal: return "agentSubsTotal"
case .agentServersSummary: return "agentServersSummary"
case .agentSubsSummary: return "agentSubsSummary"
case .chatCmdError: return "chatCmdError"
@@ -907,6 +914,7 @@ public enum ChatResponse: Decodable, Error {
case .userContactLinkDeleted: return noDetails
case let .contactConnected(u, contact, _): return withUser(u, String(describing: contact))
case let .contactConnecting(u, contact): return withUser(u, String(describing: contact))
case let .contactSndReady(u, contact): return withUser(u, String(describing: contact))
case let .receivedContactRequest(u, contactRequest): return withUser(u, String(describing: contactRequest))
case let .acceptingContactRequest(u, contact): return withUser(u, String(describing: contact))
case .contactRequestRejected: return noDetails
@@ -1002,6 +1010,7 @@ public enum ChatResponse: Decodable, Error {
case let .contactPQEnabled(u, contact, pqEnabled): return withUser(u, "contact: \(String(describing: contact))\npqEnabled: \(pqEnabled)")
case let .versionInfo(versionInfo, chatMigrations, agentMigrations): return "\(String(describing: versionInfo))\n\nchat migrations: \(chatMigrations.map(\.upName))\n\nagent migrations: \(agentMigrations.map(\.upName))"
case .cmdOk: return noDetails
case let .agentSubsTotal(u, subsTotal, hasSession): return withUser(u, "subsTotal: \(String(describing: subsTotal))\nhasSession: \(hasSession)")
case let .agentServersSummary(u, serversSummary): return withUser(u, String(describing: serversSummary))
case let .agentSubsSummary(u, subsSummary): return withUser(u, String(describing: subsSummary))
case let .chatCmdError(u, chatError): return withUser(u, String(describing: chatError))
@@ -1122,20 +1131,20 @@ public struct ProtoServersConfig: Codable {
public struct UserProtoServers: Decodable {
public var serverProtocol: ServerProtocol
public var protoServers: [ServerCfg]
public var presetServers: [String]
public var presetServers: [ServerCfg]
}
public struct ServerCfg: Identifiable, Equatable, Codable {
public struct ServerCfg: Identifiable, Equatable, Codable, Hashable {
public var server: String
public var preset: Bool
public var tested: Bool?
public var enabled: ServerEnabled
public var enabled: Bool
var createdAt = Date()
// public var sendEnabled: Bool // can we potentially want to prevent sending on the servers we use to receive?
// Even if we don't see the use case, it's probably better to allow it in the model
// In any case, "trusted/known" servers are out of scope of this change
public init(server: String, preset: Bool, tested: Bool?, enabled: ServerEnabled) {
public init(server: String, preset: Bool, tested: Bool?, enabled: Bool) {
self.server = server
self.preset = preset
self.tested = tested
@@ -1148,7 +1157,7 @@ public struct ServerCfg: Identifiable, Equatable, Codable {
public var id: String { "\(server) \(createdAt)" }
public static var empty = ServerCfg(server: "", preset: false, tested: nil, enabled: .enabled)
public static var empty = ServerCfg(server: "", preset: false, tested: nil, enabled: false)
public var isEmpty: Bool {
server.trimmingCharacters(in: .whitespaces) == ""
@@ -1165,19 +1174,19 @@ public struct ServerCfg: Identifiable, Equatable, Codable {
server: "smp://abcd@smp8.simplex.im",
preset: true,
tested: true,
enabled: .enabled
enabled: true
),
custom: ServerCfg(
server: "smp://abcd@smp9.simplex.im",
preset: false,
tested: false,
enabled: .disabled
enabled: false
),
untested: ServerCfg(
server: "smp://abcd@smp10.simplex.im",
preset: false,
tested: nil,
enabled: .enabled
enabled: true
)
)
@@ -1189,12 +1198,6 @@ public struct ServerCfg: Identifiable, Equatable, Codable {
}
}
public enum ServerEnabled: String, Codable {
case disabled
case enabled
case known
}
public enum ProtocolTestStep: String, Decodable, Equatable {
case connect
case disconnect
@@ -1234,7 +1237,7 @@ public struct ProtocolTestFailure: Decodable, Error, Equatable {
public var localizedDescription: String {
let err = String.localizedStringWithFormat(NSLocalizedString("Test failed at step %@.", comment: "server test failure"), testStep.text)
switch testError {
case .SMP(.AUTH):
case .SMP(_, .AUTH):
return err + " " + NSLocalizedString("Server requires authorization to create queues, check password", comment: "server test error")
case .XFTP(.AUTH):
return err + " " + NSLocalizedString("Server requires authorization to upload, check password", comment: "server test error")
@@ -1293,42 +1296,32 @@ public struct NetCfg: Codable, Equatable, Hashable {
var socksMode: SocksMode = .always
public var hostMode: HostMode = .publicHost
public var requiredHostMode = true
public var sessionMode: TransportSessionMode
public var smpProxyMode: SMPProxyMode = .never
public var smpProxyFallback: SMPProxyFallback = .allow
public var sessionMode = TransportSessionMode.user
public var smpProxyMode: SMPProxyMode = .unknown
public var smpProxyFallback: SMPProxyFallback = .allowProtected
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 tcpKeepAlive: KeepAliveOpts? = KeepAliveOpts.defaults
public var smpPingInterval: Int // microseconds
public var smpPingCount: Int // times
public var logTLSErrors: Bool
public var smpPingCount: Int = 3 // times
public var logTLSErrors: Bool = false
public static let defaults: NetCfg = NetCfg(
socksProxy: nil,
sessionMode: TransportSessionMode.user,
tcpConnectTimeout: 25_000_000,
tcpTimeout: 15_000_000,
tcpTimeoutPerKb: 10_000,
rcvConcurrency: 12,
tcpKeepAlive: KeepAliveOpts.defaults,
smpPingInterval: 1200_000_000,
smpPingCount: 3,
logTLSErrors: false
smpPingInterval: 1200_000_000
)
public static let proxyDefaults: NetCfg = NetCfg(
socksProxy: nil,
sessionMode: TransportSessionMode.user,
tcpConnectTimeout: 35_000_000,
tcpTimeout: 20_000_000,
tcpTimeoutPerKb: 15_000,
rcvConcurrency: 8,
tcpKeepAlive: KeepAliveOpts.defaults,
smpPingInterval: 1200_000_000,
smpPingCount: 3,
logTLSErrors: false
smpPingInterval: 1200_000_000
)
public var enableKeepAlive: Bool { tcpKeepAlive != nil }
@@ -1903,9 +1896,10 @@ public enum SQLiteError: Decodable, Hashable {
public enum AgentErrorType: Decodable, Hashable {
case CMD(cmdErr: CommandErrorType)
case CONN(connErr: ConnectionErrorType)
case SMP(smpErr: ProtocolErrorType)
case SMP(serverAddress: String, smpErr: ProtocolErrorType)
case NTF(ntfErr: ProtocolErrorType)
case XFTP(xftpErr: XFTPErrorType)
case PROXY(proxyServer: String, relayServer: String, proxyErr: ProxyClientError)
case RCP(rcpErr: RCErrorType)
case BROKER(brokerAddress: String, brokerErr: BrokerErrorType)
case AGENT(agentErr: SMPAgentError)
@@ -1943,13 +1937,23 @@ public enum ProtocolErrorType: Decodable, Hashable {
case BLOCK
case SESSION
case CMD(cmdErr: ProtocolCommandError)
indirect case PROXY(proxyErr: ProxyError)
case AUTH
case CRYPTO
case QUOTA
case NO_MSG
case LARGE_MSG
case EXPIRED
case INTERNAL
}
public enum ProxyError: Decodable, Hashable {
case PROTOCOL(protocolErr: ProtocolErrorType)
case BROKER(brokerErr: BrokerErrorType)
case BASIC_AUTH
case NO_SESSION
}
public enum XFTPErrorType: Decodable, Hashable {
case BLOCK
case SESSION
@@ -1967,6 +1971,12 @@ public enum XFTPErrorType: Decodable, Hashable {
case INTERNAL
}
public enum ProxyClientError: Decodable, Hashable {
case protocolError(protocolErr: ProtocolErrorType)
case unexpectedResponse(responseStr: String)
case responseError(responseErr: ProtocolErrorType)
}
public enum RCErrorType: Decodable, Hashable {
case `internal`(internalErr: String)
case identity
@@ -1996,6 +2006,7 @@ public enum ProtocolCommandError: Decodable, Hashable {
public enum ProtocolTransportError: Decodable, Hashable {
case badBlock
case version
case largeMsg
case badSession
case noServerAuth
@@ -2085,6 +2096,7 @@ public struct AppSettings: Codable, Equatable, Hashable {
public var privacyShowChatPreviews: Bool? = nil
public var privacySaveLastDraft: Bool? = nil
public var privacyProtectScreen: Bool? = nil
public var privacyMediaBlurRadius: Int? = nil
public var notificationMode: AppSettingsNotificationMode? = nil
public var notificationPreviewMode: NotificationPreviewMode? = nil
public var webrtcPolicyRelay: Bool? = nil
@@ -2114,6 +2126,7 @@ public struct AppSettings: Codable, Equatable, Hashable {
if privacyShowChatPreviews != def.privacyShowChatPreviews { empty.privacyShowChatPreviews = privacyShowChatPreviews }
if privacySaveLastDraft != def.privacySaveLastDraft { empty.privacySaveLastDraft = privacySaveLastDraft }
if privacyProtectScreen != def.privacyProtectScreen { empty.privacyProtectScreen = privacyProtectScreen }
if privacyMediaBlurRadius != def.privacyMediaBlurRadius { empty.privacyMediaBlurRadius = privacyMediaBlurRadius }
if notificationMode != def.notificationMode { empty.notificationMode = notificationMode }
if notificationPreviewMode != def.notificationPreviewMode { empty.notificationPreviewMode = notificationPreviewMode }
if webrtcPolicyRelay != def.webrtcPolicyRelay { empty.webrtcPolicyRelay = webrtcPolicyRelay }
@@ -2144,6 +2157,7 @@ public struct AppSettings: Codable, Equatable, Hashable {
privacyShowChatPreviews: true,
privacySaveLastDraft: true,
privacyProtectScreen: false,
privacyMediaBlurRadius: 0,
notificationMode: AppSettingsNotificationMode.instant,
notificationPreviewMode: NotificationPreviewMode.message,
webrtcPolicyRelay: true,
@@ -2319,6 +2333,8 @@ public struct ServerSessions: Codable {
ssErrors: 0,
ssConnecting: 0
)
public var hasSess: Bool { ssConnected > 0 }
}
public struct SMPServerSubs: Codable {
@@ -2372,6 +2388,10 @@ public struct AgentSMPServerStatsData: Codable {
public var _connSubAttempts: Int
public var _connSubIgnored: Int
public var _connSubErrs: Int
public var _ntfKey: Int
public var _ntfKeyAttempts: Int
public var _ntfKeyDeleted: Int
public var _ntfKeyDeleteAttempts: Int
}
public struct XFTPServersSummary: Codable {
@@ -2411,3 +2431,12 @@ public struct AgentXFTPServerStatsData: Codable {
public var _deleteAttempts: Int
public var _deleteErrs: Int
}
public struct AgentNtfServerStatsData: Codable {
public var _ntfCreated: Int
public var _ntfCreateAttempts: Int
public var _ntfChecked: Int
public var _ntfCheckAttempts: Int
public var _ntfDeleted: Int
public var _ntfDelAttempts: Int
}
+90 -5
View File
@@ -1512,10 +1512,11 @@ public struct Contact: Identifiable, Decodable, NamedChat, Hashable {
public var id: ChatId { get { "@\(contactId)" } }
public var apiId: Int64 { get { contactId } }
public var ready: Bool { get { activeConn?.connStatus == .ready } }
public var sndReady: Bool { get { ready || activeConn?.connStatus == .sndReady } }
public var active: Bool { get { contactStatus == .active } }
public var sendMsgEnabled: Bool { get {
(
ready
sndReady
&& active
&& !(activeConn?.connectionStats?.ratchetSyncSendProhibited ?? false)
&& !(activeConn?.connDisabled ?? true)
@@ -1611,11 +1612,12 @@ public struct Connection: Decodable, Hashable {
public var pqSndEnabled: Bool?
public var pqRcvEnabled: Bool?
public var authErrCounter: Int
public var quotaErrCounter: Int
public var connectionStats: ConnectionStats? = nil
private enum CodingKeys: String, CodingKey {
case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled, authErrCounter
case connId, agentConnId, peerChatVRange, connStatus, connLevel, viaGroupLink, customUserProfileId, connectionCode, pqSupport, pqEncryption, pqSndEnabled, pqRcvEnabled, authErrCounter, quotaErrCounter
}
public var id: ChatId { get { ":\(connId)" } }
@@ -1624,6 +1626,10 @@ public struct Connection: Decodable, Hashable {
authErrCounter >= 10 // authErrDisableCount in core
}
public var connInactive: Bool {
quotaErrCounter >= 5 // quotaErrInactiveCount in core
}
public var connPQEnabled: Bool {
pqSndEnabled == true && pqRcvEnabled == true
}
@@ -1637,7 +1643,8 @@ public struct Connection: Decodable, Hashable {
viaGroupLink: false,
pqSupport: false,
pqEncryption: false,
authErrCounter: 0
authErrCounter: 0,
quotaErrCounter: 0
)
}
@@ -1818,7 +1825,7 @@ public enum ConnStatus: String, Decodable, Hashable {
case .joined: return false
case .requested: return true
case .accepted: return true
case .sndReady: return false
case .sndReady: return nil
case .ready: return nil
case .deleted: return nil
}
@@ -2846,6 +2853,62 @@ public enum SndCIStatusProgress: String, Decodable, Hashable {
case complete
}
public enum GroupSndStatus: Decodable, Hashable {
case new
case forwarded
case inactive
case sent
case rcvd(msgRcptStatus: MsgReceiptStatus)
case error(agentError: SndError)
case warning(agentError: SndError)
case invalid(text: String)
public func statusIcon(_ metaColor: Color/* = .secondary*/, _ primaryColor: Color = .accentColor) -> (String, Color) {
switch self {
case .new: return ("ellipsis", metaColor)
case .forwarded: return ("chevron.forward.2", metaColor)
case .inactive: return ("person.badge.minus", metaColor)
case .sent: return ("checkmark", metaColor)
case let .rcvd(msgRcptStatus):
switch msgRcptStatus {
case .ok: return ("checkmark", metaColor)
case .badMsgHash: return ("checkmark", .red)
}
case .error: return ("multiply", .red)
case .warning: return ("exclamationmark.triangle.fill", .orange)
case .invalid: return ("questionmark", metaColor)
}
}
public var statusInfo: (String, String)? {
switch self {
case .new: return nil
case .forwarded: return (
NSLocalizedString("Message forwarded", comment: "item status text"),
NSLocalizedString("No direct connection yet, message is forwarded by admin.", comment: "item status description")
)
case .inactive: return (
NSLocalizedString("Member inactive", comment: "item status text"),
NSLocalizedString("Message may be delivered later if member becomes active.", comment: "item status description")
)
case .sent: return nil
case .rcvd: return nil
case let .error(agentError): return (
NSLocalizedString("Message delivery error", comment: "item status text"),
agentError.errorInfo
)
case let .warning(agentError): return (
NSLocalizedString("Message delivery warning", comment: "item status text"),
agentError.errorInfo
)
case let .invalid(text): return (
NSLocalizedString("Invalid status", comment: "item status text"),
text
)
}
}
}
public enum CIDeleted: Decodable, Hashable {
case deleted(deletedTs: Date?)
case blocked(deletedTs: Date?)
@@ -3231,6 +3294,28 @@ public struct CIFile: Decodable, Hashable {
}
}
}
public var showStatusIconInSmallView: Bool {
get {
switch fileStatus {
case .sndStored: fileProtocol != .local
case .sndTransfer: true
case .sndComplete: false
case .sndCancelled: true
case .sndError: true
case .sndWarning: true
case .rcvInvitation: false
case .rcvAccepted: true
case .rcvTransfer: true
case .rcvAborted: true
case .rcvCancelled: true
case .rcvComplete: false
case .rcvError: true
case .rcvWarning: true
case .invalid: true
}
}
}
}
public struct CryptoFile: Codable, Hashable {
@@ -4012,6 +4097,6 @@ public struct ChatItemVersion: Decodable, Hashable {
public struct MemberDeliveryStatus: Decodable, Hashable {
public var groupMemberId: Int64
public var memberDeliveryStatus: CIStatus
public var memberDeliveryStatus: GroupSndStatus
public var sentViaProxy: Bool?
}
@@ -7,18 +7,18 @@
//
import Foundation
import SimpleXChat
import SwiftUI
import AVKit
import SwiftyGif
func getLoadedFileSource(_ file: CIFile?) -> CryptoFile? {
public func getLoadedFileSource(_ file: CIFile?) -> CryptoFile? {
if let file = file, file.loaded {
return file.fileSource
}
return nil
}
func getLoadedImage(_ file: CIFile?) -> UIImage? {
public func getLoadedImage(_ file: CIFile?) -> UIImage? {
if let fileSource = getLoadedFileSource(file) {
let filePath = getAppFilePath(fileSource.filePath)
do {
@@ -37,7 +37,7 @@ func getLoadedImage(_ file: CIFile?) -> UIImage? {
return nil
}
func getFileData(_ path: URL, _ cfArgs: CryptoFileArgs?) throws -> Data {
public func getFileData(_ path: URL, _ cfArgs: CryptoFileArgs?) throws -> Data {
if let cfArgs = cfArgs {
return try readCryptoFile(path: path.path, cryptoArgs: cfArgs)
} else {
@@ -45,7 +45,7 @@ func getFileData(_ path: URL, _ cfArgs: CryptoFileArgs?) throws -> Data {
}
}
func getLoadedVideo(_ file: CIFile?) -> URL? {
public func getLoadedVideo(_ file: CIFile?) -> URL? {
if let fileSource = getLoadedFileSource(file) {
let filePath = getAppFilePath(fileSource.filePath)
if FileManager.default.fileExists(atPath: filePath.path) {
@@ -55,13 +55,13 @@ func getLoadedVideo(_ file: CIFile?) -> URL? {
return nil
}
func saveAnimImage(_ image: UIImage) -> CryptoFile? {
public func saveAnimImage(_ image: UIImage) -> CryptoFile? {
let fileName = generateNewFileName("IMG", "gif")
guard let imageData = image.imageData else { return nil }
return saveFile(imageData, fileName, encrypted: privacyEncryptLocalFilesGroupDefault.get())
}
func saveImage(_ uiImage: UIImage) -> CryptoFile? {
public func saveImage(_ uiImage: UIImage) -> CryptoFile? {
let hasAlpha = imageHasAlpha(uiImage)
let ext = hasAlpha ? "png" : "jpg"
if let imageDataResized = resizeImageToDataSize(uiImage, maxDataSize: MAX_IMAGE_SIZE, hasAlpha: hasAlpha) {
@@ -71,7 +71,7 @@ func saveImage(_ uiImage: UIImage) -> CryptoFile? {
return nil
}
func cropToSquare(_ image: UIImage) -> UIImage {
public func cropToSquare(_ image: UIImage) -> UIImage {
let size = image.size
let side = min(size.width, size.height)
let newSize = CGSize(width: side, height: side)
@@ -84,7 +84,7 @@ func cropToSquare(_ image: UIImage) -> UIImage {
return resizeImage(image, newBounds: CGRect(origin: .zero, size: newSize), drawIn: CGRect(origin: origin, size: size), hasAlpha: imageHasAlpha(image))
}
func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int64, hasAlpha: Bool) -> Data? {
public func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int64, hasAlpha: Bool) -> Data? {
var img = image
var data = hasAlpha ? img.pngData() : img.jpegData(compressionQuality: 0.85)
var dataSize = data?.count ?? 0
@@ -99,7 +99,7 @@ func resizeImageToDataSize(_ image: UIImage, maxDataSize: Int64, hasAlpha: Bool)
return data
}
func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) -> String? {
public func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) -> String? {
var img = image
let hasAlpha = imageHasAlpha(image)
var str = compressImageStr(img, hasAlpha: hasAlpha)
@@ -115,7 +115,7 @@ func resizeImageToStrSize(_ image: UIImage, maxDataSize: Int64) -> String? {
return str
}
func compressImageStr(_ image: UIImage, _ compressionQuality: CGFloat = 0.85, hasAlpha: Bool) -> String? {
public func compressImageStr(_ image: UIImage, _ compressionQuality: CGFloat = 0.85, hasAlpha: Bool) -> String? {
let ext = hasAlpha ? "png" : "jpg"
if let data = hasAlpha ? image.pngData() : image.jpegData(compressionQuality: compressionQuality) {
return "data:image/\(ext);base64,\(data.base64EncodedString())"
@@ -138,7 +138,7 @@ private func resizeImage(_ image: UIImage, newBounds: CGRect, drawIn: CGRect, ha
}
}
func imageHasAlpha(_ img: UIImage) -> Bool {
public func imageHasAlpha(_ img: UIImage) -> Bool {
if let cgImage = img.cgImage {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
@@ -158,7 +158,7 @@ func imageHasAlpha(_ img: UIImage) -> Bool {
return false
}
func saveFileFromURL(_ url: URL) -> CryptoFile? {
public func saveFileFromURL(_ url: URL) -> CryptoFile? {
let encrypted = privacyEncryptLocalFilesGroupDefault.get()
let savedFile: CryptoFile?
if url.startAccessingSecurityScopedResource() {
@@ -184,7 +184,7 @@ func saveFileFromURL(_ url: URL) -> CryptoFile? {
return savedFile
}
func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
public func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
do {
let encrypted = privacyEncryptLocalFilesGroupDefault.get()
let fileName = uniqueCombine(url.lastPathComponent)
@@ -197,7 +197,6 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
try FileManager.default.moveItem(at: url, to: getAppFilePath(fileName))
savedFile = CryptoFile.plain(fileName)
}
ChatModel.shared.filesToDelete.remove(url)
return savedFile
} catch {
logger.error("ImageUtils.moveTempFileFromURL error: \(error.localizedDescription)")
@@ -205,7 +204,7 @@ func moveTempFileFromURL(_ url: URL) -> CryptoFile? {
}
}
func saveWallpaperFile(url: URL) -> String? {
public func saveWallpaperFile(url: URL) -> String? {
let destFile = URL(fileURLWithPath: generateNewFileName(getWallpaperDirectory().path + "/" + "wallpaper", "jpg", fullPath: true))
do {
try FileManager.default.copyItem(atPath: url.path, toPath: destFile.path)
@@ -216,7 +215,7 @@ func saveWallpaperFile(url: URL) -> String? {
}
}
func saveWallpaperFile(image: UIImage) -> String? {
public func saveWallpaperFile(image: UIImage) -> String? {
let hasAlpha = imageHasAlpha(image)
let destFile = URL(fileURLWithPath: generateNewFileName(getWallpaperDirectory().path + "/" + "wallpaper", hasAlpha ? "png" : "jpg", fullPath: true))
let dataResized = resizeImageToDataSize(image, maxDataSize: 5_000_000, hasAlpha: hasAlpha)
@@ -229,7 +228,7 @@ func saveWallpaperFile(image: UIImage) -> String? {
}
}
func removeWallpaperFile(fileName: String? = nil) {
public func removeWallpaperFile(fileName: String? = nil) {
do {
try FileManager.default.contentsOfDirectory(atPath: getWallpaperDirectory().path).forEach {
if URL(fileURLWithPath: $0).lastPathComponent == fileName { try FileManager.default.removeItem(atPath: $0) }
@@ -242,7 +241,7 @@ func removeWallpaperFile(fileName: String? = nil) {
}
}
func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String {
public func generateNewFileName(_ prefix: String, _ ext: String, fullPath: Bool = false) -> String {
uniqueCombine("\(prefix)_\(getTimestamp()).\(ext)", fullPath: fullPath)
}
@@ -274,7 +273,7 @@ private func getTimestamp() -> String {
return df.string(from: Date())
}
func dropImagePrefix(_ s: String) -> String {
public func dropImagePrefix(_ s: String) -> String {
dropPrefix(dropPrefix(s, "data:image/png;base64,"), "data:image/jpg;base64,")
}
@@ -283,7 +282,7 @@ private func dropPrefix(_ s: String, _ prefix: String) -> String {
}
extension AVAsset {
func generatePreview() -> (UIImage, Int)? {
public func generatePreview() -> (UIImage, Int)? {
let generator = AVAssetImageGenerator(asset: self)
generator.appliesPreferredTrackTransform = true
var actualTime = CMTimeMake(value: 0, timescale: 0)
@@ -295,7 +294,7 @@ extension AVAsset {
}
extension UIImage {
func replaceColor(_ from: UIColor, _ to: UIColor) -> UIImage {
public func replaceColor(_ from: UIColor, _ to: UIColor) -> UIImage {
if let cgImage = cgImage {
let colorSpace = CGColorSpaceCreateDeviceRGB()
let bitmapInfo = CGBitmapInfo(rawValue: CGImageAlphaInfo.premultipliedFirst.rawValue)
@@ -340,4 +339,12 @@ extension UIImage {
}
return self
}
public convenience init?(base64Encoded: String?) {
if let base64Encoded, let data = Data(base64Encoded: dropImagePrefix(base64Encoded)) {
self.init(data: data)
} else {
return nil
}
}
}
+1 -1
View File
@@ -47,7 +47,7 @@ public func createContactConnectedNtf(_ user: any UserLike, _ contact: Contact)
hideContent ? NSLocalizedString("A new contact", comment: "notification title") : contact.displayName
),
body: String.localizedStringWithFormat(
NSLocalizedString("You can now send messages to %@", comment: "notification body"),
NSLocalizedString("You can now chat with %@", comment: "notification body"),
hideContent ? NSLocalizedString("this contact", comment: "notification title") : contact.chatViewName
),
targetContentIdentifier: contact.id,
+5 -16
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ е потвърдено";
/* No comment provided by engineer. */
"%@ servers" = "%@ сървъри";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ качено";
@@ -337,9 +334,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "по-горе, след това избери:";
/* No comment provided by engineer. */
"Accent color" = "Основен цвят";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Приеми";
@@ -369,7 +363,7 @@
"Add profile" = "Добави профил";
/* No comment provided by engineer. */
"Add server" = "Добави сървър";
"Add server" = "Добави сървър";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Добави сървъри чрез сканиране на QR кодове.";
@@ -831,9 +825,6 @@
/* No comment provided by engineer. */
"colored" = "цветен";
/* No comment provided by engineer. */
"Colors" = "Цветове";
/* server test step */
"Compare file" = "Сравни файл";
@@ -1011,7 +1002,7 @@
/* No comment provided by engineer. */
"Continue" = "Продължи";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Копирай";
/* No comment provided by engineer. */
@@ -1779,7 +1770,8 @@
/* No comment provided by engineer. */
"Error: " = "Грешка: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Грешка: %@";
/* No comment provided by engineer. */
@@ -3791,9 +3783,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "Текстът, който поставихте, не е SimpleX линк за връзка.";
/* No comment provided by engineer. */
"Theme" = "Тема";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Тези настройки са за текущия ви профил **%@**.";
@@ -4305,7 +4294,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Можете да го направите видим за вашите контакти в SimpleX чрез Настройки.";
/* notification body */
"You can now send messages to %@" = "Вече можете да изпращате съобщения до %@";
"You can now chat with %@" = "Вече можете да изпращате съобщения до %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Можете да зададете визуализация на известията на заключен екран през настройките.";
+5 -16
View File
@@ -130,9 +130,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ je ověřený";
/* No comment provided by engineer. */
"%@ servers" = "%@ servery";
/* notification title */
"%@ wants to connect!" = "%@ se chce připojit!";
@@ -289,9 +286,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "výše, pak vyberte:";
/* No comment provided by engineer. */
"Accent color" = "Zbarvení";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Přijmout";
@@ -318,7 +312,7 @@
"Add profile" = "Přidat profil";
/* No comment provided by engineer. */
"Add server" = "Přidat server";
"Add server" = "Přidat server";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Přidejte servery skenováním QR kódů.";
@@ -678,9 +672,6 @@
/* No comment provided by engineer. */
"colored" = "barevné";
/* No comment provided by engineer. */
"Colors" = "Barvy";
/* server test step */
"Compare file" = "Porovnat soubor";
@@ -813,7 +804,7 @@
/* No comment provided by engineer. */
"Continue" = "Pokračovat";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Kopírovat";
/* No comment provided by engineer. */
@@ -1461,7 +1452,8 @@
/* No comment provided by engineer. */
"Error: " = "Chyba: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Chyba: %@";
/* No comment provided by engineer. */
@@ -3095,9 +3087,6 @@
/* No comment provided by engineer. */
"The servers for new connections of your current chat profile **%@**." = "Servery pro nová připojení vašeho aktuálního chat profilu **%@**.";
/* No comment provided by engineer. */
"Theme" = "Téma";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Toto nastavení je pro váš aktuální profil **%@**.";
@@ -3447,7 +3436,7 @@
"You can hide or mute a user profile - swipe it to the right." = "Profil uživatele můžete skrýt nebo ztlumit - přejeďte prstem doprava.";
/* notification body */
"You can now send messages to %@" = "Nyní můžete posílat zprávy %@";
"You can now chat with %@" = "Nyní můžete posílat zprávy %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Náhled oznámení na zamykací obrazovce můžete změnit v nastavení.";
File diff suppressed because it is too large Load Diff
@@ -2,7 +2,7 @@
"CFBundleName" = "SimpleX";
/* Privacy - Camera Usage Description */
"NSCameraUsageDescription" = "SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Nutzern zu scannen und Videoanrufe durchzuführen.";
"NSCameraUsageDescription" = "SimpleX benötigt Zugriff auf die Kamera, um QR Codes für die Verbindung mit anderen Benutzern zu scannen und Videoanrufe durchzuführen.";
/* Privacy - Face ID Usage Description */
"NSFaceIDUsageDescription" = "Face ID wird von SimpleX für die lokale Authentifizierung genutzt";
+41 -43
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ está verificado";
/* No comment provided by engineer. */
"%@ servers" = "Servidores %@";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ subido";
@@ -337,9 +334,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "y después elige:";
/* No comment provided by engineer. */
"Accent color" = "Color";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Aceptar";
@@ -369,7 +363,7 @@
"Add profile" = "Añadir perfil";
/* No comment provided by engineer. */
"Add server" = "Añadir servidor";
"Add server" = "Añadir servidor";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Añadir servidores mediante el escaneo de códigos QR.";
@@ -840,9 +834,6 @@
/* No comment provided by engineer. */
"colored" = "coloreado";
/* No comment provided by engineer. */
"Colors" = "Colores";
/* server test step */
"Compare file" = "Comparar archivo";
@@ -967,7 +958,7 @@
"Connection error" = "Error conexión";
/* No comment provided by engineer. */
"Connection error (AUTH)" = "Error conexión (Autenticación)";
"Connection error (AUTH)" = "Error de conexión (Autenticación)";
/* chat list item title (it should not be shown */
"connection established" = "conexión establecida";
@@ -979,7 +970,7 @@
"Connection terminated" = "Conexión finalizada";
/* No comment provided by engineer. */
"Connection timeout" = "Tiempo de conexión expirado";
"Connection timeout" = "Tiempo de conexión agotado";
/* connection information */
"connection:%@" = "conexión: % @";
@@ -1023,7 +1014,7 @@
/* No comment provided by engineer. */
"Continue" = "Continuar";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Copiar";
/* No comment provided by engineer. */
@@ -1167,6 +1158,9 @@
/* time unit */
"days" = "días";
/* No comment provided by engineer. */
"Debug delivery" = "Informe debug";
/* No comment provided by engineer. */
"Decentralized" = "Descentralizada";
@@ -1800,7 +1794,8 @@
/* No comment provided by engineer. */
"Error: " = "Error: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Error: %@";
/* No comment provided by engineer. */
@@ -2101,7 +2096,7 @@
"ICE servers (one per line)" = "Servidores ICE (uno por línea)";
/* No comment provided by engineer. */
"If you can't meet in person, show QR code in a video call, or share the link." = "Si no puedes reunirte en persona, muestra el código QR por videollamada, o comparte el enlace.";
"If you can't meet in person, show QR code in a video call, or share the link." = "Si no puedes reunirte en persona, muestra el código QR por videollamada o comparte el enlace.";
/* No comment provided by engineer. */
"If you enter this passcode when opening the app, all app data will be irreversibly removed!" = "¡Si introduces este código al abrir la aplicación, todos los datos de la misma se eliminarán de forma irreversible!";
@@ -2496,6 +2491,9 @@
/* No comment provided by engineer. */
"Message draft" = "Borrador de mensaje";
/* No comment provided by engineer. */
"Message queue info" = "Información cola de mensajes";
/* chat feature */
"Message reactions" = "Reacciones a mensajes";
@@ -2766,7 +2764,7 @@
"on" = "Activado";
/* No comment provided by engineer. */
"One-time invitation link" = "Enlace único de invitación de un uso";
"One-time invitation link" = "Enlace de invitación de un solo uso";
/* No comment provided by engineer. */
"Onion hosts will be required for connection. Requires enabling VPN." = "Se requieren hosts .onion para la conexión. Requiere activación de la VPN.";
@@ -2850,13 +2848,13 @@
"Or paste archive link" = "O pegar enlace del archivo";
/* No comment provided by engineer. */
"Or scan QR code" = "O escanear código QR";
"Or scan QR code" = "O escanea el código QR";
/* No comment provided by engineer. */
"Or securely share this file link" = "O comparte de forma segura este enlace al archivo";
/* No comment provided by engineer. */
"Or show this code" = "O mostrar este código";
"Or show this code" = "O muestra este código QR";
/* No comment provided by engineer. */
"Other" = "Otro";
@@ -2898,7 +2896,7 @@
"Paste link to connect!" = "Pegar enlace para conectar!";
/* No comment provided by engineer. */
"Paste the link you received" = "Pegar el enlace recibido";
"Paste the link you received" = "Pega el enlace recibido";
/* No comment provided by engineer. */
"peer-to-peer" = "p2p";
@@ -2907,7 +2905,7 @@
"People can connect to you only via the links you share." = "Las personas pueden conectarse contigo solo mediante los enlaces que compartes.";
/* No comment provided by engineer. */
"Periodically" = "Periódico";
"Periodically" = "Periódicamente";
/* message decrypt error item */
"Permanent decryption error" = "Error permanente descifrado";
@@ -3063,10 +3061,10 @@
"Protect your IP address from the messaging relays chosen by your contacts.\nEnable in *Network & servers* settings." = "Protege tu dirección IP de los servidores de retransmisión elegidos por tus contactos.\nActívalo en ajustes de *Servidores y Redes*.";
/* No comment provided by engineer. */
"Protocol timeout" = "Tiempo de espera del protocolo";
"Protocol timeout" = "Timeout protocolo";
/* No comment provided by engineer. */
"Protocol timeout per KB" = "Límite de espera del protocolo por KB";
"Protocol timeout per KB" = "Timeout protocolo por KB";
/* No comment provided by engineer. */
"Push notifications" = "Notificaciones automáticas";
@@ -3090,22 +3088,22 @@
"Read" = "Leer";
/* No comment provided by engineer. */
"Read more" = "Saber más";
"Read more" = "Conoce más";
/* No comment provided by engineer. */
"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Saber más en el [Manual del Usuario](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).";
"Read more in [User Guide](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address)." = "Conoce más en el [Manual del Usuario](https://simplex.chat/docs/guide/app-settings.html#your-simplex-contact-address).";
/* No comment provided by engineer. */
"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Saber más en [Guía de Usuario](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).";
"Read more in [User Guide](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode)." = "Conoce más en la [Guía del Usuario](https://simplex.chat/docs/guide/chat-profiles.html#incognito-mode).";
/* No comment provided by engineer. */
"Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Saber más en el [Manual del Usuario](https://simplex.chat/docs/guide/readme.html#connect-to-friends).";
"Read more in [User Guide](https://simplex.chat/docs/guide/readme.html#connect-to-friends)." = "Conoce más en el [Manual del Usuario](https://simplex.chat/docs/guide/readme.html#connect-to-friends).";
/* No comment provided by engineer. */
"Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Saber más en nuestro [repositorio GitHub](https://github.com/simplex-chat/simplex-chat#readme).";
"Read more in our [GitHub repository](https://github.com/simplex-chat/simplex-chat#readme)." = "Conoce más en nuestro [repositorio GitHub](https://github.com/simplex-chat/simplex-chat#readme).";
/* No comment provided by engineer. */
"Read more in our GitHub repository." = "Saber más en nuestro repositorio GitHub.";
"Read more in our GitHub repository." = "Conoce más en nuestro repositorio GitHub.";
/* No comment provided by engineer. */
"Receipts are disabled" = "Las confirmaciones están desactivadas";
@@ -3518,6 +3516,9 @@
/* srv error text. */
"Server address is incompatible with network settings." = "La dirección del servidor es incompatible con la configuración de la red.";
/* queue info */
"server queue info: %@\n\nlast received msg: %@" = "información cola del servidor: %1$@\n\núltimo mensaje recibido: %2$@";
/* server test error */
"Server requires authorization to create queues, check password" = "El servidor requiere autorización para crear colas, comprueba la contraseña";
@@ -3591,7 +3592,7 @@
"Share link" = "Compartir enlace";
/* No comment provided by engineer. */
"Share this 1-time invite link" = "Compartir este enlace de un uso";
"Share this 1-time invite link" = "Comparte este enlace de un solo uso";
/* No comment provided by engineer. */
"Share with contacts" = "Compartir con contactos";
@@ -3771,7 +3772,7 @@
"Tap to join incognito" = "Pulsa para unirte en modo incógnito";
/* No comment provided by engineer. */
"Tap to paste link" = "Pulsa para pegar enlace";
"Tap to paste link" = "Pulsa para pegar el enlacePulsa para pegar enlace";
/* No comment provided by engineer. */
"Tap to scan" = "Pulsa para escanear";
@@ -3780,7 +3781,7 @@
"Tap to start a new chat" = "Pulsa para iniciar chat nuevo";
/* No comment provided by engineer. */
"TCP connection timeout" = "Tiempo de espera de la conexión TCP agotado";
"TCP connection timeout" = "Timeout de la conexión TCP";
/* No comment provided by engineer. */
"TCP_KEEPCNT" = "TCP_KEEPCNT";
@@ -3872,9 +3873,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "El texto pegado no es un enlace SimpleX.";
/* No comment provided by engineer. */
"Theme" = "Tema";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Esta configuración afecta a tu perfil actual **%@**.";
@@ -3942,7 +3940,7 @@
"To protect your information, turn on SimpleX Lock.\nYou will be prompted to complete authentication before this feature is enabled." = "Para proteger tu información, activa el Bloqueo SimpleX.\nSe te pedirá que completes la autenticación antes de activar esta función.";
/* No comment provided by engineer. */
"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Para proteger tu dirección IP, el enrutamiento privado usa tus servidores SMP para enviar mensajes.";
"To protect your IP address, private routing uses your SMP servers to deliver messages." = "Para proteger tu dirección IP, el enrutamiento privado usa tu lista de servidores SMP para enviar mensajes.";
/* No comment provided by engineer. */
"To record voice message please grant permission to use Microphone." = "Para grabar el mensaje de voz concede permiso para usar el micrófono.";
@@ -4029,7 +4027,7 @@
"Unknown error" = "Error desconocido";
/* No comment provided by engineer. */
"unknown relays" = "servidor de retransmisión desconocido";
"unknown relays" = "con servidores desconocidos";
/* No comment provided by engineer. */
"Unknown servers!" = "¡Servidores desconocidos!";
@@ -4041,7 +4039,7 @@
"Unless you use iOS call interface, enable Do Not Disturb mode to avoid interruptions." = "A menos que utilices la interfaz de llamadas de iOS, activa el modo No molestar para evitar interrupciones.";
/* No comment provided by engineer. */
"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A menos que tu contacto haya eliminado la conexión o\nque este enlace ya se haya usado, podría ser un error. Por favor, notifícalo.\nPara conectarte, pide a tu contacto que cree otro enlace de conexión y comprueba que tienes buena conexión de red.";
"Unless your contact deleted the connection or this link was already used, it might be a bug - please report it.\nTo connect, please ask your contact to create another connection link and check that you have a stable network connection." = "A menos que tu contacto haya eliminado la conexión o el enlace haya sido usado, podría ser un error. Por favor, notifícalo.\nPara conectarte pide a tu contacto que cree otro enlace y comprueba la conexión de red.";
/* No comment provided by engineer. */
"Unlink" = "Desenlazar";
@@ -4059,7 +4057,7 @@
"Unmute" = "Activar audio";
/* No comment provided by engineer. */
"unprotected" = "desprotegido";
"unprotected" = "con IP desprotegida";
/* No comment provided by engineer. */
"Unread" = "No leído";
@@ -4131,10 +4129,10 @@
"Use only local notifications?" = "¿Usar sólo notificaciones locales?";
/* No comment provided by engineer. */
"Use private routing with unknown servers when IP address is not protected." = "Usar enrutamiento privado con servidores desconocidos cuando la dirección IP no está protegida.";
"Use private routing with unknown servers when IP address is not protected." = "Usar enrutamiento privado con servidores desconocidos cuando tu dirección IP no está protegida.";
/* No comment provided by engineer. */
"Use private routing with unknown servers." = "Usar enrutamiento privado con servidores desconocidos.";
"Use private routing with unknown servers." = "Usar enrutamiento privado con servidores de retransmisión desconocidos.";
/* No comment provided by engineer. */
"Use server" = "Usar servidor";
@@ -4416,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Puedes hacerlo visible para tus contactos de SimpleX en Configuración.";
/* notification body */
"You can now send messages to %@" = "Ya puedes enviar mensajes a %@";
"You can now chat with %@" = "Ya puedes enviar mensajes a %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Puedes configurar las notificaciones de la pantalla de bloqueo desde Configuración.";
@@ -4524,7 +4522,7 @@
"You will be connected when your contact's device is online, please wait or check later!" = "Te conectarás cuando el dispositivo del contacto esté en línea, por favor espera o compruébalo más tarde.";
/* No comment provided by engineer. */
"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Se te pedirá identificarte cuándo inicies o continues usando la aplicación tras 30 segundos en segundo plano.";
"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Se te pedirá autenticarte cuando inicies la aplicación o sigas usándola tras 30 segundos en segundo plano.";
/* No comment provided by engineer. */
"You will connect to all group members." = "Te conectarás con todos los miembros del grupo.";
@@ -4596,7 +4594,7 @@
"Your profile **%@** will be shared." = "Tu perfil **%@** será compartido.";
/* No comment provided by engineer. */
"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Tu perfil se almacena en tu dispositivo y sólo se comparte con tus contactos.\nLos servidores de SimpleX no pueden ver tu perfil.";
"Your profile is stored on your device and shared only with your contacts.\nSimpleX servers cannot see your profile." = "Tu perfil es almacenado en tu dispositivo y solamente se comparte con tus contactos.\nLos servidores SimpleX no pueden ver tu perfil.";
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Tu perfil, contactos y mensajes se almacenan en tu dispositivo.";
+5 -16
View File
@@ -121,9 +121,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ on vahvistettu";
/* No comment provided by engineer. */
"%@ servers" = "%@ palvelimet";
/* notification title */
"%@ wants to connect!" = "%@ haluaa muodostaa yhteyden!";
@@ -280,9 +277,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "edellä, valitse sitten:";
/* No comment provided by engineer. */
"Accent color" = "Korostusväri";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Hyväksy";
@@ -309,7 +303,7 @@
"Add profile" = "Lisää profiili";
/* No comment provided by engineer. */
"Add server" = "Lisää palvelin";
"Add server" = "Lisää palvelin";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Lisää palvelimia skannaamalla QR-koodeja.";
@@ -663,9 +657,6 @@
/* No comment provided by engineer. */
"colored" = "värillinen";
/* No comment provided by engineer. */
"Colors" = "Värit";
/* server test step */
"Compare file" = "Vertaa tiedostoa";
@@ -795,7 +786,7 @@
/* No comment provided by engineer. */
"Continue" = "Jatka";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Kopioi";
/* No comment provided by engineer. */
@@ -1434,7 +1425,8 @@
/* No comment provided by engineer. */
"Error: " = "Virhe: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Virhe: %@";
/* No comment provided by engineer. */
@@ -3056,9 +3048,6 @@
/* No comment provided by engineer. */
"The servers for new connections of your current chat profile **%@**." = "Palvelimet nykyisen keskusteluprofiilisi uusille yhteyksille **%@**.";
/* No comment provided by engineer. */
"Theme" = "Teema";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Nämä asetukset koskevat nykyistä profiiliasi **%@**.";
@@ -3405,7 +3394,7 @@
"You can hide or mute a user profile - swipe it to the right." = "Voit piilottaa tai mykistää käyttäjäprofiilin pyyhkäisemällä sitä oikealle.";
/* notification body */
"You can now send messages to %@" = "Voit nyt lähettää viestejä %@:lle";
"You can now chat with %@" = "Voit nyt lähettää viestejä %@:lle";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Voit määrittää lukitusnäytön ilmoituksen esikatselun asetuksista.";
+14 -16
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ est vérifié·e";
/* No comment provided by engineer. */
"%@ servers" = "Serveurs %@";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ envoyé";
@@ -337,9 +334,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "ci-dessus, puis choisissez:";
/* No comment provided by engineer. */
"Accent color" = "Couleur principale";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Accepter";
@@ -369,7 +363,7 @@
"Add profile" = "Ajouter un profil";
/* No comment provided by engineer. */
"Add server" = "Ajouter un serveur";
"Add server" = "Ajouter un serveur";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Ajoutez des serveurs en scannant des codes QR.";
@@ -840,9 +834,6 @@
/* No comment provided by engineer. */
"colored" = "coloré";
/* No comment provided by engineer. */
"Colors" = "Couleurs";
/* server test step */
"Compare file" = "Comparer le fichier";
@@ -1023,7 +1014,7 @@
/* No comment provided by engineer. */
"Continue" = "Continuer";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Copier";
/* No comment provided by engineer. */
@@ -1167,6 +1158,9 @@
/* time unit */
"days" = "jours";
/* No comment provided by engineer. */
"Debug delivery" = "Livraison de débogage";
/* No comment provided by engineer. */
"Decentralized" = "Décentralisé";
@@ -1800,7 +1794,8 @@
/* No comment provided by engineer. */
"Error: " = "Erreur : ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Erreur: %@";
/* No comment provided by engineer. */
@@ -2496,6 +2491,9 @@
/* No comment provided by engineer. */
"Message draft" = "Brouillon de message";
/* No comment provided by engineer. */
"Message queue info" = "Informations sur la file d'attente des messages";
/* chat feature */
"Message reactions" = "Réactions aux messages";
@@ -3518,6 +3516,9 @@
/* srv error text. */
"Server address is incompatible with network settings." = "L'adresse du serveur est incompatible avec les paramètres du réseau.";
/* queue info */
"server queue info: %@\n\nlast received msg: %@" = "info sur la file d'attente du serveur: %1$@\n\ndernier message reçu: %2$@";
/* server test error */
"Server requires authorization to create queues, check password" = "Le serveur requiert une autorisation pour créer des files d'attente, vérifiez le mot de passe";
@@ -3872,9 +3873,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "Le texte collé n'est pas un lien SimpleX.";
/* No comment provided by engineer. */
"Theme" = "Thème";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Ces paramètres s'appliquent à votre profil actuel **%@**.";
@@ -4416,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Vous pouvez le rendre visible à vos contacts SimpleX via Paramètres.";
/* notification body */
"You can now send messages to %@" = "Vous pouvez maintenant envoyer des messages à %@";
"You can now chat with %@" = "Vous pouvez maintenant envoyer des messages à %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Vous pouvez configurer l'aperçu des notifications sur l'écran de verrouillage via les paramètres.";
File diff suppressed because it is too large Load Diff
+397 -12
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ è verificato/a";
/* No comment provided by engineer. */
"%@ servers" = "Server %@";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ caricati";
@@ -338,7 +335,7 @@
"above, then choose:" = "sopra, quindi scegli:";
/* No comment provided by engineer. */
"Accent color" = "Colore principale";
"Accent" = "Principale";
/* accept contact request via notification
accept incoming call via notification */
@@ -356,6 +353,12 @@
/* call status */
"accepted call" = "chiamata accettata";
/* No comment provided by engineer. */
"Acknowledged" = "Riconosciuto";
/* No comment provided by engineer. */
"Acknowledgement errors" = "Errori di riconoscimento";
/* No comment provided by engineer. */
"Add address to your profile, so that your contacts can share it with other people. Profile update will be sent to your contacts." = "Aggiungi l'indirizzo al tuo profilo, in modo che i tuoi contatti possano condividerlo con altre persone. L'aggiornamento del profilo verrà inviato ai tuoi contatti.";
@@ -369,7 +372,7 @@
"Add profile" = "Aggiungi profilo";
/* No comment provided by engineer. */
"Add server" = "Aggiungi server";
"Add server" = "Aggiungi server";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Aggiungi server scansionando codici QR.";
@@ -380,6 +383,15 @@
/* No comment provided by engineer. */
"Add welcome message" = "Aggiungi messaggio di benvenuto";
/* No comment provided by engineer. */
"Additional accent" = "Principale aggiuntivo";
/* No comment provided by engineer. */
"Additional accent 2" = "Principale aggiuntivo 2";
/* No comment provided by engineer. */
"Additional secondary" = "Secondario aggiuntivo";
/* No comment provided by engineer. */
"Address" = "Indirizzo";
@@ -401,6 +413,9 @@
/* No comment provided by engineer. */
"Advanced network settings" = "Impostazioni di rete avanzate";
/* No comment provided by engineer. */
"Advanced settings" = "Impostazioni avanzate";
/* chat item text */
"agreeing encryption for %@…" = "concordando la crittografia per %@…";
@@ -416,6 +431,9 @@
/* No comment provided by engineer. */
"All data is erased when it is entered." = "Tutti i dati vengono cancellati quando inserito.";
/* No comment provided by engineer. */
"All data is private to your device." = "Tutti i dati sono privati, nel tuo dispositivo.";
/* No comment provided by engineer. */
"All group members will remain connected." = "Tutti i membri del gruppo resteranno connessi.";
@@ -431,6 +449,9 @@
/* No comment provided by engineer. */
"All new messages from %@ will be hidden!" = "Tutti i nuovi messaggi da %@ verrranno nascosti!";
/* No comment provided by engineer. */
"All profiles" = "Tutti gli profili";
/* No comment provided by engineer. */
"All your contacts will remain connected." = "Tutti i tuoi contatti resteranno connessi.";
@@ -557,6 +578,9 @@
/* No comment provided by engineer. */
"Apply" = "Applica";
/* No comment provided by engineer. */
"Apply to" = "Applica a";
/* No comment provided by engineer. */
"Archive and upload" = "Archivia e carica";
@@ -566,6 +590,9 @@
/* No comment provided by engineer. */
"Attach" = "Allega";
/* No comment provided by engineer. */
"attempts" = "tentativi";
/* No comment provided by engineer. */
"Audio & video calls" = "Chiamate audio e video";
@@ -608,6 +635,9 @@
/* No comment provided by engineer. */
"Back" = "Indietro";
/* No comment provided by engineer. */
"Background" = "Sfondo";
/* No comment provided by engineer. */
"Bad desktop address" = "Indirizzo desktop errato";
@@ -629,6 +659,9 @@
/* No comment provided by engineer. */
"Better messages" = "Messaggi migliorati";
/* No comment provided by engineer. */
"Black" = "Nero";
/* No comment provided by engineer. */
"Block" = "Blocca";
@@ -719,6 +752,9 @@
/* No comment provided by engineer. */
"Cannot access keychain to save database password" = "Impossibile accedere al portachiavi per salvare la password del database";
/* No comment provided by engineer. */
"Cannot forward message" = "Impossibile inoltrare il messaggio";
/* No comment provided by engineer. */
"Cannot receive file" = "Impossibile ricevere il file";
@@ -777,6 +813,9 @@
/* No comment provided by engineer. */
"Chat archive" = "Archivio chat";
/* No comment provided by engineer. */
"Chat colors" = "Colori della chat";
/* No comment provided by engineer. */
"Chat console" = "Console della chat";
@@ -804,6 +843,9 @@
/* No comment provided by engineer. */
"Chat preferences" = "Preferenze della chat";
/* No comment provided by engineer. */
"Chat theme" = "Tema della chat";
/* No comment provided by engineer. */
"Chats" = "Chat";
@@ -822,6 +864,15 @@
/* No comment provided by engineer. */
"Choose from library" = "Scegli dalla libreria";
/* No comment provided by engineer. */
"Chunks deleted" = "Blocchi eliminati";
/* No comment provided by engineer. */
"Chunks downloaded" = "Blocchi scaricati";
/* No comment provided by engineer. */
"Chunks uploaded" = "Blocchi inviati";
/* No comment provided by engineer. */
"Clear" = "Svuota";
@@ -838,10 +889,10 @@
"Clear verification" = "Annulla la verifica";
/* No comment provided by engineer. */
"colored" = "colorato";
"Color mode" = "Modalità di colore";
/* No comment provided by engineer. */
"Colors" = "Colori";
"colored" = "colorato";
/* server test step */
"Compare file" = "Confronta file";
@@ -852,6 +903,9 @@
/* No comment provided by engineer. */
"complete" = "completo";
/* No comment provided by engineer. */
"Completed" = "Completato";
/* No comment provided by engineer. */
"Configure ICE servers" = "Configura server ICE";
@@ -921,18 +975,27 @@
/* No comment provided by engineer. */
"connected" = "connesso/a";
/* No comment provided by engineer. */
"Connected" = "Connesso";
/* No comment provided by engineer. */
"Connected desktop" = "Desktop connesso";
/* rcv group event chat item */
"connected directly" = "si è connesso/a direttamente";
/* No comment provided by engineer. */
"Connected servers" = "Server connessi";
/* No comment provided by engineer. */
"Connected to desktop" = "Connesso al desktop";
/* No comment provided by engineer. */
"connecting" = "in connessione";
/* No comment provided by engineer. */
"Connecting" = "In connessione";
/* No comment provided by engineer. */
"connecting (accepted)" = "in connessione (accettato)";
@@ -981,9 +1044,15 @@
/* No comment provided by engineer. */
"Connection timeout" = "Connessione scaduta";
/* No comment provided by engineer. */
"Connection with desktop stopped" = "Connessione con il desktop fermata";
/* connection information */
"connection:%@" = "connessione:% @";
/* No comment provided by engineer. */
"Connections" = "Connessioni";
/* profile update event chat item */
"contact %@ changed to %@" = "contatto %1$@ cambiato in %2$@";
@@ -1023,9 +1092,12 @@
/* No comment provided by engineer. */
"Continue" = "Continua";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Copia";
/* No comment provided by engineer. */
"Copy error" = "Copia errore";
/* No comment provided by engineer. */
"Core version: v%@" = "Versione core: v%@";
@@ -1071,6 +1143,9 @@
/* No comment provided by engineer. */
"Create your profile" = "Crea il tuo profilo";
/* No comment provided by engineer. */
"Created" = "Creato";
/* No comment provided by engineer. */
"Created at" = "Creato il";
@@ -1095,6 +1170,9 @@
/* No comment provided by engineer. */
"Current passphrase…" = "Password attuale…";
/* No comment provided by engineer. */
"Current profile" = "Profilo attuale";
/* No comment provided by engineer. */
"Currently maximum supported file size is %@." = "Attualmente la dimensione massima supportata è di %@.";
@@ -1104,9 +1182,15 @@
/* No comment provided by engineer. */
"Custom time" = "Tempo personalizzato";
/* No comment provided by engineer. */
"Customize theme" = "Personalizza il tema";
/* No comment provided by engineer. */
"Dark" = "Scuro";
/* No comment provided by engineer. */
"Dark mode colors" = "Colori modalità scura";
/* No comment provided by engineer. */
"Database downgrade" = "Downgrade del database";
@@ -1167,12 +1251,18 @@
/* time unit */
"days" = "giorni";
/* No comment provided by engineer. */
"Debug delivery" = "Debug della consegna";
/* No comment provided by engineer. */
"Decentralized" = "Decentralizzato";
/* message decrypt error item */
"Decryption error" = "Errore di decifrazione";
/* No comment provided by engineer. */
"decryption errors" = "errori di decifrazione";
/* pref value */
"default (%@)" = "predefinito (%@)";
@@ -1299,6 +1389,9 @@
/* deleted chat item */
"deleted" = "eliminato";
/* No comment provided by engineer. */
"Deleted" = "Eliminato";
/* No comment provided by engineer. */
"Deleted at" = "Eliminato il";
@@ -1311,6 +1404,9 @@
/* rcv group event chat item */
"deleted group" = "gruppo eliminato";
/* No comment provided by engineer. */
"Deletion errors" = "Errori di eliminazione";
/* No comment provided by engineer. */
"Delivery" = "Consegna";
@@ -1335,6 +1431,12 @@
/* snd error text */
"Destination server error: %@" = "Errore del server di destinazione: %@";
/* No comment provided by engineer. */
"Detailed statistics" = "Statistiche dettagliate";
/* No comment provided by engineer. */
"Details" = "Dettagli";
/* No comment provided by engineer. */
"Develop" = "Sviluppa";
@@ -1437,12 +1539,21 @@
/* chat item action */
"Download" = "Scarica";
/* No comment provided by engineer. */
"Download errors" = "Errori di scaricamento";
/* No comment provided by engineer. */
"Download failed" = "Scaricamento fallito";
/* server test step */
"Download file" = "Scarica file";
/* No comment provided by engineer. */
"Downloaded" = "Scaricato";
/* No comment provided by engineer. */
"Downloaded files" = "File scaricati";
/* No comment provided by engineer. */
"Downloading archive" = "Scaricamento archivio";
@@ -1455,6 +1566,9 @@
/* integrity error chat item */
"duplicate message" = "messaggio duplicato";
/* No comment provided by engineer. */
"duplicates" = "doppi";
/* No comment provided by engineer. */
"Duration" = "Durata";
@@ -1713,6 +1827,9 @@
/* No comment provided by engineer. */
"Error exporting chat database" = "Errore nell'esportazione del database della chat";
/* No comment provided by engineer. */
"Error exporting theme: %@" = "Errore di esportazione del tema: %@";
/* No comment provided by engineer. */
"Error importing chat database" = "Errore nell'importazione del database della chat";
@@ -1728,9 +1845,18 @@
/* No comment provided by engineer. */
"Error receiving file" = "Errore nella ricezione del file";
/* No comment provided by engineer. */
"Error reconnecting server" = "Errore di riconnessione al server";
/* No comment provided by engineer. */
"Error reconnecting servers" = "Errore di riconnessione ai server";
/* No comment provided by engineer. */
"Error removing member" = "Errore nella rimozione del membro";
/* No comment provided by engineer. */
"Error resetting statistics" = "Errore di azzeramento statistiche";
/* No comment provided by engineer. */
"Error saving %@ servers" = "Errore nel salvataggio dei server %@";
@@ -1800,7 +1926,8 @@
/* No comment provided by engineer. */
"Error: " = "Errore: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Errore: %@";
/* No comment provided by engineer. */
@@ -1809,6 +1936,9 @@
/* No comment provided by engineer. */
"Error: URL is invalid" = "Errore: l'URL non è valido";
/* No comment provided by engineer. */
"Errors" = "Errori";
/* No comment provided by engineer. */
"Even when disabled in the conversation." = "Anche quando disattivato nella conversazione.";
@@ -1821,12 +1951,18 @@
/* chat item action */
"Expand" = "Espandi";
/* No comment provided by engineer. */
"expired" = "scaduto";
/* No comment provided by engineer. */
"Export database" = "Esporta database";
/* No comment provided by engineer. */
"Export error:" = "Errore di esportazione:";
/* No comment provided by engineer. */
"Export theme" = "Esporta tema";
/* No comment provided by engineer. */
"Exported database archive." = "Archivio database esportato.";
@@ -1848,6 +1984,21 @@
/* No comment provided by engineer. */
"Favorite" = "Preferito";
/* No comment provided by engineer. */
"File error" = "Errore del file";
/* file error text */
"File not found - most likely file was deleted or cancelled." = "File non trovato - probabilmente è stato eliminato o annullato.";
/* file error text */
"File server error: %@" = "Errore del server dei file: %@";
/* No comment provided by engineer. */
"File status" = "Stato del file";
/* copied message info */
"File status: %@" = "Stato del file: %@";
/* No comment provided by engineer. */
"File will be deleted from servers." = "Il file verrà eliminato dai server.";
@@ -1962,6 +2113,12 @@
/* No comment provided by engineer. */
"GIFs and stickers" = "GIF e adesivi";
/* message preview */
"Good afternoon!" = "Buon pomeriggio!";
/* message preview */
"Good morning!" = "Buongiorno!";
/* No comment provided by engineer. */
"Group" = "Gruppo";
@@ -2139,6 +2296,9 @@
/* No comment provided by engineer. */
"Import failed" = "Importazione fallita";
/* No comment provided by engineer. */
"Import theme" = "Importa tema";
/* No comment provided by engineer. */
"Importing archive" = "Importazione archivio";
@@ -2160,6 +2320,9 @@
/* No comment provided by engineer. */
"In-call sounds" = "Suoni nelle chiamate";
/* No comment provided by engineer. */
"inactive" = "inattivo";
/* No comment provided by engineer. */
"Incognito" = "Incognito";
@@ -2223,6 +2386,9 @@
/* No comment provided by engineer. */
"Interface" = "Interfaccia";
/* No comment provided by engineer. */
"Interface colors" = "Colori dell'interfaccia";
/* invalid chat data */
"invalid chat" = "chat non valida";
@@ -2475,6 +2641,9 @@
/* rcv group event chat item */
"member connected" = "si è connesso/a";
/* item status text */
"Member inactive" = "Membro inattivo";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". All group members will be notified." = "Il ruolo del membro verrà cambiato in \"%@\". Tutti i membri del gruppo verranno avvisati.";
@@ -2484,6 +2653,9 @@
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "Il membro verrà rimosso dal gruppo, non è reversibile!";
/* No comment provided by engineer. */
"Menus" = "Menu";
/* item status text */
"Message delivery error" = "Errore di recapito del messaggio";
@@ -2496,6 +2668,15 @@
/* No comment provided by engineer. */
"Message draft" = "Bozza dei messaggi";
/* item status text */
"Message forwarded" = "Messaggio inoltrato";
/* item status description */
"Message may be delivered later if member becomes active." = "Il messaggio può essere consegnato più tardi se il membro diventa attivo.";
/* No comment provided by engineer. */
"Message queue info" = "Info coda messaggi";
/* chat feature */
"Message reactions" = "Reazioni ai messaggi";
@@ -2517,6 +2698,12 @@
/* No comment provided by engineer. */
"Message source remains private." = "La fonte del messaggio resta privata.";
/* No comment provided by engineer. */
"Message status" = "Stato del messaggio";
/* copied message info */
"Message status: %@" = "Stato del messaggio: %@";
/* No comment provided by engineer. */
"Message text" = "Testo del messaggio";
@@ -2532,6 +2719,12 @@
/* No comment provided by engineer. */
"Messages from %@ will be shown!" = "I messaggi da %@ verranno mostrati!";
/* No comment provided by engineer. */
"Messages received" = "Messaggi ricevuti";
/* No comment provided by engineer. */
"Messages sent" = "Messaggi inviati";
/* No comment provided by engineer. */
"Messages, files and calls are protected by **end-to-end encryption** with perfect forward secrecy, repudiation and break-in recovery." = "I messaggi, i file e le chiamate sono protetti da **crittografia end-to-end** con perfect forward secrecy, ripudio e recupero da intrusione.";
@@ -2697,6 +2890,9 @@
/* No comment provided by engineer. */
"No device token!" = "Nessun token del dispositivo!";
/* item status description */
"No direct connection yet, message is forwarded by admin." = "Ancora nessuna connessione diretta, il messaggio viene inoltrato dall'amministratore.";
/* No comment provided by engineer. */
"no e2e encryption" = "nessuna crittografia e2e";
@@ -2709,6 +2905,9 @@
/* No comment provided by engineer. */
"No history" = "Nessuna cronologia";
/* No comment provided by engineer. */
"No info, try to reload" = "Nessuna informazione, prova a ricaricare";
/* No comment provided by engineer. */
"No network connection" = "Nessuna connessione di rete";
@@ -2834,6 +3033,9 @@
/* authentication reason */
"Open migration to another device" = "Apri migrazione ad un altro dispositivo";
/* No comment provided by engineer. */
"Open server settings" = "Apri impostazioni server";
/* No comment provided by engineer. */
"Open Settings" = "Apri le impostazioni";
@@ -2858,9 +3060,15 @@
/* No comment provided by engineer. */
"Or show this code" = "O mostra questo codice";
/* No comment provided by engineer. */
"other" = "altro";
/* No comment provided by engineer. */
"Other" = "Altro";
/* No comment provided by engineer. */
"other errors" = "altri errori";
/* member role */
"owner" = "proprietario";
@@ -2903,6 +3111,9 @@
/* No comment provided by engineer. */
"peer-to-peer" = "peer-to-peer";
/* No comment provided by engineer. */
"Pending" = "In attesa";
/* No comment provided by engineer. */
"People can connect to you only via the links you share." = "Le persone possono connettersi a te solo tramite i link che condividi.";
@@ -2924,6 +3135,9 @@
/* No comment provided by engineer. */
"Please ask your contact to enable sending voice messages." = "Chiedi al tuo contatto di attivare l'invio dei messaggi vocali.";
/* No comment provided by engineer. */
"Please check that mobile and desktop are connected to the same local network, and that desktop firewall allows the connection.\nPlease share any other issues with the developers." = "Controlla che mobile e desktop siano collegati alla stessa rete locale e che il firewall del desktop consenta la connessione.\nSi prega di condividere qualsiasi altro problema con gli sviluppatori.";
/* No comment provided by engineer. */
"Please check that you used the correct link or ask your contact to send you another one." = "Controlla di aver usato il link giusto o chiedi al tuo contatto di inviartene un altro.";
@@ -2981,6 +3195,9 @@
/* No comment provided by engineer. */
"Preview" = "Anteprima";
/* No comment provided by engineer. */
"Previously connected servers" = "Server precedentemente connessi";
/* No comment provided by engineer. */
"Privacy & security" = "Privacy e sicurezza";
@@ -2991,7 +3208,7 @@
"Private filenames" = "Nomi di file privati";
/* No comment provided by engineer. */
"Private message routing" = "Instradamento privato messaggi";
"Private message routing" = "Instradamento privato dei messaggi";
/* No comment provided by engineer. */
"Private message routing 🚀" = "Instradamento privato dei messaggi 🚀";
@@ -3020,6 +3237,9 @@
/* No comment provided by engineer. */
"Profile password" = "Password del profilo";
/* No comment provided by engineer. */
"Profile theme" = "Tema del profilo";
/* No comment provided by engineer. */
"Profile update will be sent to your contacts." = "L'aggiornamento del profilo verrà inviato ai tuoi contatti.";
@@ -3068,6 +3288,12 @@
/* No comment provided by engineer. */
"Protocol timeout per KB" = "Scadenza del protocollo per KB";
/* No comment provided by engineer. */
"Proxied" = "Via proxy";
/* No comment provided by engineer. */
"Proxied servers" = "Server via proxy";
/* No comment provided by engineer. */
"Push notifications" = "Notifiche push";
@@ -3110,6 +3336,9 @@
/* No comment provided by engineer. */
"Receipts are disabled" = "Le ricevute sono disattivate";
/* No comment provided by engineer. */
"Receive errors" = "Errori di ricezione";
/* No comment provided by engineer. */
"received answer…" = "risposta ricevuta…";
@@ -3128,6 +3357,15 @@
/* message info title */
"Received message" = "Messaggio ricevuto";
/* No comment provided by engineer. */
"Received messages" = "Messaggi ricevuti";
/* No comment provided by engineer. */
"Received reply" = "Risposta ricevuta";
/* No comment provided by engineer. */
"Received total" = "Totale ricevuto";
/* No comment provided by engineer. */
"Receiving address will be changed to a different server. Address change will complete after sender comes online." = "L'indirizzo di ricezione verrà cambiato in un server diverso. La modifica dell'indirizzo verrà completata dopo che il mittente sarà in linea.";
@@ -3146,9 +3384,24 @@
/* No comment provided by engineer. */
"Recipients see updates as you type them." = "I destinatari vedono gli aggiornamenti mentre li digiti.";
/* No comment provided by engineer. */
"Reconnect" = "Riconnetti";
/* No comment provided by engineer. */
"Reconnect all connected servers to force message delivery. It uses additional traffic." = "Riconnetti tutti i server connessi per imporre il recapito dei messaggi. Utilizza traffico aggiuntivo.";
/* No comment provided by engineer. */
"Reconnect all servers" = "Riconnetti tutti i server";
/* No comment provided by engineer. */
"Reconnect all servers?" = "Riconnettere tutti i server?";
/* No comment provided by engineer. */
"Reconnect server to force message delivery. It uses additional traffic." = "Riconnetti il server per forzare la consegna dei messaggi. Usa traffico aggiuntivo.";
/* No comment provided by engineer. */
"Reconnect server?" = "Riconnettere il server?";
/* No comment provided by engineer. */
"Reconnect servers?" = "Riconnettere i server?";
@@ -3182,6 +3435,9 @@
/* No comment provided by engineer. */
"Remove" = "Rimuovi";
/* No comment provided by engineer. */
"Remove image" = "Rimuovi immagine";
/* No comment provided by engineer. */
"Remove member" = "Rimuovi membro";
@@ -3239,12 +3495,24 @@
/* No comment provided by engineer. */
"Reset" = "Ripristina";
/* No comment provided by engineer. */
"Reset all statistics" = "Azzera tutte le statistiche";
/* No comment provided by engineer. */
"Reset all statistics?" = "Azzerare tutte le statistiche?";
/* No comment provided by engineer. */
"Reset colors" = "Ripristina i colori";
/* No comment provided by engineer. */
"Reset to app theme" = "Ripristina al tema dell'app";
/* No comment provided by engineer. */
"Reset to defaults" = "Ripristina i predefiniti";
/* No comment provided by engineer. */
"Reset to user theme" = "Ripristina al tema dell'utente";
/* No comment provided by engineer. */
"Restart the app to create a new chat profile" = "Riavvia l'app per creare un nuovo profilo di chat";
@@ -3359,6 +3627,12 @@
/* No comment provided by engineer. */
"Saved WebRTC ICE servers will be removed" = "I server WebRTC ICE salvati verranno rimossi";
/* No comment provided by engineer. */
"Scale" = "Scala";
/* No comment provided by engineer. */
"Scan / Paste link" = "Scansiona / Incolla link";
/* No comment provided by engineer. */
"Scan code" = "Scansiona codice";
@@ -3386,6 +3660,9 @@
/* network option */
"sec" = "sec";
/* No comment provided by engineer. */
"Secondary" = "Secondario";
/* time unit */
"seconds" = "secondi";
@@ -3395,6 +3672,9 @@
/* server test step */
"Secure queue" = "Coda sicura";
/* No comment provided by engineer. */
"Secured" = "Protetto";
/* No comment provided by engineer. */
"Security assessment" = "Valutazione della sicurezza";
@@ -3407,6 +3687,9 @@
/* No comment provided by engineer. */
"Select" = "Seleziona";
/* No comment provided by engineer. */
"Selected chat preferences prohibit this message." = "Le preferenze della chat selezionata vietano questo messaggio.";
/* No comment provided by engineer. */
"Self-destruct" = "Autodistruzione";
@@ -3440,6 +3723,9 @@
/* No comment provided by engineer. */
"Send disappearing message" = "Invia messaggio a tempo";
/* No comment provided by engineer. */
"Send errors" = "Errori di invio";
/* No comment provided by engineer. */
"Send link previews" = "Invia anteprime dei link";
@@ -3506,18 +3792,39 @@
/* copied message info */
"Sent at: %@" = "Inviato il: %@";
/* No comment provided by engineer. */
"Sent directly" = "Inviato direttamente";
/* notification */
"Sent file event" = "Evento file inviato";
/* message info title */
"Sent message" = "Messaggio inviato";
/* No comment provided by engineer. */
"Sent messages" = "Messaggi inviati";
/* No comment provided by engineer. */
"Sent messages will be deleted after set time." = "I messaggi inviati verranno eliminati dopo il tempo impostato.";
/* No comment provided by engineer. */
"Sent reply" = "Risposta inviata";
/* No comment provided by engineer. */
"Sent total" = "Totale inviato";
/* No comment provided by engineer. */
"Sent via proxy" = "Inviato via proxy";
/* No comment provided by engineer. */
"Server address" = "Indirizzo server";
/* srv error text. */
"Server address is incompatible with network settings." = "L'indirizzo del server non è compatibile con le impostazioni di rete.";
/* queue info */
"server queue info: %@\n\nlast received msg: %@" = "info coda server: %1$@\n\nultimo msg ricevuto: %2$@";
/* server test error */
"Server requires authorization to create queues, check password" = "Il server richiede l'autorizzazione di creare code, controlla la password";
@@ -3527,12 +3834,21 @@
/* No comment provided by engineer. */
"Server test failed!" = "Test del server fallito!";
/* No comment provided by engineer. */
"Server type" = "Tipo server";
/* srv error text */
"Server version is incompatible with network settings." = "La versione del server non è compatibile con le impostazioni di rete.";
/* No comment provided by engineer. */
"Servers" = "Server";
/* No comment provided by engineer. */
"Servers info" = "Info dei server";
/* No comment provided by engineer. */
"Servers statistics will be reset - this cannot be undone!" = "Le statistiche dei server verranno azzerate - è irreversibile!";
/* No comment provided by engineer. */
"Session code" = "Codice di sessione";
@@ -3542,6 +3858,9 @@
/* No comment provided by engineer. */
"Set contact name…" = "Imposta nome del contatto…";
/* No comment provided by engineer. */
"Set default theme" = "Imposta tema predefinito";
/* No comment provided by engineer. */
"Set group preferences" = "Imposta le preferenze del gruppo";
@@ -3620,6 +3939,9 @@
/* No comment provided by engineer. */
"Show:" = "Mostra:";
/* No comment provided by engineer. */
"SimpleX" = "SimpleX";
/* No comment provided by engineer. */
"SimpleX address" = "Indirizzo SimpleX";
@@ -3665,6 +3987,9 @@
/* No comment provided by engineer. */
"Simplified incognito mode" = "Modalità incognito semplificata";
/* No comment provided by engineer. */
"Size" = "Dimensione";
/* No comment provided by engineer. */
"Skip" = "Salta";
@@ -3674,6 +3999,9 @@
/* No comment provided by engineer. */
"Small groups (max 20)" = "Piccoli gruppi (max 20)";
/* No comment provided by engineer. */
"SMP server" = "Server SMP";
/* No comment provided by engineer. */
"SMP servers" = "Server SMP";
@@ -3698,9 +4026,15 @@
/* No comment provided by engineer. */
"Start migration" = "Avvia la migrazione";
/* No comment provided by engineer. */
"Starting from %@." = "Inizio da %@.";
/* No comment provided by engineer. */
"starting…" = "avvio…";
/* No comment provided by engineer. */
"Statistics" = "Statistiche";
/* No comment provided by engineer. */
"Stop" = "Ferma";
@@ -3743,6 +4077,15 @@
/* No comment provided by engineer. */
"Submit" = "Invia";
/* No comment provided by engineer. */
"Subscribed" = "Iscritto";
/* No comment provided by engineer. */
"Subscription errors" = "Errori di iscrizione";
/* No comment provided by engineer. */
"Subscriptions ignored" = "Iscrizioni ignorate";
/* No comment provided by engineer. */
"Support SimpleX Chat" = "Supporta SimpleX Chat";
@@ -3791,6 +4134,9 @@
/* No comment provided by engineer. */
"TCP_KEEPINTVL" = "TCP_KEEPINTVL";
/* No comment provided by engineer. */
"Temporary file error" = "Errore del file temporaneo";
/* server test failure */
"Test failed at step %@." = "Test fallito al passo %@.";
@@ -3873,7 +4219,7 @@
"The text you pasted is not a SimpleX link." = "Il testo che hai incollato non è un link SimpleX.";
/* No comment provided by engineer. */
"Theme" = "Tema";
"Themes" = "Temi";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Queste impostazioni sono per il tuo profilo attuale **%@**.";
@@ -3917,9 +4263,15 @@
/* No comment provided by engineer. */
"This is your own SimpleX address!" = "Questo è il tuo indirizzo SimpleX!";
/* No comment provided by engineer. */
"This link was used with another mobile device, please create a new link on the desktop." = "Questo link è stato usato con un altro dispositivo mobile, creane uno nuovo sul desktop.";
/* No comment provided by engineer. */
"This setting applies to messages in your current chat profile **%@**." = "Questa impostazione si applica ai messaggi del profilo di chat attuale **%@**.";
/* No comment provided by engineer. */
"Title" = "Titoli";
/* No comment provided by engineer. */
"To ask any questions and to receive updates:" = "Per porre domande e ricevere aggiornamenti:";
@@ -3959,9 +4311,15 @@
/* No comment provided by engineer. */
"Toggle incognito when connecting." = "Attiva/disattiva l'incognito quando ti colleghi.";
/* No comment provided by engineer. */
"Total" = "Totale";
/* No comment provided by engineer. */
"Transport isolation" = "Isolamento del trasporto";
/* No comment provided by engineer. */
"Transport sessions" = "Sessioni di trasporto";
/* No comment provided by engineer. */
"Trying to connect to the server used to receive messages from this contact (error: %@)." = "Tentativo di connessione al server usato per ricevere messaggi da questo contatto (errore: %@).";
@@ -4097,12 +4455,21 @@
/* No comment provided by engineer. */
"Upgrade and open chat" = "Aggiorna e apri chat";
/* No comment provided by engineer. */
"Upload errors" = "Errori di invio";
/* No comment provided by engineer. */
"Upload failed" = "Invio fallito";
/* server test step */
"Upload file" = "Invia file";
/* No comment provided by engineer. */
"Uploaded" = "Inviato";
/* No comment provided by engineer. */
"Uploaded files" = "File inviati";
/* No comment provided by engineer. */
"Uploading archive" = "Invio dell'archivio";
@@ -4148,6 +4515,9 @@
/* No comment provided by engineer. */
"User profile" = "Profilo utente";
/* No comment provided by engineer. */
"User selection" = "Selezione utente";
/* No comment provided by engineer. */
"Using .onion hosts requires compatible VPN provider." = "L'uso di host .onion richiede un fornitore di VPN compatibile.";
@@ -4256,6 +4626,12 @@
/* No comment provided by engineer. */
"Waiting for video" = "In attesa del video";
/* No comment provided by engineer. */
"Wallpaper accent" = "Tinta dello sfondo";
/* No comment provided by engineer. */
"Wallpaper background" = "Retro dello sfondo";
/* No comment provided by engineer. */
"wants to connect to you!" = "vuole connettersi con te!";
@@ -4328,9 +4704,15 @@
/* snd error text */
"Wrong key or unknown connection - most likely this connection is deleted." = "Chiave sbagliata o connessione sconosciuta - molto probabilmente questa connessione è stata eliminata.";
/* file error text */
"Wrong key or unknown file chunk address - most likely file is deleted." = "Chiave sbagliata o indirizzo sconosciuto per frammento del file - probabilmente il file è stato eliminato.";
/* No comment provided by engineer. */
"Wrong passphrase!" = "Password sbagliata!";
/* No comment provided by engineer. */
"XFTP server" = "Server XFTP";
/* No comment provided by engineer. */
"XFTP servers" = "Server XFTP";
@@ -4388,6 +4770,9 @@
/* No comment provided by engineer. */
"You are invited to group" = "Sei stato/a invitato/a al gruppo";
/* No comment provided by engineer. */
"You are not connected to these servers. Private routing is used to deliver messages to them." = "Non sei connesso/a a questi server. L'instradamento privato è usato per consegnare loro i messaggi.";
/* No comment provided by engineer. */
"you are observer" = "sei un osservatore";
@@ -4416,7 +4801,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Puoi renderlo visibile ai tuoi contatti SimpleX nelle impostazioni.";
/* notification body */
"You can now send messages to %@" = "Ora puoi inviare messaggi a %@";
"You can now chat with %@" = "Ora puoi inviare messaggi a %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Puoi impostare l'anteprima della notifica nella schermata di blocco tramite le impostazioni.";
+5 -16
View File
@@ -148,9 +148,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ は検証されています";
/* No comment provided by engineer. */
"%@ servers" = "%@ サーバー";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ アップロード済";
@@ -331,9 +328,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "上で選んでください:";
/* No comment provided by engineer. */
"Accent color" = "アクセントカラー";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "承諾";
@@ -360,7 +354,7 @@
"Add profile" = "プロフィールを追加";
/* No comment provided by engineer. */
"Add server" = "サーバを追加";
"Add server" = "サーバを追加";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "QRコードでサーバを追加する。";
@@ -735,9 +729,6 @@
/* No comment provided by engineer. */
"colored" = "色付き";
/* No comment provided by engineer. */
"Colors" = "色";
/* server test step */
"Compare file" = "ファイルを比較";
@@ -867,7 +858,7 @@
/* No comment provided by engineer. */
"Continue" = "続ける";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "コピー";
/* No comment provided by engineer. */
@@ -1509,7 +1500,8 @@
/* No comment provided by engineer. */
"Error: " = "エラー : ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "エラー : %@";
/* No comment provided by engineer. */
@@ -3113,9 +3105,6 @@
/* No comment provided by engineer. */
"The servers for new connections of your current chat profile **%@**." = "現在のチャットプロフィールの新しい接続のサーバ **%@**。";
/* No comment provided by engineer. */
"Theme" = "テーマ";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "これらの設定は現在のプロファイル **%@** 用です。";
@@ -3459,7 +3448,7 @@
"You can hide or mute a user profile - swipe it to the right." = "ユーザープロファイルを右にスワイプすると、非表示またはミュートにすることができます。";
/* notification body */
"You can now send messages to %@" = "%@ にメッセージを送信できるようになりました";
"You can now chat with %@" = "%@ にメッセージを送信できるようになりました";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "設定からロック画面の通知プレビューを設定できます。";
+14 -16
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ is geverifieerd";
/* No comment provided by engineer. */
"%@ servers" = "%@ servers";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ geüpload";
@@ -337,9 +334,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "hier boven, kies dan:";
/* No comment provided by engineer. */
"Accent color" = "Accent kleur";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Accepteer";
@@ -369,7 +363,7 @@
"Add profile" = "Profiel toevoegen";
/* No comment provided by engineer. */
"Add server" = "Server toevoegen";
"Add server" = "Server toevoegen";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Servers toevoegen door QR-codes te scannen.";
@@ -840,9 +834,6 @@
/* No comment provided by engineer. */
"colored" = "gekleurd";
/* No comment provided by engineer. */
"Colors" = "Kleuren";
/* server test step */
"Compare file" = "Bestand vergelijken";
@@ -1023,7 +1014,7 @@
/* No comment provided by engineer. */
"Continue" = "Doorgaan";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Kopiëren";
/* No comment provided by engineer. */
@@ -1167,6 +1158,9 @@
/* time unit */
"days" = "dagen";
/* No comment provided by engineer. */
"Debug delivery" = "Foutopsporing bezorging";
/* No comment provided by engineer. */
"Decentralized" = "Gedecentraliseerd";
@@ -1800,7 +1794,8 @@
/* No comment provided by engineer. */
"Error: " = "Fout: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Fout: %@";
/* No comment provided by engineer. */
@@ -2496,6 +2491,9 @@
/* No comment provided by engineer. */
"Message draft" = "Concept bericht";
/* No comment provided by engineer. */
"Message queue info" = "Informatie over berichtenwachtrij";
/* chat feature */
"Message reactions" = "Reacties op berichten";
@@ -3518,6 +3516,9 @@
/* srv error text. */
"Server address is incompatible with network settings." = "Serveradres is niet compatibel met netwerkinstellingen.";
/* queue info */
"server queue info: %@\n\nlast received msg: %@" = "informatie over serverwachtrij: %1$@\n\nlaatst ontvangen bericht: %2$@";
/* server test error */
"Server requires authorization to create queues, check password" = "Server vereist autorisatie om wachtrijen te maken, controleer wachtwoord";
@@ -3872,9 +3873,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "De tekst die u hebt geplakt is geen SimpleX link.";
/* No comment provided by engineer. */
"Theme" = "Thema";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Deze instellingen zijn voor uw huidige profiel **%@**.";
@@ -4416,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Je kunt het via Instellingen zichtbaar maken voor je SimpleX contacten.";
/* notification body */
"You can now send messages to %@" = "Je kunt nu berichten sturen naar %@";
"You can now chat with %@" = "Je kunt nu berichten sturen naar %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "U kunt een voorbeeld van een melding op het vergrendeld scherm instellen via instellingen.";
+14 -16
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ jest zweryfikowany";
/* No comment provided by engineer. */
"%@ servers" = "%@ serwery";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ wgrane";
@@ -337,9 +334,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "powyżej, a następnie wybierz:";
/* No comment provided by engineer. */
"Accent color" = "Kolor akcentu";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Akceptuj";
@@ -369,7 +363,7 @@
"Add profile" = "Dodaj profil";
/* No comment provided by engineer. */
"Add server" = "Dodaj serwer";
"Add server" = "Dodaj serwer";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Dodaj serwery, skanując kody QR.";
@@ -840,9 +834,6 @@
/* No comment provided by engineer. */
"colored" = "kolorowy";
/* No comment provided by engineer. */
"Colors" = "Kolory";
/* server test step */
"Compare file" = "Porównaj plik";
@@ -1023,7 +1014,7 @@
/* No comment provided by engineer. */
"Continue" = "Kontynuuj";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Kopiuj";
/* No comment provided by engineer. */
@@ -1167,6 +1158,9 @@
/* time unit */
"days" = "dni";
/* No comment provided by engineer. */
"Debug delivery" = "Dostarczenie debugowania";
/* No comment provided by engineer. */
"Decentralized" = "Zdecentralizowane";
@@ -1800,7 +1794,8 @@
/* No comment provided by engineer. */
"Error: " = "Błąd: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Błąd: %@";
/* No comment provided by engineer. */
@@ -2496,6 +2491,9 @@
/* No comment provided by engineer. */
"Message draft" = "Wersja robocza wiadomości";
/* No comment provided by engineer. */
"Message queue info" = "Informacje kolejki wiadomości";
/* chat feature */
"Message reactions" = "Reakcje wiadomości";
@@ -3518,6 +3516,9 @@
/* srv error text. */
"Server address is incompatible with network settings." = "Adres serwera jest niekompatybilny z ustawieniami sieciowymi.";
/* queue info */
"server queue info: %@\n\nlast received msg: %@" = "Informacje kolejki serwera: %1$@\n\nostatnia otrzymana wiadomość: %2$@";
/* server test error */
"Server requires authorization to create queues, check password" = "Serwer wymaga autoryzacji do tworzenia kolejek, sprawdź hasło";
@@ -3872,9 +3873,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "Tekst, który wkleiłeś nie jest linkiem SimpleX.";
/* No comment provided by engineer. */
"Theme" = "Motyw";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Te ustawienia dotyczą Twojego bieżącego profilu **%@**.";
@@ -4416,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Możesz ustawić go jako widoczny dla swoich kontaktów SimpleX w Ustawieniach.";
/* notification body */
"You can now send messages to %@" = "Możesz teraz wysyłać wiadomości do %@";
"You can now chat with %@" = "Możesz teraz wysyłać wiadomości do %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Podgląd powiadomień na ekranie blokady można ustawić w ustawieniach.";
+5 -16
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ подтверждён";
/* No comment provided by engineer. */
"%@ servers" = "%@ серверы";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ загружено";
@@ -337,9 +334,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "наверху, затем выберите:";
/* No comment provided by engineer. */
"Accent color" = "Основной цвет";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Принять";
@@ -369,7 +363,7 @@
"Add profile" = "Добавить профиль";
/* No comment provided by engineer. */
"Add server" = "Добавить сервер";
"Add server" = "Добавить сервер";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Добавить серверы через QR код.";
@@ -840,9 +834,6 @@
/* No comment provided by engineer. */
"colored" = "цвет";
/* No comment provided by engineer. */
"Colors" = "Цвета";
/* server test step */
"Compare file" = "Сравнение файла";
@@ -1023,7 +1014,7 @@
/* No comment provided by engineer. */
"Continue" = "Продолжить";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Скопировать";
/* No comment provided by engineer. */
@@ -1800,7 +1791,8 @@
/* No comment provided by engineer. */
"Error: " = "Ошибка: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Ошибка: %@";
/* No comment provided by engineer. */
@@ -3872,9 +3864,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "Вставленный текст не является SimpleX-ссылкой.";
/* No comment provided by engineer. */
"Theme" = "Тема";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Установки для Вашего активного профиля **%@**.";
@@ -4416,7 +4405,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Вы можете сделать его видимым для ваших контактов в SimpleX через Настройки.";
/* notification body */
"You can now send messages to %@" = "Вы теперь можете отправлять сообщения %@";
"You can now chat with %@" = "Вы теперь можете общаться с %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Вы можете установить просмотр уведомлений на экране блокировки в настройках.";
+5 -16
View File
@@ -109,9 +109,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ ได้รับการตรวจสอบแล้ว";
/* No comment provided by engineer. */
"%@ servers" = "%@ เซิร์ฟเวอร์";
/* notification title */
"%@ wants to connect!" = "%@ อยากเชื่อมต่อ!";
@@ -259,9 +256,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "ด้านบน จากนั้นเลือก:";
/* No comment provided by engineer. */
"Accent color" = "สีเน้น";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "รับ";
@@ -285,7 +279,7 @@
"Add profile" = "เพิ่มโปรไฟล์";
/* No comment provided by engineer. */
"Add server" = "เพิ่มเซิร์ฟเวอร์";
"Add server" = "เพิ่มเซิร์ฟเวอร์";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "เพิ่มเซิร์ฟเวอร์โดยการสแกนรหัสคิวอาร์โค้ด";
@@ -639,9 +633,6 @@
/* No comment provided by engineer. */
"colored" = "มีสี";
/* No comment provided by engineer. */
"Colors" = "สี";
/* server test step */
"Compare file" = "เปรียบเทียบไฟล์";
@@ -765,7 +756,7 @@
/* No comment provided by engineer. */
"Continue" = "ดำเนินการต่อ";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "คัดลอก";
/* No comment provided by engineer. */
@@ -1386,7 +1377,8 @@
/* No comment provided by engineer. */
"Error: " = "ผิดพลาด: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "ข้อผิดพลาด: % @";
/* No comment provided by engineer. */
@@ -2975,9 +2967,6 @@
/* No comment provided by engineer. */
"The servers for new connections of your current chat profile **%@**." = "เซิร์ฟเวอร์สำหรับการเชื่อมต่อใหม่ของโปรไฟล์การแชทปัจจุบันของคุณ **%@**";
/* No comment provided by engineer. */
"Theme" = "ธีม";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "การตั้งค่าเหล่านี้ใช้สำหรับโปรไฟล์ปัจจุบันของคุณ **%@**";
@@ -3312,7 +3301,7 @@
"You can hide or mute a user profile - swipe it to the right." = "คุณสามารถซ่อนหรือปิดเสียงโปรไฟล์ผู้ใช้ - ปัดไปทางขวา";
/* notification body */
"You can now send messages to %@" = "ตอนนี้คุณสามารถส่งข้อความถึง %@";
"You can now chat with %@" = "ตอนนี้คุณสามารถส่งข้อความถึง %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "คุณสามารถตั้งค่าแสดงตัวอย่างการแจ้งเตือนบนหน้าจอล็อคผ่านการตั้งค่า";
+14 -16
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ onaylandı";
/* No comment provided by engineer. */
"%@ servers" = "%@ sunucuları";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ yüklendi";
@@ -337,9 +334,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "yukarı çıkın, ardından seçin:";
/* No comment provided by engineer. */
"Accent color" = "Vurgu rengi";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Kabul et";
@@ -369,7 +363,7 @@
"Add profile" = "Profil ekle";
/* No comment provided by engineer. */
"Add server" = "Sunucu ekle";
"Add server" = "Sunucu ekle";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Karekod taratarak sunucuları ekleyin.";
@@ -840,9 +834,6 @@
/* No comment provided by engineer. */
"colored" = "renklendirilmiş";
/* No comment provided by engineer. */
"Colors" = "Renkler";
/* server test step */
"Compare file" = "Dosya karşılaştır";
@@ -1023,7 +1014,7 @@
/* No comment provided by engineer. */
"Continue" = "Devam et";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Kopyala";
/* No comment provided by engineer. */
@@ -1167,6 +1158,9 @@
/* time unit */
"days" = "gün";
/* No comment provided by engineer. */
"Debug delivery" = "Hata ayıklama teslimatı";
/* No comment provided by engineer. */
"Decentralized" = "Merkezi Olmayan";
@@ -1800,7 +1794,8 @@
/* No comment provided by engineer. */
"Error: " = "Hata: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Hata: %@";
/* No comment provided by engineer. */
@@ -2496,6 +2491,9 @@
/* No comment provided by engineer. */
"Message draft" = "Mesaj taslağı";
/* No comment provided by engineer. */
"Message queue info" = "Mesaj kuyruğu bilgisi";
/* chat feature */
"Message reactions" = "Mesaj tepkileri";
@@ -3518,6 +3516,9 @@
/* srv error text. */
"Server address is incompatible with network settings." = "Sunucu adresi ağ ayarlarıyla uyumlu değil.";
/* queue info */
"server queue info: %@\n\nlast received msg: %@" = "sunucu kuyruk bilgisi: %1$@\n\nson alınan msj: %2$@";
/* server test error */
"Server requires authorization to create queues, check password" = "Sunucunun sıra oluşturması için yetki gereklidir, şifreyi kontrol edin";
@@ -3872,9 +3873,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "Yapıştırdığın metin bir SimpleX bağlantısı değildir.";
/* No comment provided by engineer. */
"Theme" = "Tema";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Bu ayarlar mevcut profiliniz **%@** içindir.";
@@ -4416,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Ayarlardan SimpleX kişilerinize görünür yapabilirsiniz.";
/* notification body */
"You can now send messages to %@" = "Artık %@ adresine mesaj gönderebilirsin";
"You can now chat with %@" = "Artık %@ adresine mesaj gönderebilirsin";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Kilit ekranı bildirim önizlemesini ayarlar üzerinden ayarlayabilirsiniz.";
+14 -16
View File
@@ -154,9 +154,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ перевірено";
/* No comment provided by engineer. */
"%@ servers" = "%@ сервери";
/* No comment provided by engineer. */
"%@ uploaded" = "%@ завантажено";
@@ -337,9 +334,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "вище, а потім обирайте:";
/* No comment provided by engineer. */
"Accent color" = "Акцентний колір";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "Прийняти";
@@ -369,7 +363,7 @@
"Add profile" = "Додати профіль";
/* No comment provided by engineer. */
"Add server" = "Додати сервер";
"Add server" = "Додати сервер";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "Додайте сервери, відсканувавши QR-код.";
@@ -840,9 +834,6 @@
/* No comment provided by engineer. */
"colored" = "кольоровий";
/* No comment provided by engineer. */
"Colors" = "Кольори";
/* server test step */
"Compare file" = "Порівняти файл";
@@ -1023,7 +1014,7 @@
/* No comment provided by engineer. */
"Continue" = "Продовжуйте";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "Копіювати";
/* No comment provided by engineer. */
@@ -1167,6 +1158,9 @@
/* time unit */
"days" = "днів";
/* No comment provided by engineer. */
"Debug delivery" = "Доставка налагодження";
/* No comment provided by engineer. */
"Decentralized" = "Децентралізований";
@@ -1800,7 +1794,8 @@
/* No comment provided by engineer. */
"Error: " = "Помилка: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "Помилка: %@";
/* No comment provided by engineer. */
@@ -2496,6 +2491,9 @@
/* No comment provided by engineer. */
"Message draft" = "Чернетка повідомлення";
/* No comment provided by engineer. */
"Message queue info" = "Інформація про чергу повідомлень";
/* chat feature */
"Message reactions" = "Реакції на повідомлення";
@@ -3518,6 +3516,9 @@
/* srv error text. */
"Server address is incompatible with network settings." = "Адреса сервера несумісна з налаштуваннями мережі.";
/* queue info */
"server queue info: %@\n\nlast received msg: %@" = "інформація про чергу на сервері: %1$@\n\nостаннє отримане повідомлення: %2$@";
/* server test error */
"Server requires authorization to create queues, check password" = "Сервер вимагає авторизації для створення черг, перевірте пароль";
@@ -3872,9 +3873,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "Текст, який ви вставили, не є посиланням SimpleX.";
/* No comment provided by engineer. */
"Theme" = "Тема";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "Ці налаштування стосуються вашого поточного профілю **%@**.";
@@ -4416,7 +4414,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "Ви можете зробити його видимим для ваших контактів у SimpleX за допомогою налаштувань.";
/* notification body */
"You can now send messages to %@" = "Тепер ви можете надсилати повідомлення на адресу %@";
"You can now chat with %@" = "Тепер ви можете надсилати повідомлення на адресу %@";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Ви можете налаштувати попередній перегляд сповіщень на екрані блокування за допомогою налаштувань.";
+5 -16
View File
@@ -133,9 +133,6 @@
/* No comment provided by engineer. */
"%@ is verified" = "%@ 已认证";
/* No comment provided by engineer. */
"%@ servers" = "%@ 服务器";
/* notification title */
"%@ wants to connect!" = "%@ 要连接!";
@@ -301,9 +298,6 @@
/* No comment provided by engineer. */
"above, then choose:" = "上面,然后选择:";
/* No comment provided by engineer. */
"Accent color" = "色调";
/* accept contact request via notification
accept incoming call via notification */
"Accept" = "接受";
@@ -333,7 +327,7 @@
"Add profile" = "添加个人资料";
/* No comment provided by engineer. */
"Add server" = "添加服务器";
"Add server" = "添加服务器";
/* No comment provided by engineer. */
"Add servers by scanning QR codes." = "扫描二维码来添加服务器。";
@@ -786,9 +780,6 @@
/* No comment provided by engineer. */
"colored" = "彩色";
/* No comment provided by engineer. */
"Colors" = "颜色";
/* server test step */
"Compare file" = "对比文件";
@@ -951,7 +942,7 @@
/* No comment provided by engineer. */
"Continue" = "继续";
/* chat item action */
/* No comment provided by engineer. */
"Copy" = "复制";
/* No comment provided by engineer. */
@@ -1689,7 +1680,8 @@
/* No comment provided by engineer. */
"Error: " = "错误: ";
/* snd error text */
/* file error text
snd error text */
"Error: %@" = "错误: @";
/* No comment provided by engineer. */
@@ -3644,9 +3636,6 @@
/* No comment provided by engineer. */
"The text you pasted is not a SimpleX link." = "您粘贴的文本不是 SimpleX 链接。";
/* No comment provided by engineer. */
"Theme" = "主题";
/* No comment provided by engineer. */
"These settings are for your current profile **%@**." = "这些设置适用于您当前的配置文件 **%@**。";
@@ -4122,7 +4111,7 @@
"You can make it visible to your SimpleX contacts via Settings." = "你可以通过设置让它对你的 SimpleX 联系人可见。";
/* notification body */
"You can now send messages to %@" = "您现在可以给 %@ 发送消息";
"You can now chat with %@" = "您现在可以给 %@ 发送消息";
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "您可以通过设置来设置锁屏通知预览。";
@@ -105,6 +105,7 @@ kotlin {
implementation("uk.co.caprica:vlcj:4.8.2")
implementation("com.github.NanoHttpd.nanohttpd:nanohttpd:efb2ebf85a")
implementation("com.github.NanoHttpd.nanohttpd:nanohttpd-websocket:efb2ebf85a")
implementation("com.squareup.okhttp3:okhttp:4.12.0")
}
}
val desktopTest by getting
@@ -6,8 +6,6 @@ import android.net.LocalServerSocket
import android.util.Log
import androidx.activity.ComponentActivity
import androidx.fragment.app.FragmentActivity
import chat.simplex.common.*
import chat.simplex.common.platform.*
import java.io.*
import java.lang.ref.WeakReference
import java.util.*
@@ -24,6 +22,8 @@ var isAppOnForeground: Boolean = false
@Suppress("ConstantLocale")
val defaultLocale: Locale = Locale.getDefault()
actual fun isAppVisibleAndFocused(): Boolean = isAppOnForeground
@SuppressLint("StaticFieldLeak")
lateinit var androidAppContext: Context
var mainActivity: WeakReference<FragmentActivity> = WeakReference(null)
@@ -29,6 +29,8 @@ actual val remoteHostsDir: File = File(tmpDir.absolutePath + File.separator + "r
actual fun desktopOpenDatabaseDir() {}
actual fun desktopOpenDir(dir: File) {}
@Composable
actual fun rememberFileChooserLauncher(getContent: Boolean, rememberedValue: Any?, onResult: (URI?) -> Unit): FileChooserLauncher {
val launcher = rememberLauncherForActivityResult(
@@ -28,3 +28,5 @@ actual fun Modifier.desktopOnExternalDrag(
actual fun Modifier.onRightClick(action: () -> Unit): Modifier = this
actual fun Modifier.desktopPointerHoverIconHand(): Modifier = this
actual fun Modifier.desktopOnHovered(action: (Boolean) -> Unit): Modifier = Modifier

Some files were not shown because too many files have changed in this diff Show More