ios: incognito mode (#945)

* ios: incognito types

* wip

* wip

* wip

* wip

* wip

* cleaner interface

* CIGroupInvitationView logic

* masks not filled

* ui improvements

* wip

* wip

* incognito may be compromised alerts

* help

* remove modifier

* Update apps/ios/Shared/Views/Chat/ChatItem/CIGroupInvitationView.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update apps/ios/Shared/Views/Chat/Group/AddGroupMembersView.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* Update apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* contact request

* texts

* ;

* prepare for merge

* restore help

* wip

* update help

* wip

* update incognito help

* the

* Update apps/ios/Shared/Views/UserSettings/IncognitoHelp.swift

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>

* wording

* translations

* secondary color

* translations

* translations

* fix Your Chats title

Co-authored-by: Evgeny Poberezkin <2769109+epoberezkin@users.noreply.github.com>
This commit is contained in:
JRoberts
2022-08-23 18:18:12 +04:00
committed by GitHub
parent bd7aa81625
commit e6551abc68
31 changed files with 1392 additions and 183 deletions

View File

@@ -36,6 +36,7 @@ final class ChatModel: ObservableObject {
@Published var tokenStatus: NtfTknStatus?
@Published var notificationMode = NotificationsMode.off
@Published var notificationPreview: NotificationPreviewMode? = ntfPreviewModeGroupDefault.get()
@Published var incognito: Bool = incognitoGroupDefault.get()
// pending notification actions
@Published var ntfContactRequest: ChatId?
@Published var ntfCallInvitationAction: (ChatId, NtfCallAction)?
@@ -79,7 +80,7 @@ final class ChatModel: ObservableObject {
}
func updateContact(_ contact: Contact) {
updateChat(.direct(contact: contact), addMissing: !contact.isIndirectContact())
updateChat(.direct(contact: contact), addMissing: !contact.isIndirectContact)
}
func updateGroup(_ groupInfo: GroupInfo) {

View File

@@ -167,6 +167,12 @@ func apiSetFilesFolder(filesFolder: String) throws {
throw r
}
func apiSetIncognito(incognito: Bool) throws {
let r = chatSendCmdSync(.setIncognito(incognito: incognito))
if case .cmdOk = r { return }
throw r
}
func apiExportArchive(config: ArchiveConfig) async throws {
try await sendCommandOkResp(.apiExportArchive(config: config))
}
@@ -312,15 +318,15 @@ func apiSetChatSettings(type: ChatType, id: Int64, chatSettings: ChatSettings) a
try await sendCommandOkResp(.apiSetChatSettings(type: type, id: id, chatSettings: chatSettings))
}
func apiContactInfo(contactId: Int64) async throws -> ConnectionStats? {
func apiContactInfo(contactId: Int64) async throws -> (ConnectionStats?, Profile?) {
let r = await chatSendCmd(.apiContactInfo(contactId: contactId))
if case let .contactInfo(_, connStats) = r { return connStats }
if case let .contactInfo(_, connStats, customUserProfile) = r { return (connStats, customUserProfile) }
throw r
}
func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> ConnectionStats? {
func apiGroupMemberInfo(_ groupId: Int64, _ groupMemberId: Int64) async throws -> (ConnectionStats?, Profile?) {
let r = await chatSendCmd(.apiGroupMemberInfo(groupId: groupId, groupMemberId: groupMemberId))
if case let .groupMemberInfo(_, _, connStats_) = r { return connStats_ }
if case let .groupMemberInfo(_, _, connStats_, mainProfile) = r { return (connStats_, mainProfile) }
throw r
}
@@ -646,6 +652,7 @@ func initializeChat(start: Bool) throws {
do {
let m = ChatModel.shared
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
try apiSetIncognito(incognito: incognitoGroupDefault.get())
m.currentUser = try apiGetActiveUser()
if m.currentUser == nil {
m.onboardingStage = .step1_SimpleXInfo

View File

@@ -19,6 +19,10 @@ struct ChatInfoToolbar: View {
var body: some View {
let cInfo = chat.chatInfo
return HStack {
if (cInfo.incognito) {
Image(systemName: "theatermasks").frame(maxWidth: 24, maxHeight: 24, alignment: .center).foregroundColor(.indigo)
Spacer().frame(width: 16)
}
ChatInfoImage(
chat: chat,
color: colorScheme == .dark

View File

@@ -47,6 +47,7 @@ struct ChatInfoView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@ObservedObject var chat: Chat
var connectionStats: ConnectionStats?
var customUserProfile: Profile?
@State private var alert: ChatInfoViewAlert? = nil
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@@ -64,6 +65,12 @@ struct ChatInfoView: View {
contactInfoHeader()
.listRowBackground(Color.clear)
if let customUserProfile = customUserProfile {
Section("Incognito") {
infoRow("Your random profile", customUserProfile.chatViewName)
}
}
if let connStats = connectionStats {
Section("Servers") {
networkStatusRow()

View File

@@ -10,14 +10,17 @@ import SwiftUI
import SimpleXChat
struct CIGroupInvitationView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.colorScheme) var colorScheme
var chatItem: ChatItem
var groupInvitation: CIGroupInvitation
var memberRole: GroupMemberRole
var chatIncognito: Bool = false
@State private var frameWidth: CGFloat = 0
var body: some View {
let action = !chatItem.chatDir.sent && groupInvitation.status == .pending
let unsafeToJoinIncognito = interactiveIncognito && !chatIncognito
let v = ZStack(alignment: .bottomTrailing) {
VStack(alignment: .leading) {
groupInfoView(action)
@@ -29,10 +32,13 @@ struct CIGroupInvitationView: View {
Divider().frame(width: frameWidth)
if action {
groupInvitationText().overlay(DetermineWidth())
Text("Tap to join")
.foregroundColor(.accentColor)
groupInvitationText()
.overlay(DetermineWidth())
Text(interactiveIncognito ? "Tap to join incognito" : "Tap to join")
.foregroundColor(interactiveIncognito ? .indigo : .accentColor)
.font(.callout)
.padding(.trailing, 60)
.overlay(DetermineWidth())
} else {
groupInvitationText()
.padding(.trailing, 60)
@@ -52,17 +58,34 @@ struct CIGroupInvitationView: View {
.onPreferenceChange(DetermineWidth.Key.self) { frameWidth = $0 }
if action {
v.onTapGesture { acceptInvitation() }
v.onTapGesture {
if unsafeToJoinIncognito {
AlertManager.shared.showAlert(unsafeToJoinIncognitoAlert(groupInvitation.groupId))
} else {
joinGroup(groupInvitation.groupId)
}
}
} else {
v
}
}
private var interactiveIncognito: Bool {
(groupInvitation.invitedIncognito ?? false) || chatModel.incognito
}
private func groupInfoView(_ action: Bool) -> some View {
HStack(alignment: .top) {
var color: Color
if action {
color = interactiveIncognito ? .indigo : .accentColor
} else {
color = Color(uiColor: .tertiaryLabel)
}
return HStack(alignment: .top) {
ProfileImage(
imageStr: groupInvitation.groupProfile.image,
iconName: "person.2.circle.fill",
color: action ? .accentColor : Color(uiColor: .tertiaryLabel)
color: color
)
.frame(width: 44, height: 44)
.padding(.trailing, 4)
@@ -84,7 +107,7 @@ struct CIGroupInvitationView: View {
private func groupInvitationStr() -> LocalizedStringKey {
if chatItem.chatDir.sent {
return "You sent group invitation"
return (groupInvitation.invitedIncognito ?? false) ? "You sent group invitation incognito" : "You sent group invitation"
} else {
switch groupInvitation.status {
case .pending: return "You are invited to group"
@@ -94,13 +117,6 @@ struct CIGroupInvitationView: View {
}
}
}
private func acceptInvitation() {
Task {
logger.debug("acceptInvitation")
await joinGroup(groupInvitation.groupId)
}
}
}
struct CIGroupInvitationView_Previews: PreviewProvider {

View File

@@ -48,7 +48,7 @@ struct ChatItemView: View {
}
private func groupInvitationItemView(_ groupInvitation: CIGroupInvitation, _ memberRole: GroupMemberRole) -> some View {
CIGroupInvitationView(chatItem: chatItem, groupInvitation: groupInvitation, memberRole: memberRole)
CIGroupInvitationView(chatItem: chatItem, groupInvitation: groupInvitation, memberRole: memberRole, chatIncognito: chatInfo.incognito)
}
private func groupEventItemView() -> some View {

View File

@@ -22,6 +22,7 @@ struct ChatView: View {
@FocusState private var keyboardVisible: Bool
@State private var showDeleteMessage = false
@State private var connectionStats: ConnectionStats?
@State private var customUserProfile: Profile?
@State private var tableView: UITableView?
@State private var loadingItems = false
@State private var firstPage = false
@@ -76,8 +77,11 @@ struct ChatView: View {
if case .direct = cInfo {
Task {
do {
let stats = try await apiContactInfo(contactId: chat.chatInfo.apiId)
await MainActor.run { connectionStats = stats }
let (stats, profile) = try await apiContactInfo(contactId: chat.chatInfo.apiId)
await MainActor.run {
connectionStats = stats
customUserProfile = profile
}
} catch let error {
logger.error("apiContactInfo error: \(responseError(error))")
}
@@ -98,7 +102,7 @@ struct ChatView: View {
.sheet(isPresented: $showChatInfoSheet) {
switch cInfo {
case .direct:
ChatInfoView(chat: chat, connectionStats: connectionStats)
ChatInfoView(chat: chat, connectionStats: connectionStats, customUserProfile: customUserProfile)
case let .group(groupInfo):
GroupChatInfoView(chat: chat, groupInfo: groupInfo)
default:

View File

@@ -18,10 +18,29 @@ struct AddGroupMembersView: View {
var addedMembersCb: ((Set<Int64>) -> Void)? = nil
@State private var selectedContacts = Set<Int64>()
@State private var selectedRole: GroupMemberRole = .admin
@State private var alert: AddGroupMembersAlert?
private enum AddGroupMembersAlert: Identifiable {
case prohibitedToInviteIncognito
case warnUnsafeToInviteIncognito
case error(title: LocalizedStringKey, error: String = "")
var id: String {
switch self {
case .prohibitedToInviteIncognito: return "prohibitedToInviteIncognito"
case .warnUnsafeToInviteIncognito: return "warnUnsafeToInviteIncognito"
case let .error(title, _): return "error \(title)"
}
}
}
var body: some View {
NavigationView {
let membersToAdd = filterMembersToAdd(chatModel.groupMembers)
let nonIncognitoConnectionsSelected = membersToAdd
.filter{ selectedContacts.contains($0.apiId) }
.contains(where: { !$0.contactConnIncognito })
let unsafeToInviteIncognito = chat.chatInfo.incognito && nonIncognitoConnectionsSelected
let v = List {
ChatInfoToolbar(chat: chat, imageSize: 48)
@@ -39,7 +58,7 @@ struct AddGroupMembersView: View {
let count = selectedContacts.count
Section {
rolePicker()
inviteMembersButton()
inviteMembersButton(unsafeToInviteIncognito)
.disabled(count < 1)
} footer: {
if (count >= 1) {
@@ -77,26 +96,33 @@ struct AddGroupMembersView: View {
}
}
.frame(maxHeight: .infinity, alignment: .top)
.alert(item: $alert) { alert in
switch alert {
case .prohibitedToInviteIncognito:
return Alert(
title: Text("Can't invite contact!"),
message: Text("You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile")
)
case .warnUnsafeToInviteIncognito:
return Alert(
title: Text("Your main profile may be shared"),
message: Text("Some selected contacts have your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members."),
primaryButton: .destructive(Text("Invite anyway")) {
inviteMembers()
}, secondaryButton: .cancel()
)
case let .error(title, error):
return Alert(title: Text(title), message: Text("\(error)"))
}
}
}
func inviteMembersButton() -> some View {
private func inviteMembersButton(_ unsafeToInviteIncognito: Bool) -> some View {
Button {
Task {
do {
for contactId in selectedContacts {
let member = try await apiAddMember(groupInfo.groupId, contactId, selectedRole)
await MainActor.run { _ = ChatModel.shared.upsertGroupMember(groupInfo, member) }
}
await MainActor.run { dismiss() }
if let cb = addedMembersCb { cb(selectedContacts) }
} catch {
AlertManager.shared.showAlert(
Alert(
title: Text("Error adding member(s)"),
message: Text(responseError(error))
)
)
}
if unsafeToInviteIncognito {
alert = .warnUnsafeToInviteIncognito
} else {
inviteMembers()
}
} label: {
HStack {
@@ -107,7 +133,22 @@ struct AddGroupMembersView: View {
.frame(maxWidth: .infinity, alignment: .trailing)
}
func rolePicker() -> some View {
private func inviteMembers() {
Task {
do {
for contactId in selectedContacts {
let member = try await apiAddMember(groupInfo.groupId, contactId, selectedRole)
await MainActor.run { _ = ChatModel.shared.upsertGroupMember(groupInfo, member) }
}
await MainActor.run { dismiss() }
if let cb = addedMembersCb { cb(selectedContacts) }
} catch {
alert = .error(title: "Error adding member(s)", error: responseError(error))
}
}
}
private func rolePicker() -> some View {
Picker("New member role", selection: $selectedRole) {
ForEach(GroupMemberRole.allCases) { role in
if role <= groupInfo.membership.memberRole {
@@ -117,13 +158,33 @@ struct AddGroupMembersView: View {
}
}
func contactCheckView(_ contact: Contact) -> some View {
private func contactCheckView(_ contact: Contact) -> some View {
let checked = selectedContacts.contains(contact.apiId)
return Button {
let prohibitedToInviteIncognito = !chat.chatInfo.incognito && contact.contactConnIncognito
let safeToInviteIncognito = chat.chatInfo.incognito && contact.contactConnIncognito
var icon: String
var iconColor: Color
if prohibitedToInviteIncognito {
icon = "theatermasks.circle.fill"
iconColor = Color(uiColor: .tertiaryLabel)
} else {
if checked {
selectedContacts.remove(contact.apiId)
icon = "checkmark.circle.fill"
iconColor = .accentColor
} else {
selectedContacts.insert(contact.apiId)
icon = "circle"
iconColor = Color(uiColor: .tertiaryLabel)
}
}
return Button {
if prohibitedToInviteIncognito {
alert = .prohibitedToInviteIncognito
} else {
if checked {
selectedContacts.remove(contact.apiId)
} else {
selectedContacts.insert(contact.apiId)
}
}
} label: {
HStack{
@@ -131,11 +192,16 @@ struct AddGroupMembersView: View {
.frame(width: 30, height: 30)
.padding(.trailing, 2)
Text(ChatInfo.direct(contact: contact).chatViewName)
.foregroundColor(.primary)
.foregroundColor(prohibitedToInviteIncognito ? .secondary : .primary)
.lineLimit(1)
Spacer()
Image(systemName: checked ? "checkmark.circle.fill": "circle")
.foregroundColor(checked ? .accentColor : Color(uiColor: .tertiaryLabel))
if safeToInviteIncognito {
Image(systemName: "theatermasks")
.foregroundColor(.indigo)
.font(.footnote)
}
Image(systemName: icon)
.foregroundColor(iconColor)
}
}
}

View File

@@ -20,6 +20,7 @@ struct GroupChatInfoView: View {
@State private var selectedMember: GroupMember? = nil
@State private var showGroupProfile: Bool = false
@State private var connectionStats: ConnectionStats?
@State private var mainProfile: Profile?
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
enum GroupChatInfoViewAlert: Identifiable {
@@ -49,8 +50,11 @@ struct GroupChatInfoView: View {
Button {
Task {
do {
let stats = try await apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
await MainActor.run { connectionStats = stats }
let (stats, profile) = try await apiGroupMemberInfo(groupInfo.apiId, member.groupMemberId)
await MainActor.run {
connectionStats = stats
mainProfile = profile
}
} catch let error {
logger.error("apiGroupMemberInfo error: \(responseError(error))")
}
@@ -63,7 +67,7 @@ struct GroupChatInfoView: View {
AddGroupMembersView(chat: chat, groupInfo: groupInfo)
}
.sheet(item: $selectedMember, onDismiss: { connectionStats = nil }) { member in
GroupMemberInfoView(groupInfo: groupInfo, member: member, connectionStats: connectionStats)
GroupMemberInfoView(groupInfo: groupInfo, member: member, connectionStats: connectionStats, mainProfile: mainProfile)
}
.sheet(isPresented: $showGroupProfile) {
GroupProfileView(groupId: groupInfo.apiId, groupProfile: groupInfo.groupProfile)
@@ -150,7 +154,7 @@ struct GroupChatInfoView: View {
VStack(alignment: .leading) {
Text(member.chatViewName)
.lineLimit(1)
.foregroundColor(.primary)
.foregroundColor(member.memberIncognito ? .indigo : .primary)
let s = Text(member.memberStatus.shortText)
(user ? Text ("you: ") + s : s)
.lineLimit(1)

View File

@@ -15,6 +15,7 @@ struct GroupMemberInfoView: View {
var groupInfo: GroupInfo
var member: GroupMember
var connectionStats: ConnectionStats?
var mainProfile: Profile?
@State private var alert: GroupMemberInfoViewAlert?
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@@ -32,6 +33,9 @@ struct GroupMemberInfoView: View {
Section("Member") {
infoRow("Group", groupInfo.displayName)
if let mainProfile = mainProfile {
mainProfileRow(mainProfile)
}
// TODO change role
// localizedInfoRow("Role", member.memberRole.text)
// TODO invited by - need to get contact by contact id
@@ -72,6 +76,20 @@ struct GroupMemberInfoView: View {
}
}
private func mainProfileRow(_ mainProfile: Profile) -> some View {
HStack {
Text("Known main profile")
Spacer()
if (mainProfile.image != nil) {
ProfileImage(imageStr: member.image)
.frame(width: 38, height: 38)
.padding(.trailing, 2)
}
Text(mainProfile.chatViewName)
.foregroundColor(.secondary)
}
}
private func groupMemberInfoHeader() -> some View {
VStack {
ProfileImage(imageStr: member.image, color: Color(uiColor: .tertiarySystemFill))

View File

@@ -75,14 +75,20 @@ struct ChatListNavLink: View {
ChatPreviewView(chat: chat)
.frame(height: 80)
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
joinGroupButton()
joinGroupButton(groupInfo.hostConnCustomUserProfileId)
if groupInfo.canDelete {
deleteGroupChatButton(groupInfo)
}
}
.onTapGesture { showJoinGroupDialog = true }
.confirmationDialog("Group invitation", isPresented: $showJoinGroupDialog, titleVisibility: .visible) {
Button("Join group") { Task { await joinGroup(groupInfo.groupId) } }
Button(interactiveIncognito ? "Join incognito" : "Join group") {
if unsafeToJoinIncognito(groupInfo.hostConnCustomUserProfileId) {
AlertManager.shared.showAlert(unsafeToJoinIncognitoAlert(chat.chatInfo.apiId))
} else {
joinGroup(groupInfo.groupId)
}
}
Button("Delete invitation", role: .destructive) { Task { await deleteChat(chat) } }
}
case .memAccepted:
@@ -113,7 +119,7 @@ struct ChatListNavLink: View {
} label: {
Label("Leave", systemImage: "rectangle.portrait.and.arrow.right")
}
.tint(Color.indigo)
.tint(Color.yellow)
}
}
.swipeActions(edge: .trailing) {
@@ -124,13 +130,25 @@ struct ChatListNavLink: View {
}
}
private func joinGroupButton() -> some View {
private var interactiveIncognito: Bool {
chat.chatInfo.incognito || chatModel.incognito
}
private func joinGroupButton(_ hostConnCustomUserProfileId: Int64?) -> some View {
Button {
Task { await joinGroup(chat.chatInfo.apiId) }
if unsafeToJoinIncognito(hostConnCustomUserProfileId) {
AlertManager.shared.showAlert(unsafeToJoinIncognitoAlert(chat.chatInfo.apiId))
} else {
joinGroup(chat.chatInfo.apiId)
}
} label: {
Label("Join", systemImage: "ipad.and.arrow.forward")
Label("Join", systemImage: interactiveIncognito ? "theatermasks" : "ipad.and.arrow.forward")
}
.tint(Color.accentColor)
.tint(interactiveIncognito ? .indigo : .accentColor)
}
private func unsafeToJoinIncognito(_ hostConnCustomUserProfileId: Int64?) -> Bool {
interactiveIncognito && hostConnCustomUserProfileId == nil
}
private func markReadButton() -> some View {
@@ -162,9 +180,10 @@ struct ChatListNavLink: View {
private func contactRequestNavLink(_ contactRequest: UserContactRequest) -> some View {
ContactRequestView(contactRequest: contactRequest, chat: chat)
.swipeActions(edge: .trailing, allowsFullSwipe: true) {
Button { Task { await acceptContactRequest(contactRequest) } }
label: { Label("Accept", systemImage: "checkmark") }
.tint(Color.accentColor)
Button {
Task { await acceptContactRequest(contactRequest) }
} label: { Label("Accept", systemImage: chatModel.incognito ? "theatermasks" : "checkmark") }
.tint(chatModel.incognito ? .indigo : .accentColor)
Button(role: .destructive) {
AlertManager.shared.showAlert(rejectContactRequestAlert(contactRequest))
} label: {
@@ -174,7 +193,7 @@ struct ChatListNavLink: View {
.frame(height: 80)
.onTapGesture { showContactRequestDialog = true }
.confirmationDialog("Connection request", isPresented: $showContactRequestDialog, titleVisibility: .visible) {
Button("Accept contact") { Task { await acceptContactRequest(contactRequest) } }
Button(chatModel.incognito ? "Accept incognito" : "Accept contact") { Task { await acceptContactRequest(contactRequest) } }
Button("Reject contact (sender NOT notified)", role: .destructive) { Task { await rejectContactRequest(contactRequest) } }
}
}
@@ -325,32 +344,46 @@ struct ChatListNavLink: View {
}
}
func joinGroup(_ groupId: Int64) async {
do {
let r = try await apiJoinGroup(groupId)
switch r {
case let .joined(groupInfo):
await MainActor.run { ChatModel.shared.updateGroup(groupInfo) }
case .invitationRemoved:
AlertManager.shared.showAlertMsg(title: "Invitation expired!", message: "Group invitation is no longer valid, it was removed by sender.")
await deleteGroup()
case .groupNotFound:
AlertManager.shared.showAlertMsg(title: "No group!", message: "This group no longer exists.")
await deleteGroup()
}
} catch let error {
let err = responseError(error)
AlertManager.shared.showAlert(Alert(title: Text("Error joining group"), message: Text(err)))
logger.error("apiJoinGroup error: \(err)")
}
func unsafeToJoinIncognitoAlert(_ groupId: Int64) -> Alert {
Alert(
title: Text("Your main profile may be shared"),
message: Text("The contact who invited you has your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members."),
primaryButton: .destructive(Text("Join anyway")) {
joinGroup(groupId)
},
secondaryButton: .cancel()
)
}
func deleteGroup() async {
func joinGroup(_ groupId: Int64) {
Task {
logger.debug("joinGroup")
do {
// TODO this API should update chat item with the invitation as well
try await apiDeleteChat(type: .group, id: groupId)
await MainActor.run { ChatModel.shared.removeChat("#\(groupId)") }
} catch {
logger.error("apiDeleteChat error: \(responseError(error))")
let r = try await apiJoinGroup(groupId)
switch r {
case let .joined(groupInfo):
await MainActor.run { ChatModel.shared.updateGroup(groupInfo) }
case .invitationRemoved:
AlertManager.shared.showAlertMsg(title: "Invitation expired!", message: "Group invitation is no longer valid, it was removed by sender.")
await deleteGroup()
case .groupNotFound:
AlertManager.shared.showAlertMsg(title: "No group!", message: "This group no longer exists.")
await deleteGroup()
}
} catch let error {
let err = responseError(error)
AlertManager.shared.showAlert(Alert(title: Text("Error joining group"), message: Text(err)))
logger.error("apiJoinGroup error: \(err)")
}
func deleteGroup() async {
do {
// TODO this API should update chat item with the invitation as well
try await apiDeleteChat(type: .group, id: groupId)
await MainActor.run { ChatModel.shared.removeChat("#\(groupId)") }
} catch {
logger.error("apiDeleteChat error: \(responseError(error))")
}
}
}
}

View File

@@ -44,6 +44,17 @@ struct ChatListView: View {
ToolbarItem(placement: .navigationBarLeading) {
SettingsButton()
}
ToolbarItem(placement: .principal) {
if (chatModel.incognito) {
HStack {
if (chatModel.chats.count > 8) {
Text("Your chats").font(.headline)
Spacer().frame(width: 16)
}
Image(systemName: "theatermasks").frame(maxWidth: 24, maxHeight: 24, alignment: .center).foregroundColor(.indigo)
}
}
}
ToolbarItem(placement: .navigationBarTrailing) {
switch chatModel.chatRunning {
case .some(true): NewChatButton()

View File

@@ -10,6 +10,7 @@ import SwiftUI
import SimpleXChat
struct ChatPreviewView: View {
@EnvironmentObject var chatModel: ChatModel
@ObservedObject var chat: Chat
@Environment(\.colorScheme) var colorScheme
var darkGreen = Color(red: 0, green: 0.5, blue: 0)
@@ -23,7 +24,6 @@ struct ChatPreviewView: View {
.frame(width: 63, height: 63)
chatPreviewImageOverlayIcon()
.padding([.bottom, .trailing], 1)
}
.padding(.leading, 4)
@@ -89,7 +89,7 @@ struct ChatPreviewView: View {
case .group(groupInfo: let groupInfo):
switch (groupInfo.membership.memberStatus) {
case .memInvited:
v.foregroundColor(.accentColor)
interactiveIncognito ? v.foregroundColor(.indigo) : v.foregroundColor(.accentColor)
case .memAccepted:
v.foregroundColor(.secondary)
default: v
@@ -98,6 +98,10 @@ struct ChatPreviewView: View {
}
}
private var interactiveIncognito: Bool {
chat.chatInfo.incognito || chatModel.incognito
}
@ViewBuilder private func chatPreviewText(_ cItem: ChatItem?, _ unread: Int) -> some View {
if let cItem = cItem {
ZStack(alignment: .topTrailing) {
@@ -127,7 +131,7 @@ struct ChatPreviewView: View {
}
case let .group(groupInfo):
switch (groupInfo.membership.memberStatus) {
case .memInvited: chatPreviewInfoText("you are invited to group")
case .memInvited: chatPreviewInfoText(groupInfo.membership.memberIncognito ? "you are invited to group incognito" : "you are invited to group")
case .memAccepted: chatPreviewInfoText("connecting…")
default: EmptyView()
}

View File

@@ -10,6 +10,7 @@ import SwiftUI
import SimpleXChat
struct ContactRequestView: View {
@EnvironmentObject var chatModel: ChatModel
var contactRequest: UserContactRequest
@ObservedObject var chat: Chat
@@ -17,12 +18,13 @@ struct ContactRequestView: View {
return HStack(spacing: 8) {
ChatInfoImage(chat: chat)
.frame(width: 63, height: 63)
.padding(.leading, 4)
VStack(alignment: .leading, spacing: 4) {
HStack(alignment: .top) {
Text(contactRequest.chatViewName)
.font(.title3)
.fontWeight(.bold)
.foregroundColor(.blue)
.foregroundColor(chatModel.incognito ? .indigo : .accentColor)
.padding(.leading, 8)
.padding(.top, 4)
.frame(maxHeight: .infinity, alignment: .topLeading)

View File

@@ -10,6 +10,7 @@ import SwiftUI
import CoreImage.CIFilterBuiltins
struct AddContactView: View {
@EnvironmentObject var chatModel: ChatModel
var connReqInvitation: String
var body: some View {
ScrollView {
@@ -17,8 +18,23 @@ struct AddContactView: View {
Text("One-time invitation link")
.font(.title)
.padding(.vertical)
Text("Your contact can scan it from the app")
Text("Your contact can scan it from the app.")
.padding(.bottom, 4)
if (chatModel.incognito) {
HStack {
Image(systemName: "theatermasks").foregroundColor(.indigo).font(.footnote)
Spacer().frame(width: 8)
Text("A random profile will be sent to your contact").font(.footnote)
}
.padding(.bottom)
} else {
HStack {
Image(systemName: "info.circle").foregroundColor(.secondary).font(.footnote)
Spacer().frame(width: 8)
Text("Your chat profile will be sent to your contact").font(.footnote)
}
.padding(.bottom)
}
QRCode(uri: connReqInvitation)
.padding(.bottom)
Text("If you can't meet in person, **show QR code in the video call**, or share the link.")

View File

@@ -44,7 +44,21 @@ struct AddGroupView: View {
.padding(.vertical, 4)
Text("The group is fully decentralized it is visible only to the members.")
.padding(.bottom, 4)
if (m.incognito) {
HStack {
Image(systemName: "theatermasks").foregroundColor(.indigo).font(.footnote)
Spacer().frame(width: 8)
Text("You will use a random profile for this group").font(.footnote)
}
.padding(.bottom)
} else {
HStack {
Image(systemName: "info.circle").foregroundColor(.secondary).font(.footnote)
Spacer().frame(width: 8)
Text("Your chat profile will be sent to group members").font(.footnote)
}
.padding(.bottom)
}
ZStack(alignment: .center) {
ZStack(alignment: .topTrailing) {

View File

@@ -9,6 +9,7 @@
import SwiftUI
struct PasteToConnectView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss: DismissAction
@State private var connectionLink: String = ""
@@ -18,8 +19,22 @@ struct PasteToConnectView: View {
.font(.title)
.padding(.vertical)
Text("Paste the link you received into the box below to connect with your contact.")
Text("Your profile will be sent to the contact that you received this link from")
.padding(.bottom, 4)
if (chatModel.incognito) {
HStack {
Image(systemName: "theatermasks").foregroundColor(.indigo).font(.footnote)
Spacer().frame(width: 8)
Text("A random profile will be sent to the contact that you received this link from").font(.footnote)
}
.padding(.bottom)
} else {
HStack {
Image(systemName: "info.circle").foregroundColor(.secondary).font(.footnote)
Spacer().frame(width: 8)
Text("Your profile will be sent to the contact that you received this link from").font(.footnote)
}
.padding(.bottom)
}
TextEditor(text: $connectionLink)
.onSubmit(connect)
.textInputAutocapitalization(.never)
@@ -55,7 +70,7 @@ struct PasteToConnectView: View {
.frame(height: 48)
.padding(.bottom)
Text("You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button")
Text("You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.")
}
.padding()
.frame(maxHeight: .infinity, alignment: .top)

View File

@@ -10,6 +10,7 @@ import SwiftUI
import CodeScanner
struct ScanToConnectView: View {
@EnvironmentObject var chatModel: ChatModel
@Environment(\.dismiss) var dismiss: DismissAction
var body: some View {
@@ -17,8 +18,21 @@ struct ScanToConnectView: View {
Text("Scan QR code")
.font(.title)
.padding(.vertical)
Text("Your chat profile will be sent to your contact")
if (chatModel.incognito) {
HStack {
Image(systemName: "theatermasks").foregroundColor(.indigo).font(.footnote)
Spacer().frame(width: 8)
Text("A random profile will be sent to your contact").font(.footnote)
}
.padding(.bottom)
} else {
HStack {
Image(systemName: "info.circle").foregroundColor(.secondary).font(.footnote)
Spacer().frame(width: 8)
Text("Your chat profile will be sent to your contact").font(.footnote)
}
.padding(.bottom)
}
ZStack {
CodeScannerView(codeTypes: [.qr], completion: processQRCode)
.aspectRatio(1, contentMode: .fit)

View File

@@ -39,12 +39,12 @@ struct CallSettings: View {
}
}
}
}
private func textListItem(_ n: String, _ text: LocalizedStringKey) -> some View {
ZStack(alignment: .topLeading) {
Text(n)
Text(text).frame(maxWidth: .infinity, alignment: .leading).padding(.leading, 20)
}
func textListItem(_ n: String, _ text: LocalizedStringKey) -> some View {
ZStack(alignment: .topLeading) {
Text(n)
Text(text).frame(maxWidth: .infinity, alignment: .leading).padding(.leading, 20)
}
}

View File

@@ -0,0 +1,57 @@
//
// IncognitoHelp.swift
// SimpleX (iOS)
//
// Created by JRoberts on 22.08.2022.
// Copyright © 2022 SimpleX Chat. All rights reserved.
//
import SwiftUI
struct IncognitoHelp: View {
var body: some View {
VStack(alignment: .leading) {
Text("Incognito mode")
.font(.largeTitle)
.padding(.vertical)
ScrollView {
VStack(alignment: .leading, spacing: 24) {
Text("Incognito mode protects the privacy of your main profile name and image — for each new contact and group a new random profile is created.")
Text("It allows having many anonymous connections without any shared data between them in a single chat profile.")
Text("To find the profile used for an incognito connection, tap the contact or group name on top of the chat.")
}
.padding(.bottom)
VStack(alignment: .leading, spacing: 8) {
Text("Incognito groups")
.font(.title2)
.padding(.top)
Text("When you join a group incognito, your new member profile is created and shared with all members.")
Group {
Text("Your are incognito in a group when:")
.padding(.top)
textListItem("", "the group is created in Incognito mode,")
textListItem("", "you or the member who invited you joined in Incognito mode,")
textListItem("", "you have an incognito connection with the member who invited you.")
}
Group {
Text("Risks and limitations:")
.padding(.top)
textListItem("", "It is not allowed to invite contacts with whom you have an incognito connection to a group where you use your main profile otherwise they might find out your main profile.")
textListItem("", "There is a risk to have your main profile shared, if you have contacts who know your main profile in an incognito group. Before you invite or join group with such contacts a warning will be shown.")
}
}
.padding(.bottom)
}
}
.frame(maxWidth: .infinity)
.padding()
}
}
struct IncognitoHelp_Previews: PreviewProvider {
static var previews: some View {
IncognitoHelp()
}
}

View File

@@ -58,6 +58,7 @@ struct SettingsView: View {
@EnvironmentObject var chatModel: ChatModel
@Binding var showSettings: Bool
@AppStorage(DEFAULT_DEVELOPER_TOOLS) private var developerTools = false
@State private var settingsSheet: SettingsSheet?
var body: some View {
let user: User = chatModel.currentUser!
@@ -74,6 +75,8 @@ struct SettingsView: View {
}
.disabled(chatModel.chatRunning != true)
incognitoRow()
NavigationLink {
UserAddress()
.navigationTitle("Your chat address")
@@ -202,9 +205,49 @@ struct SettingsView: View {
}
.navigationTitle("Your settings")
}
.sheet(item: $settingsSheet) { sheet in
switch sheet {
case .incognitoInfo: IncognitoHelp()
}
}
}
enum NotificationAlert {
@ViewBuilder private func incognitoRow() -> some View {
ZStack(alignment: .leading) {
Image(systemName: chatModel.incognito ? "theatermasks.fill" : "theatermasks")
.frame(maxWidth: 24, maxHeight: 24, alignment: .center)
.foregroundColor(chatModel.incognito ? Color.indigo : .secondary)
Toggle(isOn: $chatModel.incognito) {
HStack {
Text("Incognito")
Spacer().frame(width: 4)
Image(systemName: "info.circle")
.foregroundColor(.accentColor)
.font(.system(size: 14))
}
.onTapGesture {
settingsSheet = .incognitoInfo
}
}
.onChange(of: chatModel.incognito) { incognito in
incognitoGroupDefault.set(incognito)
do {
try apiSetIncognito(incognito: incognito)
} catch {
logger.error("apiSetIncognito: cannot set incognito \(responseError(error))")
}
}
.padding(.leading, indent)
}
}
private enum SettingsSheet: Identifiable {
case incognitoInfo
var id: SettingsSheet { get { self } }
}
private enum NotificationAlert {
case enable
case error(LocalizedStringKey, String)
}

View File

@@ -77,7 +77,7 @@ struct UserProfile: View {
profileNameView("Display name:", user.profile.displayName)
profileNameView("Full name:", user.profile.fullName)
Button("Edit") {
profile = user.profile
profile = fromLocalProfile(user.profile)
editProfile = true
}
}
@@ -131,7 +131,7 @@ struct UserProfile: View {
}
func startEditingImage(_ user: User) {
profile = user.profile
profile = fromLocalProfile(user.profile)
editProfile = true
showChooseSource = true
}
@@ -141,7 +141,9 @@ struct UserProfile: View {
do {
if let newProfile = try await apiUpdateProfile(profile: profile) {
DispatchQueue.main.async {
chatModel.currentUser?.profile = newProfile
if let profileId = chatModel.currentUser?.profile.profileId {
chatModel.currentUser?.profile = toLocalProfile(profileId, newProfile)
}
profile = newProfile
}
}

View File

@@ -5,6 +5,13 @@
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
</header>
<body>
<trans-unit id="&#10;" xml:space="preserve">
<source>
</source>
<target>
</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=" " xml:space="preserve">
<source> </source>
<target> </target>
@@ -165,6 +172,16 @@
<target>A new contact</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="A random profile will be sent to the contact that you received this link from" xml:space="preserve">
<source>A random profile will be sent to the contact that you received this link from</source>
<target>A random profile will be sent to the contact that you received this link from</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="A random profile will be sent to your contact" xml:space="preserve">
<source>A random profile will be sent to your contact</source>
<target>A random profile will be sent to your contact</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="About SimpleX" xml:space="preserve">
<source>About SimpleX</source>
<target>About SimpleX</target>
@@ -175,6 +192,11 @@
<target>About SimpleX Chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Accent color" xml:space="preserve">
<source>Accent color</source>
<target>Accent color</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Accept" xml:space="preserve">
<source>Accept</source>
<target>Accept</target>
@@ -191,6 +213,11 @@
<target>Accept contact request from %@?</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>Accept incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add contact to start a new chat" xml:space="preserve">
<source>Add contact to start a new chat</source>
<target>Add contact to start a new chat</target>
@@ -261,6 +288,11 @@
<target>Call already ended!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>Can't invite contact!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Cancel</target>
@@ -309,7 +341,7 @@
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Chats</target>
<note>back button to return to chats list</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Choose file" xml:space="preserve">
<source>Choose file</source>
@@ -336,6 +368,11 @@
<target>Clear conversation?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Colors" xml:space="preserve">
<source>Colors</source>
<target>Colors</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure SMP servers" xml:space="preserve">
<source>Configure SMP servers</source>
<target>Configure SMP servers</target>
@@ -449,7 +486,7 @@
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Copy</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Create" xml:space="preserve">
<source>Create</source>
@@ -514,7 +551,7 @@
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>Delete</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
@@ -659,7 +696,7 @@
<trans-unit id="Edit" xml:space="preserve">
<source>Edit</source>
<target>Edit</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Edit group profile" xml:space="preserve">
<source>Edit group profile</source>
@@ -701,6 +738,11 @@
<target>Error accessing database file</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Error adding member(s)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error creating group" xml:space="preserve">
<source>Error creating group</source>
<target>Error creating group</target>
@@ -961,6 +1003,26 @@
<target>In person or via a video call the most secure way to connect.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito" xml:space="preserve">
<source>Incognito</source>
<target>Incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Incognito groups</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source>
<target>Incognito mode</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode protects the privacy of your main profile name and image — for each new contact and group a new random profile is created." xml:space="preserve">
<source>Incognito mode protects the privacy of your main profile name and image — for each new contact and group a new random profile is created.</source>
<target>Incognito mode protects the privacy of your main profile name and image — for each new contact and group a new random profile is created.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incoming audio call" xml:space="preserve">
<source>Incoming audio call</source>
<target>Incoming audio call</target>
@@ -996,6 +1058,11 @@
<target>Invitation expired!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invite anyway" xml:space="preserve">
<source>Invite anyway</source>
<target>Invite anyway</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invite members" xml:space="preserve">
<source>Invite members</source>
<target>Invite members</target>
@@ -1006,6 +1073,11 @@
<target>Invite to group</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
<source>It allows having many anonymous connections without any shared data between them in a single chat profile.</source>
<target>It allows having many anonymous connections without any shared data between them in a single chat profile.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It can happen when:&#10;1. The messages expire on the server if they were not received for 30 days,&#10;2. The server you use to receive the messages from this contact was updated and restarted.&#10;3. The connection is compromised.&#10;Please connect to the developers via Settings to receive the updates about the servers.&#10;We will be adding server redundancy to prevent lost messages." xml:space="preserve">
<source>It can happen when:
1. The messages expire on the server if they were not received for 30 days,
@@ -1021,6 +1093,11 @@ Please connect to the developers via Settings to receive the updates about the s
We will be adding server redundancy to prevent lost messages.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It is not allowed to invite contacts with whom you have an incognito connection to a group where you use your main profile otherwise they might find out your main profile." xml:space="preserve">
<source>It is not allowed to invite contacts with whom you have an incognito connection to a group where you use your main profile otherwise they might find out your main profile.</source>
<target>It is not allowed to invite contacts with whom you have an incognito connection to a group where you use your main profile otherwise they might find out your main profile.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</target>
@@ -1036,16 +1113,31 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Join</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Join anyway" xml:space="preserve">
<source>Join anyway</source>
<target>Join anyway</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
<target>Join group</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Join incognito" xml:space="preserve">
<source>Join incognito</source>
<target>Join incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Joining group" xml:space="preserve">
<source>Joining group</source>
<target>Joining group</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Known main profile" xml:space="preserve">
<source>Known main profile</source>
<target>Known main profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Large file!" xml:space="preserve">
<source>Large file!</source>
<target>Large file!</target>
@@ -1091,6 +1183,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Mark read" xml:space="preserve">
<source>Mark read</source>
<target>Mark read</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Markdown in messages" xml:space="preserve">
<source>Markdown in messages</source>
<target>Markdown in messages</target>
@@ -1141,6 +1238,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Most likely this contact has deleted the connection with you.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>Mute</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
<target>Network &amp; servers</target>
@@ -1181,6 +1283,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>New message</target>
<note>notification</note>
</trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<target>No</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>No contacts selected</target>
@@ -1236,6 +1343,21 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>One-time invitation link</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">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<target>Onion hosts will be required for connection. Requires enabling VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<target>Onion hosts will be used when available. Requires enabling VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
<source>Onion hosts will not be used.</source>
<target>Onion hosts will not be used.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<target>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</target>
@@ -1376,6 +1498,16 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Reject contact request</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Relay server is only used if necessary. Another party can observe your IP address.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Relay server protects your IP address, but it can observe the duration of the call." xml:space="preserve">
<source>Relay server protects your IP address, but it can observe the duration of the call.</source>
<target>Relay server protects your IP address, but it can observe the duration of the call.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove" xml:space="preserve">
<source>Remove</source>
<target>Remove</target>
@@ -1394,6 +1526,16 @@ We will be adding server redundancy to prevent lost messages.</target>
<trans-unit id="Reply" xml:space="preserve">
<source>Reply</source>
<target>Reply</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Required" xml:space="preserve">
<source>Required</source>
<target>Required</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset colors" xml:space="preserve">
<source>Reset colors</source>
<target>Reset colors</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset to defaults" xml:space="preserve">
@@ -1416,6 +1558,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Revert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Risks and limitations:" xml:space="preserve">
<source>Risks and limitations:</source>
<target>Risks and limitations:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Run chat" xml:space="preserve">
<source>Run chat</source>
<target>Run chat</target>
@@ -1434,7 +1581,7 @@ We will be adding server redundancy to prevent lost messages.</target>
<trans-unit id="Save" xml:space="preserve">
<source>Save</source>
<target>Save</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Save (and notify contacts)" xml:space="preserve">
<source>Save (and notify contacts)</source>
@@ -1466,6 +1613,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Scan contact's QR code</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Search" xml:space="preserve">
<source>Search</source>
<target>Search</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send link previews" xml:space="preserve">
<source>Send link previews</source>
<target>Send link previews</target>
@@ -1509,7 +1661,7 @@ We will be adding server redundancy to prevent lost messages.</target>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>Share</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Share invitation link" xml:space="preserve">
<source>Share invitation link</source>
@@ -1551,6 +1703,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Skipped messages</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some selected contacts have your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members." xml:space="preserve">
<source>Some selected contacts have your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members.</source>
<target>Some selected contacts have your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>Somebody</target>
@@ -1621,6 +1778,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>Tap to join</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to join incognito" xml:space="preserve">
<source>Tap to join incognito</source>
<target>Tap to join incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Thank you for installing SimpleX Chat!" xml:space="preserve">
<source>Thank you for installing SimpleX Chat!</source>
<target>Thank you for installing SimpleX Chat!</target>
@@ -1641,6 +1803,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>The connection you accepted will be cancelled!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The contact who invited you has your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members." xml:space="preserve">
<source>The contact who invited you has your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members.</source>
<target>The contact who invited you has your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The contact you shared this link with will NOT be able to connect!" xml:space="preserve">
<source>The contact you shared this link with will NOT be able to connect!</source>
<target>The contact you shared this link with will NOT be able to connect!</target>
@@ -1681,6 +1848,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>The sender will NOT be notified</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="There is a risk to have your main profile shared, if you have contacts who know your main profile in an incognito group. Before you invite or join group with such contacts a warning will be shown." xml:space="preserve">
<source>There is a risk to have your main profile shared, if you have contacts who know your main profile in an incognito group. Before you invite or join group with such contacts a warning will be shown.</source>
<target>There is a risk to have your main profile shared, if you have contacts who know your main profile in an incognito group. Before you invite or join group with such contacts a warning will be shown.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." xml:space="preserve">
<source>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</source>
<target>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</target>
@@ -1701,6 +1873,11 @@ We will be adding server redundancy to prevent lost messages.</target>
<target>To ask any questions and to receive updates:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To find the profile used for an incognito connection, tap the contact or group name on top of the chat." xml:space="preserve">
<source>To find the profile used for an incognito connection, tap the contact or group name on top of the chat.</source>
<target>To find the profile used for an incognito connection, tap the contact or group name on top of the chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source>
<target>To make a new connection</target>
@@ -1780,6 +1957,16 @@ To connect, please ask your contact to create another connection link and check
<target>Unlock</target>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>Unmute</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>Update .onion hosts setting?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update network settings?" xml:space="preserve">
<source>Update network settings?</source>
<target>Update network settings?</target>
@@ -1790,6 +1977,16 @@ To connect, please ask your contact to create another connection link and check
<target>Updating settings will re-connect the client to all servers.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>Updating this setting will re-connect the client to all servers.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use .onion hosts" xml:space="preserve">
<source>Use .onion hosts</source>
<target>Use .onion hosts</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use SimpleX Chat servers?" xml:space="preserve">
<source>Use SimpleX Chat servers?</source>
<target>Use SimpleX Chat servers?</target>
@@ -1800,11 +1997,21 @@ To connect, please ask your contact to create another connection link and check
<target>Use chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>Using .onion hosts requires compatible VPN provider.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>Using SimpleX Chat servers.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve">
<source>Video call</source>
<target>Video call</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source>
<target>Waiting for file</target>
@@ -1820,6 +2027,16 @@ To connect, please ask your contact to create another connection link and check
<target>Welcome %@!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When available" xml:space="preserve">
<source>When available</source>
<target>When available</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When you join a group incognito, your new member profile is created and shared with all members." xml:space="preserve">
<source>When you join a group incognito, your new member profile is created and shared with all members.</source>
<target>When you join a group incognito, your new member profile is created and shared with all members.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>You</target>
@@ -1845,9 +2062,9 @@ To connect, please ask your contact to create another connection link and check
<target>You are invited to group</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button" xml:space="preserve">
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button</source>
<target>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button</target>
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
<target>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can now send messages to %@" xml:space="preserve">
@@ -1915,6 +2132,11 @@ To connect, please ask your contact to create another connection link and check
<target>You sent group invitation</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You sent group invitation incognito" xml:space="preserve">
<source>You sent group invitation incognito</source>
<target>You sent group invitation incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You will be connected when your connection request is accepted, please wait or check later!" xml:space="preserve">
<source>You will be connected when your connection request is accepted, please wait or check later!</source>
<target>You will be connected when your connection request is accepted, please wait or check later!</target>
@@ -1935,6 +2157,16 @@ To connect, please ask your contact to create another connection link and check
<target>You will stop receiving messages from this group. Chat history will be preserved.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You will use a random profile for this group" xml:space="preserve">
<source>You will use a random profile for this group</source>
<target>You will use a random profile for this group</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile" xml:space="preserve">
<source>You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile</source>
<target>You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your SMP servers" xml:space="preserve">
<source>Your SMP servers</source>
<target>Your SMP servers</target>
@@ -1945,6 +2177,11 @@ To connect, please ask your contact to create another connection link and check
<target>Your SimpleX contact address</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your are incognito in a group when:" xml:space="preserve">
<source>Your are incognito in a group when:</source>
<target>Your are incognito in a group when:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your calls" xml:space="preserve">
<source>Your calls</source>
<target>Your calls</target>
@@ -1965,6 +2202,11 @@ To connect, please ask your contact to create another connection link and check
<target>Your chat profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your chat profile will be sent to group members" xml:space="preserve">
<source>Your chat profile will be sent to group members</source>
<target>Your chat profile will be sent to group members</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your chat profile will be sent to your contact" xml:space="preserve">
<source>Your chat profile will be sent to your contact</source>
<target>Your chat profile will be sent to your contact</target>
@@ -1975,9 +2217,9 @@ To connect, please ask your contact to create another connection link and check
<target>Your chats</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact can scan it from the app" xml:space="preserve">
<source>Your contact can scan it from the app</source>
<target>Your contact can scan it from the app</target>
<trans-unit id="Your contact can scan it from the app." xml:space="preserve">
<source>Your contact can scan it from the app.</source>
<target>Your contact can scan it from the app.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
@@ -1997,6 +2239,11 @@ You can cancel this connection and remove the contact (and try later with a new
<target>Your current chat database will be DELETED and REPLACED with the imported one.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your main profile may be shared" xml:space="preserve">
<source>Your main profile may be shared</source>
<target>Your main profile may be shared</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your privacy" xml:space="preserve">
<source>Your privacy</source>
<target>Your privacy</target>
@@ -2019,6 +2266,11 @@ SimpleX servers cannot see your profile.</target>
<target>Your profile, contacts and delivered messages are stored on your device.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your random profile" xml:space="preserve">
<source>Your random profile</source>
<target>Your random profile</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your settings" xml:space="preserve">
<source>Your settings</source>
<target>Your settings</target>
@@ -2219,6 +2471,21 @@ SimpleX servers cannot see your profile.</target>
<target>group profile updated</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="incognito invitation to group %@" xml:space="preserve">
<source>incognito invitation to group %@</source>
<target>incognito invitation to group %@</target>
<note>group name</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>
<note>chat list item description</note>
</trans-unit>
<trans-unit id="incognito via one-time link" xml:space="preserve">
<source>incognito via one-time link</source>
<target>incognito via one-time link</target>
<note>chat list item description</note>
</trans-unit>
<trans-unit id="indirect (%d)" xml:space="preserve">
<source>indirect (%d)</source>
<target>indirect (%d)</target>
@@ -2249,6 +2516,11 @@ SimpleX servers cannot see your profile.</target>
<target>italic</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="known to you as %@ connected incognito" xml:space="preserve">
<source>known to you as %@ connected incognito</source>
<target>known to you as %@ connected incognito</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="left" xml:space="preserve">
<source>left</source>
<target>left</target>
@@ -2349,6 +2621,11 @@ SimpleX servers cannot see your profile.</target>
<target>strike</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="the group is created in Incognito mode," xml:space="preserve">
<source>the group is created in Incognito mode,</source>
<target>the group is created in Incognito mode,</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="this contact" xml:space="preserve">
<source>this contact</source>
<target>this contact</target>
@@ -2409,11 +2686,26 @@ SimpleX servers cannot see your profile.</target>
<target>you are invited to group</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group incognito" xml:space="preserve">
<source>you are invited to group incognito</source>
<target>you are invited to group incognito</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you have an incognito connection with the member who invited you." xml:space="preserve">
<source>you have an incognito connection with the member who invited you.</source>
<target>you have an incognito connection with the member who invited you.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you left" xml:space="preserve">
<source>you left</source>
<target>you left</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you or the member who invited you joined in Incognito mode," xml:space="preserve">
<source>you or the member who invited you joined in Incognito mode,</source>
<target>you or the member who invited you joined in Incognito mode,</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you removed %@" xml:space="preserve">
<source>you removed %@</source>
<target>you removed %@</target>
@@ -2424,6 +2716,11 @@ SimpleX servers cannot see your profile.</target>
<target>you shared one-time link</target>
<note>chat list item description</note>
</trans-unit>
<trans-unit id="you shared one-time link incognito" xml:space="preserve">
<source>you shared one-time link incognito</source>
<target>you shared one-time link incognito</target>
<note>chat list item description</note>
</trans-unit>
<trans-unit id="you: " xml:space="preserve">
<source>you: </source>
<target>you: </target>

View File

@@ -5,6 +5,13 @@
<tool tool-id="com.apple.dt.xcode" tool-name="Xcode" tool-version="13.3" build-num="13E113"/>
</header>
<body>
<trans-unit id="&#10;" xml:space="preserve">
<source>
</source>
<target>
</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id=" " xml:space="preserve">
<source> </source>
<target> </target>
@@ -165,6 +172,16 @@
<target>Новый контакт</target>
<note>notification title</note>
</trans-unit>
<trans-unit id="A random profile will be sent to the contact that you received this link from" xml:space="preserve">
<source>A random profile will be sent to the contact that you received this link from</source>
<target>Контакту, от которого вы получили эту ссылку, будет отправлен случайный профиль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="A random profile will be sent to your contact" xml:space="preserve">
<source>A random profile will be sent to your contact</source>
<target>Вашему контакту будет отправлен случайный профиль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="About SimpleX" xml:space="preserve">
<source>About SimpleX</source>
<target>О SimpleX</target>
@@ -175,6 +192,11 @@
<target>Информация о SimpleX Chat</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Accent color" xml:space="preserve">
<source>Accent color</source>
<target>Цвет акцента</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Accept" xml:space="preserve">
<source>Accept</source>
<target>Принять</target>
@@ -191,6 +213,11 @@
<target>Принять запрос на соединение от %@?</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="Accept incognito" xml:space="preserve">
<source>Accept incognito</source>
<target>Принять инкогнито</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Add contact to start a new chat" xml:space="preserve">
<source>Add contact to start a new chat</source>
<target>Добавьте контакт, чтобы начать разговор</target>
@@ -261,6 +288,11 @@
<target>Звонок уже завершен!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Can't invite contact!" xml:space="preserve">
<source>Can't invite contact!</source>
<target>Нельзя пригласить контакт!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Cancel" xml:space="preserve">
<source>Cancel</source>
<target>Отменить</target>
@@ -309,7 +341,7 @@
<trans-unit id="Chats" xml:space="preserve">
<source>Chats</source>
<target>Чаты</target>
<note>back button to return to chats list</note>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Choose file" xml:space="preserve">
<source>Choose file</source>
@@ -336,6 +368,11 @@
<target>Очистить разговор?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Colors" xml:space="preserve">
<source>Colors</source>
<target>Цвета</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Configure SMP servers" xml:space="preserve">
<source>Configure SMP servers</source>
<target>Настройка SMP серверов</target>
@@ -449,7 +486,7 @@
<trans-unit id="Copy" xml:space="preserve">
<source>Copy</source>
<target>Скопировать</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Create" xml:space="preserve">
<source>Create</source>
@@ -514,7 +551,7 @@
<trans-unit id="Delete" xml:space="preserve">
<source>Delete</source>
<target>Удалить</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Delete Contact" xml:space="preserve">
<source>Delete Contact</source>
@@ -659,7 +696,7 @@
<trans-unit id="Edit" xml:space="preserve">
<source>Edit</source>
<target>Редактировать</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Edit group profile" xml:space="preserve">
<source>Edit group profile</source>
@@ -701,6 +738,11 @@
<target>Ошибка при доступе к данным чата</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
<source>Error adding member(s)</source>
<target>Ошибка при добавлении членов группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error creating group" xml:space="preserve">
<source>Error creating group</source>
<target>Ошибка при создании группы</target>
@@ -961,6 +1003,26 @@
<target>При встрече или в видеозвонке самый безопасный способ установить соединение</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito" xml:space="preserve">
<source>Incognito</source>
<target>Инкогнито</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito groups" xml:space="preserve">
<source>Incognito groups</source>
<target>Инкогнито группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode" xml:space="preserve">
<source>Incognito mode</source>
<target>Режим Инкогнито</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incognito mode protects the privacy of your main profile name and image — for each new contact and group a new random profile is created." xml:space="preserve">
<source>Incognito mode protects the privacy of your main profile name and image — for each new contact and group a new random profile is created.</source>
<target>Режим Инкогнито защищает конфиденциальность имени и изображения вашего основного профиля — для каждого нового контакта и группы создается новый случайный профиль.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Incoming audio call" xml:space="preserve">
<source>Incoming audio call</source>
<target>Входящий аудиозвонок</target>
@@ -996,6 +1058,11 @@
<target>Приглашение истекло!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invite anyway" xml:space="preserve">
<source>Invite anyway</source>
<target>Пригласить</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invite members" xml:space="preserve">
<source>Invite members</source>
<target>Пригласить членов группы</target>
@@ -1006,6 +1073,11 @@
<target>Пригласить в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It allows having many anonymous connections without any shared data between them in a single chat profile." xml:space="preserve">
<source>It allows having many anonymous connections without any shared data between them in a single chat profile.</source>
<target>Это позволяет иметь много анонимных соединений без общих данных между ними в одном профиле пользователя.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It can happen when:&#10;1. The messages expire on the server if they were not received for 30 days,&#10;2. The server you use to receive the messages from this contact was updated and restarted.&#10;3. The connection is compromised.&#10;Please connect to the developers via Settings to receive the updates about the servers.&#10;We will be adding server redundancy to prevent lost messages." xml:space="preserve">
<source>It can happen when:
1. The messages expire on the server if they were not received for 30 days,
@@ -1021,6 +1093,11 @@ We will be adding server redundancy to prevent lost messages.</source>
Мы планируем добавить избыточную доставку сообщений, чтобы не терять сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It is not allowed to invite contacts with whom you have an incognito connection to a group where you use your main profile otherwise they might find out your main profile." xml:space="preserve">
<source>It is not allowed to invite contacts with whom you have an incognito connection to a group where you use your main profile otherwise they might find out your main profile.</source>
<target>Нельзя приглашать контакты, с которыми у вас установлено инкогнито соединение, в группу, где вы используете свой основной профиль — иначе они могут узнать ваш основной профиль.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="It seems like you are already connected via this link. If it is not the case, there was an error (%@)." xml:space="preserve">
<source>It seems like you are already connected via this link. If it is not the case, there was an error (%@).</source>
<target>Возможно, вы уже соединились через эту ссылку. Если это не так, то это ошибка (%@).</target>
@@ -1036,16 +1113,31 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Вступить</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Join anyway" xml:space="preserve">
<source>Join anyway</source>
<target>Вступить</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Join group" xml:space="preserve">
<source>Join group</source>
<target>Вступить в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Join incognito" xml:space="preserve">
<source>Join incognito</source>
<target>Вступить инкогнито</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Joining group" xml:space="preserve">
<source>Joining group</source>
<target>Вступление в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Known main profile" xml:space="preserve">
<source>Known main profile</source>
<target>Известный основной профиль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Large file!" xml:space="preserve">
<source>Large file!</source>
<target>Большой файл!</target>
@@ -1091,6 +1183,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Много пользователей спросили: *как SimpleX доставляет сообщения без идентификаторов пользователей?*</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Mark read" xml:space="preserve">
<source>Mark read</source>
<target>Прочитано</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Markdown in messages" xml:space="preserve">
<source>Markdown in messages</source>
<target>Форматирование сообщений</target>
@@ -1141,6 +1238,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Скорее всего, этот контакт удалил соединение с вами.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Mute" xml:space="preserve">
<source>Mute</source>
<target>Без звука</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Network &amp; servers" xml:space="preserve">
<source>Network &amp; servers</source>
<target>Сеть &amp; серверы</target>
@@ -1181,6 +1283,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Новое сообщение</target>
<note>notification</note>
</trans-unit>
<trans-unit id="No" xml:space="preserve">
<source>No</source>
<target>Нет</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
<source>No contacts selected</source>
<target>Контакты не выбраны</target>
@@ -1236,6 +1343,21 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Одноразовая ссылка</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">
<source>Onion hosts will be required for connection. Requires enabling VPN.</source>
<target>Подключаться только к onion хостам. Требуется включенный VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will be used when available. Requires enabling VPN." xml:space="preserve">
<source>Onion hosts will be used when available. Requires enabling VPN.</source>
<target>Onion хосты используются, если возможно. Требуется включенный VPN.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Onion hosts will not be used." xml:space="preserve">
<source>Onion hosts will not be used.</source>
<target>Onion хосты не используются.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." xml:space="preserve">
<source>Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**.</source>
<target>Только пользовательские устройства хранят контакты, группы и сообщения, которые отправляются **с двухуровневым end-to-end шифрованием**</target>
@@ -1376,6 +1498,16 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Отклонить запрос</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
<source>Relay server is only used if necessary. Another party can observe your IP address.</source>
<target>Relay сервер используется только при необходимости. Другая сторона может видеть ваш IP адрес.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Relay server protects your IP address, but it can observe the duration of the call." xml:space="preserve">
<source>Relay server protects your IP address, but it can observe the duration of the call.</source>
<target>Relay сервер защищает ваш IP адрес, но может отслеживать продолжительность звонка.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove" xml:space="preserve">
<source>Remove</source>
<target>Удалить</target>
@@ -1394,6 +1526,16 @@ We will be adding server redundancy to prevent lost messages.</source>
<trans-unit id="Reply" xml:space="preserve">
<source>Reply</source>
<target>Ответить</target>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Required" xml:space="preserve">
<source>Required</source>
<target>Обязательно</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset colors" xml:space="preserve">
<source>Reset colors</source>
<target>Сбросить цвета</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Reset to defaults" xml:space="preserve">
@@ -1416,6 +1558,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Отменить изменения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Risks and limitations:" xml:space="preserve">
<source>Risks and limitations:</source>
<target>Риски и ограничения:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Run chat" xml:space="preserve">
<source>Run chat</source>
<target>Запустить chat</target>
@@ -1434,7 +1581,7 @@ We will be adding server redundancy to prevent lost messages.</source>
<trans-unit id="Save" xml:space="preserve">
<source>Save</source>
<target>Сохранить</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Save (and notify contacts)" xml:space="preserve">
<source>Save (and notify contacts)</source>
@@ -1466,6 +1613,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Сосканировать QR код контакта</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Search" xml:space="preserve">
<source>Search</source>
<target>Поиск</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Send link previews" xml:space="preserve">
<source>Send link previews</source>
<target>Отправлять картинки ссылок</target>
@@ -1509,7 +1661,7 @@ We will be adding server redundancy to prevent lost messages.</source>
<trans-unit id="Share" xml:space="preserve">
<source>Share</source>
<target>Поделиться</target>
<note>No comment provided by engineer.</note>
<note>chat item action</note>
</trans-unit>
<trans-unit id="Share invitation link" xml:space="preserve">
<source>Share invitation link</source>
@@ -1551,6 +1703,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Пропущенные сообщения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Some selected contacts have your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members." xml:space="preserve">
<source>Some selected contacts have your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members.</source>
<target>У некоторых выбранных контактов есть ваш основной профиль. Если они используют приложение SimpleX версии раньше чем 3.2 или другой клиент, они могут поделиться с другими участниками вашим основным профилем вместо случайного инкогнито профиля.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Somebody" xml:space="preserve">
<source>Somebody</source>
<target>Контакт</target>
@@ -1621,6 +1778,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Нажмите, чтобы вступить</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Tap to join incognito" xml:space="preserve">
<source>Tap to join incognito</source>
<target>Нажмите, чтобы вступить инкогнито</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Thank you for installing SimpleX Chat!" xml:space="preserve">
<source>Thank you for installing SimpleX Chat!</source>
<target>Спасибо, что установили SimpleX Chat!</target>
@@ -1641,6 +1803,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Подтвержденное соединение будет отменено!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The contact who invited you has your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members." xml:space="preserve">
<source>The contact who invited you has your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members.</source>
<target>У контакта есть ваш основной профиль. Если он использует приложение SimpleX версии раньше чем 3.2 или другой клиент, он может поделиться с другими членами группы вашим основным профилем вместо случайного инкогнито профиля.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The contact you shared this link with will NOT be able to connect!" xml:space="preserve">
<source>The contact you shared this link with will NOT be able to connect!</source>
<target>Контакт, которому вы отправили эту ссылку, не сможет соединиться!</target>
@@ -1681,6 +1848,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Отправитель не будет уведомлён</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="There is a risk to have your main profile shared, if you have contacts who know your main profile in an incognito group. Before you invite or join group with such contacts a warning will be shown." xml:space="preserve">
<source>There is a risk to have your main profile shared, if you have contacts who know your main profile in an incognito group. Before you invite or join group with such contacts a warning will be shown.</source>
<target>Если в инкогнито группе есть члены, которые знают ваш основной профиль, существует риск его раскрытия. Перед тем, как вы пригласите ваши контакты или вступите в такую группу, будет показано предупреждение.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." xml:space="preserve">
<source>This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost.</source>
<target>Это действие нельзя отменить — ваш профиль, контакты, сообщения и файлы будут безвозвратно утеряны.</target>
@@ -1701,6 +1873,11 @@ We will be adding server redundancy to prevent lost messages.</source>
<target>Чтобы задать вопросы и получать уведомления о новых версиях,</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To find the profile used for an incognito connection, tap the contact or group name on top of the chat." xml:space="preserve">
<source>To find the profile used for an incognito connection, tap the contact or group name on top of the chat.</source>
<target>Чтобы найти инкогнито профиль, используемый в разговоре, нажмите на имя контакта или группы в верхней части чата.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="To make a new connection" xml:space="preserve">
<source>To make a new connection</source>
<target>Чтобы соединиться</target>
@@ -1780,6 +1957,16 @@ To connect, please ask your contact to create another connection link and check
<target>Разблокировать</target>
<note>authentication reason</note>
</trans-unit>
<trans-unit id="Unmute" xml:space="preserve">
<source>Unmute</source>
<target>Уведомлять</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update .onion hosts setting?" xml:space="preserve">
<source>Update .onion hosts setting?</source>
<target>Обновить настройки .onion хостов?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update network settings?" xml:space="preserve">
<source>Update network settings?</source>
<target>Обновить настройки сети?</target>
@@ -1787,7 +1974,17 @@ To connect, please ask your contact to create another connection link and check
</trans-unit>
<trans-unit id="Updating settings will re-connect the client to all servers." xml:space="preserve">
<source>Updating settings will re-connect the client to all servers.</source>
<target>Обновление настроек приведет к переподключению клиента ко всем серверам.</target>
<target>Обновление настроек приведет к сбросу и установке нового соединения со всеми серверами.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Updating this setting will re-connect the client to all servers." xml:space="preserve">
<source>Updating this setting will re-connect the client to all servers.</source>
<target>Обновление этих настроек приведет к сбросу и установке нового соединения со всеми серверами.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use .onion hosts" xml:space="preserve">
<source>Use .onion hosts</source>
<target>Использовать .onion хосты</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use SimpleX Chat servers?" xml:space="preserve">
@@ -1800,11 +1997,21 @@ To connect, please ask your contact to create another connection link and check
<target>Использовать чат</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using .onion hosts requires compatible VPN provider." xml:space="preserve">
<source>Using .onion hosts requires compatible VPN provider.</source>
<target>Для использования .onion хостов требуется совместимый VPN провайдер.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Using SimpleX Chat servers." xml:space="preserve">
<source>Using SimpleX Chat servers.</source>
<target>Используются серверы, предоставленные SimpleX Chat.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Video call" xml:space="preserve">
<source>Video call</source>
<target>Видеозвонок</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Waiting for file" xml:space="preserve">
<source>Waiting for file</source>
<target>Ожидается прием файла</target>
@@ -1820,6 +2027,16 @@ To connect, please ask your contact to create another connection link and check
<target>Здравствуйте %@!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When available" xml:space="preserve">
<source>When available</source>
<target>Когда возможно</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="When you join a group incognito, your new member profile is created and shared with all members." xml:space="preserve">
<source>When you join a group incognito, your new member profile is created and shared with all members.</source>
<target>Когда вы вступаете в группу инкогнито, создается новый случайный профиль, который отправляется другим членам группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You" xml:space="preserve">
<source>You</source>
<target>Вы</target>
@@ -1845,8 +2062,8 @@ To connect, please ask your contact to create another connection link and check
<target>Вы приглашены в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button" xml:space="preserve">
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button</source>
<trans-unit id="You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." xml:space="preserve">
<source>You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button.</source>
<target>Вы также можете соединиться, открыв ссылку. Если ссылка откроется в браузере, нажмите кнопку **Open in mobile app**.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
@@ -1915,6 +2132,11 @@ To connect, please ask your contact to create another connection link and check
<target>Вы отправили приглашение в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You sent group invitation incognito" xml:space="preserve">
<source>You sent group invitation incognito</source>
<target>Вы отправили приглашение в группу инкогнито</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You will be connected when your connection request is accepted, please wait or check later!" xml:space="preserve">
<source>You will be connected when your connection request is accepted, please wait or check later!</source>
<target>Соединение будет установлено, когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!</target>
@@ -1935,6 +2157,16 @@ To connect, please ask your contact to create another connection link and check
<target>Вы перестанете получать сообщения от этой группы. История чата будет сохранена.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You will use a random profile for this group" xml:space="preserve">
<source>You will use a random profile for this group</source>
<target>Вы будете использовать случайный профиль для этой группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile" xml:space="preserve">
<source>You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile</source>
<target>Вы пытаетесь пригласить инкогнито контакт в группу, где вы используете свой основной профиль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your SMP servers" xml:space="preserve">
<source>Your SMP servers</source>
<target>Ваши SMP серверы</target>
@@ -1945,6 +2177,11 @@ To connect, please ask your contact to create another connection link and check
<target>Ваш SimpleX адрес</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your are incognito in a group when:" xml:space="preserve">
<source>Your are incognito in a group when:</source>
<target>Вы инкогнито в группе, когда:</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your calls" xml:space="preserve">
<source>Your calls</source>
<target>Ваши звонки</target>
@@ -1965,6 +2202,11 @@ To connect, please ask your contact to create another connection link and check
<target>Ваш профиль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your chat profile will be sent to group members" xml:space="preserve">
<source>Your chat profile will be sent to group members</source>
<target>Ваш профиль чата будет отправлен участникам группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your chat profile will be sent to your contact" xml:space="preserve">
<source>Your chat profile will be sent to your contact</source>
<target>Ваш профиль будет отправлен вашему контакту</target>
@@ -1975,9 +2217,9 @@ To connect, please ask your contact to create another connection link and check
<target>Ваши чаты</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact can scan it from the app" xml:space="preserve">
<source>Your contact can scan it from the app</source>
<target>Ваш контакт может сосканировать QR в приложении</target>
<trans-unit id="Your contact can scan it from the app." xml:space="preserve">
<source>Your contact can scan it from the app.</source>
<target>Ваш контакт может сосканировать QR код в приложении</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your contact needs to be online for the connection to complete.&#10;You can cancel this connection and remove the contact (and try later with a new link)." xml:space="preserve">
@@ -1997,6 +2239,11 @@ You can cancel this connection and remove the contact (and try later with a new
<target>Текущие данные вашего чата будет УДАЛЕНЫ и ЗАМЕНЕНЫ импортированными.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your main profile may be shared" xml:space="preserve">
<source>Your main profile may be shared</source>
<target>Вашим основным профилем могут поделиться</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your privacy" xml:space="preserve">
<source>Your privacy</source>
<target>Конфиденциальность</target>
@@ -2019,6 +2266,11 @@ SimpleX серверы не могут получить доступ к ваше
<target>Ваш профиль, контакты и доставленные сообщения хранятся на вашем устройстве.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your random profile" xml:space="preserve">
<source>Your random profile</source>
<target>Ваш случайный профиль</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Your settings" xml:space="preserve">
<source>Your settings</source>
<target>Настройки</target>
@@ -2219,6 +2471,21 @@ SimpleX серверы не могут получить доступ к ваше
<target>профиль группы обновлен</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="incognito invitation to group %@" xml:space="preserve">
<source>incognito invitation to group %@</source>
<target>инкогнито приглашение в группу %@</target>
<note>group name</note>
</trans-unit>
<trans-unit id="incognito via contact address link" xml:space="preserve">
<source>incognito via contact address link</source>
<target>инкогнито через ссылку-контакт</target>
<note>chat list item description</note>
</trans-unit>
<trans-unit id="incognito via one-time link" xml:space="preserve">
<source>incognito via one-time link</source>
<target>инкогнито через одноразовую ссылку</target>
<note>chat list item description</note>
</trans-unit>
<trans-unit id="indirect (%d)" xml:space="preserve">
<source>indirect (%d)</source>
<target>непрямое (%d)</target>
@@ -2249,6 +2516,11 @@ SimpleX серверы не могут получить доступ к ваше
<target>курсив</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="known to you as %@ connected incognito" xml:space="preserve">
<source>known to you as %@ connected incognito</source>
<target>известный(ая) вам как %@ соединен(а) инкогнито</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="left" xml:space="preserve">
<source>left</source>
<target>покинул(а) группу</target>
@@ -2349,6 +2621,11 @@ SimpleX серверы не могут получить доступ к ваше
<target>зачеркнуть</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="the group is created in Incognito mode," xml:space="preserve">
<source>the group is created in Incognito mode,</source>
<target>группа создана в режиме Инкогнито,</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="this contact" xml:space="preserve">
<source>this contact</source>
<target>этот контакт</target>
@@ -2409,11 +2686,26 @@ SimpleX серверы не могут получить доступ к ваше
<target>вы приглашены в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you are invited to group incognito" xml:space="preserve">
<source>you are invited to group incognito</source>
<target>вы приглашены в группу инкогнито</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you have an incognito connection with the member who invited you." xml:space="preserve">
<source>you have an incognito connection with the member who invited you.</source>
<target>вы соединены инкогнито с членом группы, который вас пригласил.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you left" xml:space="preserve">
<source>you left</source>
<target>вы покинули группу</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you or the member who invited you joined in Incognito mode," xml:space="preserve">
<source>you or the member who invited you joined in Incognito mode,</source>
<target>вы или пригласивший вас член группы вступили в группу в режиме Инкогнито,</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="you removed %@" xml:space="preserve">
<source>you removed %@</source>
<target>вы удалили %@</target>
@@ -2424,6 +2716,11 @@ SimpleX серверы не могут получить доступ к ваше
<target>вы создали ссылку</target>
<note>chat list item description</note>
</trans-unit>
<trans-unit id="you shared one-time link incognito" xml:space="preserve">
<source>you shared one-time link incognito</source>
<target>вы создали ссылку инкогнито</target>
<note>chat list item description</note>
</trans-unit>
<trans-unit id="you: " xml:space="preserve">
<source>you: </source>
<target>вы: </target>

View File

@@ -160,6 +160,7 @@ func startChat() -> User? {
let justStarted = try apiStartChat()
if justStarted {
try apiSetFilesFolder(filesFolder: getAppFilesDirectory().path)
try apiSetIncognito(incognito: incognitoGroupDefault.get())
chatLastStartGroupDefault.set(Date.now)
Task { await receiveMessages() }
}
@@ -248,6 +249,12 @@ func apiSetFilesFolder(filesFolder: String) throws {
throw r
}
func apiSetIncognito(incognito: Bool) throws {
let r = sendSimpleXCmd(.setIncognito(incognito: incognito))
if case .cmdOk = r { return }
throw r
}
func apiGetNtfMessage(nonce: String, encNtfInfo: String) -> NtfMessages? {
let r = sendSimpleXCmd(.apiGetNtfMessage(nonce: nonce, encNtfInfo: encNtfInfo))
if case let .ntfMessages(connEntity, msgTs, ntfMessages) = r {

View File

@@ -13,11 +13,6 @@
3CDBCF4227FAE51000354CDD /* ComposeLinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */; };
3CDBCF4827FF621E00354CDD /* CILinkView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 3CDBCF4727FF621E00354CDD /* CILinkView.swift */; };
5C00164428A26FBC0094D739 /* ContextMenu.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C00164328A26FBC0094D739 /* ContextMenu.swift */; };
5C00165628B02AF40094D739 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00165128B02AF30094D739 /* libffi.a */; };
5C00165728B02AF40094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00165228B02AF30094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J-ghc8.10.7.a */; };
5C00165828B02AF40094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00165328B02AF30094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J.a */; };
5C00165928B02AF40094D739 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00165428B02AF30094D739 /* libgmp.a */; };
5C00165A28B02AF40094D739 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 5C00165528B02AF30094D739 /* libgmpxx.a */; };
5C029EA82837DBB3004A9677 /* CICallItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA72837DBB3004A9677 /* CICallItemView.swift */; };
5C029EAA283942EA004A9677 /* CallController.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C029EA9283942EA004A9677 /* CallController.swift */; };
5C05DF532840AA1D00C683F9 /* CallSettings.swift in Sources */ = {isa = PBXBuildFile; fileRef = 5C05DF522840AA1D00C683F9 /* CallSettings.swift */; };
@@ -131,6 +126,12 @@
64AA1C6927EE10C800AC7277 /* ContextItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6827EE10C800AC7277 /* ContextItemView.swift */; };
64AA1C6C27F3537400AC7277 /* DeletedItemView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */; };
64E972072881BB22008DBC02 /* CIGroupInvitationView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */; };
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */; };
64F1CC4128B3A99A00CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64F1CC3C28B3A99900CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5.a */; };
64F1CC4228B3A99A00CD1FB1 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64F1CC3D28B3A99900CD1FB1 /* libgmpxx.a */; };
64F1CC4328B3A99A00CD1FB1 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64F1CC3E28B3A99900CD1FB1 /* libgmp.a */; };
64F1CC4428B3A99A00CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5-ghc8.10.7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64F1CC3F28B3A99A00CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5-ghc8.10.7.a */; };
64F1CC4528B3A99A00CD1FB1 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64F1CC4028B3A99A00CD1FB1 /* libffi.a */; };
/* End PBXBuildFile section */
/* Begin PBXContainerItemProxy section */
@@ -196,11 +197,6 @@
3CDBCF4127FAE51000354CDD /* ComposeLinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ComposeLinkView.swift; sourceTree = "<group>"; };
3CDBCF4727FF621E00354CDD /* CILinkView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CILinkView.swift; sourceTree = "<group>"; };
5C00164328A26FBC0094D739 /* ContextMenu.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ContextMenu.swift; sourceTree = "<group>"; };
5C00165128B02AF30094D739 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
5C00165228B02AF30094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J-ghc8.10.7.a"; sourceTree = "<group>"; };
5C00165328B02AF30094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J.a"; sourceTree = "<group>"; };
5C00165428B02AF30094D739 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
5C00165528B02AF30094D739 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
5C029EA72837DBB3004A9677 /* CICallItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CICallItemView.swift; sourceTree = "<group>"; };
5C029EA9283942EA004A9677 /* CallController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallController.swift; sourceTree = "<group>"; };
5C05DF522840AA1D00C683F9 /* CallSettings.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CallSettings.swift; sourceTree = "<group>"; };
@@ -317,6 +313,12 @@
64AA1C6B27F3537400AC7277 /* DeletedItemView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DeletedItemView.swift; sourceTree = "<group>"; };
64DAE1502809D9F5000DA960 /* FileUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = FileUtils.swift; sourceTree = "<group>"; };
64E972062881BB22008DBC02 /* CIGroupInvitationView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CIGroupInvitationView.swift; sourceTree = "<group>"; };
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = IncognitoHelp.swift; sourceTree = "<group>"; };
64F1CC3C28B3A99900CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5.a"; sourceTree = "<group>"; };
64F1CC3D28B3A99900CD1FB1 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
64F1CC3E28B3A99900CD1FB1 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
64F1CC3F28B3A99A00CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5-ghc8.10.7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5-ghc8.10.7.a"; sourceTree = "<group>"; };
64F1CC4028B3A99A00CD1FB1 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
/* End PBXFileReference section */
/* Begin PBXFrameworksBuildPhase section */
@@ -349,13 +351,13 @@
isa = PBXFrameworksBuildPhase;
buildActionMask = 2147483647;
files = (
64F1CC4428B3A99A00CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5-ghc8.10.7.a in Frameworks */,
64F1CC4228B3A99A00CD1FB1 /* libgmpxx.a in Frameworks */,
64F1CC4328B3A99A00CD1FB1 /* libgmp.a in Frameworks */,
64F1CC4128B3A99A00CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5.a in Frameworks */,
64F1CC4528B3A99A00CD1FB1 /* libffi.a in Frameworks */,
5CE2BA93284534B000EC33A6 /* libiconv.tbd in Frameworks */,
5C00165728B02AF40094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J-ghc8.10.7.a in Frameworks */,
5C00165828B02AF40094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J.a in Frameworks */,
5C00165628B02AF40094D739 /* libffi.a in Frameworks */,
5C00165928B02AF40094D739 /* libgmp.a in Frameworks */,
5CE2BA94284534BB00EC33A6 /* libz.tbd in Frameworks */,
5C00165A28B02AF40094D739 /* libgmpxx.a in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
};
@@ -410,11 +412,11 @@
5C764E5C279C70B7000C6508 /* Libraries */ = {
isa = PBXGroup;
children = (
5C00165128B02AF30094D739 /* libffi.a */,
5C00165428B02AF30094D739 /* libgmp.a */,
5C00165528B02AF30094D739 /* libgmpxx.a */,
5C00165228B02AF30094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J-ghc8.10.7.a */,
5C00165328B02AF30094D739 /* libHSsimplex-chat-3.1.0-KA4pfwpgEHbFrTKfOobU7J.a */,
64F1CC4028B3A99A00CD1FB1 /* libffi.a */,
64F1CC3E28B3A99900CD1FB1 /* libgmp.a */,
64F1CC3D28B3A99900CD1FB1 /* libgmpxx.a */,
64F1CC3F28B3A99A00CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5-ghc8.10.7.a */,
64F1CC3C28B3A99900CD1FB1 /* libHSsimplex-chat-3.2.0-6p2ah0FJ9icAh1HFBZcXP5.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -553,6 +555,7 @@
5C577F7C27C83AA10006112D /* MarkdownHelp.swift */,
640F50E227CF991C001E05C2 /* SMPServers.swift */,
5C3F1D592844B4DE00EC8A82 /* ExperimentalFeaturesView.swift */,
64F1CC3A28B39D8600CD1FB1 /* IncognitoHelp.swift */,
);
path = UserSettings;
sourceTree = "<group>";
@@ -869,6 +872,7 @@
5C3A88D127DF57800060F1C2 /* FramedItemView.swift in Sources */,
5CB924E427A8683A00ACCCDD /* UserAddress.swift in Sources */,
640F50E327CF991C001E05C2 /* SMPServers.swift in Sources */,
64F1CC3B28B39D8600CD1FB1 /* IncognitoHelp.swift in Sources */,
5CB0BA90282713D900B3292C /* SimpleXInfo.swift in Sources */,
5C063D2727A4564100AEC577 /* ChatPreviewView.swift in Sources */,
5C35CFCB27B2E91D00FB6C6D /* NtfManager.swift in Sources */,

View File

@@ -20,6 +20,7 @@ public enum ChatCommand {
case apiActivateChat
case apiSuspendChat(timeoutMicroseconds: Int)
case setFilesFolder(filesFolder: String)
case setIncognito(incognito: Bool)
case apiExportArchive(config: ArchiveConfig)
case apiImportArchive(config: ArchiveConfig)
case apiDeleteStorage
@@ -82,6 +83,7 @@ public enum ChatCommand {
case .apiActivateChat: return "/_app activate"
case let .apiSuspendChat(timeoutMicroseconds): return "/_app suspend \(timeoutMicroseconds)"
case let .setFilesFolder(filesFolder): return "/_files_folder \(filesFolder)"
case let .setIncognito(incognito): return "/incognito \(incognito ? "on" : "off")"
case let .apiExportArchive(cfg): return "/_db export \(encodeJSON(cfg))"
case let .apiImportArchive(cfg): return "/_db import \(encodeJSON(cfg))"
case .apiDeleteStorage: return "/_db delete"
@@ -148,6 +150,7 @@ public enum ChatCommand {
case .apiActivateChat: return "apiActivateChat"
case .apiSuspendChat: return "apiSuspendChat"
case .setFilesFolder: return "setFilesFolder"
case .setIncognito: return "setIncognito"
case .apiExportArchive: return "apiExportArchive"
case .apiImportArchive: return "apiImportArchive"
case .apiDeleteStorage: return "apiDeleteStorage"
@@ -225,8 +228,8 @@ public enum ChatResponse: Decodable, Error {
case apiChat(chat: ChatData)
case userSMPServers(smpServers: [String])
case networkConfig(networkConfig: NetCfg)
case contactInfo(contact: Contact, connectionStats: ConnectionStats)
case groupMemberInfo(groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?)
case contactInfo(contact: Contact, connectionStats: ConnectionStats, customUserProfile: Profile?)
case groupMemberInfo(groupInfo: GroupInfo, member: GroupMember, connectionStats_: ConnectionStats?, mainProfile: Profile?)
case invitation(connReqInvitation: String)
case sentConfirmation
case sentInvitation
@@ -407,8 +410,8 @@ public enum ChatResponse: Decodable, Error {
case let .apiChat(chat): return String(describing: chat)
case let .userSMPServers(smpServers): return String(describing: smpServers)
case let .networkConfig(networkConfig): return String(describing: networkConfig)
case let .contactInfo(contact, connectionStats): return "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))"
case let .groupMemberInfo(groupInfo, member, connectionStats_): return "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\\nconnectionStats_: \(String(describing: connectionStats_))"
case let .contactInfo(contact, connectionStats, customUserProfile): return "contact: \(String(describing: contact))\nconnectionStats: \(String(describing: connectionStats))\ncustomUserProfile: \(String(describing: customUserProfile))"
case let .groupMemberInfo(groupInfo, member, connectionStats_, mainProfile): return "groupInfo: \(String(describing: groupInfo))\nmember: \(String(describing: member))\nconnectionStats_: \(String(describing: connectionStats_))\nmainProfile: \(String(describing: mainProfile))"
case let .invitation(connReqInvitation): return connReqInvitation
case .sentConfirmation: return noDetails
case .sentInvitation: return noDetails

View File

@@ -23,6 +23,7 @@ let GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE = "networkEnableKeepAlive"
let GROUP_DEFAULT_NETWORK_TCP_KEEP_IDLE = "networkTCPKeepIdle"
let GROUP_DEFAULT_NETWORK_TCP_KEEP_INTVL = "networkTCPKeepIntvl"
let GROUP_DEFAULT_NETWORK_TCP_KEEP_CNT = "networkTCPKeepCnt"
let GROUP_DEFAULT_INCOGNITO = "incognito"
let APP_GROUP_NAME = "group.chat.simplex.app"
@@ -37,7 +38,8 @@ public func registerGroupDefaults() {
GROUP_DEFAULT_NETWORK_ENABLE_KEEP_ALIVE: NetCfg.defaults.enableKeepAlive,
GROUP_DEFAULT_NETWORK_TCP_KEEP_IDLE: KeepAliveOpts.defaults.keepIdle,
GROUP_DEFAULT_NETWORK_TCP_KEEP_INTVL: KeepAliveOpts.defaults.keepIntvl,
GROUP_DEFAULT_NETWORK_TCP_KEEP_CNT: KeepAliveOpts.defaults.keepCnt
GROUP_DEFAULT_NETWORK_TCP_KEEP_CNT: KeepAliveOpts.defaults.keepCnt,
GROUP_DEFAULT_INCOGNITO: false
])
}
@@ -82,6 +84,8 @@ public let ntfPreviewModeGroupDefault = EnumDefault<NotificationPreviewMode>(
withDefault: .message
)
public let incognitoGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_INCOGNITO)
public let privacyAcceptImagesGroupDefault = BoolDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_PRIVACY_ACCEPT_IMAGES)
public let ntfBadgeCountGroupDefault = IntDefault(defaults: groupDefaults, forKey: GROUP_DEFAULT_NTF_BADGE_COUNT)

View File

@@ -13,7 +13,7 @@ public struct User: Decodable, NamedChat {
var userId: Int64
var userContactId: Int64
var localDisplayName: ContactName
public var profile: Profile
public var profile: LocalProfile
var activeUser: Bool
public var displayName: String { get { profile.displayName } }
@@ -24,7 +24,7 @@ public struct User: Decodable, NamedChat {
userId: 1,
userContactId: 1,
localDisplayName: "alice",
profile: Profile.sampleData,
profile: LocalProfile.sampleData,
activeUser: true
)
}
@@ -54,6 +54,38 @@ public struct Profile: Codable, NamedChat {
)
}
public struct LocalProfile: Codable, NamedChat {
public init(profileId: Int64, displayName: String, fullName: String, image: String? = nil) {
self.profileId = profileId
self.displayName = displayName
self.fullName = fullName
self.image = image
}
public var profileId: Int64
public var displayName: String
public var fullName: String
public var image: String?
var profileViewName: String {
(fullName == "" || displayName == fullName) ? displayName : "\(displayName) (\(fullName))"
}
static let sampleData = LocalProfile(
profileId: 1,
displayName: "alice",
fullName: "Alice"
)
}
public func toLocalProfile (_ profileId: Int64, _ profile: Profile ) -> LocalProfile {
LocalProfile(profileId: profileId, displayName: profile.displayName, fullName: profile.fullName, image: profile.image)
}
public func fromLocalProfile (_ profile: LocalProfile) -> Profile {
Profile(displayName: profile.displayName, fullName: profile.fullName, image: profile.image)
}
public enum ChatType: String {
case direct = "@"
case group = "#"
@@ -180,6 +212,17 @@ public enum ChatInfo: Identifiable, Decodable, NamedChat {
}
}
public var incognito: Bool {
get {
switch self {
case let .direct(contact): return contact.contactConnIncognito
case let .group(groupInfo): return groupInfo.membership.memberIncognito
case .contactRequest: return false
case let .contactConnection(contactConnection): return contactConnection.incognito
}
}
}
public var contact: Contact? {
get {
switch self {
@@ -249,7 +292,7 @@ public struct ChatStats: Decodable {
public struct Contact: Identifiable, Decodable, NamedChat {
var contactId: Int64
var localDisplayName: ContactName
public var profile: Profile
public var profile: LocalProfile
public var activeConn: Connection
public var viaGroup: Int64?
public var chatSettings: ChatSettings
@@ -264,14 +307,18 @@ public struct Contact: Identifiable, Decodable, NamedChat {
public var fullName: String { get { profile.fullName } }
public var image: String? { get { profile.image } }
public func isIndirectContact() -> Bool {
return activeConn.connLevel > 0 || viaGroup != nil
public var isIndirectContact: Bool {
activeConn.connLevel > 0 || viaGroup != nil
}
public var contactConnIncognito: Bool {
activeConn.customUserProfileId != nil
}
public static let sampleData = Contact(
contactId: 1,
localDisplayName: "alice",
profile: Profile.sampleData,
profile: LocalProfile.sampleData,
activeConn: Connection.sampleData,
chatSettings: ChatSettings.defaults,
createdAt: .now,
@@ -295,6 +342,7 @@ public struct Connection: Decodable {
var connId: Int64
var connStatus: ConnStatus
public var connLevel: Int
public var customUserProfileId: Int64?
public var id: ChatId { get { ":\(connId)" } }
@@ -352,6 +400,7 @@ public struct PendingContactConnection: Decodable, NamedChat {
var pccAgentConnId: String
var pccConnStatus: ConnStatus
public var viaContactUri: Bool
public var customUserProfileId: Int64?
var createdAt: Date
public var updatedAt: Date
@@ -378,14 +427,34 @@ public struct PendingContactConnection: Decodable, NamedChat {
public var image: String? { get { nil } }
public var initiated: Bool { get { (pccConnStatus.initiated ?? false) && !viaContactUri } }
public var incognito: Bool {
customUserProfileId != nil
}
public var description: String {
get {
if let initiated = pccConnStatus.initiated {
return initiated && !viaContactUri
? NSLocalizedString("you shared one-time link", comment: "chat list item description")
: viaContactUri
? NSLocalizedString("via contact address link", comment: "chat list item description")
: NSLocalizedString("via one-time link", comment: "chat list item description")
var desc: String
if initiated && !viaContactUri {
if incognito {
desc = NSLocalizedString("you shared one-time link incognito", comment: "chat list item description")
} else {
desc = NSLocalizedString("you shared one-time link", comment: "chat list item description")
}
} else if viaContactUri {
if incognito {
desc = NSLocalizedString("incognito via contact address link", comment: "chat list item description")
} else {
desc = NSLocalizedString("via contact address link", comment: "chat list item description")
}
} else {
if incognito {
desc = NSLocalizedString("incognito via one-time link", comment: "chat list item description")
} else {
desc = NSLocalizedString("via one-time link", comment: "chat list item description")
}
}
return desc
} else {
return ""
}
@@ -443,6 +512,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat {
var localDisplayName: GroupName
public var groupProfile: GroupProfile
public var membership: GroupMember
public var hostConnCustomUserProfileId: Int64?
public var chatSettings: ChatSettings
var createdAt: Date
var updatedAt: Date
@@ -472,6 +542,7 @@ public struct GroupInfo: Identifiable, Decodable, NamedChat {
localDisplayName: "team",
groupProfile: GroupProfile.sampleData,
membership: GroupMember.sampleData,
hostConnCustomUserProfileId: nil,
chatSettings: ChatSettings.defaults,
createdAt: .now,
updatedAt: .now
@@ -504,8 +575,9 @@ public struct GroupMember: Identifiable, Decodable {
public var memberStatus: GroupMemberStatus
public var invitedBy: InvitedBy
public var localDisplayName: ContactName
public var memberProfile: Profile
public var memberProfile: LocalProfile
public var memberContactId: Int64?
public var memberContactProfileId: Int64
public var activeConn: Connection?
public var id: String { "#\(groupId) @\(groupMemberId)" }
@@ -568,6 +640,10 @@ public struct GroupMember: Identifiable, Decodable {
&& userRole >= .admin && userRole >= memberRole && membership.memberCurrent
}
public var memberIncognito: Bool {
memberProfile.profileId != memberContactProfileId
}
public static let sampleData = GroupMember(
groupMemberId: 1,
groupId: 1,
@@ -577,8 +653,9 @@ public struct GroupMember: Identifiable, Decodable {
memberStatus: .memComplete,
invitedBy: .user,
localDisplayName: "alice",
memberProfile: Profile.sampleData,
memberProfile: LocalProfile.sampleData,
memberContactId: 1,
memberContactProfileId: 1,
activeConn: Connection.sampleData
)
}
@@ -1280,9 +1357,12 @@ public struct CIGroupInvitation: Decodable {
public var localDisplayName: GroupName
public var groupProfile: GroupProfile
public var status: CIGroupInvitationStatus
public var invitedIncognito: Bool?
var text: String {
String.localizedStringWithFormat(NSLocalizedString("invitation to group %@", comment: "group name"), groupProfile.displayName)
(invitedIncognito ?? false) ?
String.localizedStringWithFormat(NSLocalizedString("incognito invitation to group %@", comment: "group name"), groupProfile.displayName)
: String.localizedStringWithFormat(NSLocalizedString("invitation to group %@", comment: "group name"), groupProfile.displayName)
}
public static func getSample(groupId: Int64 = 1, groupMemberId: Int64 = 1, localDisplayName: GroupName = "team", groupProfile: GroupProfile = GroupProfile.sampleData, status: CIGroupInvitationStatus = .pending) -> CIGroupInvitation {
@@ -1299,7 +1379,7 @@ public enum CIGroupInvitationStatus: String, Decodable {
public enum RcvGroupEvent: Decodable {
case memberAdded(groupMemberId: Int64, profile: Profile)
case memberConnected
case memberConnected(contactMainProfile: Profile?)
case memberLeft
case memberDeleted(groupMemberId: Int64, profile: Profile)
case userDeleted
@@ -1310,7 +1390,12 @@ public enum RcvGroupEvent: Decodable {
switch self {
case let .memberAdded(_, profile):
return String.localizedStringWithFormat(NSLocalizedString("invited %@", comment: "rcv group event chat item"), profile.profileViewName)
case .memberConnected: return NSLocalizedString("member connected", comment: "rcv group event chat item")
case let .memberConnected(contactMainProfile):
if let contactMainProfile = contactMainProfile {
return String.localizedStringWithFormat(NSLocalizedString("known to you as %@ connected incognito", comment: "rcv group event chat item"), contactMainProfile.profileViewName)
} else {
return NSLocalizedString("member connected", comment: "rcv group event chat item")
}
case .memberLeft: return NSLocalizedString("left", comment: "rcv group event chat item")
case let .memberDeleted(_, profile):
return String.localizedStringWithFormat(NSLocalizedString("removed %@", comment: "rcv group event chat item"), profile.profileViewName)

View File

@@ -1,3 +1,6 @@
/* No comment provided by engineer. */
"\n" = "\n";
/* No comment provided by engineer. */
" " = " ";
@@ -106,6 +109,12 @@
/* notification title */
"A new contact" = "Новый контакт";
/* No comment provided by engineer. */
"A random profile will be sent to the contact that you received this link from" = "Контакту, от которого вы получили эту ссылку, будет отправлен случайный профиль";
/* No comment provided by engineer. */
"A random profile will be sent to your contact" = "Вашему контакту будет отправлен случайный профиль";
/* No comment provided by engineer. */
"About SimpleX" = "О SimpleX";
@@ -115,6 +124,9 @@
/* 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" = "Принять";
@@ -125,6 +137,9 @@
/* notification body */
"Accept contact request from %@?" = "Принять запрос на соединение от %@?";
/* No comment provided by engineer. */
"Accept incognito" = "Принять инкогнито";
/* call status */
"accepted call" = " принятый звонок";
@@ -194,6 +209,9 @@
/* call status */
"calling…" = "входящий звонок…";
/* No comment provided by engineer. */
"Can't invite contact!" = "Нельзя пригласить контакт!";
/* No comment provided by engineer. */
"Cancel" = "Отменить";
@@ -221,7 +239,7 @@
/* No comment provided by engineer. */
"Chat with the developers" = "Соединиться с разработчиками";
/* back button to return to chats list */
/* No comment provided by engineer. */
"Chats" = "Чаты";
/* No comment provided by engineer. */
@@ -242,6 +260,9 @@
/* No comment provided by engineer. */
"colored" = "цвет";
/* No comment provided by engineer. */
"Colors" = "Цвета";
/* No comment provided by engineer. */
"complete" = "соединение завершено";
@@ -350,7 +371,7 @@
/* No comment provided by engineer. */
"Contact name" = "Имена контактов";
/* No comment provided by engineer. */
/* chat item action */
"Copy" = "Скопировать";
/* No comment provided by engineer. */
@@ -392,7 +413,7 @@
/* No comment provided by engineer. */
"Decentralized" = "Децентрализованный";
/* No comment provided by engineer. */
/* chat item action */
"Delete" = "Удалить";
/* No comment provided by engineer. */
@@ -494,7 +515,7 @@
/* No comment provided by engineer. */
"e2e encrypted" = "e2e зашифровано";
/* No comment provided by engineer. */
/* chat item action */
"Edit" = "Редактировать";
/* No comment provided by engineer. */
@@ -530,6 +551,9 @@
/* No comment provided by engineer. */
"Error accessing database file" = "Ошибка при доступе к данным чата";
/* No comment provided by engineer. */
"Error adding member(s)" = "Ошибка при добавлении членов группы";
/* No comment provided by engineer. */
"Error creating group" = "Ошибка при создании группы";
@@ -692,6 +716,27 @@
/* No comment provided by engineer. */
"In person or via a video call the most secure way to connect." = "При встрече или в видеозвонке самый безопасный способ установить соединение";
/* No comment provided by engineer. */
"Incognito" = "Инкогнито";
/* No comment provided by engineer. */
"Incognito groups" = "Инкогнито группы";
/* group name */
"incognito invitation to group %@" = "инкогнито приглашение в группу %@";
/* No comment provided by engineer. */
"Incognito mode" = "Режим Инкогнито";
/* No comment provided by engineer. */
"Incognito mode protects the privacy of your main profile name and image — for each new contact and group a new random profile is created." = "Режим Инкогнито защищает конфиденциальность имени и изображения вашего основного профиля — для каждого нового контакта и группы создается новый случайный профиль.";
/* chat list item description */
"incognito via contact address link" = "инкогнито через ссылку-контакт";
/* chat list item description */
"incognito via one-time link" = "инкогнито через одноразовую ссылку";
/* notification */
"Incoming audio call" = "Входящий аудиозвонок";
@@ -719,6 +764,9 @@
/* group name */
"invitation to group %@" = "приглашение в группу %@";
/* No comment provided by engineer. */
"Invite anyway" = "Пригласить";
/* No comment provided by engineer. */
"Invite members" = "Пригласить членов группы";
@@ -734,9 +782,15 @@
/* chat list item title */
"invited to connect" = "приглашение";
/* No comment provided by engineer. */
"It allows having many anonymous connections without any shared data between them in a single chat profile." = "Это позволяет иметь много анонимных соединений без общих данных между ними в одном профиле пользователя.";
/* No comment provided by engineer. */
"It can happen when:\n1. The messages expire on the server if they were not received for 30 days,\n2. The server you use to receive the messages from this contact was updated and restarted.\n3. The connection is compromised.\nPlease connect to the developers via Settings to receive the updates about the servers.\nWe will be adding server redundancy to prevent lost messages." = "Это может случится, когда:\n1. Сервер удалил сообщения, если они не были доставлены в течение 30 дней.\n2. Сервер, через который вы получаете сообщения от контакта, был обновлён и перезапущен.\n3. Соединение компроментировано.\nПожалуйста, соединитесь с девелоперами через Настройки, чтобы получать уведомления о серверах.\nМы планируем добавить избыточную доставку сообщений, чтобы не терять сообщения.";
/* No comment provided by engineer. */
"It is not allowed to invite contacts with whom you have an incognito connection to a group where you use your main profile otherwise they might find out your main profile." = "Нельзя приглашать контакты, с которыми у вас установлено инкогнито соединение, в группу, где вы используете свой основной профиль — иначе они могут узнать ваш основной профиль.";
/* No comment provided by engineer. */
"It seems like you are already connected via this link. If it is not the case, there was an error (%@)." = "Возможно, вы уже соединились через эту ссылку. Если это не так, то это ошибка (%@).";
@@ -749,12 +803,24 @@
/* No comment provided by engineer. */
"Join" = "Вступить";
/* No comment provided by engineer. */
"Join anyway" = "Вступить";
/* No comment provided by engineer. */
"Join group" = "Вступить в группу";
/* No comment provided by engineer. */
"Join incognito" = "Вступить инкогнито";
/* No comment provided by engineer. */
"Joining group" = "Вступление в группу";
/* No comment provided by engineer. */
"Known main profile" = "Известный основной профиль";
/* rcv group event chat item */
"known to you as %@ connected incognito" = "известный(ая) вам как %@ соединен(а) инкогнито";
/* No comment provided by engineer. */
"Large file!" = "Большой файл!";
@@ -785,6 +851,9 @@
/* No comment provided by engineer. */
"Many people asked: *if SimpleX has no user identifiers, how can it deliver messages?*" = "Много пользователей спросили: *как SimpleX доставляет сообщения без идентификаторов пользователей?*";
/* No comment provided by engineer. */
"Mark read" = "Прочитано";
/* No comment provided by engineer. */
"Markdown in messages" = "Форматирование сообщений";
@@ -827,6 +896,9 @@
/* No comment provided by engineer. */
"Most likely this contact has deleted the connection with you." = "Скорее всего, этот контакт удалил соединение с вами.";
/* No comment provided by engineer. */
"Mute" = "Без звука";
/* No comment provided by engineer. */
"Network & servers" = "Сеть & серверы";
@@ -854,6 +926,9 @@
/* notification */
"New message" = "Новое сообщение";
/* No comment provided by engineer. */
"No" = "Нет";
/* No comment provided by engineer. */
"No contacts selected" = "Контакты не выбраны";
@@ -890,6 +965,15 @@
/* No comment provided by engineer. */
"One-time invitation link" = "Одноразовая ссылка";
/* No comment provided by engineer. */
"Onion hosts will be required for connection. Requires enabling VPN." = "Подключаться только к onion хостам. Требуется включенный VPN.";
/* No comment provided by engineer. */
"Onion hosts will be used when available. Requires enabling VPN." = "Onion хосты используются, если возможно. Требуется включенный VPN.";
/* No comment provided by engineer. */
"Onion hosts will not be used." = "Onion хосты не используются.";
/* No comment provided by engineer. */
"Only client devices store user profiles, contacts, groups, and messages sent with **2-layer end-to-end encryption**." = "Только пользовательские устройства хранят контакты, группы и сообщения, которые отправляются **с двухуровневым end-to-end шифрованием**";
@@ -992,6 +1076,12 @@
/* call status */
"rejected call" = "отклонённый звонок";
/* No comment provided by engineer. */
"Relay server is only used if necessary. Another party can observe your IP address." = "Relay сервер используется только при необходимости. Другая сторона может видеть ваш IP адрес.";
/* No comment provided by engineer. */
"Relay server protects your IP address, but it can observe the duration of the call." = "Relay сервер защищает ваш IP адрес, но может отслеживать продолжительность звонка.";
/* No comment provided by engineer. */
"Remove" = "Удалить";
@@ -1010,9 +1100,15 @@
/* rcv group event chat item */
"removed you" = "удалил(а) вас из группы";
/* No comment provided by engineer. */
/* chat item action */
"Reply" = "Ответить";
/* No comment provided by engineer. */
"Required" = "Обязательно";
/* No comment provided by engineer. */
"Reset colors" = "Сбросить цвета";
/* No comment provided by engineer. */
"Reset to defaults" = "Сбросить настройки";
@@ -1026,9 +1122,12 @@
"Revert" = "Отменить изменения";
/* No comment provided by engineer. */
"Run chat" = "Запустить chat";
"Risks and limitations:" = "Риски и ограничения:";
/* No comment provided by engineer. */
"Run chat" = "Запустить chat";
/* chat item action */
"Save" = "Сохранить";
/* No comment provided by engineer. */
@@ -1049,6 +1148,9 @@
/* No comment provided by engineer. */
"Scan QR code" = "Сканировать QR код";
/* No comment provided by engineer. */
"Search" = "Поиск";
/* network option */
"sec" = "сек";
@@ -1079,7 +1181,7 @@
/* No comment provided by engineer. */
"Settings" = "Настройки";
/* No comment provided by engineer. */
/* chat item action */
"Share" = "Поделиться";
/* No comment provided by engineer. */
@@ -1112,6 +1214,9 @@
/* No comment provided by engineer. */
"SMP servers (one per line)" = "SMP серверы (один на строке)";
/* No comment provided by engineer. */
"Some selected contacts have your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members." = "У некоторых выбранных контактов есть ваш основной профиль. Если они используют приложение SimpleX версии раньше чем 3.2 или другой клиент, они могут поделиться с другими участниками вашим основным профилем вместо случайного инкогнито профиля.";
/* notification title */
"Somebody" = "Контакт";
@@ -1148,6 +1253,9 @@
/* No comment provided by engineer. */
"Tap to join" = "Нажмите, чтобы вступить";
/* No comment provided by engineer. */
"Tap to join incognito" = "Нажмите, чтобы вступить инкогнито";
/* No comment provided by engineer. */
"TCP connection timeout" = "Таймаут TCP соединения";
@@ -1172,12 +1280,18 @@
/* No comment provided by engineer. */
"The connection you accepted will be cancelled!" = "Подтвержденное соединение будет отменено!";
/* No comment provided by engineer. */
"The contact who invited you has your main profile. If they use SimpleX app older than v3.2 or some other client, they may share your main profile instead of a random incognito profile with other members." = "У контакта есть ваш основной профиль. Если он использует приложение SimpleX версии раньше чем 3.2 или другой клиент, он может поделиться с другими членами группы вашим основным профилем вместо случайного инкогнито профиля.";
/* No comment provided by engineer. */
"The contact you shared this link with will NOT be able to connect!" = "Контакт, которому вы отправили эту ссылку, не сможет соединиться!";
/* No comment provided by engineer. */
"The created archive is available via app Settings / Database / Old database archive." = "Созданный архив доступен через Настройки приложения.";
/* No comment provided by engineer. */
"the group is created in Incognito mode," = "группа создана в режиме Инкогнито,";
/* No comment provided by engineer. */
"The group is fully decentralized it is visible only to the members." = "Группа полностью децентрализована — она видна только членам.";
@@ -1196,6 +1310,9 @@
/* No comment provided by engineer. */
"The sender will NOT be notified" = "Отправитель не будет уведомлён";
/* No comment provided by engineer. */
"There is a risk to have your main profile shared, if you have contacts who know your main profile in an incognito group. Before you invite or join group with such contacts a warning will be shown." = "Если в инкогнито группе есть члены, которые знают ваш основной профиль, существует риск его раскрытия. Перед тем, как вы пригласите ваши контакты или вступите в такую группу, будет показано предупреждение.";
/* No comment provided by engineer. */
"This action cannot be undone - your profile, contacts, messages and files will be irreversibly lost." = "Это действие нельзя отменить — ваш профиль, контакты, сообщения и файлы будут безвозвратно утеряны.";
@@ -1211,6 +1328,9 @@
/* No comment provided by engineer. */
"To ask any questions and to receive updates:" = "Чтобы задать вопросы и получать уведомления о новых версиях,";
/* No comment provided by engineer. */
"To find the profile used for an incognito connection, tap the contact or group name on top of the chat." = "Чтобы найти инкогнито профиль, используемый в разговоре, нажмите на имя контакта или группы в верхней части чата.";
/* No comment provided by engineer. */
"To make a new connection" = "Чтобы соединиться";
@@ -1259,6 +1379,12 @@
/* authentication reason */
"Unlock" = "Разблокировать";
/* No comment provided by engineer. */
"Unmute" = "Уведомлять";
/* No comment provided by engineer. */
"Update .onion hosts setting?" = "Обновить настройки .onion хостов?";
/* No comment provided by engineer. */
"Update network settings?" = "Обновить настройки сети?";
@@ -1266,7 +1392,13 @@
"updated group profile" = "обновил(а) профиль группы";
/* No comment provided by engineer. */
"Updating settings will re-connect the client to all servers." = "Обновление настроек приведет к переподключению клиента ко всем серверам.";
"Updating settings will re-connect the client to all servers." = "Обновление настроек приведет к сбросу и установке нового соединения со всеми серверами.";
/* No comment provided by engineer. */
"Updating this setting will re-connect the client to all servers." = "Обновление этих настроек приведет к сбросу и установке нового соединения со всеми серверами.";
/* No comment provided by engineer. */
"Use .onion hosts" = "Использовать .onion хосты";
/* No comment provided by engineer. */
"Use chat" = "Использовать чат";
@@ -1274,6 +1406,9 @@
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "Использовать серверы предосталенные SimpleX Chat?";
/* No comment provided by engineer. */
"Using .onion hosts requires compatible VPN provider." = "Для использования .onion хостов требуется совместимый VPN провайдер.";
/* No comment provided by engineer. */
"Using SimpleX Chat servers." = "Используются серверы, предоставленные SimpleX Chat.";
@@ -1289,6 +1424,9 @@
/* No comment provided by engineer. */
"via relay" = "через relay сервер";
/* No comment provided by engineer. */
"Video call" = "Видеозвонок";
/* No comment provided by engineer. */
"video call (not e2e encrypted)" = "видеозвонок (не e2e зашифрованный)";
@@ -1310,6 +1448,12 @@
/* No comment provided by engineer. */
"Welcome %@!" = "Здравствуйте %@!";
/* No comment provided by engineer. */
"When available" = "Когда возможно";
/* No comment provided by engineer. */
"When you join a group incognito, your new member profile is created and shared with all members." = "Когда вы вступаете в группу инкогнито, создается новый случайный профиль, который отправляется другим членам группы.";
/* No comment provided by engineer. */
"You" = "Вы";
@@ -1329,7 +1473,10 @@
"You are invited to group" = "Вы приглашены в группу";
/* No comment provided by engineer. */
"You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button" = "Вы также можете соединиться, открыв ссылку. Если ссылка откроется в браузере, нажмите кнопку **Open in mobile app**.";
"you are invited to group incognito" = "вы приглашены в группу инкогнито";
/* No comment provided by engineer. */
"You can also connect by clicking the link. If it opens in the browser, click **Open in mobile app** button." = "Вы также можете соединиться, открыв ссылку. Если ссылка откроется в браузере, нажмите кнопку **Open in mobile app**.";
/* notification body */
"You can now send messages to %@" = "Вы теперь можете отправлять сообщения %@";
@@ -1352,6 +1499,9 @@
/* No comment provided by engineer. */
"You could not be verified; please try again." = "Верификация не удалась; пожалуйста, попробуйте ещё раз.";
/* No comment provided by engineer. */
"you have an incognito connection with the member who invited you." = "вы соединены инкогнито с членом группы, который вас пригласил.";
/* No comment provided by engineer. */
"You invited your contact" = "Вы пригласили ваш контакт";
@@ -1367,6 +1517,9 @@
/* No comment provided by engineer. */
"You must use the most recent version of your chat database on one device ONLY, otherwise you may stop receiving the messages from some contacts." = "Вы должны всегда использовать самую новую версию данных чата, ТОЛЬКО на одном устройстве, инача вы можете перестать получать сообщения от каких то контактов.";
/* No comment provided by engineer. */
"you or the member who invited you joined in Incognito mode," = "вы или пригласивший вас член группы вступили в группу в режиме Инкогнито,";
/* No comment provided by engineer. */
"You rejected group invitation" = "Вы отклонили приглашение в группу";
@@ -1376,9 +1529,15 @@
/* No comment provided by engineer. */
"You sent group invitation" = "Вы отправили приглашение в группу";
/* No comment provided by engineer. */
"You sent group invitation incognito" = "Вы отправили приглашение в группу инкогнито";
/* chat list item description */
"you shared one-time link" = "вы создали ссылку";
/* chat list item description */
"you shared one-time link incognito" = "вы создали ссылку инкогнито";
/* No comment provided by engineer. */
"You will be connected when your connection request is accepted, please wait or check later!" = "Соединение будет установлено, когда ваш запрос будет принят. Пожалуйста, подождите или проверьте позже!";
@@ -1391,9 +1550,18 @@
/* No comment provided by engineer. */
"You will stop receiving messages from this group. Chat history will be preserved." = "Вы перестанете получать сообщения от этой группы. История чата будет сохранена.";
/* No comment provided by engineer. */
"You will use a random profile for this group" = "Вы будете использовать случайный профиль для этой группы";
/* No comment provided by engineer. */
"you: " = "вы: ";
/* No comment provided by engineer. */
"You're trying to invite contact with whom you've shared an incognito profile to the group in which you're using your main profile" = "Вы пытаетесь пригласить инкогнито контакт в группу, где вы используете свой основной профиль";
/* No comment provided by engineer. */
"Your are incognito in a group when:" = "Вы инкогнито в группе, когда:";
/* No comment provided by engineer. */
"Your calls" = "Ваши звонки";
@@ -1406,6 +1574,9 @@
/* No comment provided by engineer. */
"Your chat profile" = "Ваш профиль";
/* No comment provided by engineer. */
"Your chat profile will be sent to group members" = "Ваш профиль чата будет отправлен участникам группы";
/* No comment provided by engineer. */
"Your chat profile will be sent to your contact" = "Ваш профиль будет отправлен вашему контакту";
@@ -1413,7 +1584,7 @@
"Your chats" = "Ваши чаты";
/* No comment provided by engineer. */
"Your contact can scan it from the app" = "Ваш контакт может сосканировать QR в приложении";
"Your contact can scan it from the app." = "Ваш контакт может сосканировать QR код в приложении";
/* No comment provided by engineer. */
"Your contact needs to be online for the connection to complete.\nYou can cancel this connection and remove the contact (and try later with a new link)." = "Ваш контакт должен быть в сети чтобы установить соединение.\nВы можете отменить соединение и удалить контакт (и попробовать позже с другой ссылкой).";
@@ -1424,6 +1595,9 @@
/* No comment provided by engineer. */
"Your current chat database will be DELETED and REPLACED with the imported one." = "Текущие данные вашего чата будет УДАЛЕНЫ и ЗАМЕНЕНЫ импортированными.";
/* No comment provided by engineer. */
"Your main profile may be shared" = "Вашим основным профилем могут поделиться";
/* No comment provided by engineer. */
"Your privacy" = "Конфиденциальность";
@@ -1436,6 +1610,9 @@
/* No comment provided by engineer. */
"Your profile, contacts and delivered messages are stored on your device." = "Ваш профиль, контакты и доставленные сообщения хранятся на вашем устройстве.";
/* No comment provided by engineer. */
"Your random profile" = "Ваш случайный профиль";
/* No comment provided by engineer. */
"Your settings" = "Настройки";