Merge branch 'master' into master-android

This commit is contained in:
Evgeny Poberezkin
2025-06-01 22:52:08 +01:00
71 changed files with 1364 additions and 416 deletions
+6
View File
@@ -80,6 +80,7 @@ enum ChatCommand: ChatCmdProtocol {
case apiGroupLinkMemberRole(groupId: Int64, memberRole: GroupMemberRole)
case apiDeleteGroupLink(groupId: Int64)
case apiGetGroupLink(groupId: Int64)
case apiAddGroupShortLink(groupId: Int64)
case apiCreateMemberContact(groupId: Int64, groupMemberId: Int64)
case apiSendMemberContactInvitation(contactId: Int64, msg: MsgContent)
case apiTestProtoServer(userId: Int64, server: String)
@@ -134,6 +135,7 @@ enum ChatCommand: ChatCmdProtocol {
case apiCreateMyAddress(userId: Int64, short: Bool)
case apiDeleteMyAddress(userId: Int64)
case apiShowMyAddress(userId: Int64)
case apiAddMyAddressShortLink(userId: Int64)
case apiSetProfileAddress(userId: Int64, on: Bool)
case apiAddressAutoAccept(userId: Int64, autoAccept: AutoAccept?)
case apiAcceptContact(incognito: Bool, contactReqId: Int64)
@@ -262,6 +264,7 @@ enum ChatCommand: ChatCmdProtocol {
case let .apiGroupLinkMemberRole(groupId, memberRole): return "/_set link role #\(groupId) \(memberRole)"
case let .apiDeleteGroupLink(groupId): return "/_delete link #\(groupId)"
case let .apiGetGroupLink(groupId): return "/_get link #\(groupId)"
case let .apiAddGroupShortLink(groupId): return "/_short link #\(groupId)"
case let .apiCreateMemberContact(groupId, groupMemberId): return "/_create member contact #\(groupId) \(groupMemberId)"
case let .apiSendMemberContactInvitation(contactId, mc): return "/_invite member contact @\(contactId) \(mc.cmdString)"
case let .apiTestProtoServer(userId, server): return "/_server test \(userId) \(server)"
@@ -326,6 +329,7 @@ enum ChatCommand: ChatCmdProtocol {
case let .apiCreateMyAddress(userId, short): return "/_address \(userId) short=\(onOff(short))"
case let .apiDeleteMyAddress(userId): return "/_delete_address \(userId)"
case let .apiShowMyAddress(userId): return "/_show_address \(userId)"
case let .apiAddMyAddressShortLink(userId): return "/_short_link_address \(userId)"
case let .apiSetProfileAddress(userId, on): return "/_profile_address \(userId) \(onOff(on))"
case let .apiAddressAutoAccept(userId, autoAccept): return "/_auto_accept \(userId) \(AutoAccept.cmdString(autoAccept))"
case let .apiAcceptContact(incognito, contactReqId): return "/_accept incognito=\(onOff(incognito)) \(contactReqId)"
@@ -438,6 +442,7 @@ enum ChatCommand: ChatCmdProtocol {
case .apiGroupLinkMemberRole: return "apiGroupLinkMemberRole"
case .apiDeleteGroupLink: return "apiDeleteGroupLink"
case .apiGetGroupLink: return "apiGetGroupLink"
case .apiAddGroupShortLink: return "apiAddGroupShortLink"
case .apiCreateMemberContact: return "apiCreateMemberContact"
case .apiSendMemberContactInvitation: return "apiSendMemberContactInvitation"
case .apiTestProtoServer: return "apiTestProtoServer"
@@ -491,6 +496,7 @@ enum ChatCommand: ChatCmdProtocol {
case .apiCreateMyAddress: return "apiCreateMyAddress"
case .apiDeleteMyAddress: return "apiDeleteMyAddress"
case .apiShowMyAddress: return "apiShowMyAddress"
case .apiAddMyAddressShortLink: return "apiAddMyAddressShortLink"
case .apiSetProfileAddress: return "apiSetProfileAddress"
case .apiAddressAutoAccept: return "apiAddressAutoAccept"
case .apiAcceptContact: return "apiAcceptContact"
+13
View File
@@ -1210,6 +1210,13 @@ private func userAddressResponse(_ r: APIResult<ChatResponse1>) throws -> UserCo
}
}
func apiAddMyAddressShortLink() async throws -> UserContactLink {
let userId = try currentUserId("apiAddMyAddressShortLink")
let r: ChatResponse1 = try await chatSendCmd(.apiAddMyAddressShortLink(userId: userId))
if case let .userContactLink(_, contactLink) = r { return contactLink }
throw r.unexpected
}
func userAddressAutoAccept(_ autoAccept: AutoAccept?) async throws -> UserContactLink? {
let userId = try currentUserId("userAddressAutoAccept")
let r: APIResult<ChatResponse1> = await chatApiSendCmd(.apiAddressAutoAccept(userId: userId, autoAccept: autoAccept))
@@ -1736,6 +1743,12 @@ func apiGetGroupLink(_ groupId: Int64) throws -> (CreatedConnLink, GroupMemberRo
}
}
func apiAddGroupShortLink(_ groupId: Int64) async throws -> (CreatedConnLink, GroupMemberRole) {
let r: ChatResponse2 = try await chatSendCmd(.apiAddGroupShortLink(groupId: groupId))
if case let .groupLink(_, _, connLink, memberRole) = r { return (connLink, memberRole) }
throw r.unexpected
}
func apiCreateMemberContact(_ groupId: Int64, _ groupMemberId: Int64) async throws -> Contact {
let r: ChatResponse2 = try await chatSendCmd(.apiCreateMemberContact(groupId: groupId, groupMemberId: groupMemberId))
if case let .newMemberContact(_, contact, _, _) = r { return contact }
@@ -633,9 +633,6 @@ struct GroupChatInfoView: View {
groupInfo: $groupInfo,
groupProfile: groupInfo.groupProfile
)
.navigationBarTitle("Group profile")
.modifier(ThemedBackground())
.navigationBarTitleDisplayMode(.large)
} label: {
Label("Edit group profile", systemImage: "pencil")
}
@@ -35,16 +35,23 @@ struct GroupLinkView: View {
}
var body: some View {
if creatingGroup {
groupLinkView()
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button ("Continue") { linkCreatedCb?() }
ZStack {
if creatingGroup {
groupLinkView()
.navigationBarBackButtonHidden()
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button ("Continue") { linkCreatedCb?() }
}
}
}
} else {
groupLinkView()
} else {
groupLinkView()
}
if creatingLink {
ProgressView()
.scaleEffect(2)
.frame(maxWidth: .infinity)
}
}
}
@@ -79,6 +86,14 @@ struct GroupLinkView: View {
Label("Share link", systemImage: "square.and.arrow.up")
}
if (groupLink.connShortLink == nil && UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS)) {
Button {
addShortLink()
} label: {
Label("Add short link", systemImage: "plus")
}
}
if !creatingGroup {
Button(role: .destructive) { alert = .deleteLink } label: {
Label("Delete link", systemImage: "trash")
@@ -89,11 +104,6 @@ struct GroupLinkView: View {
Label("Create link", systemImage: "link.badge.plus")
}
.disabled(creatingLink)
if creatingLink {
ProgressView()
.scaleEffect(2)
.frame(maxWidth: .infinity)
}
}
} header: {
if let groupLink, groupLink.connShortLink != nil {
@@ -160,6 +170,26 @@ struct GroupLinkView: View {
}
}
}
private func addShortLink() {
Task {
do {
creatingLink = true
let link = try await apiAddGroupShortLink(groupId)
await MainActor.run {
creatingLink = false
(groupLink, groupLinkMemberRole) = link
}
} catch let error {
logger.error("apiAddGroupShortLink: \(responseError(error))")
await MainActor.run {
creatingLink = false
let a = getErrorAlert(error, "Error adding short link")
alert = .error(title: a.title, error: a.message)
}
}
}
}
}
struct GroupLinkView_Previews: PreviewProvider {
@@ -26,6 +26,7 @@ struct GroupProfileView: View {
@Environment(\.dismiss) var dismiss: DismissAction
@Binding var groupInfo: GroupInfo
@State var groupProfile: GroupProfile
@State private var currentProfileHash: Int?
@State private var showChooseSource = false
@State private var showImagePicker = false
@State private var showTakePhoto = false
@@ -34,60 +35,40 @@ struct GroupProfileView: View {
@FocusState private var focusDisplayName
var body: some View {
return VStack(alignment: .leading) {
Text("Group profile is stored on members' devices, not on the servers.")
.padding(.vertical)
List {
EditProfileImage(profileImage: $groupProfile.image, showChooseSource: $showChooseSource)
.if(!focusDisplayName) { $0.padding(.top) }
ZStack(alignment: .center) {
ZStack(alignment: .topTrailing) {
profileImageView(groupProfile.image)
if groupProfile.image != nil {
Button {
groupProfile.image = nil
} label: {
Image(systemName: "multiply")
.resizable()
.aspectRatio(contentMode: .fit)
.frame(width: 12)
}
}
}
editImageButton { showChooseSource = true }
}
.frame(maxWidth: .infinity, alignment: .center)
VStack(alignment: .leading) {
ZStack(alignment: .topLeading) {
if !validNewProfileName() {
Section {
HStack {
TextField("Group display name", text: $groupProfile.displayName)
.focused($focusDisplayName)
if !validNewProfileName {
Button {
alert = .invalidName(validName: mkValidName(groupProfile.displayName))
} label: {
Image(systemName: "exclamationmark.circle").foregroundColor(.red)
}
} else {
Image(systemName: "exclamationmark.circle").foregroundColor(.clear)
}
profileNameTextEdit("Group display name", $groupProfile.displayName)
.focused($focusDisplayName)
}
.padding(.bottom)
let fullName = groupInfo.groupProfile.fullName
if fullName != "" && fullName != groupProfile.displayName {
profileNameTextEdit("Group full name (optional)", $groupProfile.fullName)
.padding(.bottom)
}
HStack(spacing: 20) {
Button("Cancel") { dismiss() }
Button("Save group profile") { saveProfile() }
.disabled(!canUpdateProfile())
TextField("Group full name (optional)", text: $groupProfile.fullName)
}
} footer: {
Text("Group profile is stored on members' devices, not on the servers.")
}
.frame(maxWidth: .infinity, minHeight: 120, alignment: .leading)
Section {
Button("Reset") {
groupProfile = groupInfo.groupProfile
currentProfileHash = groupProfile.hashValue
}
.disabled(currentProfileHash == groupProfile.hashValue)
Button("Save group profile", action: saveProfile)
.disabled(!canUpdateProfile)
}
}
.padding()
.frame(maxHeight: .infinity, alignment: .top)
.confirmationDialog("Group image", isPresented: $showChooseSource, titleVisibility: .visible) {
Button("Take picture") {
showTakePhoto = true
@@ -95,6 +76,11 @@ struct GroupProfileView: View {
Button("Choose from library") {
showImagePicker = true
}
if UIPasteboard.general.hasImages {
Button("Paste image") {
chosenImage = UIPasteboard.general.image
}
}
}
.fullScreenCover(isPresented: $showTakePhoto) {
ZStack {
@@ -120,8 +106,20 @@ struct GroupProfileView: View {
}
}
.onAppear {
currentProfileHash = groupProfile.hashValue
DispatchQueue.main.asyncAfter(deadline: .now() + 1) {
focusDisplayName = true
withAnimation { focusDisplayName = true }
}
}
.onDisappear {
if canUpdateProfile {
showAlert(
title: NSLocalizedString("Save group profile?", comment: "alert title"),
message: NSLocalizedString("Group profile was changed. If you save it, the updated profile will be sent to group members.", comment: "alert message"),
buttonTitle: NSLocalizedString("Save (and notify members)", comment: "alert button"),
buttonAction: saveProfile,
cancelButton: true
)
}
}
.alert(item: $alert) { a in
@@ -135,30 +133,30 @@ struct GroupProfileView: View {
return createInvalidNameAlert(name, $groupProfile.displayName)
}
}
.contentShape(Rectangle())
.onTapGesture { hideKeyboard() }
.navigationBarTitle("Group profile")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(focusDisplayName ? .inline : .large)
}
private func canUpdateProfile() -> Bool {
groupProfile.displayName.trimmingCharacters(in: .whitespaces) != "" && validNewProfileName()
private var canUpdateProfile: Bool {
currentProfileHash != groupProfile.hashValue &&
groupProfile.displayName.trimmingCharacters(in: .whitespaces) != "" &&
validNewProfileName
}
private func validNewProfileName() -> Bool {
private var validNewProfileName: Bool {
groupProfile.displayName == groupInfo.groupProfile.displayName
|| validDisplayName(groupProfile.displayName.trimmingCharacters(in: .whitespaces))
}
func profileNameTextEdit(_ label: LocalizedStringKey, _ name: Binding<String>) -> some View {
TextField(label, text: name)
.padding(.leading, 32)
}
func saveProfile() {
Task {
do {
groupProfile.displayName = groupProfile.displayName.trimmingCharacters(in: .whitespaces)
groupProfile.fullName = groupProfile.fullName.trimmingCharacters(in: .whitespaces)
let gInfo = try await apiUpdateGroup(groupInfo.groupId, groupProfile)
await MainActor.run {
currentProfileHash = groupProfile.hashValue
groupInfo = gInfo
chatModel.updateGroup(gInfo)
dismiss()
+52 -67
View File
@@ -24,85 +24,70 @@ struct VerifyCodeView: View {
}
private func verifyCodeView(_ code: String) -> some View {
ScrollView {
let splitCode = splitToParts(code, length: 24)
VStack(alignment: .leading) {
Group {
let splitCode = splitToParts(code, length: 24)
return List {
Section {
QRCode(uri: code, small: true)
Text(splitCode)
.multilineTextAlignment(.leading)
.font(.body.monospaced())
.lineLimit(20)
.frame(maxWidth: .infinity, alignment: .center)
} header: {
if connectionVerified {
HStack {
if connectionVerified {
Image(systemName: "checkmark.shield")
.foregroundColor(theme.colors.secondary)
Text("\(displayName) is verified")
} else {
Text("\(displayName) is not verified")
}
Image(systemName: "checkmark.shield").foregroundColor(theme.colors.secondary)
Text("\(displayName) is verified").textCase(.none)
}
.frame(height: 24)
QRCode(uri: code)
.padding(.horizontal)
Text(splitCode)
.multilineTextAlignment(.leading)
.font(.body.monospaced())
.lineLimit(20)
.padding(.bottom, 8)
} else {
Text("\(displayName) is not verified").textCase(.none)
}
.frame(maxWidth: .infinity, alignment: .center)
} footer: {
Text("To verify end-to-end encryption with your contact compare (or scan) the code on your devices.")
.padding(.bottom)
}
Group {
if connectionVerified {
Button {
verifyCode(nil)
} label: {
Label("Clear verification", systemImage: "shield")
}
.padding()
} else {
HStack {
NavigationLink {
ScanCodeView(connectionVerified: $connectionVerified, verify: verify)
.navigationBarTitleDisplayMode(.large)
.navigationTitle("Scan code")
.modifier(ThemedBackground())
} label: {
Label("Scan code", systemImage: "qrcode")
}
.padding()
Button {
verifyCode(code) { verified in
if !verified { showCodeError = true }
}
} label: {
Label("Mark verified", systemImage: "checkmark.shield")
}
.padding()
.alert(isPresented: $showCodeError) {
Alert(title: Text("Incorrect security code!"))
}
}
}
}
.frame(maxWidth: .infinity, alignment: .center)
}
.padding()
.frame(maxWidth: .infinity, maxHeight: .infinity, alignment: .top)
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Section {
if connectionVerified {
Button {
showShareSheet(items: [splitCode])
verifyCode(nil)
} label: {
Image(systemName: "square.and.arrow.up")
Label("Clear verification", systemImage: "shield")
}
} else {
NavigationLink {
ScanCodeView(connectionVerified: $connectionVerified, verify: verify)
.navigationBarTitleDisplayMode(.large)
.navigationTitle("Scan code")
.modifier(ThemedBackground())
} label: {
Label("Scan code", systemImage: "qrcode")
}
Button {
verifyCode(code) { verified in
if !verified { showCodeError = true }
}
} label: {
Label("Mark verified", systemImage: "checkmark.shield")
}
.alert(isPresented: $showCodeError) {
Alert(title: Text("Incorrect security code!"))
}
}
}
.onChange(of: connectionVerified) { _ in
if connectionVerified { dismiss() }
}
.toolbar {
ToolbarItem(placement: .navigationBarTrailing) {
Button {
showShareSheet(items: [splitCode])
} label: {
Image(systemName: "square.and.arrow.up")
}
}
}
.onChange(of: connectionVerified) { _ in
if connectionVerified { dismiss() }
}
}
private func verifyCode(_ code: String?, _ cb: ((Bool) -> Void)? = nil) {
@@ -21,7 +21,7 @@ enum DatabaseAlert: Identifiable {
case deleteLegacyDatabase
case deleteFilesAndMedia
case setChatItemTTL(ttl: ChatItemTTL)
case error(title: LocalizedStringKey, error: String = "")
case error(title: String, error: String = "")
var id: String {
switch self {
@@ -456,7 +456,7 @@ struct DatabaseView: View {
}
} catch let error {
await MainActor.run {
alert = .error(title: "Error exporting chat database", error: responseError(error))
alert = .error(title: NSLocalizedString("Error exporting chat database", comment: "alert title"), error: responseError(error))
progressIndicator = false
}
}
@@ -492,10 +492,10 @@ struct DatabaseView: View {
return migration
}
} catch let error {
await operationEnded(.error(title: "Error importing chat database", error: responseError(error)), progressIndicator, alert)
await operationEnded(.error(title: NSLocalizedString("Error importing chat database", comment: "alert title"), error: responseError(error)), progressIndicator, alert)
}
} catch let error {
await operationEnded(.error(title: "Error deleting chat database", error: responseError(error)), progressIndicator, alert)
await operationEnded(.error(title: NSLocalizedString("Error deleting chat database", comment: "alert title"), error: responseError(error)), progressIndicator, alert)
}
} else {
showAlert("Error accessing database file")
@@ -513,7 +513,7 @@ struct DatabaseView: View {
await DatabaseView.operationEnded(.chatDeleted, $progressIndicator, $alert)
return true
} catch let error {
await DatabaseView.operationEnded(.error(title: "Error deleting database", error: responseError(error)), $progressIndicator, $alert)
await DatabaseView.operationEnded(.error(title: NSLocalizedString("Error deleting database", comment: "alert title"), error: responseError(error)), $progressIndicator, $alert)
return false
}
}
@@ -522,7 +522,7 @@ struct DatabaseView: View {
if removeLegacyDatabaseAndFiles() {
legacyDatabase = false
} else {
alert = .error(title: "Error deleting old database")
alert = .error(title: NSLocalizedString("Error deleting old database", comment: "alert title"))
}
}
@@ -546,7 +546,7 @@ struct DatabaseView: View {
let (title, message) = chatDeletedAlertText()
showAlert(title, message: message, actions: { [okAlertActionWaiting] })
} else if case let .error(title, error) = dbAlert {
showAlert("\(title)", message: error, actions: { [okAlertActionWaiting] })
showAlert(title, message: error, actions: { [okAlertActionWaiting] })
} else {
alert.wrappedValue = dbAlert
cont.resume()
@@ -567,7 +567,7 @@ struct DatabaseView: View {
}
} catch {
await MainActor.run {
alert = .error(title: "Error changing setting", error: responseError(error))
alert = .error(title: NSLocalizedString("Error changing setting", comment: "alert title"), error: responseError(error))
chatItemTTL = currentChatItemTTL
afterSetCiTTL()
}
@@ -125,7 +125,7 @@ struct NewChatSheet: View {
}
NavigationLink {
AddGroupView()
.navigationTitle("Create secret group")
.navigationTitle("Create group")
.modifier(ThemedBackground(grouped: true))
.navigationBarTitleDisplayMode(.large)
} label: {
+22 -13
View File
@@ -12,11 +12,12 @@ import SimpleXChat
struct MutableQRCode: View {
@Binding var uri: String
var small: Bool = false
var withLogo: Bool = true
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
var body: some View {
QRCode(uri: uri, withLogo: withLogo, tintColor: tintColor)
QRCode(uri: uri, small: small, withLogo: withLogo, tintColor: tintColor)
.id("simplex-qrcode-view-for-\(uri)")
}
}
@@ -27,7 +28,7 @@ struct SimpleXCreatedLinkQRCode: View {
var onShare: (() -> Void)? = nil
var body: some View {
QRCode(uri: link.simplexChatUri(short: short), onShare: onShare)
QRCode(uri: link.simplexChatUri(short: short), small: short && link.connShortLink != nil, onShare: onShare)
}
}
@@ -38,50 +39,57 @@ struct SimpleXLinkQRCode: View {
var onShare: (() -> Void)? = nil
var body: some View {
QRCode(uri: simplexChatLink(uri), withLogo: withLogo, tintColor: tintColor, onShare: onShare)
QRCode(uri: simplexChatLink(uri), small: uri.count < 200, withLogo: withLogo, tintColor: tintColor, onShare: onShare)
}
}
private let smallQRRatio: CGFloat = 0.63
struct QRCode: View {
let uri: String
var small: Bool = false
var withLogo: Bool = true
var tintColor = UIColor(red: 0.023, green: 0.176, blue: 0.337, alpha: 1)
var onShare: (() -> Void)? = nil
@State private var image: UIImage? = nil
@State private var makeScreenshotFunc: () -> Void = {}
@State private var width: CGFloat = .infinity
var body: some View {
ZStack {
if let image = image {
qrCodeImage(image)
GeometryReader { geo in
qrCodeImage(image).frame(width: width, height: width)
GeometryReader { g in
let w = g.size.width * (small ? smallQRRatio : 1)
let l = w * (small ? 0.195 : 0.16)
let m = w * 0.005
ZStack {
if withLogo {
let w = geo.size.width
Image("icon-light")
.resizable()
.scaledToFit()
.frame(width: w * 0.16, height: w * 0.16)
.frame(width: w * 0.165, height: w * 0.165)
.frame(width: l, height: l)
.frame(width: l + m, height: l + m)
.background(.white)
.clipShape(Circle())
}
}
.onAppear {
width = w
makeScreenshotFunc = {
let size = CGSizeMake(1024 / UIScreen.main.scale, 1024 / UIScreen.main.scale)
showShareSheet(items: [makeScreenshot(geo.frame(in: .local).origin, size)])
showShareSheet(items: [makeScreenshot(g.frame(in: .local).origin, size)])
onShare?()
}
}
.frame(width: geo.size.width, height: geo.size.height)
.frame(width: g.size.width, height: g.size.height)
}
} else {
Color.clear.aspectRatio(1, contentMode: .fit)
Color.clear.aspectRatio(small ? 1 / smallQRRatio : 1, contentMode: .fit)
}
}
.onTapGesture(perform: makeScreenshotFunc)
.task { image = await generateImage(uri, tintColor: tintColor) }
.task { image = await generateImage(uri, tintColor: tintColor, errorLevel: small ? "M" : "L") }
.frame(maxWidth: .infinity, maxHeight: .infinity)
}
}
@@ -94,10 +102,11 @@ private func qrCodeImage(_ image: UIImage) -> some View {
.textSelection(.enabled)
}
private func generateImage(_ uri: String, tintColor: UIColor) async -> UIImage? {
private func generateImage(_ uri: String, tintColor: UIColor, errorLevel: String) async -> UIImage? {
let context = CIContext()
let filter = CIFilter.qrCodeGenerator()
filter.message = Data(uri.utf8)
filter.correctionLevel = errorLevel
if let outputImage = filter.outputImage,
let cgImage = context.createCGImage(outputImage, from: outputImage.extent) {
return UIImage(cgImage: cgImage).replaceColor(UIColor.black, tintColor)
@@ -65,7 +65,7 @@ struct NewServerView: View {
useServerSection(valid)
if valid {
Section(header: Text("Add to another device").foregroundColor(theme.colors.secondary)) {
MutableQRCode(uri: $serverToEdit.server)
MutableQRCode(uri: $serverToEdit.server, small: true)
.listRowInsets(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
}
}
@@ -110,7 +110,7 @@ struct ProtocolServerView: View {
useServerSection(valid)
if valid {
Section(header: Text("Add to another device").foregroundColor(theme.colors.secondary)) {
MutableQRCode(uri: $serverToEdit.server)
MutableQRCode(uri: $serverToEdit.server, small: true)
.listRowInsets(EdgeInsets(top: 12, leading: 12, bottom: 12, trailing: 12))
}
}
@@ -153,6 +153,9 @@ struct UserAddressView: View {
}
}
addressSettingsButton(userAddress)
if (userAddress.connLinkContact.connShortLink == nil && UserDefaults.standard.bool(forKey: DEFAULT_PRIVACY_SHORT_LINKS)) {
addShortLinkButton()
}
} header: {
ToggleShortLinkHeader(text: Text("For social media"), link: userAddress.connLinkContact, short: $showShortLink)
} footer: {
@@ -209,6 +212,32 @@ struct UserAddressView: View {
}
}
private func addShortLinkButton() -> some View {
Button {
addShortLink()
} label: {
Label("Add short link", systemImage: "plus")
}
}
private func addShortLink() {
progressIndicator = true
Task {
do {
let userAddress = try await apiAddMyAddressShortLink()
await MainActor.run {
chatModel.userAddress = userAddress
}
await MainActor.run { progressIndicator = false }
} catch let error {
logger.error("apiAddMyAddressShortLink: \(responseError(error))")
let a = getErrorAlert(error, "Error creating address")
alert = .error(title: a.title, error: a.message)
await MainActor.run { progressIndicator = false }
}
}
}
private func createOneTimeLinkButton() -> some View {
NavigationLink {
NewChatView(selection: .invite)
@@ -25,28 +25,8 @@ struct UserProfile: View {
var body: some View {
List {
Group {
if profile.image != nil {
ZStack(alignment: .bottomTrailing) {
ZStack(alignment: .topTrailing) {
profileImageView(profile.image)
.onTapGesture { showChooseSource = true }
overlayButton("multiply", edge: .top) { profile.image = nil }
}
overlayButton("camera", edge: .bottom) { showChooseSource = true }
}
} else {
ZStack(alignment: .center) {
profileImageView(profile.image)
editImageButton { showChooseSource = true }
}
}
}
.frame(maxWidth: .infinity, alignment: .center)
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.padding(.top)
.contentShape(Rectangle())
EditProfileImage(profileImage: $profile.image, showChooseSource: $showChooseSource)
.padding(.top)
Section {
HStack {
@@ -133,25 +113,6 @@ struct UserProfile: View {
.alert(item: $alert) { a in userProfileAlert(a, $profile.displayName) }
}
private func overlayButton(
_ systemName: String,
edge: Edge.Set,
action: @escaping () -> Void
) -> some View {
Image(systemName: systemName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 12)
.foregroundColor(theme.colors.primary)
.padding(6)
.frame(width: 36, height: 36, alignment: .center)
.background(radius >= 20 ? Color.clear : theme.colors.background.opacity(0.5))
.clipShape(Circle())
.contentShape(Circle())
.padding([.trailing, edge], -12)
.onTapGesture(perform: action)
}
private func showFullName(_ user: User) -> Bool {
user.profile.fullName != "" && user.profile.fullName != user.profile.displayName
}
@@ -189,8 +150,54 @@ struct UserProfile: View {
}
}
func profileImageView(_ imageStr: String?) -> some View {
ProfileImage(imageStr: imageStr, size: 192)
struct EditProfileImage: View {
@EnvironmentObject var theme: AppTheme
@AppStorage(DEFAULT_PROFILE_IMAGE_CORNER_RADIUS) private var radius = defaultProfileImageCorner
@Binding var profileImage: String?
@Binding var showChooseSource: Bool
var body: some View {
Group {
if profileImage != nil {
ZStack(alignment: .bottomTrailing) {
ZStack(alignment: .topTrailing) {
ProfileImage(imageStr: profileImage, size: 160)
.onTapGesture { showChooseSource = true }
overlayButton("multiply", edge: .top) { profileImage = nil }
}
overlayButton("camera", edge: .bottom) { showChooseSource = true }
}
} else {
ZStack(alignment: .center) {
ProfileImage(imageStr: profileImage, size: 160)
editImageButton { showChooseSource = true }
}
}
}
.frame(maxWidth: .infinity, alignment: .center)
.listRowBackground(Color.clear)
.listRowInsets(EdgeInsets(top: 0, leading: 0, bottom: 0, trailing: 0))
.contentShape(Rectangle())
}
private func overlayButton(
_ systemName: String,
edge: Edge.Set,
action: @escaping () -> Void
) -> some View {
Image(systemName: systemName)
.resizable()
.aspectRatio(contentMode: .fit)
.frame(height: 12)
.foregroundColor(theme.colors.primary)
.padding(6)
.frame(width: 36, height: 36, alignment: .center)
.background(radius >= 20 ? Color.clear : theme.colors.background.opacity(0.5))
.clipShape(Circle())
.contentShape(Circle())
.padding([.trailing, edge], -12)
.onTapGesture(perform: action)
}
}
func editImageButton(action: @escaping () -> Void) -> some View {
@@ -567,10 +567,12 @@ swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<target>Als Mitglied übernehmen</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<target>Als Beobachter übernehmen</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
@@ -596,6 +598,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<target>Mitglied übernehmen</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
@@ -1596,10 +1599,12 @@ set passcode view</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<target>Chat mit Administratoren</target>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<target>Chat mit einem Mitglied</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
@@ -1609,6 +1614,7 @@ set passcode view</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<target>Chats mit Mitgliedern</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
@@ -2428,6 +2434,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<target>Chat mit dem Mitglied löschen?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
@@ -3173,6 +3180,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<target>Fehler beim Übernehmen des Mitglieds</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
@@ -3272,6 +3280,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<target>Fehler beim Löschen des Chats mit dem Mitglied</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
@@ -4768,6 +4777,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<target>Aufnahme von Mitgliedern</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
@@ -4807,6 +4817,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<target>Ein Mitglied wird der Gruppe beitreten. Übernehmen?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
@@ -5231,6 +5242,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<target>Ein neues Mitglied will der Gruppe beitreten.</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
@@ -5275,6 +5287,7 @@ Das ist Ihr Link für die Gruppe %@!</target>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<target>Keine Chats mit Mitgliedern</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
@@ -5884,6 +5897,7 @@ Fehler: %@</target>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<target>Bitte warten Sie auf die Überprüfung Ihrer Anfrage durch die Gruppen-Moderatoren, um der Gruppe beitreten zu können.</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
@@ -6341,6 +6355,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<target>Mitglied ablehnen?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
@@ -6455,6 +6470,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<target>Meldung wurde an die Moderatoren gesendet</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
@@ -6574,10 +6590,12 @@ swipe action</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<target>Überprüfung der Mitglieder</target>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<target>Überprüfung der Mitglieder vor der Aufnahme ("Anklopfen").</target>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
@@ -6638,6 +6656,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<target>Speichern der Aufnahme-Einstellungen?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
@@ -7157,6 +7176,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<target>Aufnahme von Mitgliedern festlegen</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
@@ -8923,6 +8943,7 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<target>Sie können Ihre Meldungen im Chat mit den Administratoren sehen.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
@@ -9229,6 +9250,7 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<target>%@ übernommen</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
@@ -9243,6 +9265,7 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<target>hat Sie übernommen</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
@@ -9267,6 +9290,7 @@ Verbindungsanfrage wiederholen?</target>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<target>alle</target>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
@@ -9357,6 +9381,7 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<target>Es können keine Nachrichten gesendet werden</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
@@ -9466,10 +9491,12 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<target>Kontakt gelöscht</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<target>Kontakt deaktiviert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
@@ -9484,6 +9511,7 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<target>Kontakt nicht bereit</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
@@ -9659,6 +9687,7 @@ pref value</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<target>Gruppe wird gelöscht</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
@@ -9788,6 +9817,7 @@ pref value</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<target>Das Mitglied hat eine alte App-Version</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
@@ -9857,6 +9887,7 @@ pref value</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<target>Nicht synchronisiert</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
@@ -9924,6 +9955,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<target>Ausstehende Überprüfung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
@@ -9968,6 +10000,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<target>Von der Gruppe entfernt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
@@ -9982,6 +10015,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<target>Beitrittsanfrage abgelehnt</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
@@ -9991,10 +10025,12 @@ time to disappear</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<target>Überprüfung</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<target>Von Administratoren überprüft</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
@@ -10188,6 +10224,7 @@ Zuletzt empfangene Nachricht: %2$@</target>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<target>Sie haben dieses Mitglied übernommen</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
@@ -567,10 +567,12 @@ swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<target>Accetta come membro</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<target>Accetta come osservatore</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
@@ -596,6 +598,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<target>Accetta membro</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
@@ -1596,10 +1599,12 @@ set passcode view</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<target>Chat con amministratori</target>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<target>Chatta con il membro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
@@ -1609,6 +1614,7 @@ set passcode view</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<target>Chat con membri</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
@@ -2428,6 +2434,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<target>Eliminare la chat con il membro?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
@@ -3173,6 +3180,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<target>Errore di accettazione del membro</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error adding member(s)" xml:space="preserve">
@@ -3272,6 +3280,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<target>Errore di eliminazione della chat con il membro</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
@@ -4768,6 +4777,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<target>Ammissione del membro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
@@ -4807,6 +4817,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<target>Il membro entrerà nel gruppo, accettarlo?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
@@ -5231,6 +5242,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<target>Un nuovo membro vuole entrare nel gruppo.</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
@@ -5275,6 +5287,7 @@ Questo è il tuo link per il gruppo %@!</target>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<target>Nessuna chat con membri</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
@@ -5884,6 +5897,7 @@ Errore: %@</target>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<target>Attendi che i moderatori del gruppo revisionino la tua richiesta di entrare nel gruppo.</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
@@ -6341,6 +6355,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<target>Rifiutare il membro?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
@@ -6455,6 +6470,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<target>Segnalazione inviata ai moderatori</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
@@ -6574,10 +6590,12 @@ swipe action</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<target>Revisiona i membri</target>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<target>Revisiona i membri prima di ammetterli ("bussare").</target>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
@@ -6638,6 +6656,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<target>Salvare le impostazioni di ammissione?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
@@ -7157,6 +7176,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<target>Imposta l'ammissione del membro</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
@@ -8923,6 +8943,7 @@ Ripetere la richiesta di ingresso?</target>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<target>Puoi vedere le tue segnalazioni nella chat con gli amministratori.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
@@ -9229,6 +9250,7 @@ Ripetere la richiesta di connessione?</target>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<target>%@ accettato</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
@@ -9243,6 +9265,7 @@ Ripetere la richiesta di connessione?</target>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<target>ti ha accettato/a</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
@@ -9267,6 +9290,7 @@ Ripetere la richiesta di connessione?</target>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<target>tutti</target>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
@@ -9357,6 +9381,7 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<target>impossibile inviare messaggi</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
@@ -9466,10 +9491,12 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<target>contatto eliminato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<target>contatto disattivato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
@@ -9484,6 +9511,7 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<target>contatto non pronto</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
@@ -9659,6 +9687,7 @@ pref value</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<target>il gruppo è eliminato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
@@ -9788,6 +9817,7 @@ pref value</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<target>il membro ha una versione vecchia</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
@@ -9857,6 +9887,7 @@ pref value</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<target>non sincronizzato</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
@@ -9924,6 +9955,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<target>in attesa di revisione</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
@@ -9968,6 +10000,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<target>rimosso dal gruppo</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
@@ -9982,6 +10015,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<target>richiesta di entrare rifiutata</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
@@ -9991,10 +10025,12 @@ time to disappear</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<target>revisiona</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<target>revisionato dagli amministratori</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
@@ -10188,6 +10224,7 @@ ultimo msg ricevuto: %2$@</target>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<target>hai accettato questo membro</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
@@ -167,7 +167,7 @@
</trans-unit>
<trans-unit id="%d hours" xml:space="preserve">
<source>%d hours</source>
<target>%d ч.</target>
<target>%d час.</target>
<note>time interval</note>
</trans-unit>
<trans-unit id="%d messages not forwarded" xml:space="preserve">
@@ -567,10 +567,12 @@ swipe action</note>
</trans-unit>
<trans-unit id="Accept as member" xml:space="preserve">
<source>Accept as member</source>
<target>Принять в группу</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept as observer" xml:space="preserve">
<source>Accept as observer</source>
<target>Принять как читателя</target>
<note>alert action</note>
</trans-unit>
<trans-unit id="Accept conditions" xml:space="preserve">
@@ -596,6 +598,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Accept member" xml:space="preserve">
<source>Accept member</source>
<target>Принять члена</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Accepted conditions" xml:space="preserve">
@@ -810,6 +813,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="All servers" xml:space="preserve">
<source>All servers</source>
<target>Все серверы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="All your contacts will remain connected." xml:space="preserve">
@@ -1595,10 +1599,12 @@ set passcode view</note>
</trans-unit>
<trans-unit id="Chat with admins" xml:space="preserve">
<source>Chat with admins</source>
<target>Чат с админами</target>
<note>chat toolbar</note>
</trans-unit>
<trans-unit id="Chat with member" xml:space="preserve">
<source>Chat with member</source>
<target>Чат с членом группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Chats" xml:space="preserve">
@@ -1608,6 +1614,7 @@ set passcode view</note>
</trans-unit>
<trans-unit id="Chats with members" xml:space="preserve">
<source>Chats with members</source>
<target>Чаты с членами группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Check messages every 20 min." xml:space="preserve">
@@ -2427,6 +2434,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Delete chat with member?" xml:space="preserve">
<source>Delete chat with member?</source>
<target>Удалить чат с членом группы?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Delete chat?" xml:space="preserve">
@@ -2716,6 +2724,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Direct messages between members are prohibited." xml:space="preserve">
<source>Direct messages between members are prohibited.</source>
<target>Прямые сообщения между членами запрещены.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Disable (keep overrides)" xml:space="preserve">
@@ -2820,6 +2829,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Do not send history to new members." xml:space="preserve">
<source>Do not send history to new members.</source>
<target>Не отправлять историю новым членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Do not use credentials with proxy." xml:space="preserve">
@@ -2945,6 +2955,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Enable Flux in Network &amp; servers settings for better metadata privacy." xml:space="preserve">
<source>Enable Flux in Network &amp; servers settings for better metadata privacy.</source>
<target>Включите Flux в настройках Сеть и серверы для лучшей конфиденциальности метаданных.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Enable SimpleX Lock" xml:space="preserve">
@@ -3169,10 +3180,12 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error accepting member" xml:space="preserve">
<source>Error accepting member</source>
<target>Ошибка вступления члена группы</target>
<note>alert title</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 adding server" xml:space="preserve">
@@ -3237,6 +3250,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error creating member contact" xml:space="preserve">
<source>Error creating member contact</source>
<target>Ошибка при создании контакта</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error creating message" xml:space="preserve">
@@ -3266,6 +3280,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error deleting chat with member" xml:space="preserve">
<source>Error deleting chat with member</source>
<target>Ошибка при удалении чата с членом группы</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error deleting chat!" xml:space="preserve">
@@ -3375,6 +3390,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error removing member" xml:space="preserve">
<source>Error removing member</source>
<target>Ошибка при удалении члена группы</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Error reordering lists" xml:space="preserve">
@@ -3439,6 +3455,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Error sending member contact invitation" xml:space="preserve">
<source>Error sending member contact invitation</source>
<target>Ошибка при отправке приглашения члену</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Error sending message" xml:space="preserve">
@@ -3774,6 +3791,7 @@ snd error text</note>
</trans-unit>
<trans-unit id="Fix not supported by group member" xml:space="preserve">
<source>Fix not supported by group member</source>
<target>Починка не поддерживается членом группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="For all moderators" xml:space="preserve">
@@ -3907,6 +3925,7 @@ Error: %2$@</source>
</trans-unit>
<trans-unit id="Fully decentralized visible only to members." xml:space="preserve">
<source>Fully decentralized visible only to members.</source>
<target>Группа полностью децентрализована – она видна только членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Fully re-implemented - work in background!" xml:space="preserve">
@@ -4016,6 +4035,7 @@ Error: %2$@</source>
</trans-unit>
<trans-unit id="Group profile is stored on members' devices, not on the servers." xml:space="preserve">
<source>Group profile is stored on members' devices, not on the servers.</source>
<target>Профиль группы хранится на устройствах членов, а не на серверах.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group welcome message" xml:space="preserve">
@@ -4025,6 +4045,7 @@ Error: %2$@</source>
</trans-unit>
<trans-unit id="Group will be deleted for all members - this cannot be undone!" xml:space="preserve">
<source>Group will be deleted for all members - this cannot be undone!</source>
<target>Группа будет удалена для всех членов - это действие нельзя отменить!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Group will be deleted for you - this cannot be undone!" xml:space="preserve">
@@ -4089,6 +4110,7 @@ Error: %2$@</source>
</trans-unit>
<trans-unit id="History is not sent to new members." xml:space="preserve">
<source>History is not sent to new members.</source>
<target>История не отправляется новым членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="How SimpleX works" xml:space="preserve">
@@ -4436,6 +4458,7 @@ More improvements are coming soon!</source>
</trans-unit>
<trans-unit id="Invite members" xml:space="preserve">
<source>Invite members</source>
<target>Пригласить членов группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Invite to chat" xml:space="preserve">
@@ -4748,14 +4771,17 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Member" xml:space="preserve">
<source>Member</source>
<target>Член группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member admission" xml:space="preserve">
<source>Member admission</source>
<target>Приём членов в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member inactive" xml:space="preserve">
<source>Member inactive</source>
<target>Член неактивен</target>
<note>item status text</note>
</trans-unit>
<trans-unit id="Member reports" xml:space="preserve">
@@ -4770,58 +4796,72 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. All group members will be notified." xml:space="preserve">
<source>Member role will be changed to "%@". All group members will be notified.</source>
<target>Роль члена будет изменена на "%@". Все члены группы получат уведомление.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member role will be changed to &quot;%@&quot;. The member will receive a new invitation." xml:space="preserve">
<source>Member role will be changed to "%@". The member will receive a new invitation.</source>
<target>Роль члена будет изменена на "%@". Будет отправлено новое приглашение.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will be removed from chat - this cannot be undone!" xml:space="preserve">
<source>Member will be removed from chat - this cannot be undone!</source>
<target>Член будет удален из разговора - это действие нельзя отменить!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will be removed from group - this cannot be undone!" xml:space="preserve">
<source>Member will be removed from group - this cannot be undone!</source>
<target>Член группы будет удален - это действие нельзя отменить!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Member will join the group, accept member?" xml:space="preserve">
<source>Member will join the group, accept member?</source>
<target>Участник хочет присоединиться к группе. Принять?</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="Members can add message reactions." xml:space="preserve">
<source>Members can add message reactions.</source>
<target>Члены могут добавлять реакции на сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can irreversibly delete sent messages. (24 hours)" xml:space="preserve">
<source>Members can irreversibly delete sent messages. (24 hours)</source>
<target>Члены могут необратимо удалять отправленные сообщения. (24 часа)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can report messsages to moderators." xml:space="preserve">
<source>Members can report messsages to moderators.</source>
<target>Члены группы могут пожаловаться модераторам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send SimpleX links." xml:space="preserve">
<source>Members can send SimpleX links.</source>
<target>Члены могут отправлять ссылки SimpleX.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send direct messages." xml:space="preserve">
<source>Members can send direct messages.</source>
<target>Члены могут посылать прямые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send disappearing messages." xml:space="preserve">
<source>Members can send disappearing messages.</source>
<target>Члены могут посылать исчезающие сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send files and media." xml:space="preserve">
<source>Members can send files and media.</source>
<target>Члены могут слать файлы и медиа.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Members can send voice messages." xml:space="preserve">
<source>Members can send voice messages.</source>
<target>Члены могут отправлять голосовые сообщения.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Mention members 👋" xml:space="preserve">
<source>Mention members 👋</source>
<target>Упоминайте участников 👋</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Menus" xml:space="preserve">
@@ -4856,6 +4896,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="Message may be delivered later if member becomes active." xml:space="preserve">
<source>Message may be delivered later if member becomes active.</source>
<target>Сообщение может быть доставлено позже, если член группы станет активным.</target>
<note>item status description</note>
</trans-unit>
<trans-unit id="Message queue info" xml:space="preserve">
@@ -5195,10 +5236,12 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="New member role" xml:space="preserve">
<source>New member role</source>
<target>Роль члена группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="New member wants to join the group." xml:space="preserve">
<source>New member wants to join the group.</source>
<target>Новый участник хочет присоединиться к группе.</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="New message" xml:space="preserve">
@@ -5243,6 +5286,7 @@ This is your link for group %@!</source>
</trans-unit>
<trans-unit id="No chats with members" xml:space="preserve">
<source>No chats with members</source>
<target>Нет чатов с членами группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="No contacts selected" xml:space="preserve">
@@ -5419,6 +5463,9 @@ This is your link for group %@!</source>
<source>Now admins can:
- delete members' messages.
- disable members ("observer" role)</source>
<target>Теперь админы могут:
- удалять сообщения членов.
- приостанавливать членов (роль наблюдатель)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="OK" xml:space="preserve">
@@ -5593,6 +5640,7 @@ Requires compatible VPN.</source>
</trans-unit>
<trans-unit id="Open link?" xml:space="preserve">
<source>Open link?</source>
<target>Открыть ссылку?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Open migration to another device" xml:space="preserve">
@@ -5709,6 +5757,7 @@ Requires compatible VPN.</source>
</trans-unit>
<trans-unit id="Past member %@" xml:space="preserve">
<source>Past member %@</source>
<target>Бывший член %@</target>
<note>past/unknown group member</note>
</trans-unit>
<trans-unit id="Paste desktop address" xml:space="preserve">
@@ -5847,6 +5896,7 @@ Error: %@</source>
</trans-unit>
<trans-unit id="Please wait for group moderators to review your request to join the group." xml:space="preserve">
<source>Please wait for group moderators to review your request to join the group.</source>
<target>Пожалуйста, подождите, пока модераторы группы рассмотрят ваш запрос на вступление.</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="Please wait for token activation to complete." xml:space="preserve">
@@ -6021,6 +6071,7 @@ Error: %@</source>
</trans-unit>
<trans-unit id="Prohibit sending direct messages to members." xml:space="preserve">
<source>Prohibit sending direct messages to members.</source>
<target>Запретить посылать прямые сообщения членам группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Prohibit sending disappearing messages." xml:space="preserve">
@@ -6303,6 +6354,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Reject member?" xml:space="preserve">
<source>Reject member?</source>
<target>Отклонить участника?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Relay server is only used if necessary. Another party can observe your IP address." xml:space="preserve">
@@ -6332,10 +6384,12 @@ swipe action</note>
</trans-unit>
<trans-unit id="Remove member" xml:space="preserve">
<source>Remove member</source>
<target>Удалить члена группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove member?" xml:space="preserve">
<source>Remove member?</source>
<target>Удалить члена группы?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Remove passphrase from keychain?" xml:space="preserve">
@@ -6415,6 +6469,7 @@ swipe action</note>
</trans-unit>
<trans-unit id="Report sent to moderators" xml:space="preserve">
<source>Report sent to moderators</source>
<target>Жалоба отправлена модераторам</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Report spam: only group moderators will see it." xml:space="preserve">
@@ -6534,10 +6589,12 @@ swipe action</note>
</trans-unit>
<trans-unit id="Review members" xml:space="preserve">
<source>Review members</source>
<target>Одобрять членов</target>
<note>admission stage</note>
</trans-unit>
<trans-unit id="Review members before admitting (&quot;knocking&quot;)." xml:space="preserve">
<source>Review members before admitting ("knocking").</source>
<target>Одобрять членов для вступления в группу.</target>
<note>admission stage description</note>
</trans-unit>
<trans-unit id="Revoke" xml:space="preserve">
@@ -6598,6 +6655,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Save admission settings?" xml:space="preserve">
<source>Save admission settings?</source>
<target>Сохранить настройки вступления?</target>
<note>alert title</note>
</trans-unit>
<trans-unit id="Save and notify contact" xml:space="preserve">
@@ -6607,6 +6665,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Save and notify group members" xml:space="preserve">
<source>Save and notify group members</source>
<target>Сохранить и уведомить членов группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Save and reconnect" xml:space="preserve">
@@ -6891,6 +6950,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Send up to 100 last messages to new members." xml:space="preserve">
<source>Send up to 100 last messages to new members.</source>
<target>Отправить до 100 последних сообщений новым членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Sender cancelled file transfer." xml:space="preserve">
@@ -7115,6 +7175,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Set member admission" xml:space="preserve">
<source>Set member admission</source>
<target>Приём членов в группу</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set message expiration in chats." xml:space="preserve">
@@ -7139,6 +7200,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Set the message shown to new members!" xml:space="preserve">
<source>Set the message shown to new members!</source>
<target>Установить сообщение для новых членов группы!</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Set timeouts for proxy/VPN" xml:space="preserve">
@@ -7229,6 +7291,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="Short link" xml:space="preserve">
<source>Short link</source>
<target>Короткая ссылка</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Show QR code" xml:space="preserve">
@@ -7333,6 +7396,7 @@ chat item action</note>
</trans-unit>
<trans-unit id="SimpleX channel link" xml:space="preserve">
<source>SimpleX channel link</source>
<target>SimpleX ссылка канала</target>
<note>simplex link type</note>
</trans-unit>
<trans-unit id="SimpleX contact address" xml:space="preserve">
@@ -7777,18 +7841,22 @@ It can happen because of some bug or when the connection is compromised.</source
</trans-unit>
<trans-unit id="The message will be deleted for all members." xml:space="preserve">
<source>The message will be deleted for all members.</source>
<target>Сообщение будет удалено для всех членов группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The message will be marked as moderated for all members." xml:space="preserve">
<source>The message will be marked as moderated for all members.</source>
<target>Сообщение будет помечено как удаленное для всех членов группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The messages will be deleted for all members." xml:space="preserve">
<source>The messages will be deleted for all members.</source>
<target>Сообщения будут удалены для всех членов группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The messages will be marked as moderated for all members." xml:space="preserve">
<source>The messages will be marked as moderated for all members.</source>
<target>Сообщения будут помечены как удаленные для всех членов группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="The old database was not removed during the migration, it can be deleted." xml:space="preserve">
@@ -7898,6 +7966,7 @@ It can happen because of some bug or when the connection is compromised.</source
</trans-unit>
<trans-unit id="This group has over %lld members, delivery receipts are not sent." xml:space="preserve">
<source>This group has over %lld members, delivery receipts are not sent.</source>
<target>В этой группе более %lld членов, отчёты о доставке не отправляются.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This group no longer exists." xml:space="preserve">
@@ -7917,6 +7986,7 @@ It can happen because of some bug or when the connection is compromised.</source
</trans-unit>
<trans-unit id="This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." xml:space="preserve">
<source>This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link.</source>
<target>Эта ссылка требует новую версию. Обновите приложение или попросите Ваш контакт прислать совместимую ссылку.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="This link was used with another mobile device, please create a new link on the desktop." xml:space="preserve">
@@ -8108,14 +8178,17 @@ You will be prompted to complete authentication before this feature is enabled.<
</trans-unit>
<trans-unit id="Unblock member" xml:space="preserve">
<source>Unblock member</source>
<target>Разблокировать члена группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unblock member for all?" xml:space="preserve">
<source>Unblock member for all?</source>
<target>Разблокировать члена для всех?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Unblock member?" xml:space="preserve">
<source>Unblock member?</source>
<target>Разблокировать члена группы?</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Undelivered messages" xml:space="preserve">
@@ -8217,10 +8290,12 @@ To connect, please ask your contact to create another connection link and check
</trans-unit>
<trans-unit id="Unsupported connection link" xml:space="preserve">
<source>Unsupported connection link</source>
<target>Ссылка не поддерживается</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Up to 100 last messages are sent to new members." xml:space="preserve">
<source>Up to 100 last messages are sent to new members.</source>
<target>До 100 последних сообщений отправляются новым членам.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Update" xml:space="preserve">
@@ -8315,6 +8390,7 @@ To connect, please ask your contact to create another connection link and check
</trans-unit>
<trans-unit id="Use TCP port 443 for preset servers only." xml:space="preserve">
<source>Use TCP port 443 for preset servers only.</source>
<target>Использовать TCP-порт 443 только для серверов по умолчанию.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use chat" xml:space="preserve">
@@ -8384,6 +8460,7 @@ To connect, please ask your contact to create another connection link and check
</trans-unit>
<trans-unit id="Use short links (BETA)" xml:space="preserve">
<source>Use short links (BETA)</source>
<target>Короткие ссылки (БЕТА)</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="Use the app while in the call." xml:space="preserve">
@@ -8830,6 +8907,7 @@ Repeat join request?</source>
</trans-unit>
<trans-unit id="You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it." xml:space="preserve">
<source>You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it.</source>
<target>Вы можете поделиться ссылкой или QR кодом - через них можно присоединиться к группе. Вы сможете удалить ссылку, сохранив членов группы, которые через нее соединились.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You can share this address with your contacts to let them connect with **%@**." xml:space="preserve">
@@ -8864,6 +8942,7 @@ Repeat join request?</source>
</trans-unit>
<trans-unit id="You can view your reports in Chat with admins." xml:space="preserve">
<source>You can view your reports in Chat with admins.</source>
<target>Вы можете найти Ваши жалобы в Чате с админами.</target>
<note>alert message</note>
</trans-unit>
<trans-unit id="You can't send messages!" xml:space="preserve">
@@ -8910,6 +8989,7 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="You joined this group. Connecting to inviting group member." xml:space="preserve">
<source>You joined this group. Connecting to inviting group member.</source>
<target>Вы вступили в эту группу. Устанавливается соединение с пригласившим членом группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You may migrate the exported database." xml:space="preserve">
@@ -8979,6 +9059,7 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="You will connect to all group members." xml:space="preserve">
<source>You will connect to all group members.</source>
<target>Вы соединитесь со всеми членами группы.</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="You will still receive calls and notifications from muted profiles when they are active." xml:space="preserve">
@@ -9168,6 +9249,7 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="accepted %@" xml:space="preserve">
<source>accepted %@</source>
<target>принят %@</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="accepted call" xml:space="preserve">
@@ -9182,6 +9264,7 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="accepted you" xml:space="preserve">
<source>accepted you</source>
<target>Вы приняты</target>
<note>rcv group event chat item</note>
</trans-unit>
<trans-unit id="admin" xml:space="preserve">
@@ -9206,10 +9289,12 @@ Repeat connection request?</source>
</trans-unit>
<trans-unit id="all" xml:space="preserve">
<source>all</source>
<target>все</target>
<note>member criteria value</note>
</trans-unit>
<trans-unit id="all members" xml:space="preserve">
<source>all members</source>
<target>все члены</target>
<note>feature role</note>
</trans-unit>
<trans-unit id="always" xml:space="preserve">
@@ -9295,6 +9380,7 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="can't send messages" xml:space="preserve">
<source>can't send messages</source>
<target>нельзя отправлять</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="cancelled %@" xml:space="preserve">
@@ -9404,10 +9490,12 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="contact deleted" xml:space="preserve">
<source>contact deleted</source>
<target>контакт удален</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact disabled" xml:space="preserve">
<source>contact disabled</source>
<target>контакт выключен</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="contact has e2e encryption" xml:space="preserve">
@@ -9422,6 +9510,7 @@ marked deleted chat item preview text</note>
</trans-unit>
<trans-unit id="contact not ready" xml:space="preserve">
<source>contact not ready</source>
<target>контакт не готов</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="creator" xml:space="preserve">
@@ -9597,6 +9686,7 @@ pref value</note>
</trans-unit>
<trans-unit id="group is deleted" xml:space="preserve">
<source>group is deleted</source>
<target>группа удалена</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="group profile updated" xml:space="preserve">
@@ -9711,10 +9801,12 @@ pref value</note>
</trans-unit>
<trans-unit id="member" xml:space="preserve">
<source>member</source>
<target>член группы</target>
<note>member role</note>
</trans-unit>
<trans-unit id="member %@ changed to %@" xml:space="preserve">
<source>member %1$@ changed to %2$@</source>
<target>член %1$@ изменился на %2$@</target>
<note>profile update event chat item</note>
</trans-unit>
<trans-unit id="member connected" xml:space="preserve">
@@ -9724,6 +9816,7 @@ pref value</note>
</trans-unit>
<trans-unit id="member has old version" xml:space="preserve">
<source>member has old version</source>
<target>член имеет старую версию</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="message" xml:space="preserve">
@@ -9793,6 +9886,7 @@ pref value</note>
</trans-unit>
<trans-unit id="not synchronized" xml:space="preserve">
<source>not synchronized</source>
<target>не синхронизирован</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="observer" xml:space="preserve">
@@ -9860,6 +9954,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="pending review" xml:space="preserve">
<source>pending review</source>
<target>ожидает одобрения</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="quantum resistant e2e encryption" xml:space="preserve">
@@ -9904,6 +9999,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="removed from group" xml:space="preserve">
<source>removed from group</source>
<target>удален из группы</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="removed profile picture" xml:space="preserve">
@@ -9918,6 +10014,7 @@ time to disappear</note>
</trans-unit>
<trans-unit id="request to join rejected" xml:space="preserve">
<source>request to join rejected</source>
<target>запрос на вступление отклонён</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="requested to connect" xml:space="preserve">
@@ -9927,10 +10024,12 @@ time to disappear</note>
</trans-unit>
<trans-unit id="review" xml:space="preserve">
<source>review</source>
<target>рассмотрение</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="reviewed by admins" xml:space="preserve">
<source>reviewed by admins</source>
<target>одобрен админами</target>
<note>No comment provided by engineer.</note>
</trans-unit>
<trans-unit id="saved" xml:space="preserve">
@@ -10124,6 +10223,7 @@ last received msg: %2$@</source>
</trans-unit>
<trans-unit id="you accepted this member" xml:space="preserve">
<source>you accepted this member</source>
<target>Вы приняли этого члена</target>
<note>snd group event chat item</note>
</trans-unit>
<trans-unit id="you are invited to group" xml:space="preserve">
@@ -10269,6 +10369,7 @@ last received msg: %2$@</source>
</trans-unit>
<trans-unit id="From %d chat(s)" xml:space="preserve">
<source>From %d chat(s)</source>
<target>Из %d чатов</target>
<note>notification body</note>
</trans-unit>
<trans-unit id="From: %@" xml:space="preserve">
@@ -1,6 +1,9 @@
/* notification body */
"%d new events" = "%d новых сообщений";
/* notification body */
"From %d chat(s)" = "Из %d чатов";
/* notification body */
"From: %@" = "От: %@";
+18 -18
View File
@@ -184,8 +184,8 @@
64C3B0212A0D359700E19930 /* CustomTimePicker.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64C3B0202A0D359700E19930 /* CustomTimePicker.swift */; };
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829982D54AEED006B9E89 /* libgmp.a */; };
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C829992D54AEEE006B9E89 /* libffi.a */; };
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7-ghc9.6.3.a */; };
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7.a */; };
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */; };
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */; };
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */ = {isa = PBXBuildFile; fileRef = 64C8299C2D54AEEE006B9E89 /* libgmpxx.a */; };
64D0C2C029F9688300B38D5F /* UserAddressView.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2BF29F9688300B38D5F /* UserAddressView.swift */; };
64D0C2C229FA57AB00B38D5F /* UserAddressLearnMore.swift in Sources */ = {isa = PBXBuildFile; fileRef = 64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */; };
@@ -553,8 +553,8 @@
64C3B0202A0D359700E19930 /* CustomTimePicker.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = CustomTimePicker.swift; sourceTree = "<group>"; };
64C829982D54AEED006B9E89 /* libgmp.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmp.a; sourceTree = "<group>"; };
64C829992D54AEEE006B9E89 /* libffi.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libffi.a; sourceTree = "<group>"; };
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7-ghc9.6.3.a"; sourceTree = "<group>"; };
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7.a"; sourceTree = "<group>"; };
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a"; sourceTree = "<group>"; };
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = "libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a"; sourceTree = "<group>"; };
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */ = {isa = PBXFileReference; lastKnownFileType = archive.ar; path = libgmpxx.a; sourceTree = "<group>"; };
64D0C2BF29F9688300B38D5F /* UserAddressView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressView.swift; sourceTree = "<group>"; };
64D0C2C129FA57AB00B38D5F /* UserAddressLearnMore.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = UserAddressLearnMore.swift; sourceTree = "<group>"; };
@@ -712,8 +712,8 @@
64C8299D2D54AEEE006B9E89 /* libgmp.a in Frameworks */,
64C8299E2D54AEEE006B9E89 /* libffi.a in Frameworks */,
64C829A12D54AEEE006B9E89 /* libgmpxx.a in Frameworks */,
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7-ghc9.6.3.a in Frameworks */,
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7.a in Frameworks */,
64C8299F2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a in Frameworks */,
64C829A02D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a in Frameworks */,
CE38A29C2C3FCD72005ED185 /* SwiftyGif in Frameworks */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -798,8 +798,8 @@
64C829992D54AEEE006B9E89 /* libffi.a */,
64C829982D54AEED006B9E89 /* libgmp.a */,
64C8299C2D54AEEE006B9E89 /* libgmpxx.a */,
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7-ghc9.6.3.a */,
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.1-7Y3Lr8U6bNmEaeIx88dGf7.a */,
64C8299A2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F-ghc9.6.3.a */,
64C8299B2D54AEEE006B9E89 /* libHSsimplex-chat-6.4.0.2-K4qWCwk6PxbL8qHn42QC4F.a */,
);
path = Libraries;
sourceTree = "<group>";
@@ -2001,7 +2001,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -2051,7 +2051,7 @@
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_ENTITLEMENTS = "SimpleX (iOS).entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEAD_CODE_STRIPPING = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
@@ -2093,7 +2093,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@@ -2113,7 +2113,7 @@
buildSettings = {
ALWAYS_EMBED_SWIFT_STANDARD_LIBRARIES = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
GENERATE_INFOPLIST_FILE = YES;
IPHONEOS_DEPLOYMENT_TARGET = 15.0;
@@ -2138,7 +2138,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
GCC_OPTIMIZATION_LEVEL = s;
@@ -2175,7 +2175,7 @@
CODE_SIGN_ENTITLEMENTS = "SimpleX NSE/SimpleX NSE.entitlements";
CODE_SIGN_IDENTITY = "Apple Development";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_BITCODE = NO;
ENABLE_CODE_COVERAGE = NO;
@@ -2212,7 +2212,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2263,7 +2263,7 @@
CLANG_TIDY_BUGPRONE_REDUNDANT_BRANCH_CONDITION = YES;
CLANG_TIDY_MISC_REDUNDANT_EXPRESSION = YES;
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEFINES_MODULE = YES;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
DYLIB_COMPATIBILITY_VERSION = 1;
@@ -2314,7 +2314,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
@@ -2348,7 +2348,7 @@
CLANG_CXX_LANGUAGE_STANDARD = "gnu++20";
CODE_SIGN_ENTITLEMENTS = "SimpleX SE/SimpleX SE.entitlements";
CODE_SIGN_STYLE = Automatic;
CURRENT_PROJECT_VERSION = 278;
CURRENT_PROJECT_VERSION = 279;
DEVELOPMENT_TEAM = 5NN7GUYB6T;
ENABLE_USER_SCRIPT_SANDBOXING = YES;
GCC_C_LANGUAGE_STANDARD = gnu17;
+1 -1
View File
@@ -3158,7 +3158,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Острани член";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Острани член?";
/* No comment provided by engineer. */
+1 -1
View File
@@ -2478,7 +2478,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Odstranit člena";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Odebrat člena?";
/* No comment provided by engineer. */
+112 -1
View File
@@ -345,6 +345,12 @@ accept incoming call via notification
swipe action */
"Accept" = "Annehmen";
/* alert action */
"Accept as member" = "Als Mitglied übernehmen";
/* alert action */
"Accept as observer" = "Als Beobachter übernehmen";
/* No comment provided by engineer. */
"Accept conditions" = "Nutzungsbedingungen akzeptieren";
@@ -358,6 +364,12 @@ swipe action */
swipe action */
"Accept incognito" = "Inkognito akzeptieren";
/* alert title */
"Accept member" = "Mitglied übernehmen";
/* rcv group event chat item */
"accepted %@" = "%@ übernommen";
/* call status */
"accepted call" = "Anruf angenommen";
@@ -367,6 +379,9 @@ swipe action */
/* chat list item title */
"accepted invitation" = "Einladung angenommen";
/* rcv group event chat item */
"accepted you" = "hat Sie übernommen";
/* No comment provided by engineer. */
"Acknowledged" = "Bestätigt";
@@ -463,6 +478,9 @@ swipe action */
/* chat item text */
"agreeing encryption…" = "Verschlüsselung zustimmen…";
/* member criteria value */
"all" = "alle";
/* No comment provided by engineer. */
"All" = "Alle";
@@ -905,6 +923,9 @@ marked deleted chat item preview text */
/* No comment provided by engineer. */
"Can't message member" = "Mitglied kann nicht benachrichtigt werden";
/* No comment provided by engineer. */
"can't send messages" = "Es können keine Nachrichten gesendet werden";
/* alert action
alert button */
"Cancel" = "Abbrechen";
@@ -1042,9 +1063,18 @@ set passcode view */
/* No comment provided by engineer. */
"Chat will be deleted for you - this cannot be undone!" = "Der Chat wird für Sie gelöscht. Dies kann nicht rückgängig gemacht werden!";
/* chat toolbar */
"Chat with admins" = "Chat mit Administratoren";
/* No comment provided by engineer. */
"Chat with member" = "Chat mit einem Mitglied";
/* No comment provided by engineer. */
"Chats" = "Chats";
/* No comment provided by engineer. */
"Chats with members" = "Chats mit Mitgliedern";
/* No comment provided by engineer. */
"Check messages every 20 min." = "Alle 20min Nachrichten überprüfen.";
@@ -1333,9 +1363,15 @@ set passcode view */
/* No comment provided by engineer. */
"Contact already exists" = "Der Kontakt ist bereits vorhanden";
/* No comment provided by engineer. */
"contact deleted" = "Kontakt gelöscht";
/* No comment provided by engineer. */
"Contact deleted!" = "Kontakt gelöscht!";
/* No comment provided by engineer. */
"contact disabled" = "Kontakt deaktiviert";
/* No comment provided by engineer. */
"contact has e2e encryption" = "Kontakt nutzt E2E-Verschlüsselung";
@@ -1354,6 +1390,9 @@ set passcode view */
/* No comment provided by engineer. */
"Contact name" = "Kontaktname";
/* No comment provided by engineer. */
"contact not ready" = "Kontakt nicht bereit";
/* No comment provided by engineer. */
"Contact preferences" = "Kontakt-Präferenzen";
@@ -1602,6 +1641,9 @@ swipe action */
/* No comment provided by engineer. */
"Delete chat profile?" = "Chat-Profil löschen?";
/* alert title */
"Delete chat with member?" = "Chat mit dem Mitglied löschen?";
/* No comment provided by engineer. */
"Delete chat?" = "Chat löschen?";
@@ -2116,6 +2158,9 @@ chat item action */
/* No comment provided by engineer. */
"Error accepting contact request" = "Fehler beim Annehmen der Kontaktanfrage";
/* alert title */
"Error accepting member" = "Fehler beim Übernehmen des Mitglieds";
/* No comment provided by engineer. */
"Error adding member(s)" = "Fehler beim Hinzufügen von Mitgliedern";
@@ -2173,6 +2218,9 @@ chat item action */
/* No comment provided by engineer. */
"Error deleting chat database" = "Fehler beim Löschen der Chat-Datenbank";
/* alert title */
"Error deleting chat with member" = "Fehler beim Löschen des Chats mit dem Mitglied";
/* No comment provided by engineer. */
"Error deleting chat!" = "Fehler beim Löschen des Chats!";
@@ -2610,6 +2658,9 @@ snd error text */
/* No comment provided by engineer. */
"Group invitation is no longer valid, it was removed by sender." = "Die Gruppeneinladung ist nicht mehr gültig, da sie vom Absender entfernt wurde.";
/* No comment provided by engineer. */
"group is deleted" = "Gruppe wird gelöscht";
/* No comment provided by engineer. */
"Group link" = "Gruppen-Link";
@@ -3138,9 +3189,15 @@ snd error text */
/* profile update event chat item */
"member %@ changed to %@" = "Der Mitgliedsname von %1$@ wurde auf %2$@ geändert";
/* No comment provided by engineer. */
"Member admission" = "Aufnahme von Mitgliedern";
/* rcv group event chat item */
"member connected" = "ist der Gruppe beigetreten";
/* No comment provided by engineer. */
"member has old version" = "Das Mitglied hat eine alte App-Version";
/* item status text */
"Member inactive" = "Mitglied inaktiv";
@@ -3162,6 +3219,9 @@ snd error text */
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "Das Mitglied wird aus der Gruppe entfernt. Dies kann nicht rückgängig gemacht werden!";
/* alert message */
"Member will join the group, accept member?" = "Ein Mitglied wird der Gruppe beitreten. Übernehmen?";
/* No comment provided by engineer. */
"Members can add message reactions." = "Gruppenmitglieder können eine Reaktion auf Nachrichten geben.";
@@ -3432,6 +3492,9 @@ snd error text */
/* No comment provided by engineer. */
"New member role" = "Neue Mitgliedsrolle";
/* rcv group event chat item */
"New member wants to join the group." = "Ein neues Mitglied will der Gruppe beitreten.";
/* notification */
"new message" = "Neue Nachricht";
@@ -3471,6 +3534,9 @@ snd error text */
/* No comment provided by engineer. */
"No chats in list %@" = "Keine Chats in der Liste %@";
/* No comment provided by engineer. */
"No chats with members" = "Keine Chats mit Mitgliedern";
/* No comment provided by engineer. */
"No contacts selected" = "Keine Kontakte ausgewählt";
@@ -3555,6 +3621,9 @@ snd error text */
/* No comment provided by engineer. */
"Not compatible!" = "Nicht kompatibel!";
/* No comment provided by engineer. */
"not synchronized" = "Nicht synchronisiert";
/* No comment provided by engineer. */
"Notes" = "Anmerkungen";
@@ -3802,6 +3871,9 @@ alert button */
/* No comment provided by engineer. */
"pending approval" = "ausstehende Genehmigung";
/* No comment provided by engineer. */
"pending review" = "Ausstehende Überprüfung";
/* No comment provided by engineer. */
"Periodic" = "Periodisch";
@@ -3871,6 +3943,9 @@ alert button */
/* token info */
"Please try to disable and re-enable notfications." = "Bitte versuchen Sie, die Benachrichtigungen zu deaktivieren und wieder zu aktivieren.";
/* snd group event chat item */
"Please wait for group moderators to review your request to join the group." = "Bitte warten Sie auf die Überprüfung Ihrer Anfrage durch die Gruppen-Moderatoren, um der Gruppe beitreten zu können.";
/* token info */
"Please wait for token activation to complete." = "Bitte warten Sie, bis die Token-Aktivierung abgeschlossen ist.";
@@ -4151,6 +4226,9 @@ swipe action */
/* No comment provided by engineer. */
"Reject contact request" = "Kontaktanfrage ablehnen";
/* alert title */
"Reject member?" = "Mitglied ablehnen?";
/* No comment provided by engineer. */
"rejected" = "abgelehnt";
@@ -4175,7 +4253,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Mitglied entfernen";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Das Mitglied entfernen?";
/* No comment provided by engineer. */
@@ -4190,6 +4268,9 @@ swipe action */
/* profile update event chat item */
"removed contact address" = "Die Kontaktadresse wurde entfernt";
/* No comment provided by engineer. */
"removed from group" = "Von der Gruppe entfernt";
/* profile update event chat item */
"removed profile picture" = "Das Profil-Bild wurde entfernt";
@@ -4238,6 +4319,9 @@ swipe action */
/* No comment provided by engineer. */
"Report reason?" = "Grund der Meldung?";
/* alert title */
"Report sent to moderators" = "Meldung wurde an die Moderatoren gesendet";
/* report reason */
"Report spam: only group moderators will see it." = "Spam melden: Nur Gruppenmoderatoren werden es sehen.";
@@ -4253,6 +4337,9 @@ swipe action */
/* No comment provided by engineer. */
"Reports" = "Meldungen";
/* No comment provided by engineer. */
"request to join rejected" = "Beitrittsanfrage abgelehnt";
/* chat list item title */
"requested to connect" = "Zur Verbindung aufgefordert";
@@ -4307,9 +4394,21 @@ swipe action */
/* chat item action */
"Reveal" = "Aufdecken";
/* No comment provided by engineer. */
"review" = "Überprüfung";
/* No comment provided by engineer. */
"Review conditions" = "Nutzungsbedingungen einsehen";
/* admission stage */
"Review members" = "Überprüfung der Mitglieder";
/* admission stage description */
"Review members before admitting (\"knocking\")." = "Überprüfung der Mitglieder vor der Aufnahme (\"Anklopfen\").";
/* No comment provided by engineer. */
"reviewed by admins" = "Von Administratoren überprüft";
/* No comment provided by engineer. */
"Revoke" = "Widerrufen";
@@ -4338,6 +4437,9 @@ chat item action */
/* alert button */
"Save (and notify contacts)" = "Speichern (und Kontakte benachrichtigen)";
/* alert title */
"Save admission settings?" = "Speichern der Aufnahme-Einstellungen?";
/* alert button */
"Save and notify contact" = "Speichern und Kontakt benachrichtigen";
@@ -4674,6 +4776,9 @@ chat item action */
/* No comment provided by engineer. */
"Set it instead of system authentication." = "Anstelle der System-Authentifizierung festlegen.";
/* No comment provided by engineer. */
"Set member admission" = "Aufnahme von Mitgliedern festlegen";
/* No comment provided by engineer. */
"Set message expiration in chats." = "Verfallsdatum von Nachrichten in Chats festlegen.";
@@ -5714,6 +5819,9 @@ report reason */
/* No comment provided by engineer. */
"You accepted connection" = "Sie haben die Verbindung akzeptiert";
/* snd group event chat item */
"you accepted this member" = "Sie haben dieses Mitglied übernommen";
/* No comment provided by engineer. */
"You allow" = "Sie erlauben";
@@ -5825,6 +5933,9 @@ report reason */
/* alert message */
"You can view invitation link again in connection details." = "Den Einladungslink können Sie in den Details der Verbindung nochmals sehen.";
/* alert message */
"You can view your reports in Chat with admins." = "Sie können Ihre Meldungen im Chat mit den Administratoren sehen.";
/* No comment provided by engineer. */
"You can't send messages!" = "Sie können keine Nachrichten versenden!";
+1 -1
View File
@@ -4172,7 +4172,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Expulsar miembro";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "¿Expulsar miembro?";
/* No comment provided by engineer. */
+1 -1
View File
@@ -2409,7 +2409,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Poista jäsen";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Poista jäsen?";
/* No comment provided by engineer. */
+1 -1
View File
@@ -4016,7 +4016,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Retirer le membre";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Retirer ce membre?";
/* No comment provided by engineer. */
+112 -1
View File
@@ -345,6 +345,12 @@ accept incoming call via notification
swipe action */
"Accept" = "Accetta";
/* alert action */
"Accept as member" = "Accetta come membro";
/* alert action */
"Accept as observer" = "Accetta come osservatore";
/* No comment provided by engineer. */
"Accept conditions" = "Accetta le condizioni";
@@ -358,6 +364,12 @@ swipe action */
swipe action */
"Accept incognito" = "Accetta in incognito";
/* alert title */
"Accept member" = "Accetta membro";
/* rcv group event chat item */
"accepted %@" = "%@ accettato";
/* call status */
"accepted call" = "chiamata accettata";
@@ -367,6 +379,9 @@ swipe action */
/* chat list item title */
"accepted invitation" = "invito accettato";
/* rcv group event chat item */
"accepted you" = "ti ha accettato/a";
/* No comment provided by engineer. */
"Acknowledged" = "Riconosciuto";
@@ -463,6 +478,9 @@ swipe action */
/* chat item text */
"agreeing encryption…" = "concordando la crittografia…";
/* member criteria value */
"all" = "tutti";
/* No comment provided by engineer. */
"All" = "Tutte";
@@ -905,6 +923,9 @@ marked deleted chat item preview text */
/* No comment provided by engineer. */
"Can't message member" = "Impossibile inviare un messaggio al membro";
/* No comment provided by engineer. */
"can't send messages" = "impossibile inviare messaggi";
/* alert action
alert button */
"Cancel" = "Annulla";
@@ -1042,9 +1063,18 @@ set passcode view */
/* No comment provided by engineer. */
"Chat will be deleted for you - this cannot be undone!" = "La chat verrà eliminata solo per te, non è reversibile!";
/* chat toolbar */
"Chat with admins" = "Chat con amministratori";
/* No comment provided by engineer. */
"Chat with member" = "Chatta con il membro";
/* No comment provided by engineer. */
"Chats" = "Chat";
/* No comment provided by engineer. */
"Chats with members" = "Chat con membri";
/* No comment provided by engineer. */
"Check messages every 20 min." = "Controlla i messaggi ogni 20 min.";
@@ -1333,9 +1363,15 @@ set passcode view */
/* No comment provided by engineer. */
"Contact already exists" = "Il contatto esiste già";
/* No comment provided by engineer. */
"contact deleted" = "contatto eliminato";
/* No comment provided by engineer. */
"Contact deleted!" = "Contatto eliminato!";
/* No comment provided by engineer. */
"contact disabled" = "contatto disattivato";
/* No comment provided by engineer. */
"contact has e2e encryption" = "il contatto ha la crittografia e2e";
@@ -1354,6 +1390,9 @@ set passcode view */
/* No comment provided by engineer. */
"Contact name" = "Nome del contatto";
/* No comment provided by engineer. */
"contact not ready" = "contatto non pronto";
/* No comment provided by engineer. */
"Contact preferences" = "Preferenze del contatto";
@@ -1602,6 +1641,9 @@ swipe action */
/* No comment provided by engineer. */
"Delete chat profile?" = "Eliminare il profilo di chat?";
/* alert title */
"Delete chat with member?" = "Eliminare la chat con il membro?";
/* No comment provided by engineer. */
"Delete chat?" = "Eliminare la chat?";
@@ -2116,6 +2158,9 @@ chat item action */
/* No comment provided by engineer. */
"Error accepting contact request" = "Errore nell'accettazione della richiesta di contatto";
/* alert title */
"Error accepting member" = "Errore di accettazione del membro";
/* No comment provided by engineer. */
"Error adding member(s)" = "Errore di aggiunta membro/i";
@@ -2173,6 +2218,9 @@ chat item action */
/* No comment provided by engineer. */
"Error deleting chat database" = "Errore nell'eliminazione del database della chat";
/* alert title */
"Error deleting chat with member" = "Errore di eliminazione della chat con il membro";
/* No comment provided by engineer. */
"Error deleting chat!" = "Errore nell'eliminazione della chat!";
@@ -2610,6 +2658,9 @@ snd error text */
/* No comment provided by engineer. */
"Group invitation is no longer valid, it was removed by sender." = "L'invito al gruppo non è più valido, è stato rimosso dal mittente.";
/* No comment provided by engineer. */
"group is deleted" = "il gruppo è eliminato";
/* No comment provided by engineer. */
"Group link" = "Link del gruppo";
@@ -3138,9 +3189,15 @@ snd error text */
/* profile update event chat item */
"member %@ changed to %@" = "il membro %1$@ è diventato %2$@";
/* No comment provided by engineer. */
"Member admission" = "Ammissione del membro";
/* rcv group event chat item */
"member connected" = "si è connesso/a";
/* No comment provided by engineer. */
"member has old version" = "il membro ha una versione vecchia";
/* item status text */
"Member inactive" = "Membro inattivo";
@@ -3162,6 +3219,9 @@ snd error text */
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "Il membro verrà rimosso dal gruppo, non è reversibile!";
/* alert message */
"Member will join the group, accept member?" = "Il membro entrerà nel gruppo, accettarlo?";
/* No comment provided by engineer. */
"Members can add message reactions." = "I membri del gruppo possono aggiungere reazioni ai messaggi.";
@@ -3432,6 +3492,9 @@ snd error text */
/* No comment provided by engineer. */
"New member role" = "Nuovo ruolo del membro";
/* rcv group event chat item */
"New member wants to join the group." = "Un nuovo membro vuole entrare nel gruppo.";
/* notification */
"new message" = "messaggio nuovo";
@@ -3471,6 +3534,9 @@ snd error text */
/* No comment provided by engineer. */
"No chats in list %@" = "Nessuna chat nell'elenco %@";
/* No comment provided by engineer. */
"No chats with members" = "Nessuna chat con membri";
/* No comment provided by engineer. */
"No contacts selected" = "Nessun contatto selezionato";
@@ -3555,6 +3621,9 @@ snd error text */
/* No comment provided by engineer. */
"Not compatible!" = "Non compatibile!";
/* No comment provided by engineer. */
"not synchronized" = "non sincronizzato";
/* No comment provided by engineer. */
"Notes" = "Note";
@@ -3802,6 +3871,9 @@ alert button */
/* No comment provided by engineer. */
"pending approval" = "in attesa di approvazione";
/* No comment provided by engineer. */
"pending review" = "in attesa di revisione";
/* No comment provided by engineer. */
"Periodic" = "Periodicamente";
@@ -3871,6 +3943,9 @@ alert button */
/* token info */
"Please try to disable and re-enable notfications." = "Prova a disattivare e riattivare le notifiche.";
/* snd group event chat item */
"Please wait for group moderators to review your request to join the group." = "Attendi che i moderatori del gruppo revisionino la tua richiesta di entrare nel gruppo.";
/* token info */
"Please wait for token activation to complete." = "Attendi il completamento dell'attivazione del token.";
@@ -4151,6 +4226,9 @@ swipe action */
/* No comment provided by engineer. */
"Reject contact request" = "Rifiuta la richiesta di contatto";
/* alert title */
"Reject member?" = "Rifiutare il membro?";
/* No comment provided by engineer. */
"rejected" = "rifiutato";
@@ -4175,7 +4253,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Rimuovi membro";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Rimuovere il membro?";
/* No comment provided by engineer. */
@@ -4190,6 +4268,9 @@ swipe action */
/* profile update event chat item */
"removed contact address" = "indirizzo di contatto rimosso";
/* No comment provided by engineer. */
"removed from group" = "rimosso dal gruppo";
/* profile update event chat item */
"removed profile picture" = "immagine del profilo rimossa";
@@ -4238,6 +4319,9 @@ swipe action */
/* No comment provided by engineer. */
"Report reason?" = "Motivo della segnalazione?";
/* alert title */
"Report sent to moderators" = "Segnalazione inviata ai moderatori";
/* report reason */
"Report spam: only group moderators will see it." = "Segnala spam: solo i moderatori del gruppo lo vedranno.";
@@ -4253,6 +4337,9 @@ swipe action */
/* No comment provided by engineer. */
"Reports" = "Segnalazioni";
/* No comment provided by engineer. */
"request to join rejected" = "richiesta di entrare rifiutata";
/* chat list item title */
"requested to connect" = "richiesto di connettersi";
@@ -4307,9 +4394,21 @@ swipe action */
/* chat item action */
"Reveal" = "Rivela";
/* No comment provided by engineer. */
"review" = "revisiona";
/* No comment provided by engineer. */
"Review conditions" = "Leggi le condizioni";
/* admission stage */
"Review members" = "Revisiona i membri";
/* admission stage description */
"Review members before admitting (\"knocking\")." = "Revisiona i membri prima di ammetterli (\"bussare\").";
/* No comment provided by engineer. */
"reviewed by admins" = "revisionato dagli amministratori";
/* No comment provided by engineer. */
"Revoke" = "Revoca";
@@ -4338,6 +4437,9 @@ chat item action */
/* alert button */
"Save (and notify contacts)" = "Salva (e avvisa i contatti)";
/* alert title */
"Save admission settings?" = "Salvare le impostazioni di ammissione?";
/* alert button */
"Save and notify contact" = "Salva e avvisa il contatto";
@@ -4674,6 +4776,9 @@ chat item action */
/* No comment provided by engineer. */
"Set it instead of system authentication." = "Impostalo al posto dell'autenticazione di sistema.";
/* No comment provided by engineer. */
"Set member admission" = "Imposta l'ammissione del membro";
/* No comment provided by engineer. */
"Set message expiration in chats." = "Imposta la scadenza dei messaggi nelle chat.";
@@ -5714,6 +5819,9 @@ report reason */
/* No comment provided by engineer. */
"You accepted connection" = "Hai accettato la connessione";
/* snd group event chat item */
"you accepted this member" = "hai accettato questo membro";
/* No comment provided by engineer. */
"You allow" = "Lo consenti";
@@ -5825,6 +5933,9 @@ report reason */
/* alert message */
"You can view invitation link again in connection details." = "Puoi vedere di nuovo il link di invito nei dettagli di connessione.";
/* alert message */
"You can view your reports in Chat with admins." = "Puoi vedere le tue segnalazioni nella chat con gli amministratori.";
/* No comment provided by engineer. */
"You can't send messages!" = "Non puoi inviare messaggi!";
+1 -1
View File
@@ -2628,7 +2628,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "メンバーを除名する";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "メンバーを除名しますか?";
/* No comment provided by engineer. */
+1 -1
View File
@@ -4169,7 +4169,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Lid verwijderen";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Lid verwijderen?";
/* No comment provided by engineer. */
+1 -1
View File
@@ -3746,7 +3746,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Usuń członka";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Usunąć członka?";
/* No comment provided by engineer. */
+302 -9
View File
@@ -164,7 +164,7 @@
"%d file(s) were not downloaded." = "%d файлов не было загружено.";
/* time interval */
"%d hours" = "%d ч.";
"%d hours" = "%d час.";
/* alert title */
"%d messages not forwarded" = "%d сообщений не переслано";
@@ -345,6 +345,12 @@ accept incoming call via notification
swipe action */
"Accept" = "Принять";
/* alert action */
"Accept as member" = "Принять в группу";
/* alert action */
"Accept as observer" = "Принять как читателя";
/* No comment provided by engineer. */
"Accept conditions" = "Принять условия";
@@ -358,6 +364,12 @@ swipe action */
swipe action */
"Accept incognito" = "Принять инкогнито";
/* alert title */
"Accept member" = "Принять члена";
/* rcv group event chat item */
"accepted %@" = "принят %@";
/* call status */
"accepted call" = "принятый звонок";
@@ -367,6 +379,9 @@ swipe action */
/* chat list item title */
"accepted invitation" = "принятое приглашение";
/* rcv group event chat item */
"accepted you" = "Вы приняты";
/* No comment provided by engineer. */
"Acknowledged" = "Подтверждено";
@@ -463,6 +478,9 @@ swipe action */
/* chat item text */
"agreeing encryption…" = "шифрование согласовывается…";
/* member criteria value */
"all" = "все";
/* No comment provided by engineer. */
"All" = "Все";
@@ -484,6 +502,9 @@ swipe action */
/* No comment provided by engineer. */
"All group members will remain connected." = "Все члены группы останутся соединены.";
/* feature role */
"all members" = "все члены";
/* No comment provided by engineer. */
"All messages and files are sent **end-to-end encrypted**, with post-quantum security in direct messages." = "Все сообщения и файлы отправляются с **end-to-end шифрованием**, с постквантовой безопасностью в прямых разговорах.";
@@ -502,6 +523,9 @@ swipe action */
/* No comment provided by engineer. */
"All reports will be archived for you." = "Все сообщения о нарушениях будут заархивированы для вас.";
/* No comment provided by engineer. */
"All servers" = "Все серверы";
/* No comment provided by engineer. */
"All your contacts will remain connected." = "Все контакты, которые соединились через этот адрес, сохранятся.";
@@ -899,6 +923,9 @@ marked deleted chat item preview text */
/* No comment provided by engineer. */
"Can't message member" = "Не удаётся отправить сообщение члену группы";
/* No comment provided by engineer. */
"can't send messages" = "нельзя отправлять";
/* alert action
alert button */
"Cancel" = "Отменить";
@@ -1036,9 +1063,18 @@ set passcode view */
/* No comment provided by engineer. */
"Chat will be deleted for you - this cannot be undone!" = "Разговор будет удален для Вас - это действие нельзя отменить!";
/* chat toolbar */
"Chat with admins" = "Чат с админами";
/* No comment provided by engineer. */
"Chat with member" = "Чат с членом группы";
/* No comment provided by engineer. */
"Chats" = "Чаты";
/* No comment provided by engineer. */
"Chats with members" = "Чаты с членами группы";
/* No comment provided by engineer. */
"Check messages every 20 min." = "Проверять сообщения каждые 20 минут.";
@@ -1327,9 +1363,15 @@ set passcode view */
/* No comment provided by engineer. */
"Contact already exists" = "Существующий контакт";
/* No comment provided by engineer. */
"contact deleted" = "контакт удален";
/* No comment provided by engineer. */
"Contact deleted!" = "Контакт удален!";
/* No comment provided by engineer. */
"contact disabled" = "контакт выключен";
/* No comment provided by engineer. */
"contact has e2e encryption" = "у контакта есть e2e шифрование";
@@ -1348,6 +1390,9 @@ set passcode view */
/* No comment provided by engineer. */
"Contact name" = "Имена контактов";
/* No comment provided by engineer. */
"contact not ready" = "контакт не готов";
/* No comment provided by engineer. */
"Contact preferences" = "Предпочтения контакта";
@@ -1551,13 +1596,13 @@ set passcode view */
/* delete after time
pref value */
"default (%@)" = "по умолчанию (%@)";
"default (%@)" = "базовый (%@)";
/* No comment provided by engineer. */
"default (no)" = "по умолчанию (нет)";
"default (no)" = "базовый (нет)";
/* No comment provided by engineer. */
"default (yes)" = "по умолчанию (да)";
"default (yes)" = "базовый (да)";
/* alert action
swipe action */
@@ -1596,6 +1641,9 @@ swipe action */
/* No comment provided by engineer. */
"Delete chat profile?" = "Удалить профиль?";
/* alert title */
"Delete chat with member?" = "Удалить чат с членом группы?";
/* No comment provided by engineer. */
"Delete chat?" = "Удалить разговор?";
@@ -1657,7 +1705,7 @@ swipe action */
"Delete messages" = "Удалить сообщения";
/* No comment provided by engineer. */
"Delete messages after" = "Удалять сообщения через";
"Delete messages after" = "Удалять сообщения";
/* No comment provided by engineer. */
"Delete old database" = "Удалить предыдущую версию данных";
@@ -1782,6 +1830,9 @@ swipe action */
/* No comment provided by engineer. */
"Direct messages between members are prohibited in this chat." = "Личные сообщения запрещены в этой группе.";
/* No comment provided by engineer. */
"Direct messages between members are prohibited." = "Прямые сообщения между членами запрещены.";
/* No comment provided by engineer. */
"Disable (keep overrides)" = "Выключить (кроме исключений)";
@@ -1836,6 +1887,9 @@ swipe action */
/* No comment provided by engineer. */
"Do it later" = "Отложить";
/* No comment provided by engineer. */
"Do not send history to new members." = "Не отправлять историю новым членам.";
/* No comment provided by engineer. */
"Do NOT send messages directly, even if your or destination server does not support private routing." = "Не отправлять сообщения напрямую, даже если сервер получателя не поддерживает конфиденциальную доставку.";
@@ -1933,6 +1987,9 @@ chat item action */
/* No comment provided by engineer. */
"Enable camera access" = "Включить доступ к камере";
/* No comment provided by engineer. */
"Enable Flux in Network & servers settings for better metadata privacy." = "Включите Flux в настройках Сеть и серверы для лучшей конфиденциальности метаданных.";
/* No comment provided by engineer. */
"Enable for all" = "Включить для всех";
@@ -2101,6 +2158,12 @@ chat item action */
/* No comment provided by engineer. */
"Error accepting contact request" = "Ошибка при принятии запроса на соединение";
/* alert title */
"Error accepting member" = "Ошибка вступления члена группы";
/* No comment provided by engineer. */
"Error adding member(s)" = "Ошибка при добавлении членов группы";
/* alert title */
"Error adding server" = "Ошибка добавления сервера";
@@ -2137,6 +2200,9 @@ chat item action */
/* alert title */
"Error creating list" = "Ошибка создания списка";
/* No comment provided by engineer. */
"Error creating member contact" = "Ошибка при создании контакта";
/* No comment provided by engineer. */
"Error creating message" = "Ошибка создания сообщения";
@@ -2152,6 +2218,9 @@ chat item action */
/* No comment provided by engineer. */
"Error deleting chat database" = "Ошибка при удалении данных чата";
/* alert title */
"Error deleting chat with member" = "Ошибка при удалении чата с членом группы";
/* No comment provided by engineer. */
"Error deleting chat!" = "Ошибка при удалении чата!";
@@ -2215,6 +2284,9 @@ chat item action */
/* alert title */
"Error registering for notifications" = "Ошибка регистрации для уведомлений";
/* alert title */
"Error removing member" = "Ошибка при удалении члена группы";
/* alert title */
"Error reordering lists" = "Ошибка сортировки списков";
@@ -2251,6 +2323,9 @@ chat item action */
/* No comment provided by engineer. */
"Error sending email" = "Ошибка отправки email";
/* No comment provided by engineer. */
"Error sending member contact invitation" = "Ошибка при отправке приглашения члену";
/* No comment provided by engineer. */
"Error sending message" = "Ошибка при отправке сообщения";
@@ -2451,6 +2526,9 @@ snd error text */
/* No comment provided by engineer. */
"Fix not supported by contact" = "Починка не поддерживается контактом";
/* No comment provided by engineer. */
"Fix not supported by group member" = "Починка не поддерживается членом группы.";
/* No comment provided by engineer. */
"For all moderators" = "Для всех модераторов";
@@ -2529,6 +2607,9 @@ snd error text */
/* No comment provided by engineer. */
"Full name (optional)" = "Полное имя (не обязательно)";
/* No comment provided by engineer. */
"Fully decentralized visible only to members." = "Группа полностью децентрализована – она видна только членам.";
/* No comment provided by engineer. */
"Fully re-implemented - work in background!" = "Полностью обновлены - работают в фоне!";
@@ -2577,6 +2658,9 @@ snd error text */
/* No comment provided by engineer. */
"Group invitation is no longer valid, it was removed by sender." = "Приглашение в группу больше не действительно, оно было удалено отправителем.";
/* No comment provided by engineer. */
"group is deleted" = "группа удалена";
/* No comment provided by engineer. */
"Group link" = "Ссылка группы";
@@ -2595,12 +2679,18 @@ snd error text */
/* No comment provided by engineer. */
"Group profile" = "Профиль группы";
/* No comment provided by engineer. */
"Group profile is stored on members' devices, not on the servers." = "Профиль группы хранится на устройствах членов, а не на серверах.";
/* snd group event chat item */
"group profile updated" = "профиль группы обновлен";
/* No comment provided by engineer. */
"Group welcome message" = "Приветственное сообщение группы";
/* No comment provided by engineer. */
"Group will be deleted for all members - this cannot be undone!" = "Группа будет удалена для всех членов - это действие нельзя отменить!";
/* No comment provided by engineer. */
"Group will be deleted for you - this cannot be undone!" = "Группа будет удалена для Вас - это действие нельзя отменить!";
@@ -2637,6 +2727,9 @@ snd error text */
/* No comment provided by engineer. */
"History" = "История";
/* No comment provided by engineer. */
"History is not sent to new members." = "История не отправляется новым членам.";
/* time unit */
"hours" = "часов";
@@ -2871,6 +2964,9 @@ snd error text */
/* No comment provided by engineer. */
"Invite friends" = "Пригласить друзей";
/* No comment provided by engineer. */
"Invite members" = "Пригласить членов группы";
/* No comment provided by engineer. */
"Invite to chat" = "Пригласить в разговор";
@@ -3084,15 +3180,75 @@ snd error text */
/* blur media */
"Medium" = "Среднее";
/* member role */
"member" = "член группы";
/* No comment provided by engineer. */
"Member" = "Член группы";
/* profile update event chat item */
"member %@ changed to %@" = "член %1$@ изменился на %2$@";
/* No comment provided by engineer. */
"Member admission" = "Приём членов в группу";
/* rcv group event chat item */
"member connected" = "соединен(а)";
/* No comment provided by engineer. */
"member has old version" = "член имеет старую версию";
/* item status text */
"Member inactive" = "Член неактивен";
/* chat feature */
"Member reports" = "Сообщения о нарушениях";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". All chat members will be notified." = "Роль участника будет изменена на \"%@\". Все участники разговора получат уведомление.";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". All group members will be notified." = "Роль члена будет изменена на \"%@\". Все члены группы получат уведомление.";
/* No comment provided by engineer. */
"Member role will be changed to \"%@\". The member will receive a new invitation." = "Роль члена будет изменена на \"%@\". Будет отправлено новое приглашение.";
/* No comment provided by engineer. */
"Member will be removed from chat - this cannot be undone!" = "Член будет удален из разговора - это действие нельзя отменить!";
/* No comment provided by engineer. */
"Member will be removed from group - this cannot be undone!" = "Член группы будет удален - это действие нельзя отменить!";
/* alert message */
"Member will join the group, accept member?" = "Участник хочет присоединиться к группе. Принять?";
/* No comment provided by engineer. */
"Members can add message reactions." = "Члены могут добавлять реакции на сообщения.";
/* No comment provided by engineer. */
"Members can irreversibly delete sent messages. (24 hours)" = "Члены могут необратимо удалять отправленные сообщения. (24 часа)";
/* No comment provided by engineer. */
"Members can report messsages to moderators." = "Члены группы могут пожаловаться модераторам.";
/* No comment provided by engineer. */
"Members can send direct messages." = "Члены могут посылать прямые сообщения.";
/* No comment provided by engineer. */
"Members can send disappearing messages." = "Члены могут посылать исчезающие сообщения.";
/* No comment provided by engineer. */
"Members can send files and media." = "Члены могут слать файлы и медиа.";
/* No comment provided by engineer. */
"Members can send SimpleX links." = "Члены могут отправлять ссылки SimpleX.";
/* No comment provided by engineer. */
"Members can send voice messages." = "Члены могут отправлять голосовые сообщения.";
/* No comment provided by engineer. */
"Mention members 👋" = "Упоминайте участников 👋";
/* No comment provided by engineer. */
"Menus" = "Меню";
@@ -3114,6 +3270,9 @@ snd error text */
/* item status text */
"Message forwarded" = "Сообщение переслано";
/* item status description */
"Message may be delivered later if member becomes active." = "Сообщение может быть доставлено позже, если член группы станет активным.";
/* No comment provided by engineer. */
"Message queue info" = "Информация об очереди сообщений";
@@ -3330,6 +3489,12 @@ snd error text */
/* No comment provided by engineer. */
"New media options" = "Новые медиа-опции";
/* No comment provided by engineer. */
"New member role" = "Роль члена группы";
/* rcv group event chat item */
"New member wants to join the group." = "Новый участник хочет присоединиться к группе.";
/* notification */
"new message" = "новое сообщение";
@@ -3369,6 +3534,9 @@ snd error text */
/* No comment provided by engineer. */
"No chats in list %@" = "Нет чатов в списке %@";
/* No comment provided by engineer. */
"No chats with members" = "Нет чатов с членами группы";
/* No comment provided by engineer. */
"No contacts selected" = "Контакты не выбраны";
@@ -3453,6 +3621,9 @@ snd error text */
/* No comment provided by engineer. */
"Not compatible!" = "Несовместимая версия!";
/* No comment provided by engineer. */
"not synchronized" = "не синхронизирован";
/* No comment provided by engineer. */
"Notes" = "Заметки";
@@ -3477,6 +3648,9 @@ snd error text */
/* alert title */
"Notifications status" = "Статус уведомлений";
/* No comment provided by engineer. */
"Now admins can:\n- delete members' messages.\n- disable members (\"observer\" role)" = "Теперь админы могут:\n- удалять сообщения членов.\n- приостанавливать членов (роль наблюдатель)";
/* member role */
"observer" = "читатель";
@@ -3592,6 +3766,9 @@ alert button */
/* No comment provided by engineer. */
"Open group" = "Открыть группу";
/* alert title */
"Open link?" = "Открыть ссылку?";
/* authentication reason */
"Open migration to another device" = "Открытие миграции на другое устройство";
@@ -3620,7 +3797,7 @@ alert button */
"Or securely share this file link" = "Или передайте эту ссылку";
/* No comment provided by engineer. */
"Or show this code" = "Или покажите этот код";
"Or show this code" = "Или покажите код";
/* No comment provided by engineer. */
"Or to share privately" = "Или поделиться конфиденциально";
@@ -3667,6 +3844,9 @@ alert button */
/* No comment provided by engineer. */
"Password to show" = "Пароль чтобы раскрыть";
/* past/unknown group member */
"Past member %@" = "Бывший член %@";
/* No comment provided by engineer. */
"Paste desktop address" = "Вставить адрес компьютера";
@@ -3691,6 +3871,9 @@ alert button */
/* No comment provided by engineer. */
"pending approval" = "ожидает утверждения";
/* No comment provided by engineer. */
"pending review" = "ожидает одобрения";
/* No comment provided by engineer. */
"Periodic" = "Периодически";
@@ -3760,6 +3943,9 @@ alert button */
/* token info */
"Please try to disable and re-enable notfications." = "Попробуйте выключить и снова включить уведомления.";
/* snd group event chat item */
"Please wait for group moderators to review your request to join the group." = "Пожалуйста, подождите, пока модераторы группы рассмотрят ваш запрос на вступление.";
/* token info */
"Please wait for token activation to complete." = "Пожалуйста, дождитесь завершения активации токена.";
@@ -3859,6 +4045,9 @@ alert button */
/* No comment provided by engineer. */
"Prohibit reporting messages to moderators." = "Запретить жаловаться модераторам группы.";
/* No comment provided by engineer. */
"Prohibit sending direct messages to members." = "Запретить посылать прямые сообщения членам группы.";
/* No comment provided by engineer. */
"Prohibit sending disappearing messages." = "Запретить посылать исчезающие сообщения.";
@@ -4037,6 +4226,9 @@ swipe action */
/* No comment provided by engineer. */
"Reject contact request" = "Отклонить запрос";
/* alert title */
"Reject member?" = "Отклонить участника?";
/* No comment provided by engineer. */
"rejected" = "отклонён";
@@ -4058,6 +4250,12 @@ swipe action */
/* No comment provided by engineer. */
"Remove image" = "Удалить изображение";
/* No comment provided by engineer. */
"Remove member" = "Удалить члена группы";
/* No comment provided by engineer. */
"Remove member?" = "Удалить члена группы?";
/* No comment provided by engineer. */
"Remove passphrase from keychain?" = "Удалить пароль из Keychain?";
@@ -4070,6 +4268,9 @@ swipe action */
/* profile update event chat item */
"removed contact address" = "удалён адрес контакта";
/* No comment provided by engineer. */
"removed from group" = "удален из группы";
/* profile update event chat item */
"removed profile picture" = "удалена картинка профиля";
@@ -4118,6 +4319,9 @@ swipe action */
/* No comment provided by engineer. */
"Report reason?" = "Причина сообщения?";
/* alert title */
"Report sent to moderators" = "Жалоба отправлена модераторам";
/* report reason */
"Report spam: only group moderators will see it." = "Пожаловаться на спам: увидят только модераторы группы.";
@@ -4133,6 +4337,9 @@ swipe action */
/* No comment provided by engineer. */
"Reports" = "Сообщения о нарушениях";
/* No comment provided by engineer. */
"request to join rejected" = "запрос на вступление отклонён";
/* chat list item title */
"requested to connect" = "запрошено соединение";
@@ -4187,9 +4394,21 @@ swipe action */
/* chat item action */
"Reveal" = "Показать";
/* No comment provided by engineer. */
"review" = "рассмотрение";
/* No comment provided by engineer. */
"Review conditions" = "Посмотреть условия";
/* admission stage */
"Review members" = "Одобрять членов";
/* admission stage description */
"Review members before admitting (\"knocking\")." = "Одобрять членов для вступления в группу.";
/* No comment provided by engineer. */
"reviewed by admins" = "одобрен админами";
/* No comment provided by engineer. */
"Revoke" = "Отозвать";
@@ -4218,9 +4437,15 @@ chat item action */
/* alert button */
"Save (and notify contacts)" = "Сохранить (и уведомить контакты)";
/* alert title */
"Save admission settings?" = "Сохранить настройки вступления?";
/* alert button */
"Save and notify contact" = "Сохранить и уведомить контакт";
/* No comment provided by engineer. */
"Save and notify group members" = "Сохранить и уведомить членов группы";
/* No comment provided by engineer. */
"Save and reconnect" = "Сохранить и переподключиться";
@@ -4408,11 +4633,14 @@ chat item action */
"Send questions and ideas" = "Отправьте вопросы и идеи";
/* No comment provided by engineer. */
"Send receipts" = "Отправлять отчёты о доставке";
"Send receipts" = "Отчёты о доставке";
/* No comment provided by engineer. */
"Send them from gallery or custom keyboards." = "Отправьте из галереи или из дополнительных клавиатур.";
/* No comment provided by engineer. */
"Send up to 100 last messages to new members." = "Отправить до 100 последних сообщений новым членам.";
/* alert message */
"Sender cancelled file transfer." = "Отправитель отменил передачу файла.";
@@ -4548,6 +4776,9 @@ chat item action */
/* No comment provided by engineer. */
"Set it instead of system authentication." = "Установите код вместо системной аутентификации.";
/* No comment provided by engineer. */
"Set member admission" = "Приём членов в группу";
/* No comment provided by engineer. */
"Set message expiration in chats." = "Установите срок хранения сообщений в чатах.";
@@ -4566,6 +4797,9 @@ chat item action */
/* No comment provided by engineer. */
"Set passphrase to export" = "Установите пароль";
/* No comment provided by engineer. */
"Set the message shown to new members!" = "Установить сообщение для новых членов группы!";
/* No comment provided by engineer. */
"Set timeouts for proxy/VPN" = "Установить таймауты для прокси/VPN";
@@ -4610,7 +4844,7 @@ chat item action */
"Share SimpleX address on social media." = "Поделитесь SimpleX адресом в социальных сетях.";
/* No comment provided by engineer. */
"Share this 1-time invite link" = "Поделиться одноразовой ссылкой-приглашением";
"Share this 1-time invite link" = "Поделитесь одноразовой ссылкой";
/* No comment provided by engineer. */
"Share to SimpleX" = "Поделиться в SimpleX";
@@ -4618,6 +4852,9 @@ chat item action */
/* No comment provided by engineer. */
"Share with contacts" = "Поделиться с контактами";
/* No comment provided by engineer. */
"Short link" = "Короткая ссылка";
/* No comment provided by engineer. */
"Show → on messages sent via private routing." = "Показать → на сообщениях доставленных конфиденциально.";
@@ -4660,6 +4897,9 @@ chat item action */
/* No comment provided by engineer. */
"SimpleX address or 1-time link?" = "Адрес SimpleX или одноразовая ссылка?";
/* simplex link type */
"SimpleX channel link" = "SimpleX ссылка канала";
/* No comment provided by engineer. */
"SimpleX Chat and Flux made an agreement to include Flux-operated servers into the app." = "SimpleX Chat и Flux заключили соглашение добавить серверы под управлением Flux в приложение.";
@@ -4955,6 +5195,18 @@ report reason */
/* No comment provided by engineer. */
"The ID of the next message is incorrect (less or equal to the previous).\nIt can happen because of some bug or when the connection is compromised." = "Неправильный ID предыдущего сообщения (меньше или равен предыдущему).\nЭто может произойти из-за ошибки программы, или когда соединение компроментировано.";
/* No comment provided by engineer. */
"The message will be deleted for all members." = "Сообщение будет удалено для всех членов группы.";
/* No comment provided by engineer. */
"The message will be marked as moderated for all members." = "Сообщение будет помечено как удаленное для всех членов группы.";
/* No comment provided by engineer. */
"The messages will be deleted for all members." = "Сообщения будут удалены для всех членов группы.";
/* No comment provided by engineer. */
"The messages will be marked as moderated for all members." = "Сообщения будут помечены как удаленные для всех членов группы.";
/* No comment provided by engineer. */
"The old database was not removed during the migration, it can be deleted." = "Предыдущая версия данных чата не удалена при перемещении, её можно удалить.";
@@ -5021,6 +5273,9 @@ report reason */
/* No comment provided by engineer. */
"This display name is invalid. Please choose another name." = "Ошибка имени профиля. Пожалуйста, выберите другое имя.";
/* No comment provided by engineer. */
"This group has over %lld members, delivery receipts are not sent." = "В этой группе более %lld членов, отчёты о доставке не отправляются.";
/* No comment provided by engineer. */
"This group no longer exists." = "Эта группа больше не существует.";
@@ -5030,6 +5285,9 @@ report reason */
/* No comment provided by engineer. */
"This is your own SimpleX address!" = "Это ваш собственный адрес SimpleX!";
/* No comment provided by engineer. */
"This link requires a newer app version. Please upgrade the app or ask your contact to send a compatible link." = "Эта ссылка требует новую версию. Обновите приложение или попросите Ваш контакт прислать совместимую ссылку.";
/* No comment provided by engineer. */
"This link was used with another mobile device, please create a new link on the desktop." = "Эта ссылка была использована на другом мобильном, пожалуйста, создайте новую ссылку на компьютере.";
@@ -5141,6 +5399,15 @@ report reason */
/* No comment provided by engineer. */
"Unblock for all" = "Разблокировать для всех";
/* No comment provided by engineer. */
"Unblock member" = "Разблокировать члена группы";
/* No comment provided by engineer. */
"Unblock member for all?" = "Разблокировать члена для всех?";
/* No comment provided by engineer. */
"Unblock member?" = "Разблокировать члена группы?";
/* rcv group event chat item */
"unblocked %@" = "%@ разблокирован";
@@ -5213,6 +5480,12 @@ report reason */
/* swipe action */
"Unread" = "Не прочитано";
/* No comment provided by engineer. */
"Unsupported connection link" = "Ссылка не поддерживается";
/* No comment provided by engineer. */
"Up to 100 last messages are sent to new members." = "До 100 последних сообщений отправляются новым членам.";
/* No comment provided by engineer. */
"Update" = "Обновить";
@@ -5303,6 +5576,9 @@ report reason */
/* No comment provided by engineer. */
"Use servers" = "Использовать серверы";
/* No comment provided by engineer. */
"Use short links (BETA)" = "Короткие ссылки (БЕТА)";
/* No comment provided by engineer. */
"Use SimpleX Chat servers?" = "Использовать серверы предосталенные SimpleX Chat?";
@@ -5312,6 +5588,9 @@ report reason */
/* No comment provided by engineer. */
"Use TCP port %@ when no port is specified." = "Использовать TCP-порт %@, когда порт не указан.";
/* No comment provided by engineer. */
"Use TCP port 443 for preset servers only." = "Использовать TCP-порт 443 только для серверов по умолчанию.";
/* No comment provided by engineer. */
"Use the app while in the call." = "Используйте приложение во время звонка.";
@@ -5540,6 +5819,9 @@ report reason */
/* No comment provided by engineer. */
"You accepted connection" = "Вы приняли приглашение соединиться";
/* snd group event chat item */
"you accepted this member" = "Вы приняли этого члена";
/* No comment provided by engineer. */
"You allow" = "Вы разрешаете";
@@ -5630,6 +5912,9 @@ report reason */
/* No comment provided by engineer. */
"You can set lock screen notification preview via settings." = "Вы можете установить просмотр уведомлений на экране блокировки в настройках.";
/* No comment provided by engineer. */
"You can share a link or a QR code - anybody will be able to join the group. You won't lose members of the group if you later delete it." = "Вы можете поделиться ссылкой или QR кодом - через них можно присоединиться к группе. Вы сможете удалить ссылку, сохранив членов группы, которые через нее соединились.";
/* No comment provided by engineer. */
"You can share this address with your contacts to let them connect with **%@**." = "Вы можете поделиться этим адресом с Вашими контактами, чтобы они могли соединиться с **%@**.";
@@ -5648,6 +5933,9 @@ report reason */
/* alert message */
"You can view invitation link again in connection details." = "Вы можете увидеть ссылку-приглашение снова открыв соединение.";
/* alert message */
"You can view your reports in Chat with admins." = "Вы можете найти Ваши жалобы в Чате с админами.";
/* No comment provided by engineer. */
"You can't send messages!" = "Вы не можете отправлять сообщения!";
@@ -5684,6 +5972,9 @@ report reason */
/* No comment provided by engineer. */
"You joined this group" = "Вы вступили в эту группу";
/* No comment provided by engineer. */
"You joined this group. Connecting to inviting group member." = "Вы вступили в эту группу. Устанавливается соединение с пригласившим членом группы.";
/* snd group event chat item */
"you left" = "Вы покинули группу";
@@ -5738,6 +6029,9 @@ report reason */
/* No comment provided by engineer. */
"You will be required to authenticate when you start or resume the app after 30 seconds in background." = "Вы будете аутентифицированы при запуске и возобновлении приложения, которое было 30 секунд в фоновом режиме.";
/* No comment provided by engineer. */
"You will connect to all group members." = "Вы соединитесь со всеми членами группы.";
/* No comment provided by engineer. */
"You will still receive calls and notifications from muted profiles when they are active." = "Вы все равно получите звонки и уведомления в профилях без звука, когда они активные.";
@@ -5833,4 +6127,3 @@ report reason */
/* No comment provided by engineer. */
"Your SimpleX address" = "Ваш адрес SimpleX";
+1 -1
View File
@@ -2340,7 +2340,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "ลบสมาชิกออก";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "ลบสมาชิกออก?";
/* No comment provided by engineer. */
+1 -1
View File
@@ -3782,7 +3782,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Kişiyi sil";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Kişi silinsin mi?";
/* No comment provided by engineer. */
+1 -1
View File
@@ -3854,7 +3854,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "Видалити учасника";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "Видалити учасника?";
/* No comment provided by engineer. */
+1 -1
View File
@@ -4109,7 +4109,7 @@ swipe action */
/* No comment provided by engineer. */
"Remove member" = "删除成员";
/* alert title */
/* No comment provided by engineer. */
"Remove member?" = "删除成员吗?";
/* No comment provided by engineer. */
@@ -101,13 +101,13 @@ actual fun GrayU8.toImageBitmap(): ImageBitmap = ConvertBitmap.grayToBitmap(this
actual fun ImageBitmap.hasAlpha(): Boolean = hasAlpha
actual fun ImageBitmap.addLogo(): ImageBitmap = asAndroidBitmap().applyCanvas {
val radius = (width * 0.16f) / 2
actual fun ImageBitmap.addLogo(size: Float): ImageBitmap = asAndroidBitmap().applyCanvas {
val radius = (width * size) / 2
val paint = android.graphics.Paint()
paint.color = android.graphics.Color.WHITE
drawCircle(width / 2f, height / 2f, radius, paint)
val logo = androidAppContext.resources.getDrawable(R.drawable.icon_foreground_android_common, null).toBitmap()
val logoSize = (width * 0.24).toInt()
val logoSize = (width * size * 1.5).toInt()
translate((width - logoSize) / 2f, (height - logoSize) / 2f)
drawBitmap(logo, null, android.graphics.Rect(0, 0, logoSize, logoSize), null)
}.asImageBitmap()
@@ -1570,6 +1570,16 @@ object ChatController {
return null
}
suspend fun apiAddMyAddressShortLink(rh: Long?): UserContactLinkRec? {
val userId = kotlin.runCatching { currentUserId("apiAddMyAddressShortLink") }.getOrElse { return null }
val r = sendCmd(rh, CC.ApiAddMyAddressShortLink(userId))
if (r is API.Result && r.res is CR.UserContactLink) return r.res.contactLink
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiAddMyAddressShortLink", generalGetString(MR.strings.error_creating_address), r)
}
return null
}
suspend fun userAddressAutoAccept(rh: Long?, autoAccept: AutoAccept?): UserContactLinkRec? {
val userId = kotlin.runCatching { currentUserId("userAddressAutoAccept") }.getOrElse { return null }
val r = sendCmd(rh, CC.ApiAddressAutoAccept(userId, autoAccept))
@@ -2016,6 +2026,15 @@ object ChatController {
return null
}
suspend fun apiAddGroupShortLink(rh: Long?, groupId: Long): Pair<CreatedConnLink, GroupMemberRole>? {
val r = sendCmd(rh, CC.ApiAddGroupShortLink(groupId))
if (r is API.Result && r.res is CR.GroupLink) return r.res.connLinkContact to r.res.memberRole
if (!(networkErrorAlert(r))) {
apiErrorAlert("apiAddGroupShortLink", generalGetString(MR.strings.error_creating_link_for_group), r)
}
return null
}
suspend fun apiCreateMemberContact(rh: Long?, groupId: Long, groupMemberId: Long): Contact? {
val r = sendCmd(rh, CC.APICreateMemberContact(groupId, groupMemberId))
if (r is API.Result && r.res is CR.NewMemberContact) return r.res.contact
@@ -3368,6 +3387,7 @@ sealed class CC {
class APIGroupLinkMemberRole(val groupId: Long, val memberRole: GroupMemberRole): CC()
class APIDeleteGroupLink(val groupId: Long): CC()
class APIGetGroupLink(val groupId: Long): CC()
class ApiAddGroupShortLink(val groupId: Long): CC()
class APICreateMemberContact(val groupId: Long, val groupMemberId: Long): CC()
class APISendMemberContactInvitation(val contactId: Long, val mc: MsgContent): CC()
class APITestProtoServer(val userId: Long, val server: String): CC()
@@ -3422,6 +3442,7 @@ sealed class CC {
class ApiCreateMyAddress(val userId: Long, val short: Boolean): CC()
class ApiDeleteMyAddress(val userId: Long): CC()
class ApiShowMyAddress(val userId: Long): CC()
class ApiAddMyAddressShortLink(val userId: Long): CC()
class ApiSetProfileAddress(val userId: Long, val on: Boolean): CC()
class ApiAddressAutoAccept(val userId: Long, val autoAccept: AutoAccept?): CC()
class ApiGetCallInvitations: CC()
@@ -3555,6 +3576,7 @@ sealed class CC {
is APIGroupLinkMemberRole -> "/_set link role #$groupId ${memberRole.name.lowercase()}"
is APIDeleteGroupLink -> "/_delete link #$groupId"
is APIGetGroupLink -> "/_get link #$groupId"
is ApiAddGroupShortLink -> "/_short link #$groupId"
is APICreateMemberContact -> "/_create member contact #$groupId $groupMemberId"
is APISendMemberContactInvitation -> "/_invite member contact @$contactId ${mc.cmdString}"
is APITestProtoServer -> "/_server test $userId $server"
@@ -3609,6 +3631,7 @@ sealed class CC {
is ApiCreateMyAddress -> "/_address $userId short=${onOff(short)}"
is ApiDeleteMyAddress -> "/_delete_address $userId"
is ApiShowMyAddress -> "/_show_address $userId"
is ApiAddMyAddressShortLink -> "/_short_link_address $userId"
is ApiSetProfileAddress -> "/_profile_address $userId ${onOff(on)}"
is ApiAddressAutoAccept -> "/_auto_accept $userId ${AutoAccept.cmdString(autoAccept)}"
is ApiAcceptContact -> "/_accept incognito=${onOff(incognito)} $contactReqId"
@@ -3720,6 +3743,7 @@ sealed class CC {
is APIGroupLinkMemberRole -> "apiGroupLinkMemberRole"
is APIDeleteGroupLink -> "apiDeleteGroupLink"
is APIGetGroupLink -> "apiGetGroupLink"
is ApiAddGroupShortLink -> "apiAddGroupShortLink"
is APICreateMemberContact -> "apiCreateMemberContact"
is APISendMemberContactInvitation -> "apiSendMemberContactInvitation"
is APITestProtoServer -> "testProtoServer"
@@ -3774,6 +3798,7 @@ sealed class CC {
is ApiCreateMyAddress -> "apiCreateMyAddress"
is ApiDeleteMyAddress -> "apiDeleteMyAddress"
is ApiShowMyAddress -> "apiShowMyAddress"
is ApiAddMyAddressShortLink -> "apiAddMyAddressShortLink"
is ApiSetProfileAddress -> "apiSetProfileAddress"
is ApiAddressAutoAccept -> "apiAddressAutoAccept"
is ApiAcceptContact -> "apiAcceptContact"
@@ -16,7 +16,7 @@ expect fun compressImageData(bitmap: ImageBitmap, usePng: Boolean): ByteArrayOut
expect fun GrayU8.toImageBitmap(): ImageBitmap
expect fun ImageBitmap.hasAlpha(): Boolean
expect fun ImageBitmap.addLogo(): ImageBitmap
expect fun ImageBitmap.addLogo(size: Float): ImageBitmap
expect fun ImageBitmap.scale(width: Int, height: Int): ImageBitmap
expect fun isImage(uri: URI): Boolean
@@ -68,7 +68,7 @@ private fun VerifyCodeLayout(
}
}
QRCode(connectionCode, padding = PaddingValues(vertical = DEFAULT_PADDING_HALF))
QRCode(connectionCode, small = true, padding = PaddingValues(vertical = DEFAULT_PADDING_HALF))
Row(Modifier.fillMaxWidth(), verticalAlignment = Alignment.CenterVertically) {
Spacer(Modifier.weight(2f))
@@ -49,6 +49,18 @@ fun GroupLinkView(
creatingLink = false
}
}
fun addShortLink() {
creatingLink = true
withBGApi {
val link = chatModel.controller.apiAddGroupShortLink(rhId, groupInfo.groupId)
if (link != null) {
groupLink = link.first
groupLinkMemberRole.value = link.second
onGroupLinkUpdated?.invoke(link)
}
creatingLink = false
}
}
LaunchedEffect(Unit) {
if (groupLink == null && !creatingLink) {
createLink()
@@ -60,6 +72,7 @@ fun GroupLinkView(
groupLinkMemberRole,
creatingLink,
createLink = ::createLink,
addShortLink = ::addShortLink,
updateLink = {
val role = groupLinkMemberRole.value
if (role != null) {
@@ -105,6 +118,7 @@ fun GroupLinkLayout(
groupLinkMemberRole: MutableState<GroupMemberRole?>,
creatingLink: Boolean,
createLink: () -> Unit,
addShortLink: () -> Unit,
updateLink: () -> Unit,
deleteLink: () -> Unit,
creatingGroup: Boolean = false,
@@ -182,12 +196,26 @@ fun GroupLinkLayout(
)
}
}
if (groupLink.connShortLink == null && appPreferences.privacyShortLinks.get()) {
AddShortLinkButton(addShortLink)
}
}
}
SectionBottomSpacer()
}
}
@Composable
private fun AddShortLinkButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(MR.images.ic_add),
stringResource(MR.strings.add_short_link),
onClick,
iconColor = MaterialTheme.colors.primary,
textColor = MaterialTheme.colors.primary,
)
}
@Composable
private fun RoleSelectionRow(groupInfo: GroupInfo, selectedRole: MutableState<GroupMemberRole?>, enabled: Boolean = true) {
Row(
@@ -31,6 +31,7 @@ fun SimpleXCreatedLinkQRCode(
) {
QRCode(
connLink.simplexChatUri(short),
small = short && connLink.connShortLink != null,
modifier,
padding,
tintColor,
@@ -50,6 +51,7 @@ fun SimpleXLinkQRCode(
) {
QRCode(
simplexChatLink(connReq),
small = connReq.count() < 200,
modifier,
padding,
tintColor,
@@ -61,6 +63,7 @@ fun SimpleXLinkQRCode(
@Composable
fun QRCode(
connReq: String,
small: Boolean = false,
modifier: Modifier = Modifier,
padding: PaddingValues = PaddingValues(horizontal = DEFAULT_PADDING * 2f, vertical = DEFAULT_PADDING_HALF),
tintColor: Color = Color(0xff062d56),
@@ -68,9 +71,11 @@ fun QRCode(
onShare: (() -> Unit)? = null,
) {
val scope = rememberCoroutineScope()
val logoSize = if (small) 0.21f else 0.16f
val errorLevel = if (small) QrCode.ErrorLevel.M else QrCode.ErrorLevel.L
val qr = remember(connReq, tintColor, withLogo) {
qrCodeBitmap(connReq, 1024).replaceColor(Color.Black.toArgb(), tintColor.toArgb())
.let { if (withLogo) it.addLogo() else it }
qrCodeBitmap(connReq, 1024, errorLevel).replaceColor(Color.Black.toArgb(), tintColor.toArgb())
.let { if (withLogo) it.addLogo(logoSize) else it }
}
Box(Modifier.fillMaxWidth(), contentAlignment = Alignment.Center) {
Image(
@@ -79,12 +84,13 @@ fun QRCode(
Modifier
.padding(padding)
.widthIn(max = 400.dp)
.fillMaxWidth(if (small) 0.63f else 1f)
.aspectRatio(1f)
.then(modifier)
.clickable {
scope.launch {
val image = qrCodeBitmap(connReq, 1024).replaceColor(Color.Black.toArgb(), tintColor.toArgb())
.let { if (withLogo) it.addLogo() else it }
val image = qrCodeBitmap(connReq, 1024, errorLevel).replaceColor(Color.Black.toArgb(), tintColor.toArgb())
.let { if (withLogo) it.addLogo(logoSize) else it }
val file = saveTempImageUncompressed(image, true)
if (file != null) {
shareFile("", CryptoFile.plain(file.absolutePath))
@@ -96,8 +102,8 @@ fun QRCode(
}
}
fun qrCodeBitmap(content: String, size: Int = 1024): ImageBitmap {
val qrCode = QrCodeEncoder().addAutomatic(content).setError(QrCode.ErrorLevel.L).fixate()
fun qrCodeBitmap(content: String, size: Int = 1024, errorLevel: QrCode.ErrorLevel): ImageBitmap {
val qrCode = QrCodeEncoder().addAutomatic(content).setError(errorLevel).fixate()
/** See [QrCodeGeneratorImage.initialize] and [FiducialImageEngine.configure] for size calculation */
val numModules = QrCode.totalModules(qrCode.version)
// Hide border on light themes to make it fit to the same place as camera in QRCodeScanner.
@@ -43,6 +43,7 @@ fun UserAddressView(
KeyChangeEffect(user.value?.remoteHostId, user.value?.userId) {
close()
}
fun setProfileAddress(on: Boolean) {
progressIndicator = true
withBGApi {
@@ -81,6 +82,17 @@ fun UserAddressView(
}
}
fun addShortLink() {
withBGApi {
progressIndicator = true
val userAddress = chatModel.controller.apiAddMyAddressShortLink(user.value?.remoteHostId)
if (userAddress != null) {
chatModel.userAddress.value = userAddress
}
progressIndicator = false
}
}
LaunchedEffect(autoCreateAddress) {
if (chatModel.userAddress.value == null && autoCreateAddress) {
createAddress()
@@ -95,6 +107,7 @@ fun UserAddressView(
userAddress = userAddress.value,
shareViaProfile,
createAddress = { createAddress() },
addShortLink = { addShortLink() },
learnMore = {
ModalManager.start.showModal {
UserAddressLearnMore()
@@ -169,6 +182,7 @@ private fun UserAddressLayout(
userAddress: UserContactLinkRec?,
shareViaProfile: MutableState<Boolean>,
createAddress: () -> Unit,
addShortLink: () -> Unit,
learnMore: () -> Unit,
share: (String) -> Unit,
sendEmail: (UserContactLinkRec) -> Unit,
@@ -211,6 +225,9 @@ private fun UserAddressLayout(
// ShareViaEmailButton { sendEmail(userAddress) }
BusinessAddressToggle(autoAcceptState) { saveAas(autoAcceptState.value, autoAcceptStateSaved) }
AddressSettingsButton(user, userAddress, shareViaProfile, setProfileAddress, saveAas)
if (userAddress.connLinkContact.connShortLink == null && appPreferences.privacyShortLinks.get()) {
AddShortLinkButton(addShortLink)
}
if (autoAcceptState.value.business) {
SectionTextFooter(stringResource(MR.strings.add_your_team_members_to_conversations))
@@ -248,6 +265,17 @@ private fun CreateAddressButton(onClick: () -> Unit) {
)
}
@Composable
private fun AddShortLinkButton(onClick: () -> Unit) {
SettingsActionItem(
painterResource(MR.images.ic_add),
stringResource(MR.strings.add_short_link),
onClick,
iconColor = MaterialTheme.colors.primary,
textColor = MaterialTheme.colors.primary,
)
}
@Composable
private fun CreateOneTimeLinkButton() {
val closeAll = { ModalManager.start.closeModals() }
@@ -559,6 +587,7 @@ fun PreviewUserAddressLayoutNoAddress() {
user = User.sampleData,
userAddress = null,
createAddress = {},
addShortLink = {},
share = { _ -> },
deleteAddress = {},
saveAas = { _, _ -> },
@@ -592,6 +621,7 @@ fun PreviewUserAddressLayoutAddressCreated() {
user = User.sampleData,
userAddress = UserContactLinkRec(CreatedConnLink("https://simplex.chat/contact#/?v=1&smp=smp%3A%2F%2FPQUV2eL0t7OStZOoAsPEV2QYWt4-xilbakvGUGOItUo%3D%40smp6.simplex.im%2FK1rslx-m5bpXVIdMZg9NLUZ_8JBm8xTt%23MCowBQYDK2VuAyEALDeVe-sG8mRY22LsXlPgiwTNs9dbiLrNuA7f3ZMAJ2w%3D", null)),
createAddress = {},
addShortLink = {},
share = { _ -> },
deleteAddress = {},
saveAas = { _, _ -> },
@@ -189,7 +189,7 @@ fun CustomServer(
if (valid.value) {
SectionDividerSpaced()
SectionView(stringResource(MR.strings.smp_servers_add_to_another_device).uppercase()) {
QRCode(serverAddress.value)
QRCode(serverAddress.value, small = true)
}
}
}
@@ -1058,6 +1058,7 @@
<string name="address_settings">Address settings</string>
<string name="business_address">Business address</string>
<string name="add_your_team_members_to_conversations">Add your team members to the conversations.</string>
<string name="add_short_link">Add short link</string>
<!-- CreateSimpleXAddress.kt -->
<string name="continue_to_next_step">Continue</string>
@@ -1014,7 +1014,7 @@
<string name="moderate_verb">Moderieren</string>
<string name="moderate_message_will_be_marked_warning">Diese Nachricht wird für alle Mitglieder als moderiert gekennzeichnet.</string>
<string name="you_are_observer">Sie sind Beobachter</string>
<string name="observer_cant_send_message_title">Sie können keine Nachrichten versenden!</string>
<string name="observer_cant_send_message_title">Sie sind Beobachter</string>
<string name="group_member_role_observer">Beobachter</string>
<string name="initial_member_role">Anfängliche Rolle</string>
<string name="delete_member_message__question">Nachricht des Mitglieds löschen\?</string>
@@ -2466,7 +2466,7 @@
<string name="group_new_support_chats_short">%d Chat(s)</string>
<string name="report_sent_alert_title">Meldung wurde an die Moderatoren gesendet</string>
<string name="snd_group_event_member_accepted">Sie haben dieses Mitglied übernommen</string>
<string name="admission_stage_review_descr">Überprüfung der Mitglieder vor der Aufnahme (Anklopfen).</string>
<string name="admission_stage_review_descr">Überprüfung der Mitglieder vor der Aufnahme (\"Anklopfen\").</string>
<string name="admission_stage_review">Überprüfung der Mitglieder</string>
<string name="member_criteria_all">alle</string>
<string name="member_criteria_off">Aus</string>
@@ -2489,10 +2489,25 @@
<string name="button_support_chat">Chat mit Administratoren</string>
<string name="rcv_group_event_member_accepted">%1$s übernommen</string>
<string name="accept_pending_member_alert_confirmation_as_member">Als Mitglied übernehmen</string>
<string name="error_accepting_member">Fehler beim Übernehmen eines Mitglieds</string>
<string name="error_accepting_member">Fehler beim Übernehmen des Mitglieds</string>
<string name="member_admission">Aufnahme von Mitgliedern</string>
<string name="group_member_status_pending_review">Ausstehende Überprüfung</string>
<string name="group_new_support_chat_one">Chat mit einem Mitglied</string>
<string name="accept_pending_member_button">Übernehmen</string>
<string name="snd_group_event_user_pending_review">Bitte warten Sie auf die Überprüfung Ihrer Anfrage durch die Gruppen-Moderatoren, um der Gruppe beitreten zu können.</string>
<string name="cant_send_message_group_deleted">Gruppe wird gelöscht</string>
<string name="cant_send_message_rejected">Beitrittsanfrage abgelehnt</string>
<string name="cant_send_message_mem_removed">Von der Gruppe entfernt</string>
<string name="cant_send_message_you_left">Sie haben die Gruppe verlassen</string>
<string name="cant_send_message_contact_disabled">Kontakt deaktiviert</string>
<string name="cant_send_message_contact_not_synchronized">Nicht synchronisiert</string>
<string name="error_deleting_member_support_chat">Fehler beim Löschen des Chats mit dem Mitglied</string>
<string name="cant_send_message_contact_not_ready">Kontakt nicht bereit</string>
<string name="cant_send_message_alert_title">Sie können keine Nachrichten senden!</string>
<string name="delete_member_support_chat_button">Chat löschen</string>
<string name="delete_member_support_chat_alert_title">Chat mit dem Mitglied löschen?</string>
<string name="reject_pending_member_alert_title">Mitglied ablehnen?</string>
<string name="cant_send_message_generic">Es können keine Nachrichten gesendet werden</string>
<string name="cant_send_message_contact_deleted">Kontakt gelöscht</string>
<string name="cant_send_message_member_has_old_version">Das Mitglied hat eine alte App-Version</string>
</resources>
@@ -940,7 +940,7 @@
<string name="error_updating_link_for_group">Errore nell\'aggiornamento del link del gruppo</string>
<string name="group_member_role_observer">osservatore</string>
<string name="observer_cant_send_message_desc">Contatta l\'amministratore del gruppo.</string>
<string name="observer_cant_send_message_title">Non puoi inviare messaggi!</string>
<string name="observer_cant_send_message_title">sei un osservatore</string>
<string name="language_system">Sistema</string>
<string name="button_add_welcome_message">Aggiungi messaggio di benvenuto</string>
<string name="button_welcome_message">Messaggio di benvenuto</string>
@@ -2414,14 +2414,29 @@
<string name="accept_pending_member_alert_title">Accetta membro</string>
<string name="button_support_chat">Chatta con gli amministratori</string>
<string name="accept_pending_member_alert_confirmation_as_observer">Accetta come osservatore</string>
<string name="rcv_group_event_new_member_pending_review">Il nuovo membro vuole entrare nel gruppo.</string>
<string name="rcv_group_event_new_member_pending_review">Un nuovo membro vuole entrare nel gruppo.</string>
<string name="member_criteria_all">tutti</string>
<string name="button_support_chat_member">Chatta con il membro</string>
<string name="member_support">Chat con membri</string>
<string name="error_accepting_member">Errore di accettazione del membro</string>
<string name="accept_pending_member_alert_confirmation_as_member">Accetta come membro</string>
<string name="set_member_admission">Imposta l\'ammissione del membro</string>
<string name="set_member_admission">Imposta l\'ammissione dei membri</string>
<string name="report_sent_alert_title">Segnalazione inviata ai moderatori</string>
<string name="group_member_status_pending_review">in attesa di revisione</string>
<string name="report_sent_alert_msg_view_in_support_chat">Puoi vedere i tuoi resoconti nella chat con gli amministratori.</string>
<string name="report_sent_alert_msg_view_in_support_chat">Puoi vedere le tue segnalazioni nella chat con gli amministratori.</string>
<string name="cant_send_message_alert_title">Non puoi inviare messaggi!</string>
<string name="cant_send_message_contact_not_ready">contatto non pronto</string>
<string name="cant_send_message_contact_deleted">contatto eliminato</string>
<string name="cant_send_message_contact_disabled">contatto disattivato</string>
<string name="cant_send_message_contact_not_synchronized">non sincronizzato</string>
<string name="cant_send_message_rejected">richiesta di entrare rifiutata</string>
<string name="cant_send_message_generic">impossibile inviare messaggi</string>
<string name="cant_send_message_group_deleted">il gruppo è eliminato</string>
<string name="cant_send_message_member_has_old_version">il membro ha una versione vecchia</string>
<string name="cant_send_message_mem_removed">rimosso dal gruppo</string>
<string name="cant_send_message_you_left">sei uscito/a</string>
<string name="delete_member_support_chat_alert_title">Eliminare la chat con il membro?</string>
<string name="reject_pending_member_alert_title">Rifiutare il membro?</string>
<string name="delete_member_support_chat_button">Elimina chat</string>
<string name="error_deleting_member_support_chat">Errore di eliminazione della chat con il membro</string>
</resources>
@@ -1019,7 +1019,7 @@
<string name="moderate_message_will_be_deleted_warning">Сообщение будет удалено для всех членов группы.</string>
<string name="moderate_message_will_be_marked_warning">Сообщение будет помечено как удаленное для всех членов группы.</string>
<string name="observer_cant_send_message_desc">Пожалуйста, свяжитесь с админом группы.</string>
<string name="observer_cant_send_message_title">Вы не можете отправлять сообщения!</string>
<string name="observer_cant_send_message_title">Вы \"читатель\"</string>
<string name="you_are_observer">только чтение сообщений</string>
<string name="group_member_role_observer">читатель</string>
<string name="initial_member_role">Роль при вступлении</string>
@@ -1132,7 +1132,7 @@
<string name="decryption_error">Ошибка расшифровки</string>
<string name="lock_not_enabled">Блокировка SimpleX не включена!</string>
<string name="alert_title_msg_bad_hash">Ошибка хэш сообщения</string>
<string name="alert_text_msg_bad_hash">Хэш предыдущего сообщения отличается\"</string>
<string name="alert_text_msg_bad_hash">Хэш предыдущего сообщения отличается.</string>
<string name="confirm_passcode">Подтвердить код</string>
<string name="incorrect_passcode">Неправильный код</string>
<string name="lock_after">Заблокировать через</string>
@@ -2467,4 +2467,58 @@
<string name="onboarding_conditions_private_chats_not_accessible">Частные разговоры, группы и Ваши контакты недоступны для операторов серверов.</string>
<string name="onboarding_conditions_configure_server_operators">Настроить операторов серверов</string>
<string name="onboarding_conditions_privacy_policy_and_conditions_of_use">Политика конфиденциальности и условия использования.</string>
<string name="member_criteria_all">все</string>
<string name="accept_pending_member_button">Принять</string>
<string name="accept_pending_member_alert_question">Участник хочет присоединиться к группе. Принять?</string>
<string name="cant_send_message_group_deleted">группа удалена</string>
<string name="cant_send_message_mem_removed">удален из группы</string>
<string name="group_new_support_chats_short">%d чата(ов)</string>
<string name="cant_send_message_contact_not_ready">контакт не готов</string>
<string name="cant_send_message_contact_deleted">контакт удален</string>
<string name="cant_send_message_contact_not_synchronized">не синхронизирован</string>
<string name="cant_send_message_rejected">запрос на вступление отклонён</string>
<string name="rcv_group_event_new_member_pending_review">Новый участник хочет присоединиться к группе.</string>
<string name="snd_group_event_user_pending_review">Пожалуйста, подождите, пока модераторы группы рассмотрят ваш запрос на вступление.</string>
<string name="group_member_status_pending_review">ожидает одобрения</string>
<string name="reject_pending_member_button">Отклонить</string>
<string name="reject_pending_member_alert_title">Отклонить участника?</string>
<string name="error_deleting_member_support_chat">Ошибка при удалении чата с членом группы</string>
<string name="full_link_button_text">Полная ссылка</string>
<string name="error_accepting_member">Ошибка вступления члена группы</string>
<string name="unsupported_connection_link">Ссылка не поддерживается</string>
<string name="link_requires_newer_app_version_please_upgrade">Эта ссылка требует новую версию. Обновите приложение или попросите Ваш контакт прислать совместимую ссылку.</string>
<string name="group_new_support_messages">%d сообщений</string>
<string name="report_sent_alert_msg_view_in_support_chat">Вы можете найти Ваши жалобы в Чате с админами.</string>
<string name="button_support_chat">Чат с админами</string>
<string name="button_support_chat_member">Чат с членом группы</string>
<string name="member_criteria_off">выключено</string>
<string name="admission_stage_review">Одобрять членов</string>
<string name="member_support">Чаты с членами группы</string>
<string name="member_admission">Приём членов в группу</string>
<string name="admission_stage_review_descr">Одобрять членов для вступления в группу.</string>
<string name="no_support_chats">Нет чатов с членами группы</string>
<string name="accept_pending_member_alert_confirmation_as_observer">Принять как читателя</string>
<string name="accept_pending_member_alert_confirmation_as_member">Принять в группу</string>
<string name="accept_pending_member_alert_title">Принять члена</string>
<string name="reviewed_by_admins">одобрен админами</string>
<string name="report_sent_alert_title">Жалоба отправлена модераторам</string>
<string name="cant_send_message_you_left">Вы вышли</string>
<string name="cant_send_message_generic">нельзя отправлять</string>
<string name="group_new_support_chats">%d чатов с членами группы</string>
<string name="cant_send_message_contact_disabled">контакт выключен</string>
<string name="cant_send_message_member_has_old_version">член имеет старую версию</string>
<string name="cant_send_message_alert_title">Вы не можете отправлять сообщения!</string>
<string name="short_link_button_text">Короткая ссылка</string>
<string name="save_admission_question">Сохранить настройки вступления?</string>
<string name="snd_group_event_member_accepted">Вы приняли этого члена</string>
<string name="group_member_status_pending_review_short">рассмотрение</string>
<string name="set_member_admission">Установить вступление в группу</string>
<string name="delete_member_support_chat_alert_title">Удалить чат с членом группы?</string>
<string name="delete_member_support_chat_button">Удалить разговор</string>
<string name="rcv_group_event_member_accepted">принят %1$s</string>
<string name="support_chat">Чат с админами</string>
<string name="rcv_group_event_user_accepted">Вы приняты</string>
<string name="group_new_support_chat_one">1 чат с членом группы</string>
<string name="simplex_link_channel">SimpleX ссылка канала</string>
<string name="privacy_short_links">Короткие ссылки (БЕТА)</string>
</resources>
@@ -929,7 +929,7 @@
<string name="group_member_role_observer">观察员</string>
<string name="you_are_observer">你是观察者</string>
<string name="error_updating_link_for_group">更新群链接错误</string>
<string name="observer_cant_send_message_title">无法发送消息!</string>
<string name="observer_cant_send_message_title">是观察员</string>
<string name="initial_member_role">初始角色</string>
<string name="observer_cant_send_message_desc">请联系群管理员。</string>
<string name="language_system">系统</string>
@@ -2408,4 +2408,19 @@
<string name="report_sent_alert_msg_view_in_support_chat">你可以在和管理员和聊天中查看你的举报。</string>
<string name="accept_pending_member_alert_confirmation_as_observer">接受为观察员</string>
<string name="group_new_support_chat_one">和一名成员的一个聊天</string>
<string name="cant_send_message_generic">无法发送消息</string>
<string name="cant_send_message_you_left">你离开了</string>
<string name="error_deleting_member_support_chat">删除和成员的聊天出错</string>
<string name="cant_send_message_alert_title">你无法发送消息!</string>
<string name="cant_send_message_contact_disabled">禁用了联系人</string>
<string name="cant_send_message_group_deleted">群被删除了</string>
<string name="cant_send_message_mem_removed">从群被删除了</string>
<string name="cant_send_message_rejected">加入请求被拒绝</string>
<string name="delete_member_support_chat_button">删除聊天</string>
<string name="delete_member_support_chat_alert_title">删除和成员的聊天吗?</string>
<string name="cant_send_message_contact_not_synchronized">未同步</string>
<string name="cant_send_message_member_has_old_version">成员有旧版本</string>
<string name="cant_send_message_contact_deleted">删除了联系人</string>
<string name="cant_send_message_contact_not_ready">联系人未就绪</string>
<string name="reject_pending_member_alert_title">拒绝成员?</string>
</resources>
@@ -133,9 +133,9 @@ actual fun ImageBitmap.hasAlpha(): Boolean {
return false
}
actual fun ImageBitmap.addLogo(): ImageBitmap {
val radius = (width * 0.16f).toInt()
val logoSize = (width * 0.24).toInt()
actual fun ImageBitmap.addLogo(size: Float): ImageBitmap {
val radius = (width * size).toInt()
val logoSize = (width * size * 1.5).toInt()
val logo: BufferedImage = MR.images.icon_foreground_common.image
val original = toAwtImage()
val withLogo = BufferedImage(width, height, original.type)
+4 -4
View File
@@ -24,11 +24,11 @@ android.nonTransitiveRClass=true
kotlin.mpp.androidSourceSetLayoutVersion=2
kotlin.jvm.target=11
android.version_name=6.4-beta.0
android.version_code=290
android.version_name=6.4-beta.1
android.version_code=291
desktop.version_name=6.4-beta.0
desktop.version_code=102
desktop.version_name=6.4-beta.1
desktop.version_code=103
kotlin.version=1.9.23
gradle.plugin.version=8.2.0
@@ -713,7 +713,7 @@ directoryServiceEvent st opts@DirectoryOpts {adminUsers, superUsers, serviceName
case mRole_ of
Nothing ->
getGroupLinkRole cc user g >>= \case
Just (_, CCLink gLink _, mRole) -> do
Just (_, CCLink gLink _, _, mRole) -> do
let anotherRole = case mRole of GRObserver -> GRMember; _ -> GRObserver
sendReply $
initialRole n mRole
@@ -1045,7 +1045,7 @@ vr :: ChatController -> VersionRangeChat
vr ChatController {config = ChatConfig {chatVRange}} = chatVRange
{-# INLINE vr #-}
getGroupLinkRole :: ChatController -> User -> GroupInfo -> IO (Maybe (Int64, CreatedLinkContact, GroupMemberRole))
getGroupLinkRole :: ChatController -> User -> GroupInfo -> IO (Maybe (Int64, CreatedLinkContact, GroupLinkId, GroupMemberRole))
getGroupLinkRole cc user gInfo =
withDB "getGroupLink" cc $ \db -> getGroupLink db user gInfo
+1 -1
View File
@@ -12,7 +12,7 @@ constraints: zip +disable-bzip2 +disable-zstd
source-repository-package
type: git
location: https://github.com/simplex-chat/simplexmq.git
tag: d352d518c2b3a42bc7a298954dde799422e1457f
tag: 4c33d8ac43874660c342c00cb078bd3814d5d9eb
source-repository-package
type: git
+3 -1
View File
@@ -10,11 +10,13 @@ vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/linu
mkdir $vlc_dir || exit 0
vlc_tag='v3.0.21-1'
vlc_url="https://github.com/simplex-chat/vlc/releases/download/${vlc_tag}/vlc-linux-x86_64.appimage"
cd /tmp
mkdir tmp 2>/dev/null || true
cd tmp
curl --tlsv1.2 https://github.com/cmatomic/VLCplayer-AppImage/releases/download/3.0.11.1/VLC_media_player-3.0.11.1-x86_64.AppImage -L -o appimage
curl --tlsv1.2 "${vlc_url}" -L -o appimage
chmod +x appimage
./appimage --appimage-extract
cp -r squashfs-root/usr/lib/* $vlc_dir
+4 -2
View File
@@ -9,7 +9,9 @@ if [ "$ARCH" == "arm64" ]; then
else
vlc_arch=intel64
fi
vlc_version=3.0.19
vlc_tag='v3.0.21-1'
vlc_url="https://github.com/simplex-chat/vlc/releases/download/${vlc_tag}/vlc-macos-${ARCH}.zip"
function readlink() {
echo "$(cd "$(dirname "$1")"; pwd -P)"
@@ -23,7 +25,7 @@ mkdir -p $vlc_dir/vlc || exit 0
cd /tmp
mkdir tmp 2>/dev/null || true
cd tmp
curl --tlsv1.2 https://github.com/simplex-chat/vlc/releases/download/v$vlc_version/vlc-macos-$ARCH.zip -L -o vlc
curl --tlsv1.2 "${vlc_url}" -L -o vlc
unzip -oqq vlc
install_name_tool -add_rpath "@loader_path/VLC.app/Contents/MacOS/lib" vlc-cache-gen
cd VLC.app/Contents/MacOS/lib
+4 -1
View File
@@ -10,10 +10,13 @@ vlc_dir=$root_dir/apps/multiplatform/common/src/commonMain/cpp/desktop/libs/wind
rm -rf $vlc_dir
mkdir -p $vlc_dir/vlc || exit 0
vlc_tag='v3.0.21-1'
vlc_url="https://github.com/simplex-chat/vlc/releases/download/${vlc_tag}/vlc-win-x86_64.zip"
cd /tmp
mkdir tmp 2>/dev/null || true
cd tmp
curl --tlsv1.2 https://irltoolkit.mm.fcix.net/videolan-ftp/vlc/3.0.18/win64/vlc-3.0.18-win64.zip -L -o vlc
curl --tlsv1.2 "${vlc_url}" -L -o vlc
$WINDIR\\System32\\tar.exe -xf vlc
cd vlc-*
# Setting the same date as the date that will be on the file after extraction from JAR to make VLC cache checker happy
+1 -1
View File
@@ -1,5 +1,5 @@
{
"https://github.com/simplex-chat/simplexmq.git"."d352d518c2b3a42bc7a298954dde799422e1457f" = "1rha84pfpaqx3mf218szkfra334vhijqf17hanxqmp1sicfbf1x3";
"https://github.com/simplex-chat/simplexmq.git"."4c33d8ac43874660c342c00cb078bd3814d5d9eb" = "0223qr6vz3vigcx9279kmfxjwb7vwfsqgd7qz271wz86sfxg9hip";
"https://github.com/simplex-chat/hs-socks.git"."a30cc7a79a08d8108316094f8f2f82a0c5e1ac51" = "0yasvnr7g91k76mjkamvzab2kvlb1g5pspjyjn2fr6v83swjhj38";
"https://github.com/simplex-chat/direct-sqlcipher.git"."f814ee68b16a9447fbb467ccc8f29bdd3546bfd9" = "1ql13f4kfwkbaq7nygkxgw84213i0zm7c1a8hwvramayxl38dq5d";
"https://github.com/simplex-chat/sqlcipher-simple.git"."a46bd361a19376c5211f1058908fc0ae6bf42446" = "1z0r78d8f0812kxbgsm735qf6xx8lvaz27k1a0b4a2m0sshpd5gl";
+1 -1
View File
@@ -256,6 +256,7 @@ library
, constraints >=0.12 && <0.14
, containers ==0.6.*
, crypton ==0.34.*
, crypton-x509 ==1.7.*
, data-default ==0.7.*
, directory ==1.3.*
, email-validate ==2.3.*
@@ -271,7 +272,6 @@ library
, optparse-applicative >=0.15 && <0.17
, random >=1.1 && <1.3
, record-hasfield ==1.0.*
, scientific ==0.3.7.*
, simple-logger ==0.1.*
, simplexmq >=6.3
, socks ==0.6.*
+1
View File
@@ -44,6 +44,7 @@ import Simplex.Messaging.Agent.Env.SQLite (AgentConfig (..), InitialAgentServers
import Simplex.Messaging.Agent.Protocol
import Simplex.Messaging.Agent.Store.Common (DBStore (dbNew))
import qualified Simplex.Messaging.Agent.Store.DB as DB
import Simplex.Messaging.Agent.Store.Entity
import Simplex.Messaging.Agent.Store.Shared (MigrationConfirmation (..), MigrationError)
import Simplex.Messaging.Client (defaultNetworkConfig)
import qualified Simplex.Messaging.Crypto as C
+6 -3
View File
@@ -87,7 +87,7 @@ import Simplex.Messaging.Notifications.Protocol (DeviceToken (..), NtfTknStatus)
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, parseAll, parseString, sumTypeJSON)
import Simplex.Messaging.Protocol (AProtoServerWithAuth, AProtocolType (..), MsgId, NMsgMeta (..), NtfServer, ProtocolType (..), QueueId, SMPMsgMeta (..), SubscriptionMode (..), XFTPServer)
import Simplex.Messaging.TMap (TMap)
import Simplex.Messaging.Transport (TLS, simplexMQVersion)
import Simplex.Messaging.Transport (TLS, TransportPeer (..), simplexMQVersion)
import Simplex.Messaging.Transport.Client (SocksProxyWithAuth, TransportHost)
import Simplex.Messaging.Util (allFinally, catchAllErrors, catchAllErrors', tryAllErrors, tryAllErrors', (<$$>))
import Simplex.RemoteControl.Client
@@ -373,6 +373,7 @@ data ChatCommand
| APIGroupLinkMemberRole GroupId GroupMemberRole
| APIDeleteGroupLink GroupId
| APIGetGroupLink GroupId
| APIAddGroupShortLink GroupId
| APICreateMemberContact GroupId GroupMemberId
| APISendMemberContactInvitation {contactId :: ContactId, msgContent_ :: Maybe MsgContent}
| GetUserProtoServers AProtocolType
@@ -461,6 +462,7 @@ data ChatCommand
| DeleteMyAddress
| APIShowMyAddress UserId
| ShowMyAddress
| APIAddMyAddressShortLink UserId
| APISetProfileAddress UserId Bool
| SetProfileAddress Bool
| APIAddressAutoAccept UserId (Maybe AutoAccept)
@@ -854,6 +856,7 @@ data ChatEvent
| CEvtChatErrors {chatErrors :: [ChatError]}
| CEvtTimedAction {action :: String, durationMilliseconds :: Int64}
| CEvtTerminalEvent TerminalEvent
| CEvtCustomChatEvent {user_ :: Maybe User, response :: Text}
deriving (Show)
data TerminalEvent
@@ -1412,7 +1415,7 @@ data RemoteCtrlSession
{ remoteCtrlId_ :: Maybe RemoteCtrlId,
ctrlDeviceName :: Text,
rcsClient :: RCCtrlClient,
tls :: TLS,
tls :: TLS 'TClient,
sessionCode :: Text,
rcsWaitSession :: Async (),
rcsWaitConfirmation :: TMVar (Either RCErrorType (RCCtrlSession, RCCtrlPairing))
@@ -1420,7 +1423,7 @@ data RemoteCtrlSession
| RCSessionConnected
{ remoteCtrlId :: RemoteCtrlId,
rcsClient :: RCCtrlClient,
tls :: TLS,
tls :: TLS 'TClient,
rcsSession :: RCCtrlSession,
http2Server :: Async (),
remoteOutputQ :: TBQueue (Either ChatError ChatEvent)
+38 -11
View File
@@ -88,6 +88,7 @@ import Simplex.FileTransfer.Description (FileDescriptionURI (..), maxFileSize, m
import Simplex.Messaging.Agent as Agent
import Simplex.Messaging.Agent.Env.SQLite (ServerCfg (..), ServerRoles (..), allRoles)
import Simplex.Messaging.Agent.Protocol
import Simplex.Messaging.Agent.Store.Entity
import Simplex.Messaging.Agent.Store.Interface (execSQL)
import Simplex.Messaging.Agent.Store.Shared (upMigration)
import qualified Simplex.Messaging.Agent.Store.DB as DB
@@ -197,7 +198,7 @@ startChatController mainApp enableSndFiles = do
startExpireCIThread user
setExpireCIFlag user True
where
shouldExpireChats =
shouldExpireChats =
fmap (fromRight False) $ runExceptT $ withStore' $ \db -> do
ttl <- getChatItemTTL db user
ttlCount <- getChatTTLCount db user
@@ -1796,9 +1797,9 @@ processChatCommand' vr = \case
CreateMyAddress short -> withUser $ \User {userId} ->
processChatCommand $ APICreateMyAddress userId short
APIDeleteMyAddress userId -> withUserId userId $ \user@User {profile = p} -> do
conns <- withFastStore $ \db -> getUserAddressConnections db vr user
conn <- withFastStore $ \db -> getUserAddressConnection db vr user
withChatLock "deleteMyAddress" $ do
deleteAgentConnectionsAsync $ map aConnId conns
deleteAgentConnectionAsync $ aConnId conn
withFastStore' (`deleteUserAddress` user)
let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing}
r <- updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing
@@ -1812,6 +1813,17 @@ processChatCommand' vr = \case
CRUserContactLink user <$> withFastStore (`getUserAddress` user)
ShowMyAddress -> withUser' $ \User {userId} ->
processChatCommand $ APIShowMyAddress userId
APIAddMyAddressShortLink userId -> withUserId' userId $ \user -> do
(ucl@UserContactLink {connLinkContact = CCLink connFullLink sLnk_}, conn) <-
withFastStore $ \db -> (,) <$> getUserAddress db user <*> getUserAddressConnection db vr user
when (isJust sLnk_) $ throwCmdError "address already has short link"
sLnk <- shortenShortLink' =<< withAgent (\a -> setConnShortLink a (aConnId conn) SCMContact "" Nothing)
case entityId conn of
Just uclId -> do
withFastStore' $ \db -> setUserContactLinkShortLink db uclId sLnk
let ucl' = (ucl :: UserContactLink) {connLinkContact = CCLink connFullLink (Just sLnk)}
pure $ CRUserContactLink user ucl'
Nothing -> throwChatError $ CEException "no user contact link id"
APISetProfileAddress userId False -> withUserId userId $ \user@User {profile = p} -> do
let p' = (fromLocalProfile p :: Profile) {contactLink = Nothing}
updateProfile_ user p' $ withFastStore' $ \db -> setUserProfileContactLink db user Nothing
@@ -2390,7 +2402,7 @@ processChatCommand' vr = \case
pure $ CRGroupLinkCreated user gInfo ccLink' mRole
APIGroupLinkMemberRole groupId mRole' -> withUser $ \user -> withGroupLock "groupLinkMemberRole" groupId $ do
gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId
(groupLinkId, groupLink, mRole) <- withFastStore $ \db -> getGroupLink db user gInfo
(groupLinkId, groupLink, _, mRole) <- withFastStore $ \db -> getGroupLink db user gInfo
assertUserGroupRole gInfo GRAdmin
when (mRole' > GRMember) $ throwChatError $ CEGroupMemberInitialRole gInfo mRole'
when (mRole' /= mRole) $ withFastStore' $ \db -> setGroupLinkMemberRole db user groupLinkId mRole'
@@ -2401,8 +2413,20 @@ processChatCommand' vr = \case
pure $ CRGroupLinkDeleted user gInfo
APIGetGroupLink groupId -> withUser $ \user -> do
gInfo <- withFastStore $ \db -> getGroupInfo db vr user groupId
(_, groupLink, mRole) <- withFastStore $ \db -> getGroupLink db user gInfo
(_, groupLink, _, mRole) <- withFastStore $ \db -> getGroupLink db user gInfo
pure $ CRGroupLink user gInfo groupLink mRole
APIAddGroupShortLink groupId -> withUser $ \user -> do
(gInfo, (uclId, _gLink@(CCLink connFullLink sLnk_), gLinkId, mRole), conn) <- withFastStore $ \db -> do
gInfo <- getGroupInfo db vr user groupId
gLink <- getGroupLink db user gInfo
conn <- getGroupLinkConnection db vr user gInfo
pure (gInfo, gLink, conn)
when (isJust sLnk_) $ throwCmdError "group link already has short link"
let crClientData = encodeJSON $ CRDataGroup gLinkId
sLnk <- shortenShortLink' =<< toShortGroupLink <$> withAgent (\a -> setConnShortLink a (aConnId conn) SCMContact "" (Just crClientData))
withFastStore' $ \db -> setUserContactLinkShortLink db uclId sLnk
let groupLink' = CCLink connFullLink (Just sLnk)
pure $ CRGroupLink user gInfo groupLink' mRole
APICreateMemberContact gId gMemberId -> withUser $ \user -> do
(g, m) <- withFastStore $ \db -> (,) <$> getGroupInfo db vr user gId <*> getGroupMember db vr user gId gMemberId
assertUserGroupRole g GRAuthor
@@ -3273,13 +3297,14 @@ processChatCommand' vr = \case
CSLContact _ ct srv linkKey -> CSLContact SLSServer ct srv linkKey
restoreShortLink' l = (`restoreShortLink` l) <$> asks (shortLinkPresetServers . config)
shortLinkUserData short = if short then Just "" else Nothing
shortenShortLink' :: ConnShortLink m -> CM (ConnShortLink m)
shortenShortLink' l = (`shortenShortLink` l) <$> asks (shortLinkPresetServers . config)
shortenCreatedLink :: CreatedConnLink m -> CM (CreatedConnLink m)
shortenCreatedLink (CCLink cReq sLnk) = CCLink cReq <$> mapM (\l -> (`shortenShortLink` l) <$> asks (shortLinkPresetServers . config)) sLnk
shortenCreatedLink (CCLink cReq sLnk) = CCLink cReq <$> mapM shortenShortLink' sLnk
createdGroupLink :: CreatedLinkContact -> CreatedLinkContact
createdGroupLink (CCLink cReq shortLink) = CCLink cReq (toGroupLink <$> shortLink)
where
toGroupLink :: ShortLinkContact -> ShortLinkContact
toGroupLink (CSLContact sch _ srv k) = CSLContact sch CCTGroup srv k
createdGroupLink (CCLink cReq shortLink) = CCLink cReq (toShortGroupLink <$> shortLink)
toShortGroupLink :: ShortLinkContact -> ShortLinkContact
toShortGroupLink (CSLContact sch _ srv k) = CSLContact sch CCTGroup srv k
updateCIGroupInvitationStatus :: User -> GroupInfo -> CIGroupInvitationStatus -> CM ()
updateCIGroupInvitationStatus user GroupInfo {groupId} newStatus = do
AChatItem _ _ cInfo ChatItem {content, meta = CIMeta {itemId}} <- withFastStore $ \db -> getChatItemByGroupId db vr user groupId
@@ -3659,7 +3684,7 @@ startExpireCIThread user@User {userId} = do
liftIO $ threadDelay' interval
setChatItemsExpiration :: User -> Int64 -> Int -> CM' ()
setChatItemsExpiration user newTTL ttlCount
setChatItemsExpiration user newTTL ttlCount
| newTTL > 0 || ttlCount > 0 = do
startExpireCIThread user
whenM chatStarted $ setExpireCIFlag user True
@@ -4222,6 +4247,7 @@ chatCommandP =
"/_set link role #" *> (APIGroupLinkMemberRole <$> A.decimal <*> memberRole),
"/_delete link #" *> (APIDeleteGroupLink <$> A.decimal),
"/_get link #" *> (APIGetGroupLink <$> A.decimal),
"/_short link #" *> (APIAddGroupShortLink <$> A.decimal),
"/create link #" *> (CreateGroupLink <$> displayNameP <*> (memberRole <|> pure GRMember) <*> shortP),
"/set link role #" *> (GroupLinkMemberRole <$> displayNameP <*> memberRole),
"/delete link #" *> (DeleteGroupLink <$> displayNameP),
@@ -4277,6 +4303,7 @@ chatCommandP =
("/delete_address" <|> "/da") $> DeleteMyAddress,
"/_show_address " *> (APIShowMyAddress <$> A.decimal),
("/show_address" <|> "/sa") $> ShowMyAddress,
"/_short_link_address " *> (APIAddMyAddressShortLink <$> A.decimal),
"/_profile_address " *> (APISetProfileAddress <$> A.decimal <* A.space <*> onOffP),
("/profile_address " <|> "/pa ") *> (SetProfileAddress <$> onOffP),
"/_auto_accept " *> (APIAddressAutoAccept <$> A.decimal <* A.space <*> autoAcceptP),
+2 -61
View File
@@ -37,7 +37,6 @@ import qualified Data.List.NonEmpty as L
import Data.Map.Strict (Map)
import qualified Data.Map.Strict as M
import Data.Maybe (fromMaybe, isNothing, mapMaybe)
import Data.Scientific (floatingOrInteger)
import Data.Set (Set)
import qualified Data.Set as S
import Data.Text (Text)
@@ -46,11 +45,11 @@ import Data.Time (addUTCTime)
import Data.Time.Clock (UTCTime, nominalDay)
import Language.Haskell.TH.Syntax (lift)
import Simplex.Chat.Operators.Conditions
import Simplex.Chat.Options.DB (FromField (..), ToField (..))
import Simplex.Chat.Types (User)
import Simplex.Chat.Types.Util (textParseJSON)
import Simplex.Messaging.Agent.Env.SQLite (ServerCfg (..), ServerRoles (..), allRoles)
import Simplex.Messaging.Agent.Store.DB (fromTextField_)
import Simplex.Messaging.Agent.Store.DB (FromField (..), ToField (..), fromTextField_)
import Simplex.Messaging.Agent.Store.Entity
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, sumTypeJSON)
import Simplex.Messaging.Protocol (AProtocolType (..), ProtoServerWithAuth (..), ProtocolServer (..), ProtocolType (..), ProtocolTypeI, SProtocolType (..), UserProtocol)
@@ -69,32 +68,6 @@ usageConditionsText =
in [|stripFrontMatter $(lift (safeDecodeUtf8 s))|]
)
data DBStored = DBStored | DBNew
data SDBStored (s :: DBStored) where
SDBStored :: SDBStored 'DBStored
SDBNew :: SDBStored 'DBNew
deriving instance Show (SDBStored s)
class DBStoredI s where sdbStored :: SDBStored s
instance DBStoredI 'DBStored where sdbStored = SDBStored
instance DBStoredI 'DBNew where sdbStored = SDBNew
data DBEntityId' (s :: DBStored) where
DBEntityId :: Int64 -> DBEntityId' 'DBStored
DBNewEntity :: DBEntityId' 'DBNew
deriving instance Show (DBEntityId' s)
deriving instance Eq (DBEntityId' s)
type DBEntityId = DBEntityId' 'DBStored
type DBNewEntity = DBEntityId' 'DBNew
data OperatorTag = OTSimplex | OTFlux
deriving (Eq, Ord, Show)
@@ -118,19 +91,6 @@ instance TextEncoding OperatorTag where
OTSimplex -> "simplex"
OTFlux -> "flux"
-- this and other types only define instances of serialization for known DB IDs only,
-- entities without IDs cannot be serialized to JSON
instance FromField DBEntityId
#if defined(dbPostgres)
where
fromField f dat = DBEntityId <$> fromField f dat
#else
where
fromField f = DBEntityId <$> fromField f
#endif
instance ToField DBEntityId where toField (DBEntityId i) = toField i
data UsageConditions = UsageConditions
{ conditionsId :: Int64,
conditionsCommit :: Text,
@@ -486,25 +446,6 @@ validateUserServers curr others = currUserErrs <> concatMap otherUserErrs others
userServers :: (UserServersClass u, UserProtocol p) => SProtocolType p -> [u] -> [AUserServer p]
userServers p = map aUserServer' . concatMap (servers' p)
instance ToJSON (DBEntityId' s) where
toEncoding = \case
DBEntityId i -> toEncoding i
DBNewEntity -> JE.null_
toJSON = \case
DBEntityId i -> toJSON i
DBNewEntity -> J.Null
instance DBStoredI s => FromJSON (DBEntityId' s) where
parseJSON v = case (v, sdbStored @s) of
(J.Null, SDBNew) -> pure DBNewEntity
(J.Number n, SDBStored) -> case floatingOrInteger n of
Left (_ :: Double) -> fail "bad DBEntityId"
Right i -> pure $ DBEntityId (fromInteger i)
_ -> fail "bad DBEntityId"
omittedField = case sdbStored @s of
SDBStored -> Nothing
SDBNew -> Just DBNewEntity
$(JQ.deriveJSON defaultJSON ''UsageConditions)
$(JQ.deriveJSON (sumTypeJSON $ dropPrefix "CA") ''ConditionsAcceptance)
+1
View File
@@ -9,6 +9,7 @@ import Data.List.NonEmpty (NonEmpty)
import qualified Data.List.NonEmpty as L
import Simplex.Chat.Operators
import Simplex.Messaging.Agent.Env.SQLite (ServerRoles (..), allRoles)
import Simplex.Messaging.Agent.Store.Entity
import Simplex.Messaging.Protocol (ProtocolType (..), SMPServer)
operatorSimpleXChat :: NewServerOperator
+3 -3
View File
@@ -58,7 +58,7 @@ import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
import qualified Simplex.Messaging.Crypto.File as CF
import Simplex.Messaging.Encoding.String (StrEncoding (..))
import qualified Simplex.Messaging.TMap as TM
import Simplex.Messaging.Transport (TLS, closeConnection, tlsUniq)
import Simplex.Messaging.Transport (TLS, TransportPeer (..), closeConnection, tlsUniq)
import Simplex.Messaging.Transport.HTTP2.Client (HTTP2ClientError, closeHTTP2Client)
import Simplex.Messaging.Transport.HTTP2.Server (HTTP2Request (..))
import Simplex.Messaging.Util
@@ -184,7 +184,7 @@ startRemoteHost rh_ rcAddrPrefs_ port_ = do
action `catchChatError` \err -> do
logError $ "startRemoteHost.waitForHostSession crashed: " <> tshow err
readTVarIO rhKeyVar >>= cancelRemoteHostSession (Just (sessSeq, RHSRCrashed err))
waitForHostSession :: Maybe RemoteHostInfo -> RHKey -> SessionSeq -> Maybe RCCtrlAddress -> TVar RHKey -> RCStepTMVar (ByteString, TLS, RCStepTMVar (RCHostSession, RCHostHello, RCHostPairing)) -> CM ()
waitForHostSession :: Maybe RemoteHostInfo -> RHKey -> SessionSeq -> Maybe RCCtrlAddress -> TVar RHKey -> RCStepTMVar (ByteString, TLS 'TServer, RCStepTMVar (RCHostSession, RCHostHello, RCHostPairing)) -> CM ()
waitForHostSession remoteHost_ rhKey sseq rcAddr_ rhKeyVar vars = do
(sessId, tls, vars') <- timeoutThrow (ChatErrorRemoteHost rhKey RHETimeout) 60000000 $ takeRCStep vars
let sessionCode = verificationCode sessId
@@ -474,7 +474,7 @@ connectRemoteCtrl verifiedInv@(RCVerifiedInvitation inv@RCInvitation {ca, app})
where
validateRemoteCtrl RCInvitation {idkey} RemoteCtrl {ctrlPairing = RCCtrlPairing {idPubKey}} =
unless (idkey == idPubKey) $ throwError $ ChatErrorRemoteCtrl $ RCEProtocolError $ PRERemoteControl RCEIdentity
waitForCtrlSession :: Maybe RemoteCtrl -> Text -> RCCtrlClient -> RCStepTMVar (ByteString, TLS, RCStepTMVar (RCCtrlSession, RCCtrlPairing)) -> CM ()
waitForCtrlSession :: Maybe RemoteCtrl -> Text -> RCCtrlClient -> RCStepTMVar (ByteString, TLS 'TClient, RCStepTMVar (RCCtrlSession, RCCtrlPairing)) -> CM ()
waitForCtrlSession rc_ ctrlName rcsClient vars = do
(uniq, tls, rcsWaitConfirmation) <- timeoutThrow (ChatErrorRemoteCtrl RCETimeout) networkIOTimeout $ takeRCStep vars
let sessionCode = verificationCode uniq
+3 -3
View File
@@ -8,18 +8,18 @@
module Simplex.Chat.Remote.RevHTTP where
import Simplex.Messaging.Transport (TLS)
import Simplex.Messaging.Transport (TLS, TransportPeer (..))
import Simplex.Messaging.Transport.HTTP2 (defaultHTTP2BufferSize, getHTTP2Body)
import Simplex.Messaging.Transport.HTTP2.Client (HTTP2Client, HTTP2ClientError (..), attachHTTP2Client, bodyHeadSize, connTimeout, defaultHTTP2ClientConfig)
import Simplex.Messaging.Transport.HTTP2.Server (HTTP2Request (..), runHTTP2ServerWith)
import Simplex.RemoteControl.Discovery
attachRevHTTP2Client :: IO () -> TLS -> IO (Either HTTP2ClientError HTTP2Client)
attachRevHTTP2Client :: IO () -> TLS 'TServer -> IO (Either HTTP2ClientError HTTP2Client)
attachRevHTTP2Client disconnected = attachHTTP2Client config ANY_ADDR_V4 "0" disconnected defaultHTTP2BufferSize
where
config = defaultHTTP2ClientConfig {bodyHeadSize = doNotPrefetchHead, connTimeout = maxBound}
attachHTTP2Server :: TLS -> (HTTP2Request -> IO ()) -> IO ()
attachHTTP2Server :: TLS 'TClient -> (HTTP2Request -> IO ()) -> IO ()
attachHTTP2Server tls processRequest =
runHTTP2ServerWith defaultHTTP2BufferSize ($ tls) $ \sessionId sessionALPN r sendResponse -> do
reqBody <- getHTTP2Body r doNotPrefetchHead
+5 -5
View File
@@ -25,7 +25,7 @@ import Simplex.Chat.Types (verificationCode)
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Crypto.File (CryptoFile)
import Simplex.Messaging.Parsers (defaultJSON, dropPrefix, enumJSON, sumTypeJSON)
import Simplex.Messaging.Transport (TLS (..), TSbChainKeys (..))
import Simplex.Messaging.Transport (TLS (..), TSbChainKeys (..), TransportPeer (..))
import Simplex.Messaging.Transport.HTTP2.Client (HTTP2Client)
import qualified Simplex.Messaging.TMap as TM
import Simplex.RemoteControl.Client
@@ -102,11 +102,11 @@ data RHPendingSession = RHPendingSession
data RemoteHostSession
= RHSessionStarting
| RHSessionConnecting {invitation :: Text, rhPendingSession :: RHPendingSession}
| RHSessionPendingConfirmation {sessionCode :: Text, tls :: TLS, rhPendingSession :: RHPendingSession}
| RHSessionConfirmed {tls :: TLS, rhPendingSession :: RHPendingSession}
| RHSessionPendingConfirmation {sessionCode :: Text, tls :: TLS 'TServer, rhPendingSession :: RHPendingSession}
| RHSessionConfirmed {tls :: TLS 'TServer, rhPendingSession :: RHPendingSession}
| RHSessionConnected
{ rchClient :: RCHostClient,
tls :: TLS,
tls :: TLS 'TServer,
rhClient :: RemoteHostClient,
pollAction :: Async (),
storePath :: FilePath
@@ -128,7 +128,7 @@ rhsSessionState = \case
RHSessionConfirmed {tls} -> RHSConfirmed {sessionCode = tlsSessionCode tls}
RHSessionConnected {tls} -> RHSConnected {sessionCode = tlsSessionCode tls}
tlsSessionCode :: TLS -> Text
tlsSessionCode :: TLS p -> Text
tlsSessionCode = verificationCode . tlsUniq
data RemoteProtocolError
+3 -3
View File
@@ -256,12 +256,12 @@ deleteGroupLink db User {userId} GroupInfo {groupId} = do
(userId, groupId)
DB.execute db "DELETE FROM user_contact_links WHERE user_id = ? AND group_id = ?" (userId, groupId)
getGroupLink :: DB.Connection -> User -> GroupInfo -> ExceptT StoreError IO (Int64, CreatedLinkContact, GroupMemberRole)
getGroupLink :: DB.Connection -> User -> GroupInfo -> ExceptT StoreError IO (Int64, CreatedLinkContact, GroupLinkId, GroupMemberRole)
getGroupLink db User {userId} gInfo@GroupInfo {groupId} =
ExceptT . firstRow groupLink (SEGroupLinkNotFound gInfo) $
DB.query db "SELECT user_contact_link_id, conn_req_contact, short_link_contact, group_link_member_role FROM user_contact_links WHERE user_id = ? AND group_id = ? LIMIT 1" (userId, groupId)
DB.query db "SELECT user_contact_link_id, conn_req_contact, short_link_contact, group_link_id, group_link_member_role FROM user_contact_links WHERE user_id = ? AND group_id = ? LIMIT 1" (userId, groupId)
where
groupLink (linkId, cReq, shortLink, mRole_) = (linkId, CCLink cReq shortLink, fromMaybe GRMember mRole_)
groupLink (linkId, cReq, shortLink, gLinkId, mRole_) = (linkId, CCLink cReq shortLink, gLinkId, fromMaybe GRMember mRole_)
getGroupLinkId :: DB.Connection -> User -> GroupInfo -> IO (Maybe GroupLinkId)
getGroupLinkId db User {userId} GroupInfo {groupId} =
+31 -23
View File
@@ -43,7 +43,7 @@ module Simplex.Chat.Store.Profiles
setUserProfileContactLink,
getUserContactProfiles,
createUserContactLink,
getUserAddressConnections,
getUserAddressConnection,
getUserContactLinks,
deleteUserAddress,
getUserAddress,
@@ -51,6 +51,7 @@ module Simplex.Chat.Store.Profiles
getGroupLinkInfo,
getUserContactLinkByConnReq,
getUserContactLinkViaShortLink,
setUserContactLinkShortLink,
getContactWithoutConnViaAddress,
updateUserAddressAutoAccept,
getProtocolServers,
@@ -110,6 +111,7 @@ import qualified Simplex.Messaging.Crypto.Ratchet as CR
import Simplex.Messaging.Encoding.String
import Simplex.Messaging.Parsers (defaultJSON)
import Simplex.Messaging.Protocol (BasicAuth (..), ProtoServerWithAuth (..), ProtocolServer (..), ProtocolType (..), ProtocolTypeI (..), SProtocolType (..), SubscriptionMode)
import Simplex.Messaging.Agent.Store.Entity
import Simplex.Messaging.Transport.Client (TransportHost)
import Simplex.Messaging.Util (eitherToMaybe, safeDecodeUtf8)
#if defined(dbPostgres)
@@ -363,26 +365,21 @@ createUserContactLink db User {userId} agentConnId (CCLink cReq shortLink) subMo
userContactLinkId <- insertedRowId db
void $ createConnection_ db userId ConnUserContact (Just userContactLinkId) agentConnId ConnNew initialChatVersion chatInitialVRange Nothing Nothing Nothing 0 currentTs subMode CR.PQSupportOff
getUserAddressConnections :: DB.Connection -> VersionRangeChat -> User -> ExceptT StoreError IO [Connection]
getUserAddressConnections db vr User {userId} = do
cs <- liftIO getUserAddressConnections_
if null cs then throwError SEUserContactLinkNotFound else pure cs
where
getUserAddressConnections_ :: IO [Connection]
getUserAddressConnections_ =
map (toConnection vr)
<$> DB.query
db
[sql|
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
FROM connections c
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|]
(userId, userId)
getUserAddressConnection :: DB.Connection -> VersionRangeChat -> User -> ExceptT StoreError IO Connection
getUserAddressConnection db vr User {userId} = do
ExceptT . firstRow (toConnection vr) SEUserContactLinkNotFound $
DB.query
db
[sql|
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
FROM connections c
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
|]
(userId, userId)
getUserContactLinks :: DB.Connection -> VersionRangeChat -> User -> IO [(Connection, UserContact)]
getUserContactLinks db vr User {userId} =
@@ -504,14 +501,14 @@ toGroupLinkInfo (groupId_, mRole_) =
getGroupLinkInfo :: DB.Connection -> UserId -> GroupId -> IO (Maybe GroupLinkInfo)
getGroupLinkInfo db userId groupId =
fmap join $ maybeFirstRow toGroupLinkInfo $
fmap join $ maybeFirstRow toGroupLinkInfo $
DB.query
db
[sql|
SELECT group_id, group_link_member_role
FROM user_contact_links
WHERE user_id = ? AND group_id = ?
|]
|]
(userId, groupId)
getUserContactLinkByConnReq :: DB.Connection -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe UserContactLink)
@@ -531,6 +528,17 @@ userContactLinkQuery =
FROM user_contact_links
|]
setUserContactLinkShortLink :: DB.Connection -> Int64 -> ShortLinkContact -> IO ()
setUserContactLinkShortLink db userContactLinkId shortLink =
DB.execute
db
[sql|
UPDATE user_contact_links
SET short_link_contact = ?
WHERE user_contact_link_id = ?
|]
(shortLink, userContactLinkId)
getContactWithoutConnViaAddress :: DB.Connection -> VersionRangeChat -> User -> (ConnReqContact, ConnReqContact) -> IO (Maybe Contact)
getContactWithoutConnViaAddress db vr user@User {userId} (cReqSchema1, cReqSchema2) = do
ctId_ <-
+3 -2
View File
@@ -10,6 +10,7 @@ import Control.Monad.Except
import Data.Int (Int64)
import Data.Text (Text)
import Data.Text.Encoding (decodeASCII, encodeUtf8)
import qualified Data.X509 as X
import Data.Word (Word16)
import Simplex.Chat.Remote.Types
import Simplex.Chat.Store.Shared
@@ -66,7 +67,7 @@ remoteHostQuery =
FROM remote_hosts
|]
toRemoteHost :: (Int64, Text, FilePath, C.APrivateSignKey, C.SignedObject C.Certificate, C.PrivateKeyEd25519, C.KeyHash, C.PublicKeyX25519, Maybe Text, Maybe Text, Maybe Word16) -> RemoteHost
toRemoteHost :: (Int64, Text, FilePath, C.APrivateSignKey, C.SignedObject X.Certificate, C.PrivateKeyEd25519, C.KeyHash, C.PublicKeyX25519, Maybe Text, Maybe Text, Maybe Word16) -> RemoteHost
toRemoteHost (remoteHostId, hostDeviceName, storePath, caKey, C.SignedObject caCert, idPrivKey, hostFingerprint, hostDhPubKey, ifaceName_, ifaceAddr_, bindPort_) =
RemoteHost {remoteHostId, hostDeviceName, storePath, hostPairing, bindAddress_, bindPort_}
where
@@ -133,7 +134,7 @@ toRemoteCtrl ::
( RemoteCtrlId,
Text,
C.APrivateSignKey,
C.SignedObject C.Certificate,
C.SignedObject X.Certificate,
C.KeyHash,
C.PublicKeyEd25519,
C.PrivateKeyX25519,
@@ -501,19 +501,6 @@ Query:
Plan:
SEARCH messages USING INDEX idx_messages_group_id_shared_msg_id (group_id=? AND shared_msg_id=?)
Query:
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
FROM connections c
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
Plan:
SEARCH uc USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
Query:
SELECT chat_item_id, contact_id, group_id, group_scope_tag, group_scope_group_member_id, note_folder_id
FROM chat_items
@@ -2823,6 +2810,19 @@ Plan:
SEARCH uc USING INDEX idx_user_contact_links_group_id (group_id=?)
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
Query:
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
c.created_at, c.security_code, c.security_code_verified_at, c.pq_support, c.pq_encryption, c.pq_snd_enabled, c.pq_rcv_enabled, c.auth_err_counter, c.quota_err_counter,
c.conn_chat_version, c.peer_chat_min_version, c.peer_chat_max_version
FROM connections c
JOIN user_contact_links uc ON c.user_contact_link_id = uc.user_contact_link_id
WHERE c.user_id = ? AND uc.user_id = ? AND uc.local_display_name = '' AND uc.group_id IS NULL
Plan:
SEARCH uc USING INDEX sqlite_autoindex_user_contact_links_1 (user_id=? AND local_display_name=?)
SEARCH c USING INDEX idx_connections_user_contact_link_id (user_contact_link_id=?)
Query:
SELECT c.connection_id, c.agent_conn_id, c.conn_level, c.via_contact, c.via_user_contact_link, c.via_group_link, c.group_link_id, c.custom_user_profile_id,
c.conn_status, c.conn_type, c.contact_conn_initiated, c.local_alias, c.contact_id, c.group_member_id, c.snd_file_id, c.rcv_file_id, c.user_contact_link_id,
@@ -5794,7 +5794,7 @@ Query: SELECT user_contact_link_id FROM contact_requests WHERE contact_request_i
Plan:
SEARCH contact_requests USING INTEGER PRIMARY KEY (rowid=?)
Query: SELECT user_contact_link_id, conn_req_contact, short_link_contact, group_link_member_role FROM user_contact_links WHERE user_id = ? AND group_id = ? LIMIT 1
Query: SELECT user_contact_link_id, conn_req_contact, short_link_contact, group_link_id, group_link_member_role FROM user_contact_links WHERE user_id = ? AND group_id = ? LIMIT 1
Plan:
SEARCH user_contact_links USING INDEX idx_user_contact_links_group_id (group_id=?)
+2
View File
@@ -60,6 +60,7 @@ import qualified Simplex.FileTransfer.Transport as XFTP
import Simplex.Messaging.Agent.Client (ProtocolTestFailure (..), ProtocolTestStep (..), SubscriptionsInfo (..))
import Simplex.Messaging.Agent.Env.SQLite (NetworkConfig (..), ServerRoles (..))
import Simplex.Messaging.Agent.Protocol
import Simplex.Messaging.Agent.Store.Entity
import Simplex.Messaging.Client (SMPProxyFallback, SMPProxyMode (..), SocksMode (..))
import qualified Simplex.Messaging.Crypto as C
import Simplex.Messaging.Crypto.File (CryptoFile (..), CryptoFileArgs (..))
@@ -532,6 +533,7 @@ chatEventToView hu ChatConfig {logLevel, showReactions, showReceipts, testView}
ttyUser u ["sent file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e]
TERcvFileSubError u RcvFileTransfer {fileId, fileInvitation = FileInvitation {fileName}} e ->
ttyUser u ["received file " <> sShow fileId <> " (" <> plain fileName <> ") error: " <> sShow e]
CEvtCustomChatEvent u r -> ttyUser' u $ map plain $ T.lines r
where
ttyUser :: User -> [StyledString] -> [StyledString]
ttyUser user@User {showNtfs, activeUser, viewPwdHash} ss
+1
View File
@@ -23,6 +23,7 @@ import Simplex.Chat.Operators.Presets
import Simplex.Chat.Types
import Simplex.FileTransfer.Client.Presets (defaultXFTPServers)
import Simplex.Messaging.Agent.Env.SQLite (ServerRoles (..), allRoles)
import Simplex.Messaging.Agent.Store.Entity
import Simplex.Messaging.Protocol
import Test.Hspec
+5 -5
View File
@@ -16,7 +16,7 @@
"simplex-explained-tab-2-p-1": "Minden kapcsolathoz két különböző üzenetküldési sorbaállítást használ a különböző kiszolgálókon keresztül történő üzenetküldéshez és -fogadáshoz.",
"simplex-explained-tab-2-p-2": "A kiszolgálók csak egyetlen irányba továbbítják az üzeneteket, anélkül, hogy teljes képet kapnának a felhasználók beszélgetéseiről vagy kapcsolatairól.",
"simplex-explained-tab-3-p-1": "A kiszolgálók minden egyes üzenetsorbaállításhoz külön névtelen hitelesítő-adatokkal rendelkeznek, és nem tudják, hogy melyik felhasználóhoz tartoznak.",
"simplex-explained-tab-3-p-2": "A felhasználók tovább fokozhatják a metaadatok adatvédelmét, ha a Tor segítségével férnek hozzá a kiszolgálókhoz, így megakadályozva az IP-cím szerinti korrelációt.",
"simplex-explained-tab-3-p-2": "A felhasználók tovább fokozhatják a metaadatok adatvédelmét, ha a Tor-hálózat használatával férnek hozzá a kiszolgálókhoz, így megakadályozva az IP-cím szerinti korrelációt.",
"smp-protocol": "SMP-protokoll",
"chat-protocol": "Csevegési protokoll",
"donate": "Adományozás",
@@ -59,7 +59,7 @@
"simplex-private-card-3-point-1": "A kliens és a kiszolgálók közötti kapcsolatokhoz csak az erős algoritmusokkal rendelkező TLS 1.2/1.3 protokollt használja.",
"simplex-private-card-3-point-2": "A kiszolgáló ujjlenyomata és a csatornakötés megakadályozza a MITM- és a visszajátszási támadásokat.",
"simplex-private-card-3-point-3": "Az újrakapcsolódás le van tiltva a munkamenet elleni támadások megelőzése érdekében.",
"simplex-private-card-4-point-1": "Az IP-címe védelme érdekében a kiszolgálókat a Tor hálózaton vagy más átvitelátfedő hálózaton keresztül is elérheti.",
"simplex-private-card-4-point-1": "Az IP-címe védelme érdekében a kiszolgálókat a Tor-hálózaton vagy más átvitelátfedő hálózaton keresztül is elérheti.",
"simplex-private-card-6-point-1": "Számos kommunikációs hálózat sebezhető a kiszolgálók vagy a hálózat-szolgáltatók MITM-támadásaival szemben.",
"simplex-private-card-6-point-2": "Ennek megakadályozása érdekében a SimpleX-alkalmazások egyszeri kulcsokat adnak át sávon kívül, amikor egy címet hivatkozásként vagy QR-kódként oszt meg.",
"simplex-private-card-7-point-1": "Az integritás garantálása érdekében az üzenetek sorszámozással vannak ellátva, és tartalmazzák az előző üzenet hasítóértékét.",
@@ -87,7 +87,7 @@
"simplex-unique-4-overlay-1-title": "Teljesen decentralizált &mdash; a SimpleX-hálózat a felhasználóké",
"hero-overlay-card-1-p-1": "Sok felhasználó kérdezte: <em>ha a SimpleXnek nincsenek felhasználói azonosítói, honnan tudja, hogy hová kell eljuttatni az üzeneteket?</em>",
"hero-overlay-card-1-p-2": "Az üzenetek kézbesítéséhez az összes többi hálózat által használt felhasználói azonosítók helyett a SimpleX az üzenetek sorbaállításához ideiglenes, névtelen, páros azonosítókat használ, külön-külön minden egyes kapcsolathoz &mdash; nincsenek hosszú távú azonosítók.",
"hero-overlay-card-1-p-4": "Ez a kialakítás megakadályozza a felhasználók metaadatainak kiszivárgását az alkalmazás szintjén. Az adatvédelem további javítása és az IP-cím védelme érdekében az üzenetküldő kiszolgálókhoz Tor hálózaton keresztül is kapcsolódhat.",
"hero-overlay-card-1-p-4": "Ez a kialakítás megakadályozza a felhasználók metaadatainak kiszivárgását az alkalmazás szintjén. Az adatvédelem további javítása és az IP-cím védelme érdekében az üzenetküldő kiszolgálókhoz Tor-hálózaton keresztül is kapcsolódhat.",
"hero-overlay-card-1-p-5": "Csak a kliensek tárolják a felhasználói profilokat, kapcsolatokat és csoportokat; az üzenetek küldése 2 rétegű végpontok közötti titkosítással történik.",
"hero-overlay-card-1-p-6": "További leírást a <a href='https://github.com/simplex-chat/simplexmq/blob/stable/protocol/overview-tjr.md' target='_blank'>SimpleX ismertetőben</a> olvashat.",
"hero-overlay-card-2-p-1": "Ha a felhasználók állandó azonosítóval rendelkeznek, még akkor is, ha ez csak egy véletlenszerű szám, például egy munkamenet-azonosító, fennáll annak a veszélye, hogy a szolgáltató vagy egy támadó megfigyelheti, azt hogy hogyan kapcsolódnak a felhasználók egymáshoz, és hány üzenetet küldenek egymásnak.",
@@ -114,7 +114,7 @@
"privacy-matters-overlay-card-3-p-4": "Nem elég csak egy végpontok között titkosított üzenetváltó-alkalmazást használnunk, mindannyiunknak olyan üzenetváltó-alkalmazásokat kell használnunk, amelyek védik a személyes partnereink magánéletét &mdash; akikkel kapcsolatban állunk.",
"simplex-unique-overlay-card-1-p-1": "Más üzenetküldő hálózatoktól eltérően a SimpleX <strong>nem rendel azonosítókat a felhasználókhoz</strong>. Nem támaszkodik telefonszámokra, tartomány-alapú címekre (mint az e-mail, XMPP vagy a Matrix), felhasználónevekre, nyilvános kulcsokra vagy akár véletlenszerű számokra a felhasználók azonosításához &mdash; a SimpleX-kiszolgálók üzemeltetői nem tudják, hogy hányan használják a kiszolgálóikat.",
"simplex-unique-overlay-card-1-p-2": "Az üzenetek kézbesítéséhez a SimpleX az egyirányú üzenet várakoztatást használ <a href='https://csrc.nist.gov/glossary/term/Pairwise_Pseudonymous_Identifier'>páronkénti névtelen címekkel</a>, külön a fogadott és külön az elküldött üzenetek számára, általában különböző kiszolgálókon keresztül.",
"simplex-unique-overlay-card-1-p-3": "Ez a kialakítás megvédi annak titkosságát, hogy kivel kommunikál, elrejtve azt a SimpleX-hálózat kiszolgálói és a megfigyelők elől. IP-címének a kiszolgálók elől való elrejtéséhez azt teheti meg, hogy <strong> Toron keresztül kapcsolódik a SimpleX-kiszolgálókhoz</strong>.",
"simplex-unique-overlay-card-1-p-3": "Ez a kialakítás védi partnerének adatait, elrejtve azt a SimpleX-hálózat kiszolgálói és a külső megfigyelők elől. Az IP-címe elrejtésének érdekében a<strong>Tor-hálózaton keresztül is kapcsolódhat a SimpleX-kiszolgálókhoz</strong>.",
"simplex-unique-overlay-card-2-p-1": "Mivel ön nem rendelkezik azonosítóval a SimpleX-hálózaton, senki sem tud kapcsolatba lépni önnel, hacsak nem oszt meg egy egyszeri vagy ideiglenes felhasználói címet, például QR-kódot vagy hivatkozást.",
"simplex-unique-overlay-card-2-p-2": "Még a felhasználói cím használata esetén is, aminek használata nem kötelező ugyanakkor ez a kéretlen kapcsolatkérelmek küldésére is használható módosíthatja vagy teljesen törölheti anélkül, hogy elveszítené a meglévő kapcsolatait.",
"simplex-unique-overlay-card-3-p-1": "A SimpleX Chat az összes felhasználói adatot kizárólag a klienseken tárolja egy <strong>hordozható titkosított adatbázis-formátumban</strong>, amely exportálható és átvihető bármely más támogatott eszközre.",
@@ -256,4 +256,4 @@
"stable-and-beta-versions-built-by-developers": "A fejlesztők által készített stabil és béta verziók",
"hero-overlay-card-3-p-3": "A Trail of Bits 2024 júliusában ismét auditálta a SimpleX-protokollok kriptográfiai és hálózati komponenseit. <a href=\"/blog/20241014-simplex-network-v6-1-security-review-better-calls-user-experience.html\">További információk</a>.",
"docs-dropdown-14": "SimpleX üzleti célra"
}
}
+1 -1
View File
@@ -218,7 +218,7 @@
"simplex-network-overlay-card-1-li-6": "Сети P2P могут быть уязвимы для <a href='https://www.usenix.org/conference/woot15/workshop-program/presentation/p2p-file-sharing-hell-exploiting-bittorrent'>DRDoS атаки</a>, когда клиенты могут ретранслировать и увеличивать трафик, что приводит к отказу всей сети. Клиенты SimpleX ретранслируют трафик только из известного соединения и не могут быть использованы злоумышленником для создания трафика во всей сети.",
"if-you-already-installed-simplex-chat-for-the-terminal": "Если Вы уже установили SimpleX Chat для терминала",
"docs-dropdown-8": "Служба Каталогов SimpleX",
"simplex-private-card-1-point-1": "Протокол двойного обновления ключей &mdash;<br>\"отрицаемые\" сообщения с идеальной прямой секретностью и восстановлением после взлома",
"simplex-private-card-1-point-1": "Протокол двойного обновления ключей &mdash;<br>\"отрицаемые\" сообщения с идеальной прямой секретностью и восстановлением после взлома.",
"simplex-private-card-8-point-1": "Серверы SimpleX действуют как узлы-миксеры с низкой задержкой &mdash; входящие и исходящие сообщения имеют разный порядок.",
"simplex-unique-overlay-card-2-p-1": "Поскольку у Вас нет идентификатора в сети SimpleX, никто не сможет связаться с Вами, если Вы сами не предоставите одноразовый или временный адрес в виде QR-кода или ссылки.",
"sign-up-to-receive-our-updates": "Подпишитесь на нашу рассылку новостей",