Merge branch 'database-refactor' into add-documents-section

This commit is contained in:
Ryan Zhao 2022-07-18 16:43:15 +10:00
commit b7d17da9cc
61 changed files with 1664 additions and 521 deletions

View File

@ -0,0 +1,251 @@
#!/usr/bin/xcrun --sdk macosx swift
//
// ListLocalizableStrings.swift
// Archa
//
// Created by Morgan Pretty on 18/5/20.
// Copyright © 2020 Archa. All rights reserved.
//
// This script is based on https://github.com/ginowu7/CleanSwiftLocalizableExample the main difference
// is canges to the localized usage regex
import Foundation
let fileManager = FileManager.default
let currentPath = (
ProcessInfo.processInfo.environment["PROJECT_DIR"] ?? fileManager.currentDirectoryPath
)
/// List of files in currentPath - recursive
var pathFiles: [String] = {
guard let enumerator = fileManager.enumerator(atPath: currentPath), let files = enumerator.allObjects as? [String] else {
fatalError("Could not locate files in path directory: \(currentPath)")
}
return files
}()
/// List of localizable files - not including Localizable files in the Pods
var localizableFiles: [String] = {
return pathFiles
.filter {
$0.hasSuffix("Localizable.strings") &&
!$0.contains(".app/") && // Exclude Built Localizable.strings files
!$0.contains("Pods") // Exclude Pods
}
}()
/// List of executable files
var executableFiles: [String] = {
return pathFiles.filter {
!$0.localizedCaseInsensitiveContains("test") && // Exclude test files
!$0.contains(".app/") && // Exclude Built Localizable.strings files
!$0.contains("Pods") && // Exclude Pods
(
NSString(string: $0).pathExtension == "swift" ||
NSString(string: $0).pathExtension == "m"
)
}
}()
/// Reads contents in path
///
/// - Parameter path: path of file
/// - Returns: content in file
func contents(atPath path: String) -> String {
print("Path: \(path)")
guard let data = fileManager.contents(atPath: path), let content = String(data: data, encoding: .utf8) else {
fatalError("Could not read from path: \(path)")
}
return content
}
/// Returns a list of strings that match regex pattern from content
///
/// - Parameters:
/// - pattern: regex pattern
/// - content: content to match
/// - Returns: list of results
func regexFor(_ pattern: String, content: String) -> [String] {
guard let regex = try? NSRegularExpression(pattern: pattern, options: []) else {
fatalError("Regex not formatted correctly: \(pattern)")
}
let matches = regex.matches(in: content, options: [], range: NSRange(location: 0, length: content.utf16.count))
return matches.map {
guard let range = Range($0.range(at: 0), in: content) else {
fatalError("Incorrect range match")
}
return String(content[range])
}
}
func create() -> [LocalizationStringsFile] {
return localizableFiles.map(LocalizationStringsFile.init(path:))
}
///
///
/// - Returns: A list of LocalizationCodeFile - contains path of file and all keys in it
func localizedStringsInCode() -> [LocalizationCodeFile] {
return executableFiles.compactMap {
let content = contents(atPath: $0)
// Note: Need to exclude escaped quotation marks from strings
let matchesOld = regexFor("(?<=NSLocalizedString\\()\\s*\"(?!.*?%d)(.*?)\"", content: content)
let matchesNew = regexFor("\"(?!.*?%d)([^(\\\")]*?)\"(?=\\s*)(?=\\.localized)", content: content)
let allMatches = (matchesOld + matchesNew)
return allMatches.isEmpty ? nil : LocalizationCodeFile(path: $0, keys: Set(allMatches))
}
}
/// Throws error if ALL localizable files does not have matching keys
///
/// - Parameter files: list of localizable files to validate
func validateMatchKeys(_ files: [LocalizationStringsFile]) {
print("------------ Validating keys match in all localizable files ------------")
guard let base = files.first, files.count > 1 else { return }
let files = Array(files.dropFirst())
files.forEach {
guard let extraKey = Set(base.keys).symmetricDifference($0.keys).first else { return }
let incorrectFile = $0.keys.contains(extraKey) ? $0 : base
printPretty("error: Found extra key: \(extraKey) in file: \(incorrectFile.path)")
}
}
/// Throws error if localizable files are missing keys
///
/// - Parameters:
/// - codeFiles: Array of LocalizationCodeFile
/// - localizationFiles: Array of LocalizableStringFiles
func validateMissingKeys(_ codeFiles: [LocalizationCodeFile], localizationFiles: [LocalizationStringsFile]) {
print("------------ Checking for missing keys -----------")
guard let baseFile = localizationFiles.first else {
fatalError("Could not locate base localization file")
}
let baseKeys = Set(baseFile.keys)
codeFiles.forEach {
let extraKeys = $0.keys.subtracting(baseKeys)
if !extraKeys.isEmpty {
printPretty("error: Found keys in code missing in strings file: \(extraKeys) from \($0.path)")
}
}
}
/// Throws warning if keys exist in localizable file but are not being used
///
/// - Parameters:
/// - codeFiles: Array of LocalizationCodeFile
/// - localizationFiles: Array of LocalizableStringFiles
func validateDeadKeys(_ codeFiles: [LocalizationCodeFile], localizationFiles: [LocalizationStringsFile]) {
print("------------ Checking for any dead keys in localizable file -----------")
guard let baseFile = localizationFiles.first else {
fatalError("Could not locate base localization file")
}
let baseKeys: Set<String> = Set(baseFile.keys)
let allCodeFileKeys: [String] = codeFiles.flatMap { $0.keys }
let deadKeys: [String] = Array(baseKeys.subtracting(allCodeFileKeys))
.sorted()
.map { $0.trimmingCharacters(in: CharacterSet(charactersIn: "\"")) }
if !deadKeys.isEmpty {
printPretty("warning: \(deadKeys) - Suggest cleaning dead keys")
}
}
protocol Pathable {
var path: String { get }
}
struct LocalizationStringsFile: Pathable {
let path: String
let kv: [String: String]
var keys: [String] {
return Array(kv.keys)
}
init(path: String) {
self.path = path
self.kv = ContentParser.parse(path)
}
/// Writes back to localizable file with sorted keys and removed whitespaces and new lines
func cleanWrite() {
print("------------ Sort and remove whitespaces: \(path) ------------")
let content = kv.keys.sorted().map { "\($0) = \(kv[$0]!);" }.joined(separator: "\n")
try! content.write(toFile: path, atomically: true, encoding: .utf8)
}
}
struct LocalizationCodeFile: Pathable {
let path: String
let keys: Set<String>
}
struct ContentParser {
/// Parses contents of a file to localizable keys and values - Throws error if localizable file have duplicated keys
///
/// - Parameter path: Localizable file paths
/// - Returns: localizable key and value for content at path
static func parse(_ path: String) -> [String: String] {
print("------------ Checking for duplicate keys: \(path) ------------")
let content = contents(atPath: path)
let trimmed = content
.replacingOccurrences(of: "\n+", with: "", options: .regularExpression, range: nil)
.trimmingCharacters(in: .whitespacesAndNewlines)
let keys = regexFor("\"([^\"]*?)\"(?= =)", content: trimmed)
let values = regexFor("(?<== )\"(.*?)\"(?=;)", content: trimmed)
if keys.count != values.count {
fatalError("Error parsing contents: Make sure all keys and values are in correct format (this could be due to extra spaces between keys and values)")
}
return zip(keys, values).reduce(into: [String: String]()) { results, keyValue in
if results[keyValue.0] != nil {
printPretty("error: Found duplicate key: \(keyValue.0) in file: \(path)")
abort()
}
results[keyValue.0] = keyValue.1
}
}
}
func printPretty(_ string: String) {
print(string.replacingOccurrences(of: "\\", with: ""))
}
let stringFiles = create()
if !stringFiles.isEmpty {
print("------------ Found \(stringFiles.count) file(s) ------------")
stringFiles.forEach { print($0.path) }
validateMatchKeys(stringFiles)
// Note: Uncomment the below file to clean out all comments from the localizable file (we don't want this because comments make it readable...)
// stringFiles.forEach { $0.cleanWrite() }
let codeFiles = localizedStringsInCode()
validateMissingKeys(codeFiles, localizationFiles: stringFiles)
validateDeadKeys(codeFiles, localizationFiles: stringFiles)
}
print("------------ SUCCESS ------------")

View File

@ -783,6 +783,7 @@
FDD250702837199200198BDA /* GarbageCollectionJob.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD2506F2837199200198BDA /* GarbageCollectionJob.swift */; };
FDD250722837234B00198BDA /* MediaGalleryNavigationController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */; };
FDE72118286C156E0093DF33 /* ChatSettingsViewController.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE72117286C156E0093DF33 /* ChatSettingsViewController.swift */; };
FDE72154287FE4470093DF33 /* HighlightMentionBackgroundView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE72153287FE4470093DF33 /* HighlightMentionBackgroundView.swift */; };
FDE77F6B280FEB28002CFC5D /* ControlMessageProcessRecord.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */; };
FDED2E3C282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDED2E3B282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift */; };
FDF0B73C27FFD3D6004C14C5 /* LinkPreview.swift in Sources */ = {isa = PBXBuildFile; fileRef = FDF0B73B27FFD3D6004C14C5 /* LinkPreview.swift */; };
@ -1815,6 +1816,9 @@
FDD2506F2837199200198BDA /* GarbageCollectionJob.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = GarbageCollectionJob.swift; sourceTree = "<group>"; };
FDD250712837234B00198BDA /* MediaGalleryNavigationController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MediaGalleryNavigationController.swift; sourceTree = "<group>"; };
FDE72117286C156E0093DF33 /* ChatSettingsViewController.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ChatSettingsViewController.swift; sourceTree = "<group>"; };
FDE7214F287E50D50093DF33 /* ProtoWrappers.py */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = text.script.python; path = ProtoWrappers.py; sourceTree = "<group>"; };
FDE72150287E50D50093DF33 /* LintLocalizableStrings.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = LintLocalizableStrings.swift; sourceTree = "<group>"; };
FDE72153287FE4470093DF33 /* HighlightMentionBackgroundView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HighlightMentionBackgroundView.swift; sourceTree = "<group>"; };
FDE77F68280F9EDA002CFC5D /* JobRunnerError.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = JobRunnerError.swift; sourceTree = "<group>"; };
FDE77F6A280FEB28002CFC5D /* ControlMessageProcessRecord.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = ControlMessageProcessRecord.swift; sourceTree = "<group>"; };
FDED2E3B282E1B5D00B2CD2A /* UICollectionView+ReusableView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UICollectionView+ReusableView.swift"; sourceTree = "<group>"; };
@ -2440,6 +2444,7 @@
4C4AEC4420EC343B0020E72B /* DismissableTextField.swift */,
45E5A6981F61E6DD001E4A8A /* MarqueeLabel.swift */,
34386A53207D271C009F5D9C /* NeverClearView.swift */,
FDE72153287FE4470093DF33 /* HighlightMentionBackgroundView.swift */,
34F308A01ECB469700BB7697 /* OWSBezierPathView.h */,
34F308A11ECB469700BB7697 /* OWSBezierPathView.m */,
34330AA11E79686200DF2FB9 /* OWSProgressView.h */,
@ -3312,6 +3317,7 @@
FD83B9BC27CF2215005E1583 /* SharedTest */,
FDC4388F27B9FFC700C60D73 /* SessionMessagingKitTests */,
FD83B9B027CF200A005E1583 /* SessionUtilitiesKitTests */,
FDE7214E287E50D50093DF33 /* Scripts */,
D221A08C169C9E5E00537ABF /* Frameworks */,
D221A08A169C9E5E00537ABF /* Products */,
2BADBA206E0B8D297E313FBA /* Pods */,
@ -3877,6 +3883,15 @@
path = Utilities;
sourceTree = "<group>";
};
FDE7214E287E50D50093DF33 /* Scripts */ = {
isa = PBXGroup;
children = (
FDE7214F287E50D50093DF33 /* ProtoWrappers.py */,
FDE72150287E50D50093DF33 /* LintLocalizableStrings.swift */,
);
path = Scripts;
sourceTree = "<group>";
};
FDF0B7452804F0A8004C14C5 /* Types */ = {
isa = PBXGroup;
children = (
@ -4147,6 +4162,7 @@
buildConfigurationList = D221A0BC169C9E5F00537ABF /* Build configuration list for PBXNativeTarget "Session" */;
buildPhases = (
0401967CF3320CC84B175A3B /* [CP] Check Pods Manifest.lock */,
FDE7214D287E50820093DF33 /* Lint Localizable.strings */,
D221A085169C9E5E00537ABF /* Sources */,
D221A086169C9E5E00537ABF /* Frameworks */,
D221A087169C9E5E00537ABF /* Resources */,
@ -4774,6 +4790,25 @@
shellScript = "diff \"${PODS_PODFILE_DIR_PATH}/Podfile.lock\" \"${PODS_ROOT}/Manifest.lock\" > /dev/null\nif [ $? != 0 ] ; then\n # print error to STDERR\n echo \"error: The sandbox is not in sync with the Podfile.lock. Run 'pod install' or update your CocoaPods installation.\" >&2\n exit 1\nfi\n# This output is used by Xcode 'outputs' to avoid re-running this script phase.\necho \"SUCCESS\" > \"${SCRIPT_OUTPUT_FILE_0}\"\n";
showEnvVarsInLog = 0;
};
FDE7214D287E50820093DF33 /* Lint Localizable.strings */ = {
isa = PBXShellScriptBuildPhase;
buildActionMask = 2147483647;
files = (
);
inputFileListPaths = (
);
inputPaths = (
);
name = "Lint Localizable.strings";
outputFileListPaths = (
);
outputPaths = (
);
runOnlyForDeploymentPostprocessing = 0;
shellPath = /bin/sh;
shellScript = "\"${SRCROOT}/Scripts/LintLocalizableStrings.swift\"\n";
showEnvVarsInLog = 0;
};
/* End PBXShellScriptBuildPhase section */
/* Begin PBXSourcesBuildPhase section */
@ -5267,6 +5302,7 @@
7B1581E6271FD2A100848B49 /* VideoPreviewVC.swift in Sources */,
B83F2B88240CB75A000A54AB /* UIImage+Scaling.swift in Sources */,
3430FE181F7751D4000EC51B /* GiphyAPI.swift in Sources */,
FDE72154287FE4470093DF33 /* HighlightMentionBackgroundView.swift in Sources */,
340FC8AA204DAC8D007AEB0F /* NotificationSettingsViewController.m in Sources */,
4C090A1B210FD9C7001FD7F9 /* HapticFeedback.swift in Sources */,
34F308A21ECB469700BB7697 /* OWSBezierPathView.m in Sources */,

View File

@ -40,8 +40,7 @@ public final class SessionCallManager: NSObject, CallManagerProtocol {
}
static func buildProviderConfiguration(useSystemCallLog: Bool) -> CXProviderConfiguration {
let localizedName = NSLocalizedString("APPLICATION_NAME", comment: "Name of application")
let providerConfiguration = CXProviderConfiguration(localizedName: localizedName)
let providerConfiguration = CXProviderConfiguration(localizedName: "Session")
providerConfiguration.supportsVideo = true
providerConfiguration.maximumCallGroups = 1
providerConfiguration.maximumCallsPerCallGroup = 1

View File

@ -82,29 +82,11 @@ extension ConversationVC:
// MARK: - Blocking
@objc func unblock() {
guard self.viewModel.threadData.threadVariant == .contact else { return }
let publicKey: String = self.viewModel.threadData.threadId
UIView.animate(
withDuration: 0.25,
animations: {
self.blockedBanner.alpha = 0
},
completion: { _ in
Storage.shared.write { db in
try Contact
.filter(id: publicKey)
.updateAll(db, Contact.Columns.isBlocked.set(to: true))
try MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete()
}
}
)
self.showBlockedModalIfNeeded()
}
func showBlockedModalIfNeeded() -> Bool {
guard viewModel.threadData.threadIsBlocked == true else { return false }
guard self.viewModel.threadData.threadIsBlocked == true else { return false }
let blockedModal = BlockedModal(publicKey: viewModel.threadData.threadId)
blockedModal.modalPresentationStyle = .overFullScreen
@ -356,83 +338,77 @@ extension ConversationVC:
let linkPreviewDraft: LinkPreviewDraft? = snInputView.linkPreviewInfo?.draft
let quoteModel: QuotedReplyModel? = snInputView.quoteDraftInfo?.model
// If this was a message request then approve it
approveMessageRequestIfNeeded(
for: threadId,
threadVariant: self.viewModel.threadData.threadVariant,
isNewThread: !oldThreadShouldBeVisible,
timestampMs: (sentTimestampMs - 1) // Set 1ms earlier as this is used for sorting
)
.done { [weak self] _ in
Storage.shared.writeAsync(
updates: { db in
guard let thread: SessionThread = try SessionThread.fetchOne(db, id: threadId) else {
return
}
// Let the viewModel know we are about to send a message
self?.viewModel.sentMessageBeforeUpdate = true
// Update the thread to be visible
_ = try SessionThread
.filter(id: threadId)
.updateAll(db, SessionThread.Columns.shouldBeVisible.set(to: true))
// Create the interaction
let interaction: Interaction = try Interaction(
threadId: threadId,
authorId: getUserHexEncodedPublicKey(db),
variant: .standardOutgoing,
body: text,
timestampMs: sentTimestampMs,
hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: text),
linkPreviewUrl: linkPreviewDraft?.urlString
).inserted(db)
// If there is a LinkPreview and it doesn't match an existing one then add it now
if
let linkPreviewDraft: LinkPreviewDraft = linkPreviewDraft,
(try? interaction.linkPreview.isEmpty(db)) == true
{
try LinkPreview(
url: linkPreviewDraft.urlString,
title: linkPreviewDraft.title,
attachmentId: LinkPreview.saveAttachmentIfPossible(
db,
imageData: linkPreviewDraft.jpegImageData,
mimeType: OWSMimeTypeImageJpeg
)
).insert(db)
}
// If there is a Quote the insert it now
if let interactionId: Int64 = interaction.id, let quoteModel: QuotedReplyModel = quoteModel {
try Quote(
interactionId: interactionId,
authorId: quoteModel.authorId,
timestampMs: quoteModel.timestampMs,
body: quoteModel.body,
attachmentId: quoteModel.generateAttachmentThumbnailIfNeeded(db)
).insert(db)
}
try MessageSender.send(
db,
interaction: interaction,
in: thread
)
},
completion: { [weak self] _, _ in
self?.handleMessageSent()
// Send the message
Storage.shared.writeAsync(
updates: { [weak self] db in
guard let thread: SessionThread = try SessionThread.fetchOne(db, id: threadId) else {
return
}
)
}
.catch(on: DispatchQueue.main) { [weak self] _ in
// Show an error indicating that approving the thread failed
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
.retainUntilComplete()
// Let the viewModel know we are about to send a message
self?.viewModel.sentMessageBeforeUpdate = true
// Update the thread to be visible
_ = try SessionThread
.filter(id: threadId)
.updateAll(db, SessionThread.Columns.shouldBeVisible.set(to: true))
// Create the interaction
let interaction: Interaction = try Interaction(
threadId: threadId,
authorId: getUserHexEncodedPublicKey(db),
variant: .standardOutgoing,
body: text,
timestampMs: sentTimestampMs,
hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: text),
linkPreviewUrl: linkPreviewDraft?.urlString
).inserted(db)
// If there is a LinkPreview and it doesn't match an existing one then add it now
if
let linkPreviewDraft: LinkPreviewDraft = linkPreviewDraft,
(try? interaction.linkPreview.isEmpty(db)) == true
{
try LinkPreview(
url: linkPreviewDraft.urlString,
title: linkPreviewDraft.title,
attachmentId: LinkPreview.saveAttachmentIfPossible(
db,
imageData: linkPreviewDraft.jpegImageData,
mimeType: OWSMimeTypeImageJpeg
)
).insert(db)
}
// If there is a Quote the insert it now
if let interactionId: Int64 = interaction.id, let quoteModel: QuotedReplyModel = quoteModel {
try Quote(
interactionId: interactionId,
authorId: quoteModel.authorId,
timestampMs: quoteModel.timestampMs,
body: quoteModel.body,
attachmentId: quoteModel.generateAttachmentThumbnailIfNeeded(db)
).insert(db)
}
try MessageSender.send(
db,
interaction: interaction,
in: thread
)
},
completion: { [weak self] _, _ in
self?.handleMessageSent()
}
)
}
func sendAttachments(_ attachments: [SignalAttachment], with text: String, onComplete: (() -> ())? = nil) {
@ -453,61 +429,55 @@ extension ConversationVC:
let oldThreadShouldBeVisible: Bool = (self.viewModel.threadData.threadShouldBeVisible == true)
let sentTimestampMs: Int64 = Int64(floor((Date().timeIntervalSince1970 * 1000)))
// If this was a message request then approve it
approveMessageRequestIfNeeded(
for: threadId,
threadVariant: self.viewModel.threadData.threadVariant,
isNewThread: !oldThreadShouldBeVisible,
timestampMs: (sentTimestampMs - 1) // Set 1ms earlier as this is used for sorting
)
.done { [weak self] _ in
Storage.shared.writeAsync(
updates: { db in
guard let thread: SessionThread = try SessionThread.fetchOne(db, id: threadId) else {
return
}
// Let the viewModel know we are about to send a message
self?.viewModel.sentMessageBeforeUpdate = true
// Update the thread to be visible
_ = try SessionThread
.filter(id: threadId)
.updateAll(db, SessionThread.Columns.shouldBeVisible.set(to: true))
// Create the interaction
let interaction: Interaction = try Interaction(
threadId: threadId,
authorId: getUserHexEncodedPublicKey(db),
variant: .standardOutgoing,
body: text,
timestampMs: sentTimestampMs,
hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: text)
).inserted(db)
try MessageSender.send(
db,
interaction: interaction,
with: attachments,
in: thread
)
},
completion: { [weak self] _, _ in
self?.handleMessageSent()
// Attachment successfully sent - dismiss the screen
DispatchQueue.main.async {
onComplete?()
}
// Send the message
Storage.shared.writeAsync(
updates: { [weak self] db in
guard let thread: SessionThread = try SessionThread.fetchOne(db, id: threadId) else {
return
}
)
}
.catch(on: DispatchQueue.main) { [weak self] _ in
// Show an error indicating that approving the thread failed
let alert = UIAlertController(title: "Session", message: "An error occurred when trying to accept this message request", preferredStyle: .alert)
alert.addAction(UIAlertAction(title: "OK", style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
.retainUntilComplete()
// Let the viewModel know we are about to send a message
self?.viewModel.sentMessageBeforeUpdate = true
// Update the thread to be visible
_ = try SessionThread
.filter(id: threadId)
.updateAll(db, SessionThread.Columns.shouldBeVisible.set(to: true))
// Create the interaction
let interaction: Interaction = try Interaction(
threadId: threadId,
authorId: getUserHexEncodedPublicKey(db),
variant: .standardOutgoing,
body: text,
timestampMs: sentTimestampMs,
hasMention: Interaction.isUserMentioned(db, threadId: threadId, body: text)
).inserted(db)
try MessageSender.send(
db,
interaction: interaction,
with: attachments,
in: thread
)
},
completion: { [weak self] _, _ in
self?.handleMessageSent()
// Attachment successfully sent - dismiss the screen
DispatchQueue.main.async {
onComplete?()
}
}
)
}
func handleMessageSent() {
@ -1653,8 +1623,8 @@ extension ConversationVC {
threadVariant: SessionThread.Variant,
isNewThread: Bool,
timestampMs: Int64
) -> Promise<Void> {
guard threadVariant == .contact else { return Promise.value(()) }
) {
guard threadVariant == .contact else { return }
// If the contact doesn't exist then we should create it so we can store the 'isApproved' state
// (it'll be updated with correct profile info if they accept the message request so this
@ -1669,97 +1639,54 @@ extension ConversationVC {
let thread: SessionThread = approvalData.thread,
!approvalData.contact.isApproved
else {
return Promise.value(())
return
}
return Promise.value(())
.then { [weak self] _ -> Promise<Void> in
guard !isNewThread else { return Promise.value(()) }
guard let strongSelf = self else { return Promise(error: MessageSenderError.noThread) }
Storage.shared.writeAsync(
updates: { db in
// If we aren't creating a new thread (ie. sending a message request) then send a
// messageRequestResponse back to the sender (this allows the sender to know that
// they have been approved and can now use this contact in closed groups)
let (promise, seal) = Promise<Void>.pending()
let messageRequestResponse: MessageRequestResponse = MessageRequestResponse(
isApproved: true,
sentTimestampMs: UInt64(timestampMs)
)
// Show a loading indicator
ModalActivityIndicatorViewController.present(fromViewController: strongSelf, canCancel: false) { _ in
seal.fulfill(())
if !isNewThread {
try MessageSender.send(
db,
message: MessageRequestResponse(
isApproved: true,
sentTimestampMs: UInt64(timestampMs)
),
interactionId: nil,
in: thread
)
}
return promise
.then { _ -> Promise<Void> in
Storage.shared.writeAsync { db in
try MessageSender.sendNonDurably(
db,
message: messageRequestResponse,
interactionId: nil,
in: thread
)
}
}
.map { _ in
if self?.presentedViewController is ModalActivityIndicatorViewController {
self?.dismiss(animated: true, completion: nil) // Dismiss the loader
}
}
}
.map { _ in
// Default 'didApproveMe' to true for the person approving the message request
Storage.shared.writeAsync(
updates: { db in
try approvalData.contact
.with(
isApproved: true,
didApproveMe: .update(approvalData.contact.didApproveMe || !isNewThread)
)
.save(db)
// Send a sync message with the details of the contact
try MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete()
},
completion: { db, _ in
// Hide the 'messageRequestView' since the request has been approved
DispatchQueue.main.async { [weak self] in
let messageRequestViewWasVisible: Bool = (self?.messageRequestView.isHidden == false)
UIView.animate(withDuration: 0.3) {
self?.messageRequestView.isHidden = true
self?.scrollButtonMessageRequestsBottomConstraint?.isActive = false
self?.scrollButtonBottomConstraint?.isActive = true
// Update the table content inset and offset to account for
// the dissapearance of the messageRequestsView
if messageRequestViewWasVisible {
let messageRequestsOffset: CGFloat = ((self?.messageRequestView.bounds.height ?? 0) + 16)
let oldContentInset: UIEdgeInsets = (self?.tableView.contentInset ?? UIEdgeInsets.zero)
self?.tableView.contentInset = UIEdgeInsets(
top: 0,
leading: 0,
bottom: max(oldContentInset.bottom - messageRequestsOffset, 0),
trailing: 0
)
}
}
// Remove the 'MessageRequestsViewController' from the nav hierarchy if present
if
let viewControllers: [UIViewController] = self?.navigationController?.viewControllers,
let messageRequestsIndex = viewControllers.firstIndex(where: { $0 is MessageRequestsViewController }),
messageRequestsIndex > 0
{
var newViewControllers = viewControllers
newViewControllers.remove(at: messageRequestsIndex)
self?.navigationController?.viewControllers = newViewControllers
}
}
try approvalData.contact
.with(
isApproved: true,
didApproveMe: .update(approvalData.contact.didApproveMe || !isNewThread)
)
.save(db)
// Send a sync message with the details of the contact
try MessageSender
.syncConfiguration(db, forceSyncNow: true)
.retainUntilComplete()
},
completion: { _, _ in
// Remove the 'MessageRequestsViewController' from the nav hierarchy if present
DispatchQueue.main.async { [weak self] in
if
let viewControllers: [UIViewController] = self?.navigationController?.viewControllers,
let messageRequestsIndex = viewControllers.firstIndex(where: { $0 is MessageRequestsViewController }),
messageRequestsIndex > 0
{
var newViewControllers = viewControllers
newViewControllers.remove(at: messageRequestsIndex)
self?.navigationController?.viewControllers = newViewControllers
}
)
}
}
)
}
@objc func acceptMessageRequest() {
@ -1769,17 +1696,6 @@ extension ConversationVC {
isNewThread: false,
timestampMs: Int64(floor(Date().timeIntervalSince1970 * 1000))
)
.catch(on: DispatchQueue.main) { [weak self] _ in
// Show an error indicating that approving the thread failed
let alert = UIAlertController(
title: "Session",
message: "MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE".localized(),
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: "BUTTON_OK".localized(), style: .default, handler: nil))
self?.present(alert, animated: true, completion: nil)
}
.retainUntilComplete()
}
@objc func deleteMessageRequest() {
@ -1813,7 +1729,9 @@ extension ConversationVC {
.filter(id: threadId)
.deleteAll(db)
try MessageSender.syncConfiguration(db, forceSyncNow: true).retainUntilComplete()
try MessageSender
.syncConfiguration(db, forceSyncNow: true)
.retainUntilComplete()
},
completion: { db, _ in
DispatchQueue.main.async { [weak self] in

View File

@ -551,31 +551,43 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
if
initialLoad ||
viewModel.threadData.threadRequiresApproval != updatedThreadData.threadRequiresApproval ||
viewModel.threadData.threadIsMessageRequest != updatedThreadData.threadIsMessageRequest ||
viewModel.threadData.profile != updatedThreadData.profile
{
updateNavBarButtons(threadData: updatedThreadData, initialVariant: viewModel.initialThreadVariant)
let messageRequestsViewWasVisible: Bool = (messageRequestView.isHidden == false)
UIView.animate(withDuration: 0.3) { [weak self] in
self?.messageRequestView.isHidden = (
updatedThreadData.threadIsMessageRequest == false ||
updatedThreadData.threadRequiresApproval == true
)
self?.scrollButtonMessageRequestsBottomConstraint?.isActive = (
updatedThreadData.threadIsMessageRequest == true
)
self?.scrollButtonBottomConstraint?.isActive = (updatedThreadData.threadIsMessageRequest == false)
// Update the table content inset and offset to account for
// the dissapearance of the messageRequestsView
if messageRequestsViewWasVisible {
let messageRequestsOffset: CGFloat = ((self?.messageRequestView.bounds.height ?? 0) + 16)
let oldContentInset: UIEdgeInsets = (self?.tableView.contentInset ?? UIEdgeInsets.zero)
self?.tableView.contentInset = UIEdgeInsets(
top: 0,
leading: 0,
bottom: max(oldContentInset.bottom - messageRequestsOffset, 0),
trailing: 0
)
}
}
}
if initialLoad || viewModel.threadData.threadIsBlocked != updatedThreadData.threadIsBlocked {
addOrRemoveBlockedBanner(threadIsBlocked: (updatedThreadData.threadIsBlocked == true))
}
if initialLoad || viewModel.threadData.threadIsMessageRequest != updatedThreadData.threadIsMessageRequest {
scrollButtonMessageRequestsBottomConstraint?.isActive = (updatedThreadData.threadIsMessageRequest == true)
scrollButtonBottomConstraint?.isActive = (updatedThreadData.threadIsMessageRequest == false)
}
if
initialLoad ||
viewModel.threadData.threadRequiresApproval != updatedThreadData.threadRequiresApproval ||
viewModel.threadData.threadIsMessageRequest != updatedThreadData.threadIsMessageRequest
{
messageRequestView.isHidden = (
updatedThreadData.threadIsMessageRequest == false ||
updatedThreadData.threadRequiresApproval == true
)
}
if initialLoad || viewModel.threadData.threadUnreadCount != updatedThreadData.threadUnreadCount {
updateUnreadCountView(unreadCount: updatedThreadData.threadUnreadCount)
}
@ -1056,7 +1068,16 @@ final class ConversationVC: BaseVC, OWSConversationSettingsViewDelegate, Convers
func addOrRemoveBlockedBanner(threadIsBlocked: Bool) {
guard threadIsBlocked else {
self.blockedBanner.removeFromSuperview()
UIView.animate(
withDuration: 0.25,
animations: { [weak self] in
self?.blockedBanner.alpha = 0
},
completion: { [weak self] _ in
self?.blockedBanner.alpha = 1
self?.blockedBanner.removeFromSuperview()
}
)
return
}

View File

@ -36,12 +36,6 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
public var lastSearchedText: String?
public let focusedInteractionId: Int64? // Note: This is used for global search
/// We maintain a local set of ids for attachments which we have automatically created attachmentDownload jobs for
/// in order to avoid creating excessive jobs while the user is actively chatting in a conversation (the attachmentDownload
/// jobs run serially and will only actually perform the download if the attachment hasn't already been downloaded so
/// we don't need to worry about duplicate jobs but it's better to avoid creating duplicate jobs when possible)
private var autoStartedDownloadJobAttachmentIds: Set<String> = []
public lazy var blockedBannerMessage: String = {
switch self.threadData.threadVariant {
case .contact:
@ -86,7 +80,10 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
// also want to skip the initial query and trigger it async so that the push animation
// doesn't stutter (it should load basically immediately but without this there is a
// distinct stutter)
self.pagedDataObserver = self.setupPagedObserver(for: threadId)
self.pagedDataObserver = self.setupPagedObserver(
for: threadId,
userPublicKey: getUserHexEncodedPublicKey()
)
// Run the initial query on a background thread so we don't block the push transition
DispatchQueue.global(qos: .default).async { [weak self] in
@ -164,7 +161,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
}
}
private func setupPagedObserver(for threadId: String) -> PagedDatabaseObserver<Interaction, MessageViewModel> {
private func setupPagedObserver(for threadId: String, userPublicKey: String) -> PagedDatabaseObserver<Interaction, MessageViewModel> {
return PagedDatabaseObserver(
pagedTable: Interaction.self,
pageSize: ConversationViewModel.pageSize,
@ -201,6 +198,7 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
groupSQL: MessageViewModel.groupSQL,
orderSQL: MessageViewModel.orderSQL,
dataQuery: MessageViewModel.baseQuery(
userPublicKey: userPublicKey,
orderSQL: MessageViewModel.orderSQL,
groupSQL: MessageViewModel.groupSQL
),
@ -256,44 +254,6 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
.filter { $0.isTypingIndicator != true }
.sorted { lhs, rhs -> Bool in lhs.timestampMs < rhs.timestampMs }
// Add download jobs for any attachments which need to be downloaded
let pendingAttachmentsToDownload: [(attachment: Attachment, interactionId: Int64)] = sortedData
.flatMap { viewModel -> [(attachment: Attachment, interactionId: Int64)] in
// Do nothing if this is an incoming message on an untrusted contact thread
guard
viewModel.variant != .standardIncoming ||
viewModel.threadIsTrusted ||
viewModel.threadVariant != .contact
else { return [] }
return (viewModel.attachments ?? [])
.appending(viewModel.quoteAttachment)
.appending(viewModel.linkPreviewAttachment)
.filter { $0.state == .pendingDownload }
.filter { !self.autoStartedDownloadJobAttachmentIds.contains($0.id) }
.map { ($0, viewModel.id) }
}
if !pendingAttachmentsToDownload.isEmpty {
Storage.shared.writeAsync { db in
pendingAttachmentsToDownload.forEach { attachment, interactionId in
JobRunner.add(
db,
job: Job(
variant: .attachmentDownload,
threadId: self.threadId,
interactionId: interactionId,
details: AttachmentDownloadJob.Details(
attachmentId: attachment.id
)
)
)
self.autoStartedDownloadJobAttachmentIds.insert(attachment.id)
}
}
}
// We load messages from newest to oldest so having a pageOffset larger than zero means
// there are newer pages to load
return [
@ -316,7 +276,8 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
// it's the last element in the 'sortedData' array
index == (sortedData.count - 1) &&
pageInfo.pageOffset == 0
)
),
currentUserBlindedPublicKey: threadData.currentUserBlindedPublicKey
)
}
.appending(typingIndicator)
@ -491,7 +452,10 @@ public class ConversationViewModel: OWSAudioPlayerDelegate {
self.threadId = updatedThreadId
self.observableThreadData = self.setupObservableThreadData(for: updatedThreadId)
self.pagedDataObserver = self.setupPagedObserver(for: updatedThreadId)
self.pagedDataObserver = self.setupPagedObserver(
for: updatedThreadId,
userPublicKey: getUserHexEncodedPublicKey()
)
// Try load everything up to the initial visible message, fallback to just the initial page of messages
// if we don't have one

View File

@ -228,6 +228,8 @@ final class InputView: UIView, InputViewButtonDelegate, InputTextViewDelegate, M
authorId: quoteDraftInfo.model.authorId,
quotedText: quoteDraftInfo.model.body,
threadVariant: threadVariant,
currentUserPublicKey: nil,
currentUserBlindedPublicKey: nil,
direction: (quoteDraftInfo.isOutgoing ? .outgoing : .incoming),
attachment: quoteDraftInfo.model.attachment,
hInset: hInset,

View File

@ -28,6 +28,8 @@ final class QuoteView: UIView {
authorId: String,
quotedText: String?,
threadVariant: SessionThread.Variant,
currentUserPublicKey: String?,
currentUserBlindedPublicKey: String?,
direction: Direction,
attachment: Attachment?,
hInset: CGFloat,
@ -43,6 +45,8 @@ final class QuoteView: UIView {
authorId: authorId,
quotedText: quotedText,
threadVariant: threadVariant,
currentUserPublicKey: currentUserPublicKey,
currentUserBlindedPublicKey: currentUserBlindedPublicKey,
direction: direction,
attachment: attachment,
hInset: hInset,
@ -63,6 +67,8 @@ final class QuoteView: UIView {
authorId: String,
quotedText: String?,
threadVariant: SessionThread.Variant,
currentUserPublicKey: String?,
currentUserBlindedPublicKey: String?,
direction: Direction,
attachment: Attachment?,
hInset: CGFloat,
@ -190,6 +196,8 @@ final class QuoteView: UIView {
MentionUtilities.highlightMentions(
in: $0,
threadVariant: threadVariant,
currentUserPublicKey: currentUserPublicKey,
currentUserBlindedPublicKey: currentUserBlindedPublicKey,
isOutgoingMessage: isOutgoing,
attributes: [:]
)
@ -207,11 +215,21 @@ final class QuoteView: UIView {
// Label stack view
var authorLabelHeight: CGFloat?
if threadVariant == .openGroup || threadVariant == .closedGroup {
let isCurrentUser: Bool = [
currentUserPublicKey,
currentUserBlindedPublicKey,
]
.compactMap { $0 }
.asSet()
.contains(authorId)
let authorLabel = UILabel()
authorLabel.lineBreakMode = .byTruncatingTail
authorLabel.text = Profile.displayName(
id: authorId,
threadVariant: threadVariant
authorLabel.text = (isCurrentUser ?
"MEDIA_GALLERY_SENDER_NAME_YOU".localized() :
Profile.displayName(
id: authorId,
threadVariant: threadVariant
)
)
authorLabel.textColor = textColor
authorLabel.font = .boldSystemFont(ofSize: Values.smallFontSize)

View File

@ -459,6 +459,8 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
authorId: quote.authorId,
quotedText: quote.body,
threadVariant: cellViewModel.threadVariant,
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
direction: (cellViewModel.variant == .standardOutgoing ?
.outgoing :
.incoming
@ -956,6 +958,8 @@ final class VisibleMessageCell: MessageCell, UITextViewDelegate, BodyTextViewDel
attributedString: MentionUtilities.highlightMentions(
in: (cellViewModel.body ?? ""),
threadVariant: cellViewModel.threadVariant,
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
isOutgoingMessage: isOutgoing,
attributes: [
.foregroundColor : textColor,

View File

@ -718,7 +718,7 @@ CGFloat kIconViewLength = 24;
- (BOOL)hasLeftGroup
{
if (self.isClosedGroup || self.isOpenGroup) {
if (self.isClosedGroup) {
return ![SMKGroupMember isCurrentUserMemberOf:self.threadId];
}

View File

@ -81,7 +81,7 @@ final class BlockedModal: Modal {
Storage.shared.writeAsync { db in
try Contact
.filter(id: publicKey)
.updateAll(db, Contact.Columns.isBlocked.set(to: true))
.updateAll(db, Contact.Columns.isBlocked.set(to: false))
try MessageSender
.syncConfiguration(db, forceSyncNow: true)

View File

@ -9,10 +9,29 @@ import UIKit
// The long press interaction that shows the context menu should still work
final class BodyTextView: UITextView {
private let snDelegate: BodyTextViewDelegate?
private let highlightedMentionBackgroundView: HighlightMentionBackgroundView = HighlightMentionBackgroundView()
override var attributedText: NSAttributedString! {
didSet {
guard attributedText != nil else { return }
highlightedMentionBackgroundView.maxPadding = highlightedMentionBackgroundView
.calculateMaxPadding(for: attributedText)
highlightedMentionBackgroundView.frame = self.bounds.insetBy(
dx: -highlightedMentionBackgroundView.maxPadding,
dy: -highlightedMentionBackgroundView.maxPadding
)
}
}
init(snDelegate: BodyTextViewDelegate?) {
self.snDelegate = snDelegate
super.init(frame: CGRect.zero, textContainer: nil)
self.clipsToBounds = false // Needed for the 'HighlightMentionBackgroundView'
addSubview(highlightedMentionBackgroundView)
setUpGestureRecognizers()
}
@ -39,6 +58,15 @@ final class BodyTextView: UITextView {
@objc private func handleDoubleTap() {
// Do nothing
}
override func layoutSubviews() {
super.layoutSubviews()
highlightedMentionBackgroundView.frame = self.bounds.insetBy(
dx: -highlightedMentionBackgroundView.maxPadding,
dy: -highlightedMentionBackgroundView.maxPadding
)
}
}
protocol BodyTextViewDelegate {

View File

@ -16,6 +16,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve
private var hasLoadedInitialThreadData: Bool = false
private var isLoadingMore: Bool = false
private var isAutoLoadingNextPage: Bool = false
private var viewHasAppeared: Bool = false
// MARK: - Intialization
@ -224,6 +225,13 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve
startObservingChanges()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.viewHasAppeared = true
self.autoLoadNextPageIfNeeded()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
@ -487,7 +495,7 @@ final class HomeVC: BaseVC, UITableViewDataSource, UITableViewDelegate, NewConve
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
guard self.hasLoadedInitialThreadData && !self.isLoadingMore else { return }
guard self.hasLoadedInitialThreadData && self.viewHasAppeared && !self.isLoadingMore else { return }
let section: HomeViewModel.SectionModel = self.viewModel.threadData[section]

View File

@ -252,6 +252,11 @@ public class HomeViewModel {
0 :
self.state.unreadMessageRequestThreadCount
)
let groupedOldData: [String: [SessionThreadViewModel]] = (self.threadData
.first(where: { $0.model == .threads })?
.elements)
.defaulting(to: [])
.grouped(by: \.threadId)
return [
// If there are no unread message requests then hide the message request banner
@ -275,6 +280,13 @@ public class HomeViewModel {
return lhs.lastInteractionDate > rhs.lastInteractionDate
}
.map { viewModel -> SessionThreadViewModel in
viewModel.populatingCurrentUserBlindedKey(
currentUserBlindedPublicKeyForThisThread: groupedOldData[viewModel.threadId]?
.first?
.currentUserBlindedPublicKey
)
}
)
],
(!data.isEmpty && (pageInfo.pageOffset + pageInfo.currentCount) < pageInfo.totalCount ?

View File

@ -14,6 +14,8 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
private var dataChangeObservable: DatabaseCancellable?
private var hasLoadedInitialThreadData: Bool = false
private var isLoadingMore: Bool = false
private var isAutoLoadingNextPage: Bool = false
private var viewHasAppeared: Bool = false
// MARK: - Intialization
@ -130,6 +132,13 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
startObservingChanges()
}
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)
self.viewHasAppeared = true
self.autoLoadNextPageIfNeeded()
}
override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)
@ -196,6 +205,13 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
clearAllButton.isHidden = updatedData.isEmpty
emptyStateLabel.isHidden = !updatedData.isEmpty
CATransaction.begin()
CATransaction.setCompletionBlock { [weak self] in
// Complete page loading
self?.isLoadingMore = false
self?.autoLoadNextPageIfNeeded()
}
// Reload the table content (animate changes after the first load)
tableView.reload(
using: StagedChangeset(source: viewModel.threadData, target: updatedData),
@ -209,6 +225,38 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
) { [weak self] updatedData in
self?.viewModel.updateThreadData(updatedData)
}
CATransaction.commit()
}
private func autoLoadNextPageIfNeeded() {
guard !self.isAutoLoadingNextPage && !self.isLoadingMore else { return }
self.isAutoLoadingNextPage = true
DispatchQueue.main.asyncAfter(deadline: .now() + PagedData.autoLoadNextPageDelay) { [weak self] in
self?.isAutoLoadingNextPage = false
// Note: We sort the headers as we want to prioritise loading newer pages over older ones
let sections: [(MessageRequestsViewModel.Section, CGRect)] = (self?.viewModel.threadData
.enumerated()
.map { index, section in (section.model, (self?.tableView.rectForHeader(inSection: index) ?? .zero)) })
.defaulting(to: [])
let shouldLoadMore: Bool = sections
.contains { section, headerRect in
section == .loadMore &&
headerRect != .zero &&
(self?.tableView.bounds.contains(headerRect) == true)
}
guard shouldLoadMore else { return }
self?.isLoadingMore = true
DispatchQueue.global(qos: .default).async { [weak self] in
self?.viewModel.pagedDataObserver?.load(.pageAfter)
}
}
}
@objc override internal func handleAppModeChangedNotification(_ notification: Notification) {
@ -277,7 +325,7 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
}
func tableView(_ tableView: UITableView, willDisplayHeaderView view: UIView, forSection section: Int) {
guard self.hasLoadedInitialThreadData && !self.isLoadingMore else { return }
guard self.hasLoadedInitialThreadData && self.viewHasAppeared && !self.isLoadingMore else { return }
let section: MessageRequestsViewModel.SectionModel = self.viewModel.threadData[section]
@ -338,7 +386,9 @@ class MessageRequestsViewController: BaseVC, UITableViewDelegate, UITableViewDat
// MARK: - Interaction
@objc private func clearAllTapped() {
guard viewModel.threadData.first { $0.model == .threads }?.elements.isEmpty == false else { return }
guard viewModel.threadData.first(where: { $0.model == .threads })?.elements.isEmpty == false else {
return
}
let threadIds: [String] = (viewModel.threadData
.first { $0.model == .threads }?

View File

@ -140,12 +140,25 @@ public class MessageRequestsViewModel {
}
private func process(data: [SessionThreadViewModel], for pageInfo: PagedData.PageInfo) -> [SectionModel] {
let groupedOldData: [String: [SessionThreadViewModel]] = (self.threadData
.first(where: { $0.model == .threads })?
.elements)
.defaulting(to: [])
.grouped(by: \.threadId)
return [
[
SectionModel(
section: .threads,
elements: data
.sorted { lhs, rhs -> Bool in lhs.lastInteractionDate > rhs.lastInteractionDate }
.map { viewModel -> SessionThreadViewModel in
viewModel.populatingCurrentUserBlindedKey(
currentUserBlindedPublicKeyForThisThread: groupedOldData[viewModel.threadId]?
.first?
.currentUserBlindedPublicKey
)
}
)
],
(!data.isEmpty && (pageInfo.pageOffset + pageInfo.currentCount) < pageInfo.totalCount ?

View File

@ -105,7 +105,7 @@ class GifPickerViewController: OWSViewController, UISearchBarDelegate, UICollect
// Loki: Customize title
let titleLabel = UILabel()
titleLabel.text = NSLocalizedString("GIF", comment: "")
titleLabel.text = "accessibility_gif_button".localized().uppercased()
titleLabel.textColor = Colors.text
titleLabel.font = .boldSystemFont(ofSize: Values.veryLargeFontSize)
navigationItem.titleView = titleLabel

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Kamera";
"accessibility_main_button_collapse" = "Optionen für Anhänge einklappen";
"invalid_recovery_phrase" = "Ungültige Wiederherstellungsphrase";
"invalid_recovery_phrase" = "Ungültige Wiederherstellungsphrase";
"DISMISS_BUTTON_TEXT" = "Verwerfen";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Einstellungen";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Möchten Sie wirklich alle Nachrichten löschen?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Fehler";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Authentifizierung gescheitert.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Zu viele gescheiterte Authentifizierungsversuche. Bitte versuche es später erneut.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Du musst einen Passcode in deinen iOS-Einstellungen festlegen, um die Bildschirmsperre zu verwenden.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Du musst einen Passcode in deinen iOS-Einstellungen festlegen, um die Bildschirmsperre zu verwenden.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Du musst einen Passcode in deinen iOS-Einstellungen festlegen, um die Bildschirmsperre zu verwenden.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Dismiss";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Settings";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,16 +651,41 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete open group messages which are older than 6 months when starting the app";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Authentication failed.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Too many failed authentication attempts. Please try again later.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";
"MEDIA_TAB_TITLE" = "Media";
"DOCUMENT_TAB_TITLE" = "Documents";
"DOCUMENT_TILES_EMPTY_DOCUMENT" = "You don't have any document in this conversation.";
"DOCUMENT_TILES_LOADING_MORE_RECENT_LABEL" = "Loading Newer Document…";
"DOCUMENT_TILES_LOADING_OLDER_LABEL" = "Loading Older Document…";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Cámara";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Frase de Recuperación Incorrecta";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Descartar";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Ajustes";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Fallo";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Fallo en la identificación.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Demasiados intentos fallidos. Prueda de nuevo más tarde.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Necesitas configurar un código en «Ajustes» de iOS para poder usar el bloqueo de acceso.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Necesitas configurar un código en «Ajustes» de iOS para poder usar el bloqueo de acceso.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Necesitas configurar un código en «Ajustes» de iOS para poder usar el bloqueo de acceso.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Dismiss";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Settings";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "خطاء";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "احراز هویت ناموفق بود.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "چندین احراز هویت ناموفق رخ داد. لطفا بعدا تلاش کنید.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "برای استفاده از قفل صفحه نمایش می بایستی یک رمزعبور از تنظیمات iOS خود فعال کنید.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "شما برای استفاده از قفل صفحه نمایس می بایستی یک رمزعبور از تنظیمات iOS خود فعال کنید.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "برای استفاده از قفل صفحه نمایش می بایستی یک رمزعبور از تنظیمات iOS فعال کنید.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Kamera";
"accessibility_main_button_collapse" = "Tiivistä liiteasetukset";
"invalid_recovery_phrase" = "Virheellinen Palautuslauseke";
"invalid_recovery_phrase" = "Virheellinen palautuslauseke";
"DISMISS_BUTTON_TEXT" = "Hylkää";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Asetukset";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Oletko varma että haluat poistaa kaikki viestipyynnöt?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Poista";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Oletko varma että haluat poistaa tämän viestipyynnön?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "Viestipyyntöä hyväksyttäessä ilmeni virhe";
"MESSAGE_REQUESTS_INFO" = "Viestin lähettäminen tälle henkilölle hyväksyy automaattisesti viestipyynnön.";
"MESSAGE_REQUESTS_ACCEPTED" = "Viestipyyntösi hyväksyttiin.";
"MESSAGE_REQUESTS_NOTIFICATION" = "Sinulla on uusi viestipyyntö";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Tunnistautuminen epäonnistui";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Liian monta epäonnistunutta tunnistautumista. Yritä myöhemmin uudelleen.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Aseta pääsykoodi puhelimesi asetuksista, jotta voit käyttää näytön lukitusta.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Aseta pääsykoodi puhelimesi asetuksista, jotta voit käyttää näytön lukitusta.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Aseta pääsykoodi puhelimesi asetuksista, jotta voit käyttää näytön lukitusta.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Caméra";
"accessibility_main_button_collapse" = "Réduire les options de pièces jointes";
"invalid_recovery_phrase" = "Phrase de récupération incorrecte";
"invalid_recovery_phrase" = "Phrase de récupération incorrecte";
"DISMISS_BUTTON_TEXT" = "Fermer";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Paramètres";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Êtes-vous sûr de vouloir supprimer toutes les demandes de messages ?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Effacer";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Êtes-vous sûr de vouloir supprimer cette demande de message ?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "Une erreur s'est produite en acceptant cette demande de message";
"MESSAGE_REQUESTS_INFO" = "Envoyer un message à cet utilisateur acceptera automatiquement sa demande de message.";
"MESSAGE_REQUESTS_ACCEPTED" = "Votre demande de message a été réceptionnée.";
"MESSAGE_REQUESTS_NOTIFICATION" = "Vous avez une nouvelle demande de message";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "Vous pouvez activer la permission \"Appels vocaux et vidéo\" dans les paramètres de confidentialité.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Erreur";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Échec dauthentification";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Trop dessais infructueux dauthentification. Veuillez réessayer plus tard.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Vous devez activer un code dans vos réglages iOS pour utiliser le verrou décran.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Vous devez activer un code dans vos réglages iOS pour utiliser le verrou décran.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Vous devez activer un code dans vos réglages iOS pour utiliser le verrou décran.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Dismiss";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Settings";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "प्रमाणीकरण असफल";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "बहुत सारी असफल प्रमाणीकरण की कोशिशें हुई हैं। कृपया थोङी देर बाद कोशिश करें।";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "सक्रीन लॉक इस्तेमाल करने के लिये अपने iOS सेटिंग्स से पासकोड की अनुमति दें।";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "सक्रीन लॉक इस्तेमाल करने के लिये अपने iOS सेटिंग्स से पासकोड की अनुमति दें।";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "सक्रीन लॉक इस्तेमाल करने के लिये अपने iOS सेटिंग्स से पासकोड की अनुमति दें।";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Kamera";
"accessibility_main_button_collapse" = "Sažmi opcije privitka";
"invalid_recovery_phrase" = "Nevažeća fraza za oporavak";
"invalid_recovery_phrase" = "Nevažeća fraza za oporavak";
"DISMISS_BUTTON_TEXT" = "Odbaci";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Postavke";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Provjera autentičnosti nije uspjela.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Previše neuspjelih pokušaja provjere autentičnosti. Pokušajte ponovo kasnije.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Kako biste koristili Zaključavanje zaslona, morate omogućiti lozinku u postavkama iOS-a.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Kako biste koristili Zaključavanje zaslona, morate omogućiti lozinku u postavkama iOS-a.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Kako biste koristili Zaključavanje zaslona, morate omogućiti lozinku u postavkama iOS-a.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Dismiss";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Settings";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Galat";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Authentication failed.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Too many failed authentication attempts. Please try again later.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Fotocamera";
"accessibility_main_button_collapse" = "Comprimi opzioni allegato";
"invalid_recovery_phrase" = "Frase Di Recupero non valida";
"invalid_recovery_phrase" = "Frase Di Ripristino Non Valida";
"DISMISS_BUTTON_TEXT" = "Chiudi";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Impostazioni";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Eliminare veramente tutte le richieste di messaggio?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Cancella";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Sei sicuro di voler eliminare questa richiesta di messaggio?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "Si è verificato un errore durante il tentativo di accettare questa richiesta di messaggio";
"MESSAGE_REQUESTS_INFO" = "L'invio di un messaggio a questo utente accetterà automaticamente la richiesta di messaggio.";
"MESSAGE_REQUESTS_ACCEPTED" = "La tua richiesta di messaggio è stata accettata.";
"MESSAGE_REQUESTS_NOTIFICATION" = "Hai una nuova richiesta di messaggio";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "È possibile abilitare l'autorizzazione 'Voce e video chiamate' nelle Impostazioni Privacy.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Errore";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Autenticazione non riuscita.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Troppi tentativi di autenticazione non riusciti. Riprova più tardi.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Devi abilitare una password nelle impostazioni di iOS per poter usare il blocco schermo.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Devi abilitare una password nelle impostazioni di iOS per poter usare il blocco schermo.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Devi abilitare una password nelle impostazioni di iOS per poter usare il blocco schermo.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "カメラ";
"accessibility_main_button_collapse" = "添付ファイルのオプションを閉じる";
"invalid_recovery_phrase" = "無効な復元フレーズ";
"invalid_recovery_phrase" = "無効な復元フレーズ";
"DISMISS_BUTTON_TEXT" = "中止";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "設定";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "本当に全てのリクエストを消去しますか?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "消去";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "本当にこのリクエストを削除しますか?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "リクエスト承認時にエラーが発生しました";
"MESSAGE_REQUESTS_INFO" = "このユーザーにメッセージを送信すると、自動的にリクエストが承認されます。";
"MESSAGE_REQUESTS_ACCEPTED" = "リクエストが承認されました";
"MESSAGE_REQUESTS_NOTIFICATION" = "新しいリクエストがあります";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "プライバシー設定から音声とビデオ通話の許可を有効にできます。";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "エラー";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "認証に失敗しました。";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "認証失敗が多すぎます。あとで再度試してください。";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "画面ロックを使用するには、iOSの設定でパスコードを有効にしてください。";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "画面ロックを使用するには、iOSの設定でパスコードを有効にしてください。";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "画面ロックを使用するには、iOSの設定でパスコードを有効にしてください。";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Bijlage-opties inklappen";
"invalid_recovery_phrase" = "Ongeldig Herstelzin";
"invalid_recovery_phrase" = "Ongeldig Herstelzin";
"DISMISS_BUTTON_TEXT" = "Negeren";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Instellingen";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Authenticatie mislukt.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Te veel pogingen tot authenticatie. Probeer het later opnieuw.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Om appvergrendeling te kunnen gebruiken, moet je een toegangscode instellen in de iOS-instellingen.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Om appvergrendeling te kunnen gebruiken, moet je een toegangscode instellen in de iOS-instellingen.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Om appvergrendeling te kunnen gebruiken, moet je een toegangscode instellen in de iOS-instellingen.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Aparat";
"accessibility_main_button_collapse" = "Zwiń opcje załączników";
"invalid_recovery_phrase" = "Nieprawidłowa fraza odzyskiwania";
"invalid_recovery_phrase" = "Nieprawidłowa fraza odzyskiwania";
"DISMISS_BUTTON_TEXT" = "Odrzuć";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Ustawienia";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Czy na pewno chcesz wyczyścić wszystkie żądania wiadomości?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Wyczyść";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Czy na pewno chcesz usunąć to żądanie wiadomości?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "Wystąpił błąd podczas próby zaakceptowania tego żądania wiadomości";
"MESSAGE_REQUESTS_INFO" = "Wysyłanie wiadomości do tego użytkownika automatycznie zaakceptuje ich żądanie wiadomości.";
"MESSAGE_REQUESTS_ACCEPTED" = "Twoje żądanie wiadomości zostało zaakceptowane.";
"MESSAGE_REQUESTS_NOTIFICATION" = "Masz nowe żądanie wiadomości";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "Możesz włączyć uprawnienie 'Połączenia głosowe i wideo' w Ustawieniach Prywatności.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Błąd";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Uwierzytelnianie nie powiodło się.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Zbyt wiele błędnych logowań. Spróbuj później.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Musisz włączyć kod dostępu w Ustawieniach systemu iOS, aby korzystać z blokady ekranu.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Musisz włączyć kod dostępu w Ustawieniach systemu iOS, aby korzystać z blokady ekranu.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Musisz włączyć kod dostępu w Ustawieniach systemu iOS, aby korzystać z blokady ekranu.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Câmera";
"accessibility_main_button_collapse" = "Recolher opções de anexo";
"invalid_recovery_phrase" = "Frase de Recuperação inválida";
"invalid_recovery_phrase" = "Frase de Recuperação inválida";
"DISMISS_BUTTON_TEXT" = "Ignorar";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Configurações";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Erro";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Falha na autenticação.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Você excedeu o número máximo permitido de tentativas de autenticação. Por favor, tente novamente mais tarde.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Você deve criar uma senha no app Ajustes do iOS para usar bloqueio de tela.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Você deve criar uma senha no app Ajustes do iOS para usar o bloqueio de tela.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Você deve criar uma senha no app Ajustes do iOS para usar o bloqueio de tela.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Камера";
"accessibility_main_button_collapse" = "Свернуть параметры вложений";
"invalid_recovery_phrase" = "Неверная секретная фраза";
"invalid_recovery_phrase" = "Неверная секретная фраза";
"DISMISS_BUTTON_TEXT" = "Закрыть";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Настройки";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Вы уверены, что хотите очистить все запросы сообщений?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Очистить";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Вы уверены, что хотите удалить это сообщение?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Ошибка";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Ошибка аутентификации.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Слишком много неудачных попыток аутентификации. Пожалуйста, повторите попытку позже.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Вы должны включить код доступа в приложении «Настройки», чтобы использовать блокировку экрана.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Вы должны включить код доступа в приложении «Настройки», чтобы использовать блокировку экрана.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Вы должны включить код доступа в приложении «Настройки», чтобы использовать блокировку экрана.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Dismiss";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "සැකසුම්";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Authentication failed.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Too many failed authentication attempts. Please try again later.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Kamera";
"accessibility_main_button_collapse" = "Uzatvor možnosti prípon";
"invalid_recovery_phrase" = "Neplatná Obnovovacia Fráza";
"invalid_recovery_phrase" = "Neplatná Obnovovacia Fráza";
"DISMISS_BUTTON_TEXT" = "Zrušiť";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Nastavenia";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Naozaj chcete vymazať všetky žiadosti o správu?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Vymazať";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Naozaj chcete vymazať túto žiadosť o správu?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "Nastala chyba pri akceptovaní žiadosti o túto správu";
"MESSAGE_REQUESTS_INFO" = "Poslanie správy tomuto používateľovi automaticky príjme ich žiadosť o správu.";
"MESSAGE_REQUESTS_ACCEPTED" = "Vaša žiadosť o správu bola prijatá.";
"MESSAGE_REQUESTS_NOTIFICATION" = "Máte novú žiadosť o správu";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Autentifikácia zlyhala";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Priveľa nepodarených pokusov o autentifikáciu. Prosím, skúste to znovu neskôr.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Pre používanie zámku obrazovky, zapnite kódový zámok v nastaveniach iOS.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Pre používanie zámku obrazovky, zapnite kódový zámok v nastaveniach iOS.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Pre používanie zámku obrazovky, zapnite kódový zámok v nastaveniach iOS.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Kamera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Dismiss";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Inställningar";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Autentisering misslyckades.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "För många misslyckade autentiseringsförsök. Försök igen senare.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "Du måste aktivera en lösenkod i dina iOS-inställningar för att använda Skärmlås.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "Du måste aktivera en lösenkod i dina iOS-inställningar för att använda Skärmlås.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "Du måste aktivera en lösenkod i dina iOS-inställningar för att använda Skärmlås.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Dismiss";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Settings";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "การรับรองความถูกต้องไม่สำเร็จ";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "รับรองความถูกต้องไม่สำเร็จหลายครั้งเกินไป โปรดลองใหม่ในภายหลัง";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "คุณต้องเปิดใช้งานรหัสผ่านในการตั้งค่า iOS ของคุณเพื่อใช้งานการล็อกหน้าจอ";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "คุณต้องเปิดใช้งานรหัสผ่านในการตั้งค่า iOS ของคุณเพื่อใช้งานการล็อกหน้าจอ";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "คุณต้องเปิดใช้งานรหัสผ่านในการตั้งค่า iOS ของคุณเพื่อใช้งานการล็อกหน้าจอ";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "Camera";
"accessibility_main_button_collapse" = "Collapse attachment options";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"invalid_recovery_phrase" = "Invalid Recovery Phrase";
"DISMISS_BUTTON_TEXT" = "Dismiss";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "Settings";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Authentication failed.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Too many failed authentication attempts. Please try again later.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "相機";
"accessibility_main_button_collapse" = "摺疊附件選項";
"invalid_recovery_phrase" = "備援暗語無效";
"invalid_recovery_phrase" = "恢復短語無效";
"DISMISS_BUTTON_TEXT" = "關閉";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "設定";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "Are you sure you want to clear all message requests?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "Clear";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "Are you sure you want to delete this message request?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "An error occurred when trying to accept this message request";
"MESSAGE_REQUESTS_INFO" = "Sending a message to this user will automatically accept their message request.";
"MESSAGE_REQUESTS_ACCEPTED" = "Your message request has been accepted.";
"MESSAGE_REQUESTS_NOTIFICATION" = "You have a new message request";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "You can enable the 'Voice and video calls' permission in the Privacy Settings.";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "Error";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "Authentication failed.";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "Too many failed authentication attempts. Please try again later.";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "You must enable a passcode in your iOS Settings in order to use Screen Lock.";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -606,7 +606,6 @@
"accessibility_camera_button" = "相机";
"accessibility_main_button_collapse" = "收起附件选项";
"invalid_recovery_phrase" = "恢复口令无效";
"invalid_recovery_phrase" = "恢复口令无效";
"DISMISS_BUTTON_TEXT" = "取消";
/* Button text which opens the settings app */
"OPEN_SETTINGS_BUTTON" = "设置";
@ -639,7 +638,6 @@
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_TITLE" = "您确定要清除所有消息请求吗?";
"MESSAGE_REQUESTS_CLEAR_ALL_CONFIRMATION_ACTON" = "清除";
"MESSAGE_REQUESTS_DELETE_CONFIRMATION_ACTON" = "您确定要删除此消息请求吗?";
"MESSAGE_REQUESTS_APPROVAL_ERROR_MESSAGE" = "尝试接受此消息请求时发生错误";
"MESSAGE_REQUESTS_INFO" = "发送消息给此用户将自动接受他们的消息请求。";
"MESSAGE_REQUESTS_ACCEPTED" = "您的消息请求已被接受。";
"MESSAGE_REQUESTS_NOTIFICATION" = "您有一个新的消息请求";
@ -653,10 +651,36 @@
"modal_call_permission_request_explanation" = "您可以在隐私设置中启用“语音和视频通话”权限。";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_TITLE" = "Oops, an error occurred";
"DEFAULT_OPEN_GROUP_LOAD_ERROR_SUBTITLE" = "Please try again later";
"ALERT_ERROR_TITLE" = "错误";
"LOADING_CONVERSATIONS" = "Loading Conversations...";
"DATABASE_MIGRATION_FAILED" = "An error occurred when optimising the database\n\nYou can export your application logs to be able to share for troubleshooting or you can restore our device\n\nWarning: Restoring your device will result in loss of any data older than two weeks";
"CHATS_TITLE" = "Chats";
"MESSAGE_TRIMMING_TITLE" = "Message Trimming";
"MESSAGE_TRIMMING_OPEN_GROUP_TITLE" = "Delete Old Open Group Messages";
"MESSAGE_TRIMMING_OPEN_GROUP_DESCRIPTION" = "Automatically delete messages which are older than 6 months from open groups with over 2,000 messages when starting the app";
"RECOVERY_PHASE_ERROR_GENERIC" = "Something went wrong. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LENGTH" = "Looks like you didn't enter enough words. Please check your recovery phrase and try again.";
"RECOVERY_PHASE_ERROR_LAST_WORD" = "You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_INVALID_WORD" = "There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.";
"RECOVERY_PHASE_ERROR_FAILED" = "Your recovery phrase couldn't be verified. Please check what you entered and try again.";
/* Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode. */
"SCREEN_LOCK_ENABLE_UNKNOWN_ERROR" = "Authentication could not be accessed.";
/* Indicates that Touch ID/Face ID/Phone Passcode authentication failed. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED" = "认证失败。";
/* Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT" = "认证失败次数太多,请稍后再试。";
/* Indicates that Touch ID/Face ID/Phone Passcode are not available on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE" = "您需要先设置您的密码来开启屏幕锁功能。";
/* Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED" = "您需要先设置您的密码来开启屏幕锁功能。";
/* Indicates that Touch ID/Face ID/Phone Passcode passcode is not set. */
"SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET" = "您需要先设置您的密码来开启屏幕锁功能。";
/* Label for the button to send a message */
"SEND_BUTTON_TITLE" = "Send";
/* Generic text for button that retries whatever the last action was. */
"RETRY_BUTTON_TEXT" = "Retry";
/* notification action */
"SHOW_THREAD_BUTTON_TITLE" = "Show Chat";
/* notification body */
"SEND_FAILED_NOTIFICATION_BODY" = "Your message failed to send.";
"INVALID_SESSION_ID_MESSAGE" = "Please check the Session ID and try again.";
"INVALID_RECOVERY_PHRASE_MESSAGE" = "Please check the Recovery Phrase and try again.";

View File

@ -224,11 +224,19 @@ public class NotificationPresenter: NSObject, NotificationsProtocol {
let userInfo = [
AppNotificationUserInfoKey.threadId: thread.id
]
let userPublicKey: String = getUserHexEncodedPublicKey(db)
let userBlindedKey: String? = SessionThread.getUserHexEncodedBlindedKey(
threadId: thread.id,
threadVariant: thread.variant
)
DispatchQueue.main.async {
notificationBody = MentionUtilities.highlightMentions(
in: (notificationBody ?? ""),
threadVariant: thread.variant
threadVariant: thread.variant,
currentUserPublicKey: userPublicKey,
currentUserBlindedPublicKey: userBlindedKey
)
let sound: Preferences.Sound? = self.requestSound(thread: thread)

View File

@ -128,7 +128,11 @@ final class LinkDeviceVC : BaseVC, UIPageViewControllerDataSource, UIPageViewCon
func continueWithSeed(_ seed: Data) {
if (seed.count != 16) {
let alert = UIAlertController(title: NSLocalizedString("invalid_recovery_phrase", comment: ""), message: NSLocalizedString("Please check the Recovery Phrase and try again.", comment: ""), preferredStyle: .alert)
let alert = UIAlertController(
title: "invalid_recovery_phrase".localized(),
message: "INVALID_RECOVERY_PHRASE_MESSAGE".localized(),
preferredStyle: .alert
)
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: { _ in
self.scanQRCodeWrapperVC.startCapture()
}))

View File

@ -121,7 +121,9 @@ final class QRCodeVC : BaseVC, UIPageViewControllerDataSource, UIPageViewControl
fileprivate func startNewPrivateChatIfPossible(with hexEncodedPublicKey: String) {
if !ECKeyPair.isValidHexEncodedPublicKey(candidate: hexEncodedPublicKey) {
let alert = UIAlertController(title: NSLocalizedString("invalid_session_id", comment: ""), message: NSLocalizedString("Please check the Session ID and try again.", comment: ""), preferredStyle: .alert)
let alert = UIAlertController(
title: "invalid_session_id".localized(),
message: "INVALID_SESSION_ID_MESSAGE".localized(), preferredStyle: .alert)
alert.addAction(UIAlertAction(title: NSLocalizedString("BUTTON_OK", comment: ""), style: .default, handler: nil))
presentAlert(alert)
}

View File

@ -267,6 +267,8 @@ public final class FullConversationCell: UITableViewCell {
cellViewModel.authorName(for: .contact) :
nil
),
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
searchText: searchText.lowercased(),
fontSize: Values.smallFontSize
)
@ -288,6 +290,8 @@ public final class FullConversationCell: UITableViewCell {
timestampLabel.isHidden = true
displayNameLabel.attributedText = getHighlightedSnippet(
content: cellViewModel.displayName,
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
searchText: searchText.lowercased(),
fontSize: Values.mediumFontSize
)
@ -299,6 +303,8 @@ public final class FullConversationCell: UITableViewCell {
bottomLabelStackView.isHidden = (cellViewModel.threadMemberNames ?? "").isEmpty
snippetLabel.attributedText = getHighlightedSnippet(
content: (cellViewModel.threadMemberNames ?? ""),
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey,
searchText: searchText.lowercased(),
fontSize: Values.smallFontSize
)
@ -440,7 +446,9 @@ public final class FullConversationCell: UITableViewCell {
attachmentCount: cellViewModel.interactionAttachmentCount,
isOpenGroupInvitation: (cellViewModel.interactionIsOpenGroupInvitation == true)
),
threadVariant: cellViewModel.threadVariant
threadVariant: cellViewModel.threadVariant,
currentUserPublicKey: cellViewModel.currentUserPublicKey,
currentUserBlindedPublicKey: cellViewModel.currentUserBlindedPublicKey
),
attributes: [
.font: font,
@ -454,6 +462,8 @@ public final class FullConversationCell: UITableViewCell {
private func getHighlightedSnippet(
content: String,
authorName: String? = nil,
currentUserPublicKey: String,
currentUserBlindedPublicKey: String?,
searchText: String,
fontSize: CGFloat
) -> NSAttributedString {
@ -473,7 +483,9 @@ public final class FullConversationCell: UITableViewCell {
// we don't want to include the truncated id as part of the name so we exclude it
let mentionReplacedContent: String = MentionUtilities.highlightMentions(
in: content,
threadVariant: .contact
threadVariant: .contact,
currentUserPublicKey: currentUserPublicKey,
currentUserBlindedPublicKey: currentUserBlindedPublicKey
)
let result: NSMutableAttributedString = NSMutableAttributedString(
string: mentionReplacedContent,

View File

@ -0,0 +1,161 @@
// Copyright © 2022 Rangeproof Pty Ltd. All rights reserved.
import UIKit
public extension NSAttributedString.Key {
static let currentUserMentionBackgroundColor: NSAttributedString.Key = NSAttributedString.Key(rawValue: "currentUserMentionBackgroundColor")
static let currentUserMentionBackgroundCornerRadius: NSAttributedString.Key = NSAttributedString.Key(rawValue: "currentUserMentionBackgroundCornerRadius")
static let currentUserMentionBackgroundPadding: NSAttributedString.Key = NSAttributedString.Key(rawValue: "currentUserMentionBackgroundPadding")
}
class HighlightMentionBackgroundView: UIView {
var maxPadding: CGFloat = 0
init() {
super.init(frame: .zero)
self.isOpaque = false
self.layer.zPosition = -1
}
required init?(coder: NSCoder) {
fatalError("init(coder:) has not been implemented")
}
// MARK: - Functions
public func calculateMaxPadding(for attributedText: NSAttributedString) -> CGFloat {
var allMentionRadii: [CGFloat?] = []
let path: CGMutablePath = CGMutablePath()
path.addRect(CGRect(
x: 0,
y: 0,
width: CGFloat.greatestFiniteMagnitude,
height: CGFloat.greatestFiniteMagnitude
))
let framesetter = CTFramesetterCreateWithAttributedString(attributedText as CFAttributedString)
let frame: CTFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, attributedText.length), path, nil)
let lines: [CTLine] = frame.lines
lines.forEach { line in
let runs: [CTRun] = line.ctruns
runs.forEach { run in
let attributes: NSDictionary = CTRunGetAttributes(run)
allMentionRadii.append(
attributes
.value(forKey: NSAttributedString.Key.currentUserMentionBackgroundPadding.rawValue) as? CGFloat
)
}
}
return allMentionRadii
.compactMap { $0 }
.max()
.defaulting(to: 0)
}
// MARK: - Drawing
override func draw(_ rect: CGRect) {
guard
let superview: UITextView = (self.superview as? UITextView),
let context = UIGraphicsGetCurrentContext()
else { return }
// Need to invery the Y axis because iOS likes to render from the bottom left instead of the top left
context.textMatrix = .identity
context.translateBy(x: 0, y: bounds.size.height)
context.scaleBy(x: 1.0, y: -1.0)
// Note: Calculations MUST happen based on the 'superview' size as this class has extra padding which
// can result in calculations being off
let path = CGMutablePath()
let size = superview.sizeThatFits(CGSize(width: superview.bounds.width, height: .greatestFiniteMagnitude))
path.addRect(CGRect(x: 0, y: 0, width: size.width, height: size.height), transform: .identity)
let framesetter = CTFramesetterCreateWithAttributedString(superview.attributedText as CFAttributedString)
let frame: CTFrame = CTFramesetterCreateFrame(framesetter, CFRangeMake(0, superview.attributedText.length), path, nil)
let lines: [CTLine] = frame.lines
var origins = [CGPoint](repeating: .zero, count: lines.count)
CTFrameGetLineOrigins(frame, CFRangeMake(0, 0), &origins)
for lineIndex in 0..<lines.count {
let line = lines[lineIndex]
let runs: [CTRun] = line.ctruns
var ascent: CGFloat = 0
var descent: CGFloat = 0
var leading: CGFloat = 0
let lineWidth = CGFloat(CTLineGetTypographicBounds(line, &ascent, &descent, &leading))
for run in runs {
let attributes: NSDictionary = CTRunGetAttributes(run)
guard let mentionBackgroundColor: UIColor = attributes.value(forKey: NSAttributedString.Key.currentUserMentionBackgroundColor.rawValue) as? UIColor else {
continue
}
let cornerRadius: CGFloat = (attributes
.value(forKey: NSAttributedString.Key.currentUserMentionBackgroundCornerRadius.rawValue) as? CGFloat)
.defaulting(to: 0)
let padding: CGFloat = (attributes
.value(forKey: NSAttributedString.Key.currentUserMentionBackgroundPadding.rawValue) as? CGFloat)
.defaulting(to: 0)
let range = CTRunGetStringRange(run)
var runBounds: CGRect = .zero
var runAscent: CGFloat = 0
var runDescent: CGFloat = 0
runBounds.size.width = CGFloat(CTRunGetTypographicBounds(run, CFRangeMake(0, 0), &runAscent, &runDescent, nil) + (padding * 2))
runBounds.size.height = (runAscent + runDescent + (padding * 2))
let xOffset: CGFloat = {
switch CTRunGetStatus(run) {
case .rightToLeft:
return CTLineGetOffsetForStringIndex(line, range.location + range.length, nil)
default:
return CTLineGetOffsetForStringIndex(line, range.location, nil)
}
}()
// HACK: This `extraYOffset` value is a hack to resolve a weird issue where the positioning
// seems to be slightly off every additional line of text we add (it doesn't seem to be related
// to line spacing or anything, more related to the bold mention text being positioned slightly
// differently from the non-bold text)
let extraYOffset: CGFloat = (CGFloat(lineIndex) * (runDescent / 12))
// Note: Changes to `origin.y` need to be inverted since the context has been flipped
runBounds.origin.x = origins[lineIndex].x + rect.origin.x + self.maxPadding + xOffset - padding
runBounds.origin.y = (
origins[lineIndex].y + rect.origin.y +
self.maxPadding -
padding -
runDescent -
extraYOffset
)
// We don't want to draw too far to the right
runBounds.size.width = (runBounds.width > lineWidth ? lineWidth : runBounds.width)
let path = UIBezierPath(roundedRect: runBounds, cornerRadius: cornerRadius)
mentionBackgroundColor.setFill()
path.fill()
}
}
}
}
extension CTFrame {
var lines: [CTLine] {
return ((CTFrameGetLines(self) as [AnyObject] as? [CTLine]) ?? [])
}
}
extension CTLine {
var ctruns: [CTRun] {
return ((CTLineGetGlyphRuns(self) as [AnyObject] as? [CTRun]) ?? [])
}
}

View File

@ -56,7 +56,7 @@ final class ScanQRCodeWrapperVC : BaseVC {
explanationLabel.autoPinWidthToSuperview(withMargin: 32)
explanationLabel.autoPinHeightToSuperview(withMargin: 32)
// Title
title = NSLocalizedString("Scan QR Code", comment: "")
title = "Scan QR Code"
}
override func viewDidAppear(_ animated: Bool) {

View File

@ -6,10 +6,17 @@ import SessionUIKit
import SessionMessagingKit
public enum MentionUtilities {
public static func highlightMentions(in string: String, threadVariant: SessionThread.Variant) -> String {
public static func highlightMentions(
in string: String,
threadVariant: SessionThread.Variant,
currentUserPublicKey: String,
currentUserBlindedPublicKey: String?
) -> String {
return highlightMentions(
in: string,
threadVariant: threadVariant,
currentUserPublicKey: currentUserPublicKey,
currentUserBlindedPublicKey: currentUserBlindedPublicKey,
isOutgoingMessage: false,
attributes: [:]
).string // isOutgoingMessage and attributes are irrelevant
@ -18,6 +25,8 @@ public enum MentionUtilities {
public static func highlightMentions(
in string: String,
threadVariant: SessionThread.Variant,
currentUserPublicKey: String?,
currentUserBlindedPublicKey: String?,
isOutgoingMessage: Bool,
attributes: [NSAttributedString.Key: Any]
) -> NSAttributedString {
@ -29,7 +38,13 @@ public enum MentionUtilities {
var string = string
var lastMatchEnd: Int = 0
var mentions: [(range: NSRange, publicKey: String)] = []
var mentions: [(range: NSRange, isCurrentUser: Bool)] = []
let currentUserPublicKeys: Set<String> = [
currentUserPublicKey,
currentUserBlindedPublicKey
]
.compactMap { $0 }
.asSet()
while let match: NSTextCheckingResult = regex.firstMatch(
in: string,
@ -39,28 +54,52 @@ public enum MentionUtilities {
guard let range: Range = Range(match.range, in: string) else { break }
let publicKey: String = String(string[range].dropFirst()) // Drop the @
let isCurrentUser: Bool = currentUserPublicKeys.contains(publicKey)
guard let displayName: String = Profile.displayNameNoFallback(id: publicKey, threadVariant: threadVariant) else {
lastMatchEnd = (match.range.location + match.range.length)
continue
}
guard let targetString: String = {
guard !isCurrentUser else { return "MEDIA_GALLERY_SENDER_NAME_YOU".localized() }
guard let displayName: String = Profile.displayNameNoFallback(id: publicKey, threadVariant: threadVariant) else {
lastMatchEnd = (match.range.location + match.range.length)
return nil
}
return displayName
}()
else { continue }
string = string.replacingCharacters(in: range, with: "@\(displayName)")
lastMatchEnd = (match.range.location + displayName.utf16.count)
string = string.replacingCharacters(in: range, with: "@\(targetString)")
lastMatchEnd = (match.range.location + targetString.utf16.count)
mentions.append((
// + 1 to include the @
range: NSRange(location: match.range.location, length: displayName.utf16.count + 1),
publicKey: publicKey
range: NSRange(location: match.range.location, length: targetString.utf16.count + 1),
isCurrentUser: isCurrentUser
))
}
let sizeDiff: CGFloat = (Values.smallFontSize / Values.mediumFontSize)
let result: NSMutableAttributedString = NSMutableAttributedString(string: string, attributes: attributes)
mentions.forEach { mention in
// FIXME: This might break when swapping between themes
let color = isOutgoingMessage ? (isLightMode ? .white : .black) : Colors.accent
result.addAttribute(.foregroundColor, value: color, range: mention.range)
result.addAttribute(.font, value: UIFont.boldSystemFont(ofSize: Values.smallFontSize), range: mention.range)
if mention.isCurrentUser {
// Note: The designs don't match with the dynamic sizing so these values need to be calculated
// to maintain a "rounded rect" effect rather than a "pill" effect
result.addAttribute(.currentUserMentionBackgroundCornerRadius, value: (8 * sizeDiff), range: mention.range)
result.addAttribute(.currentUserMentionBackgroundPadding, value: (3 * sizeDiff), range: mention.range)
result.addAttribute(.currentUserMentionBackgroundColor, value: Colors.accent, range: mention.range)
result.addAttribute(.foregroundColor, value: UIColor.black, range: mention.range)
}
else {
let color: UIColor = {
switch (isLightMode, isOutgoingMessage) {
case (_, true): return .black
case (true, false): return .black
case (false, false): return Colors.accent
}
}()
result.addAttribute(.foregroundColor, value: color, range: mention.range)
}
}
return result

View File

@ -106,33 +106,12 @@ public extension Contact {
// TODO: Remove this when possible
@objc(SMKContact)
public class SMKContact: NSObject {
@objc let isApproved: Bool
@objc let isBlocked: Bool
@objc let didApproveMe: Bool
init(isApproved: Bool, isBlocked: Bool, didApproveMe: Bool) {
self.isApproved = isApproved
self.isBlocked = isBlocked
self.didApproveMe = didApproveMe
}
@objc public static func fetchOrCreate(id: String) -> SMKContact {
let existingContact: Contact? = Storage.shared.read { db in
try Contact.fetchOne(db, id: id)
}
return SMKContact(
isApproved: existingContact?.isApproved ?? false,
isBlocked: existingContact?.isBlocked ?? false,
didApproveMe: existingContact?.didApproveMe ?? false
)
}
@objc(isBlockedFor:)
public static func isBlocked(id: String) -> Bool {
return Storage.shared
.read { db in
try Contact
.filter(id: id)
.select(.isBlocked)
.asRequest(of: Bool.self)
.fetchOne(db)

View File

@ -314,14 +314,11 @@ public extension Profile {
/// A standardised mechanism for truncating a user id for a given thread
static func truncated(id: String, threadVariant: SessionThread.Variant = .contact) -> String {
switch threadVariant {
case .openGroup: return truncated(id: id, truncating: .start)
default: return truncated(id: id, truncating: .middle)
}
return truncated(id: id, truncating: .middle)
}
/// A standardised mechanism for truncating a user id
static func truncated(id: String, truncating: Truncation = .start) -> String {
static func truncated(id: String, truncating: Truncation = .middle) -> String {
guard id.count > 8 else { return id }
switch truncating {
@ -355,7 +352,7 @@ public extension Profile {
case .openGroup:
// In open groups, where it's more likely that multiple users have the same name,
// we display a bit of the Session ID after a user's display name for added context
return "\(name) (\(Profile.truncated(id: id, truncating: .start)))"
return "\(name) (\(Profile.truncated(id: id, truncating: .middle)))"
}
}
}

View File

@ -2,6 +2,7 @@
import Foundation
import GRDB
import Sodium
import SessionUtilitiesKit
public struct SessionThread: Codable, Identifiable, Equatable, FetchableRecord, PersistableRecord, TableRecord, ColumnExpressible {
@ -314,6 +315,39 @@ public extension SessionThread {
return profile.displayName()
}
}
static func getUserHexEncodedBlindedKey(
threadId: String,
threadVariant: Variant
) -> String? {
guard
threadVariant == .openGroup,
let blindingInfo: (edkeyPair: Box.KeyPair?, publicKey: String?) = Storage.shared.read({ db in
return (
Identity.fetchUserEd25519KeyPair(db),
try OpenGroup
.filter(id: threadId)
.select(.publicKey)
.asRequest(of: String.self)
.fetchOne(db)
)
}),
let userEdKeyPair: Box.KeyPair = blindingInfo.edkeyPair,
let publicKey: String = blindingInfo.publicKey
else { return nil }
let sodium: Sodium = Sodium()
let blindedKeyPair: Box.KeyPair? = sodium.blindedKeyPair(
serverPublicKey: publicKey,
edKeyPair: userEdKeyPair,
genericHash: sodium.getGenericHash()
)
return blindedKeyPair.map { keyPair -> String in
SessionId(.blinded, publicKey: keyPair.publicKey).hexString
}
}
}
// MARK: - Objective-C Support
@ -347,7 +381,7 @@ public class SMKThread: NSObject {
public static func isOnlyNotifyingForMentions(_ threadId: String) -> Bool {
return Storage.shared.read { db in
return try SessionThread
.select(SessionThread.Columns.onlyNotifyForMentions == true)
.select(SessionThread.Columns.onlyNotifyForMentions)
.filter(id: threadId)
.asRequest(of: Bool.self)
.fetchOne(db)

View File

@ -28,6 +28,15 @@ public enum UpdateProfilePictureJob: JobExecutor {
let lastProfilePictureUpload: Date = UserDefaults.standard[.lastProfilePictureUpload],
Date().timeIntervalSince(lastProfilePictureUpload) > (14 * 24 * 60 * 60)
else {
// Reset the `nextRunTimestamp` value just in case the last run failed so we don't get stuck
// in a loop endlessly deferring the job
if let jobId: Int64 = job.id {
Storage.shared.write { db in
try Job
.filter(id: jobId)
.updateAll(db, Job.Columns.nextRunTimestamp.set(to: 0))
}
}
deferred(job)
return
}

View File

@ -204,20 +204,20 @@ extension MessageReceiver {
message.attachmentIds = attachments.map { $0.id }
// Persist quote if needed
try? Quote(
let quote: Quote? = try? Quote(
db,
proto: dataMessage,
interactionId: interactionId,
thread: thread
)?.insert(db)
)?.inserted(db)
// Parse link preview if needed
try? LinkPreview(
let linkPreview: LinkPreview? = try? LinkPreview(
db,
proto: dataMessage,
body: message.text,
sentTimestampMs: (messageSentTimestamp * 1000)
)?.save(db)
)?.saved(db)
// Open group invitations are stored as LinkPreview values so create one if needed
if
@ -232,6 +232,31 @@ extension MessageReceiver {
).save(db)
}
// Start attachment downloads if needed (ie. trusted contact or group thread)
// FIXME: Replace this to check the `autoDownloadAttachments` flag we are adding to threads
let isContactTrusted: Bool = ((try? Contact.fetchOne(db, id: sender))?.isTrusted ?? false)
if isContactTrusted || thread.variant != .contact {
attachments
.map { $0.id }
.appending(quote?.attachmentId)
.appending(linkPreview?.attachmentId)
.forEach { attachmentId in
JobRunner.add(
db,
job: Job(
variant: .attachmentDownload,
threadId: thread.id,
interactionId: interactionId,
details: AttachmentDownloadJob.Details(
attachmentId: attachmentId
)
),
canStartJob: isMainAppActive
)
}
}
// Cancel any typing indicators if needed
if isMainAppActive {
TypingIndicators.didStopTyping(db, threadId: thread.id, direction: .incoming)

View File

@ -44,11 +44,11 @@ extension MessageSender {
// Send a closed group update message to all members individually
var promises: [Promise<Void>] = []
try members.forEach { adminId in
try members.forEach { memberId in
try GroupMember(
groupId: groupPublicKey,
profileId: adminId,
role: .admin
profileId: memberId,
role: .standard
).insert(db)
}

View File

@ -29,6 +29,7 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable,
public static let quoteAttachmentKey: SQL = SQL(stringLiteral: CodingKeys.quoteAttachment.stringValue)
public static let linkPreviewKey: SQL = SQL(stringLiteral: CodingKeys.linkPreview.stringValue)
public static let linkPreviewAttachmentKey: SQL = SQL(stringLiteral: CodingKeys.linkPreviewAttachment.stringValue)
public static let currentUserPublicKeyKey: SQL = SQL(stringLiteral: CodingKeys.currentUserPublicKey.stringValue)
public static let cellTypeKey: SQL = SQL(stringLiteral: CodingKeys.cellType.stringValue)
public static let authorNameKey: SQL = SQL(stringLiteral: CodingKeys.authorName.stringValue)
public static let shouldShowProfileKey: SQL = SQL(stringLiteral: CodingKeys.shouldShowProfile.stringValue)
@ -92,6 +93,8 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable,
public let linkPreview: LinkPreview?
public let linkPreviewAttachment: Attachment?
public let currentUserPublicKey: String
// Post-Query Processing Data
/// This value includes the associated attachments
@ -132,6 +135,9 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable,
/// This value indicates whether this is the last message in the thread
public let isLast: Bool
/// This is the users blinded key (will only be set for messages within open groups)
public let currentUserBlindedPublicKey: String?
// MARK: - Mutation
@ -164,6 +170,7 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable,
quoteAttachment: self.quoteAttachment,
linkPreview: self.linkPreview,
linkPreviewAttachment: self.linkPreviewAttachment,
currentUserPublicKey: self.currentUserPublicKey,
attachments: attachments,
cellType: self.cellType,
authorName: self.authorName,
@ -175,14 +182,16 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable,
previousVariant: self.previousVariant,
positionInCluster: self.positionInCluster,
isOnlyMessageInCluster: self.isOnlyMessageInCluster,
isLast: self.isLast
isLast: self.isLast,
currentUserBlindedPublicKey: self.currentUserBlindedPublicKey
)
}
public func withClusteringChanges(
prevModel: MessageViewModel?,
nextModel: MessageViewModel?,
isLast: Bool
isLast: Bool,
currentUserBlindedPublicKey: String?
) -> MessageViewModel {
let cellType: CellType = {
guard self.isTypingIndicator != true else { return .typingIndicator }
@ -338,6 +347,7 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable,
quoteAttachment: self.quoteAttachment,
linkPreview: self.linkPreview,
linkPreviewAttachment: self.linkPreviewAttachment,
currentUserPublicKey: self.currentUserPublicKey,
attachments: self.attachments,
cellType: cellType,
authorName: authorDisplayName,
@ -385,7 +395,8 @@ public struct MessageViewModel: FetchableRecordWithRowId, Decodable, Equatable,
previousVariant: prevModel?.variant,
positionInCluster: positionInCluster,
isOnlyMessageInCluster: isOnlyMessageInCluster,
isLast: isLast
isLast: isLast,
currentUserBlindedPublicKey: currentUserBlindedPublicKey
)
}
}
@ -478,6 +489,7 @@ public extension MessageViewModel {
self.quoteAttachment = nil
self.linkPreview = nil
self.linkPreviewAttachment = nil
self.currentUserPublicKey = ""
// Post-Query Processing Data
@ -493,6 +505,7 @@ public extension MessageViewModel {
self.positionInCluster = .middle
self.isOnlyMessageInCluster = true
self.isLast = true
self.currentUserBlindedPublicKey = nil
}
}
@ -557,7 +570,11 @@ public extension MessageViewModel {
return SQL("\(interaction[.timestampMs].desc)")
}()
static func baseQuery(orderSQL: SQL, groupSQL: SQL?) -> (([Int64]) -> AdaptedFetchRequest<SQLRequest<MessageViewModel>>) {
static func baseQuery(
userPublicKey: String,
orderSQL: SQL,
groupSQL: SQL?
) -> (([Int64]) -> AdaptedFetchRequest<SQLRequest<MessageViewModel>>) {
return { rowIds -> AdaptedFetchRequest<SQLRequest<ViewModel>> in
let interaction: TypedTableAlias<Interaction> = TypedTableAlias()
let thread: TypedTableAlias<SessionThread> = TypedTableAlias()
@ -621,6 +638,8 @@ public extension MessageViewModel {
\(ViewModel.linkPreviewKey).*,
\(ViewModel.linkPreviewAttachmentKey).*,
\(SQL("\(userPublicKey)")) AS \(ViewModel.currentUserPublicKeyKey),
-- All of the below properties are set in post-query processing but to prevent the
-- query from crashing when decoding we need to provide default values
\(CellType.textOnlyMessage) AS \(ViewModel.cellTypeKey),

View File

@ -2,6 +2,7 @@
import Foundation
import GRDB
import Sodium
import DifferenceKit
import SessionUtilitiesKit
@ -124,6 +125,7 @@ public struct SessionThreadViewModel: FetchableRecordWithRowId, Decodable, Equat
private let threadContactNameInternal: String?
private let authorNameInternal: String?
public let currentUserPublicKey: String
public let currentUserBlindedPublicKey: String?
// UI specific logic
@ -275,6 +277,67 @@ public extension SessionThreadViewModel {
self.threadContactNameInternal = nil
self.authorNameInternal = nil
self.currentUserPublicKey = getUserHexEncodedPublicKey()
self.currentUserBlindedPublicKey = nil
}
}
// MARK: - Mutation
public extension SessionThreadViewModel {
func populatingCurrentUserBlindedKey(
currentUserBlindedPublicKeyForThisThread: String? = nil
) -> SessionThreadViewModel {
return SessionThreadViewModel(
rowId: self.rowId,
threadId: self.threadId,
threadVariant: self.threadVariant,
threadCreationDateTimestamp: self.threadCreationDateTimestamp,
threadMemberNames: self.threadMemberNames,
threadIsNoteToSelf: self.threadIsNoteToSelf,
threadIsMessageRequest: self.threadIsMessageRequest,
threadRequiresApproval: self.threadRequiresApproval,
threadShouldBeVisible: self.threadShouldBeVisible,
threadIsPinned: self.threadIsPinned,
threadIsBlocked: self.threadIsBlocked,
threadMutedUntilTimestamp: self.threadMutedUntilTimestamp,
threadOnlyNotifyForMentions: self.threadOnlyNotifyForMentions,
threadMessageDraft: self.threadMessageDraft,
threadContactIsTyping: self.threadContactIsTyping,
threadUnreadCount: self.threadUnreadCount,
threadUnreadMentionCount: self.threadUnreadMentionCount,
contactProfile: self.contactProfile,
closedGroupProfileFront: self.closedGroupProfileFront,
closedGroupProfileBack: self.closedGroupProfileBack,
closedGroupProfileBackFallback: self.closedGroupProfileBackFallback,
closedGroupName: self.closedGroupName,
closedGroupUserCount: self.closedGroupUserCount,
currentUserIsClosedGroupMember: self.currentUserIsClosedGroupMember,
currentUserIsClosedGroupAdmin: self.currentUserIsClosedGroupAdmin,
openGroupName: self.openGroupName,
openGroupServer: self.openGroupServer,
openGroupRoomToken: self.openGroupRoomToken,
openGroupProfilePictureData: self.openGroupProfilePictureData,
openGroupUserCount: self.openGroupUserCount,
interactionId: self.interactionId,
interactionVariant: self.interactionVariant,
interactionTimestampMs: self.interactionTimestampMs,
interactionBody: self.interactionBody,
interactionState: self.interactionState,
interactionIsOpenGroupInvitation: self.interactionIsOpenGroupInvitation,
interactionAttachmentDescriptionInfo: self.interactionAttachmentDescriptionInfo,
interactionAttachmentCount: self.interactionAttachmentCount,
authorId: self.authorId,
threadContactNameInternal: self.threadContactNameInternal,
authorNameInternal: self.authorNameInternal,
currentUserPublicKey: self.currentUserPublicKey,
currentUserBlindedPublicKey: (
currentUserBlindedPublicKeyForThisThread ??
SessionThread.getUserHexEncodedBlindedKey(
threadId: self.threadId,
threadVariant: self.threadVariant
)
)
)
}
}

View File

@ -11,4 +11,6 @@ public enum SnodeAPIEndpoint: String {
case getInfo = "info"
case clearAllData = "delete_all"
case expire = "expire"
case batch = "batch"
case sequence = "sequence"
}

View File

@ -26,7 +26,7 @@ public extension UISearchBar {
let searchTextField: UITextField = self.searchTextField
searchTextField.backgroundColor = Colors.searchBarBackground // The search bar background color
searchTextField.textColor = Colors.text
searchTextField.attributedPlaceholder = NSAttributedString(string: NSLocalizedString("Search", comment: ""), attributes: [ .foregroundColor : Colors.searchBarPlaceholder ])
searchTextField.attributedPlaceholder = NSAttributedString(string: "Search", attributes: [ .foregroundColor : Colors.searchBarPlaceholder ])
setPositionAdjustment(UIOffset(horizontal: 4, vertical: 0), for: UISearchBar.Icon.search)
searchTextPositionAdjustment = UIOffset(horizontal: 2, vertical: 0)
setPositionAdjustment(UIOffset(horizontal: -4, vertical: 0), for: UISearchBar.Icon.clear)

View File

@ -48,11 +48,11 @@ public enum Mnemonic {
public var errorDescription: String? {
switch self {
case .generic: return NSLocalizedString("Something went wrong. Please check your recovery phrase and try again.", comment: "")
case .inputTooShort: return NSLocalizedString("Looks like you didn't enter enough words. Please check your recovery phrase and try again.", comment: "")
case .missingLastWord: return NSLocalizedString("You seem to be missing the last word of your recovery phrase. Please check what you entered and try again.", comment: "")
case .invalidWord: return NSLocalizedString("There appears to be an invalid word in your recovery phrase. Please check what you entered and try again.", comment: "")
case .verificationFailed: return NSLocalizedString("Your recovery phrase couldn't be verified. Please check what you entered and try again.", comment: "")
case .generic: return "RECOVERY_PHASE_ERROR_GENERIC".localized()
case .inputTooShort: return "RECOVERY_PHASE_ERROR_LENGTH".localized()
case .missingLastWord: return "RECOVERY_PHASE_ERROR_LAST_WORD".localized()
case .invalidWord: return "RECOVERY_PHASE_ERROR_INVALID_WORD".localized()
case .verificationFailed: return "RECOVERY_PHASE_ERROR_FAILED".localized()
}
}
}

View File

@ -164,7 +164,7 @@ class AttachmentTextToolbar: UIView, UITextViewDelegate {
private lazy var placeholderTextView: UITextView = {
let placeholderTextView = buildTextView()
placeholderTextView.text = NSLocalizedString("Message", comment: "")
placeholderTextView.text = "Message"
placeholderTextView.isEditable = false
return placeholderTextView

View File

@ -118,8 +118,7 @@ import SessionMessagingKit
completion completionParam: @escaping ((OWSScreenLockOutcome) -> Void)) {
AssertIsOnMainThread()
let defaultErrorDescription = NSLocalizedString("SCREEN_LOCK_ENABLE_UNKNOWN_ERROR",
comment: "Indicates that an unknown error occurred while using Touch ID/Face ID/Phone Passcode.")
let defaultErrorDescription = "SCREEN_LOCK_ENABLE_UNKNOWN_ERROR".localized()
// Ensure completion is always called on the main thread.
let completion = { (outcome: OWSScreenLockOutcome) in
@ -140,7 +139,7 @@ import SessionMessagingKit
switch outcome {
case .success:
owsFailDebug("local authentication unexpected success")
completion(.failure(error:defaultErrorDescription))
completion(.failure(error: defaultErrorDescription))
case .cancel, .failure, .unexpectedFailure:
completion(outcome)
}
@ -177,16 +176,13 @@ import SessionMessagingKit
switch laError.code {
case .biometryNotAvailable:
Logger.error("local authentication error: biometryNotAvailable.")
return .failure(error: NSLocalizedString("SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE",
comment: "Indicates that Touch ID/Face ID/Phone Passcode are not available on this device."))
return .failure(error: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE".localized())
case .biometryNotEnrolled:
Logger.error("local authentication error: biometryNotEnrolled.")
return .failure(error: NSLocalizedString("SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED",
comment: "Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device."))
return .failure(error: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED".localized())
case .biometryLockout:
Logger.error("local authentication error: biometryLockout.")
return .failure(error: NSLocalizedString("SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT",
comment: "Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures."))
return .failure(error: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT".localized())
default:
// Fall through to second switch
break
@ -195,27 +191,22 @@ import SessionMessagingKit
switch laError.code {
case .authenticationFailed:
Logger.error("local authentication error: authenticationFailed.")
return .failure(error: NSLocalizedString("SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED",
comment: "Indicates that Touch ID/Face ID/Phone Passcode authentication failed."))
return .failure(error: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_FAILED".localized())
case .userCancel, .userFallback, .systemCancel, .appCancel:
Logger.info("local authentication cancelled.")
return .cancel
case .passcodeNotSet:
Logger.error("local authentication error: passcodeNotSet.")
return .failure(error: NSLocalizedString("SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET",
comment: "Indicates that Touch ID/Face ID/Phone Passcode passcode is not set."))
return .failure(error: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_PASSCODE_NOT_SET".localized())
case .touchIDNotAvailable:
Logger.error("local authentication error: touchIDNotAvailable.")
return .failure(error: NSLocalizedString("SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE",
comment: "Indicates that Touch ID/Face ID/Phone Passcode are not available on this device."))
return .failure(error: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_AVAILABLE".localized())
case .touchIDNotEnrolled:
Logger.error("local authentication error: touchIDNotEnrolled.")
return .failure(error: NSLocalizedString("SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED",
comment: "Indicates that Touch ID/Face ID/Phone Passcode is not configured on this device."))
return .failure(error: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_NOT_ENROLLED".localized())
case .touchIDLockout:
Logger.error("local authentication error: touchIDLockout.")
return .failure(error: NSLocalizedString("SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT",
comment: "Indicates that Touch ID/Face ID/Phone Passcode is 'locked out' on this device due to authentication failures."))
return .failure(error: "SCREEN_LOCK_ERROR_LOCAL_AUTHENTICATION_LOCKOUT".localized())
case .invalidContext:
owsFailDebug("context not valid.")
return .unexpectedFailure(error:defaultErrorDescription)

View File

@ -16,7 +16,7 @@ import Foundation
@objc
static public let doneButton = NSLocalizedString("BUTTON_DONE", comment: "Label for generic done button.")
@objc
static public let retryButton = NSLocalizedString("RETRY_BUTTON_TEXT", comment: "Generic text for button that retries whatever the last action was.")
static public let retryButton = "RETRY_BUTTON_TEXT".localized()
@objc
static public let openSettingsButton = NSLocalizedString("OPEN_SETTINGS_BUTTON", comment: "Button text which opens the settings app")
@objc
@ -31,20 +31,11 @@ import Foundation
static public let markAsReadNotificationAction = NSLocalizedString("PUSH_MANAGER_MARKREAD", comment: "Notification action button title")
@objc
static public let sendButton = NSLocalizedString("SEND_BUTTON_TITLE", comment: "Label for the button to send a message")
static public let sendButton = "SEND_BUTTON_TITLE".localized()
}
@objc
public class NotificationStrings: NSObject {
@objc
static public let incomingCallBody = NSLocalizedString("CALL_INCOMING_NOTIFICATION_BODY", comment: "notification body")
@objc
static public let missedCallBody = NSLocalizedString("CALL_MISSED_NOTIFICATION_BODY", comment: "notification body")
@objc
static public let missedCallBecauseOfIdentityChangeBody = NSLocalizedString("CALL_MISSED_BECAUSE_OF_IDENTITY_CHANGE_NOTIFICATION_BODY", comment: "notification body")
@objc
static public let incomingMessageBody = NSLocalizedString("APN_Message", comment: "notification body")
@ -55,32 +46,13 @@ public class NotificationStrings: NSObject {
static public let incomingGroupMessageTitleFormat = NSLocalizedString("NEW_GROUP_MESSAGE_NOTIFICATION_TITLE", comment: "notification title. Embeds {{author name}} and {{group name}}")
@objc
static public let failedToSendBody = NSLocalizedString("SEND_FAILED_NOTIFICATION_BODY", comment: "notification body")
static public let failedToSendBody = "SEND_FAILED_NOTIFICATION_BODY".localized()
}
@objc public class CallStrings: NSObject {
@objc
static public let callStatusFormat = NSLocalizedString("CALL_STATUS_FORMAT", comment: "embeds {{Call Status}} in call screen label. For ongoing calls, {{Call Status}} is a seconds timer like 01:23, otherwise {{Call Status}} is a short text like 'Ringing', 'Busy', or 'Failed Call'")
@objc
static public let confirmAndCallButtonTitle = NSLocalizedString("SAFETY_NUMBER_CHANGED_CONFIRM_CALL_ACTION", comment: "alert button text to confirm placing an outgoing call after the recipients Safety Number has changed.")
@objc
static public let callBackAlertTitle = NSLocalizedString("CALL_USER_ALERT_TITLE", comment: "Title for alert offering to call a user.")
@objc
static public let callBackAlertMessageFormat = NSLocalizedString("CALL_USER_ALERT_MESSAGE_FORMAT", comment: "Message format for alert offering to call a user. Embeds {{the user's display name or phone number}}.")
@objc
static public let callBackAlertCallButton = NSLocalizedString("CALL_USER_ALERT_CALL_BUTTON", comment: "Label for call button for alert offering to call a user.")
// MARK: Notification actions
@objc
static public let callBackButtonTitle = NSLocalizedString("CALLBACK_BUTTON_TITLE", comment: "notification action")
@objc
static public let showThreadButtonTitle = NSLocalizedString("SHOW_THREAD_BUTTON_TITLE", comment: "notification action")
@objc
static public let answerCallButtonTitle = NSLocalizedString("ANSWER_CALL_BUTTON_TITLE", comment: "notification action")
@objc
static public let declineCallButtonTitle = NSLocalizedString("REJECT_CALL_BUTTON_TITLE", comment: "notification action")
static public let showThreadButtonTitle = "SHOW_THREAD_BUTTON_TITLE".localized()
}
@objc public class MediaStrings: NSObject {
@ -91,9 +63,3 @@ public class NotificationStrings: NSObject {
@objc
static public let document = NSLocalizedString("DOCUMENT_TAB_TITLE", comment: "document tab title")
}
@objc public class SafetyNumberStrings: NSObject {
@objc
static public let confirmSendButton = NSLocalizedString("SAFETY_NUMBER_CHANGED_CONFIRM_SEND_ACTION",
comment: "button title to confirm sending to a recipient whose safety number recently changed")
}

View File

@ -6,25 +6,6 @@ import Foundation
import SessionUtilitiesKit
@objc public class OWSAlerts: NSObject {
/// Cleanup and present alert for no permissions
@objc
public class func showNoMicrophonePermissionAlert() {
let alertTitle = NSLocalizedString("CALL_AUDIO_PERMISSION_TITLE", comment: "Alert title when calling and permissions for microphone are missing")
let alertMessage = NSLocalizedString("CALL_AUDIO_PERMISSION_MESSAGE", comment: "Alert message when calling and permissions for microphone are missing")
let alert = UIAlertController(title: alertTitle, message: alertMessage, preferredStyle: .alert)
let dismissAction = UIAlertAction(title: CommonStrings.dismissButton, style: .cancel)
dismissAction.accessibilityIdentifier = "OWSAlerts.\("dismiss")"
alert.addAction(dismissAction)
if let settingsAction = CurrentAppContext().openSystemSettingsAction {
settingsAction.accessibilityIdentifier = "OWSAlerts.\("settings")"
alert.addAction(settingsAction)
}
CurrentAppContext().frontmostViewController()?.presentAlert(alert)
}
@objc
public class func showAlert(_ alert: UIAlertController) {
guard let frontmostViewController = CurrentAppContext().frontmostViewController() else {