Merge branch 'database-refactor' into add-documents-section
This commit is contained in:
commit
b7d17da9cc
|
@ -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 ------------")
|
|
@ -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 */,
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -718,7 +718,7 @@ CGFloat kIconViewLength = 24;
|
|||
|
||||
- (BOOL)hasLeftGroup
|
||||
{
|
||||
if (self.isClosedGroup || self.isOpenGroup) {
|
||||
if (self.isClosedGroup) {
|
||||
return ![SMKGroupMember isCurrentUserMemberOf:self.threadId];
|
||||
}
|
||||
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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 {
|
||||
|
|
|
@ -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]
|
||||
|
||||
|
|
|
@ -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 ?
|
||||
|
|
|
@ -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 }?
|
||||
|
|
|
@ -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 ?
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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 d’authentification";
|
||||
/* 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 d’essais infructueux d’authentification. 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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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.";
|
||||
|
|
|
@ -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)
|
||||
|
||||
|
|
|
@ -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()
|
||||
}))
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
|
|
@ -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,
|
||||
|
|
|
@ -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]) ?? [])
|
||||
}
|
||||
}
|
|
@ -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) {
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)))"
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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)
|
||||
}
|
||||
|
||||
|
|
|
@ -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),
|
||||
|
|
|
@ -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
|
||||
)
|
||||
)
|
||||
)
|
||||
}
|
||||
}
|
||||
|
||||
|
|
|
@ -11,4 +11,6 @@ public enum SnodeAPIEndpoint: String {
|
|||
case getInfo = "info"
|
||||
case clearAllData = "delete_all"
|
||||
case expire = "expire"
|
||||
case batch = "batch"
|
||||
case sequence = "sequence"
|
||||
}
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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()
|
||||
}
|
||||
}
|
||||
}
|
||||
|
|
|
@ -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
|
||||
|
|
|
@ -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)
|
||||
|
|
|
@ -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")
|
||||
}
|
||||
|
|
|
@ -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 {
|
||||
|
|
Loading…
Reference in New Issue