Refactoring

This commit is contained in:
Leon Böttger committed 2025-11-10 21:22:46 +01:00
1 parent 39ea48fc80
commit 9536854fa2
8 files changed
+176 -208

No files matched your search

@@ -22,6 +22,7 @@ import UIKit
class InboundNearbyConnection: NearbyConnection {
private var filesToBeReceived: [Int64: InternalFileInfo] = [:]
private var downloadedFiles = [URL]()
private var currentState: State = .initial
private var cipherCommitment: Data?
private var textPayloadID: Int64 = 0
@@ -51,7 +52,17 @@ class InboundNearbyConnection: NearbyConnection {
DispatchQueue.main.async {
self.delegate?.connectionWasTerminated(connection: self, error: self.lastError)
SaveFilesManager.shared.movePendingFilesToTarget()
if !self.downloadedFiles.isEmpty {
#if os(macOS)
if Settings.shared.openFinderAfterReceiving {
log("[SaveFilesManager] Opening \(self.downloadedFiles.count) file(s) in Finder.")
NSWorkspace.shared.activateFileViewerSelecting(self.downloadedFiles)
}
#else
FileManager.default.openDocumentFolder()
#endif
}
}
}
@@ -168,7 +179,7 @@ class InboundNearbyConnection: NearbyConnection {
#if os(macOS)
fileInfo.progress?.unpublish()
#endif
SaveFilesManager.shared.registerFileFinishedDownloading(fileInfo.destinationURL)
downloadedFiles.append(fileInfo.destinationURL)
EXIFUtils.applyEXIFTimestamps(at: fileInfo.destinationURL)
filesToBeReceived.removeValue(forKey: id)
@@ -222,7 +233,7 @@ class InboundNearbyConnection: NearbyConnection {
fileInfo.progress?.unpublish()
#endif
filesToBeReceived.removeValue(forKey: id)
SaveFilesManager.shared.registerFileFinishedDownloading(fileInfo.destinationURL)
downloadedFiles.append(fileInfo.destinationURL)
EXIFUtils.applyEXIFTimestamps(at: fileInfo.destinationURL)
if filesToBeReceived.isEmpty {
@@ -496,7 +507,7 @@ class InboundNearbyConnection: NearbyConnection {
currentState = .waitingForUserConsent
if frame.v1.introduction.fileMetadata.count > 0 && frame.v1.introduction.textMetadata.isEmpty {
let saveDirectory = SaveFilesManager.shared.getSaveDirectory()
let saveDirectory = NearbyConnectionManager.shared.getSaveDirectory()
var usedDestinations = Set<URL>()
for file in frame.v1.introduction.fileMetadata {
@@ -39,6 +39,7 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
private var qrCodeNameEncryptionKey: SymmetricKey?
private let hasConnectionMonitor = NWPathMonitor()
private let connectionMonitorQueue = DispatchQueue(label: "NetworkConnectionMonitorQueue")
private var securityScopeUrl: URL?
public let deviceInfo: EndpointInfo
public let endpointID: [UInt8] = generateEndpointID()
@@ -92,6 +93,28 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
}
hasConnectionMonitor.start(queue: connectionMonitorQueue)
// remove old temp directory
let tempPath = FileManager.default.temporaryDirectory
let fileManager = FileManager.default
do {
let contents = try fileManager.contentsOfDirectory(at: tempPath, includingPropertiesForKeys: nil)
var didSomething = false
for item in contents {
didSomething = true
try fileManager.removeItem(at: item)
}
if didSomething {
log("[SaveFilesManager] Temporary directory cleared.")
}
} catch {
log("[SaveFilesManager] Failed to list contents of temp directory: \(error)")
}
}
@@ -208,6 +231,11 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
func connectionWasTerminated(connection: InboundNearbyConnection, error: Error?) {
incomingConnections.removeValue(forKey: connection.id)
// If there are no more incoming connections, we can stop accessing the save directory
if incomingConnections.isEmpty {
self.stopAccessingSaveDirectory()
}
if !connection.wasUserRejected {
inboundAppDelegates.forEach { delegate in
delegate.connectionWasTerminated(connectionID: connection.id, from: connection.remoteDeviceInfo ?? RemoteDeviceInfo(name: "UnknownDevice".localized(), type: .unknown), wasPlainTextTransfer: connection.isPlainTextTransfer, error: error)
@@ -595,6 +623,68 @@ public class NearbyConnectionManager: NSObject, NetServiceDelegate, InboundNearb
}
// -- MARK: - Inbound Connection Data Store
public func getSaveDirectory() -> URL {
// Not supported on iOS
#if os(macOS)
if let securityScopeUrl = securityScopeUrl {
log("[SaveFilesManager] Using existing security scope URL: \(securityScopeUrl)")
return securityScopeUrl
}
if let bookmarkData = Settings.shared.saveFolderBookmark {
var isStale = false
do {
let url = try URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
if !isStale {
if url.startAccessingSecurityScopedResource() {
log("[SaveFilesManager] Successfully accessed security scoped resource: \(url)")
securityScopeUrl = url
return url
}
} else {
log("[SaveFilesManager] Bookmark is stale, using default downloads folder.")
}
} catch {
log("[SaveFilesManager] Failed to resolve bookmark: \(error), using default downloads folder.")
}
}
do {
return try FileManager.default.url(for: .downloadsDirectory, in: .userDomainMask, appropriateFor: nil, create: true).resolvingSymlinksInPath()
} catch {
fatalError("[SaveFilesManager] Failed to get downloads directory: \(error)")
}
#else
// Return the documents directory for iOS
do {
return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).resolvingSymlinksInPath()
} catch {
fatalError("Failed to get documents directory: \(error)")
}
#endif
}
private func stopAccessingSaveDirectory() {
// Clean up security scoped resource access
guard let url = securityScopeUrl else {
return
}
log("[SaveFilesManager] Stopping access to security scoped resource: \(url)")
url.stopAccessingSecurityScopedResource()
securityScopeUrl = nil
}
// -- MARK: - Internal Data Model
private struct FoundServiceInfo {
@@ -0,0 +1,25 @@
//
// FileManager+DocumentsOpener.swift
// QuickDrop
//
// Created by Leon Böttger on 10.11.25.
//
import Foundation
import UIKit
import LUI
extension FileManager {
public func openDocumentFolder() {
#if !EXTENSION
let documentsUrl = self.urls(for: .documentDirectory, in: .userDomainMask).first
if let documentsUrl = documentsUrl, let sharedUrl = URL(string: "shareddocuments://\(documentsUrl.path)"), UIApplication.shared.canOpenURL(sharedUrl) {
UIApplication.shared.open(sharedUrl, options: [:])
}
else {
showAlert(title: "CouldNotOpenDocumentsFolder", message: "CouldNotOpenDocumentsFolderDescription")
}
#endif
}
}
-146
View File
@@ -1,146 +0,0 @@
//
// SaveFilesManager.swift
// QuickDrop
//
// Created by Leon Böttger on 26.07.25.
//
import Foundation
#if os(macOS)
import AppKit
#else
import LUI
import UIKit
#endif
public class SaveFilesManager {
private init() {
// remove old temp directory
let tempPath = FileManager.default.temporaryDirectory
let fileManager = FileManager.default
do {
let contents = try fileManager.contentsOfDirectory(at: tempPath, includingPropertiesForKeys: nil)
var didSomething = false
for item in contents {
didSomething = true
try fileManager.removeItem(at: item)
}
if didSomething {
log("[SaveFilesManager] Temporary directory cleared.")
}
} catch {
log("[SaveFilesManager] Failed to list contents of temp directory: \(error)")
}
}
public static let shared = SaveFilesManager()
private var securityScopeUrl: URL?
private var filesFinishedDownloadingSinceLastRun = [URL]()
public func registerFileFinishedDownloading(_ fileURL: URL) {
filesFinishedDownloadingSinceLastRun.append(fileURL)
}
public func movePendingFilesToTarget() {
if !filesFinishedDownloadingSinceLastRun.isEmpty {
#if os(macOS)
if Settings.shared.openFinderAfterReceiving {
log("[SaveFilesManager] Opening \(filesFinishedDownloadingSinceLastRun.count) file(s) in Finder.")
NSWorkspace.shared.activateFileViewerSelecting(filesFinishedDownloadingSinceLastRun)
}
#else
openDownloadedFilesFolder()
#endif
// Clear the list of finished files
filesFinishedDownloadingSinceLastRun.removeAll()
}
stopAccessingSecurityScopedResource()
}
#if !os(macOS)
public func openDownloadedFilesFolder() {
#if !EXTENSION
let documentsUrl = FileManager.default.urls(for: .documentDirectory, in: .userDomainMask).first
if let documentsUrl = documentsUrl, let sharedUrl = URL(string: "shareddocuments://\(documentsUrl.path)"), UIApplication.shared.canOpenURL(sharedUrl) {
UIApplication.shared.open(sharedUrl, options: [:])
}
else {
showAlert(title: "CouldNotOpenDocumentsFolder", message: "CouldNotOpenDocumentsFolderDescription")
}
#endif
}
#endif
public func stopAccessingSecurityScopedResource() {
guard let url = securityScopeUrl else {
return
}
log("[SaveFilesManager] Stopping access to security scoped resource: \(url)")
url.stopAccessingSecurityScopedResource()
securityScopeUrl = nil
}
public func getSaveDirectory() -> URL {
// Not supported on iOS
#if os(macOS)
if let securityScopeUrl = securityScopeUrl {
log("[SaveFilesManager] Using existing security scope URL: \(securityScopeUrl)")
return securityScopeUrl
}
if let bookmarkData = Settings.shared.saveFolderBookmark {
var isStale = false
do {
let url = try URL(resolvingBookmarkData: bookmarkData, options: .withSecurityScope, relativeTo: nil, bookmarkDataIsStale: &isStale)
if !isStale {
if url.startAccessingSecurityScopedResource() {
log("[SaveFilesManager] Successfully accessed security scoped resource: \(url)")
securityScopeUrl = url
return url
}
} else {
log("[SaveFilesManager] Bookmark is stale, using default downloads folder.")
}
} catch {
log("[SaveFilesManager] Failed to resolve bookmark: \(error), using default downloads folder.")
}
}
do {
return try FileManager.default.url(for: .downloadsDirectory, in: .userDomainMask, appropriateFor: nil, create: true).resolvingSymlinksInPath()
} catch {
fatalError("[SaveFilesManager] Failed to get downloads directory: \(error)")
}
#else
// Return the documents directory for iOS
do {
return try FileManager.default.url(for: .documentDirectory, in: .userDomainMask, appropriateFor: nil, create: true).resolvingSymlinksInPath()
} catch {
fatalError("Failed to get documents directory: \(error)")
}
#endif
}
}
+6 -10
View File
@@ -50,16 +50,16 @@
EB4F9D202EC26C8100A74A0F /* EXIFUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB4F9D1E2EC26C7C00A74A0F /* EXIFUtils.swift */; };
EB4F9D212EC26C8100A74A0F /* EXIFUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB4F9D1E2EC26C7C00A74A0F /* EXIFUtils.swift */; };
EB4F9D222EC26C8100A74A0F /* EXIFUtils.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB4F9D1E2EC26C7C00A74A0F /* EXIFUtils.swift */; };
EB4F9D252EC27CFB00A74A0F /* FileManager+DocumentsOpener.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB4F9D232EC27CF000A74A0F /* FileManager+DocumentsOpener.swift */; };
EB4F9D272EC27CFB00A74A0F /* FileManager+DocumentsOpener.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB4F9D232EC27CF000A74A0F /* FileManager+DocumentsOpener.swift */; };
EB5C36932E34EBBD0052EFD7 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = EB5C36922E34EBBD0052EFD7 /* Localizable.xcstrings */; };
EB5C36942E34EBBD0052EFD7 /* Localizable.xcstrings in Resources */ = {isa = PBXBuildFile; fileRef = EB5C36922E34EBBD0052EFD7 /* Localizable.xcstrings */; };
EB5C36E42E34EC0E0052EFD7 /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = EB5C36E32E34EC0E0052EFD7 /* License.txt */; };
EB5C36E52E34EC0E0052EFD7 /* License.txt in Resources */ = {isa = PBXBuildFile; fileRef = EB5C36E32E34EC0E0052EFD7 /* License.txt */; };
EB5C37B82E377A6F0052EFD7 /* SleepManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB2B41E32DC63AC80070FC39 /* SleepManager.swift */; };
EB5C37B92E377A6F0052EFD7 /* SaveFilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */; };
EB5C37BA2E377A6F0052EFD7 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB2F9BFA2DA04B67004010A2 /* Helpers.swift */; };
EB5C37BC2E377A700052EFD7 /* SleepManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB2B41E32DC63AC80070FC39 /* SleepManager.swift */; };
EB5C37BD2E377A700052EFD7 /* Helpers.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB2F9BFA2DA04B67004010A2 /* Helpers.swift */; };
EB5C37BE2E377A700052EFD7 /* SaveFilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */; };
EB5C37BF2E377A780052EFD7 /* ukey.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2829E18CB500A442DA /* ukey.pb.swift */; };
EB5C37C02E377A780052EFD7 /* securemessage.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2D29E18CB500A442DA /* securemessage.pb.swift */; };
EB5C37C12E377A780052EFD7 /* offline_wire_formats.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2B29E18CB500A442DA /* offline_wire_formats.pb.swift */; };
@@ -90,7 +90,6 @@
EB5C37F02E377C3F0052EFD7 /* SwiftECC in Frameworks */ = {isa = PBXBuildFile; productRef = EB5C37EF2E377C3F0052EFD7 /* SwiftECC */; };
EB5C37F12E377CE70052EFD7 /* ukey.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2829E18CB500A442DA /* ukey.pb.swift */; };
EB5C37F22E377CE70052EFD7 /* NearbyConnection.swift in Sources */ = {isa = PBXBuildFile; fileRef = 698DFAE529E2F91A0064F247 /* NearbyConnection.swift */; };
EB5C37F32E377CE70052EFD7 /* SaveFilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */; };
EB5C37F42E377CE70052EFD7 /* NearbyConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A1E29E0C0B300A442DA /* NearbyConnectionManager.swift */; };
EB5C37F52E377CE70052EFD7 /* securemessage.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2D29E18CB500A442DA /* securemessage.pb.swift */; };
EB5C37F62E377CE70052EFD7 /* device_to_device_messages.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2A29E18CB500A442DA /* device_to_device_messages.pb.swift */; };
@@ -161,7 +160,6 @@
EB950B522E520436002D68FA /* NearbyConnectionManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A1E29E0C0B300A442DA /* NearbyConnectionManager.swift */; };
EB950B532E520436002D68FA /* ukey.pb.swift in Sources */ = {isa = PBXBuildFile; fileRef = 69DA9A2829E18CB500A442DA /* ukey.pb.swift */; };
EB950B542E520436002D68FA /* HKDF.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB886E512E4E19390052B7D9 /* HKDF.swift */; };
EB950B552E520436002D68FA /* SaveFilesManager.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */; };
EB950B562E520436002D68FA /* DataModel.swift in Sources */ = {isa = PBXBuildFile; fileRef = EB5C36E82E3546100052EFD7 /* DataModel.swift */; };
EB950B572E520591002D68FA /* ErrorAlertHandler.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBC05C5A2E33936C00EECC18 /* ErrorAlertHandler.swift */; };
EB950B582E5205B4002D68FA /* Zip.swift in Sources */ = {isa = PBXBuildFile; fileRef = EBCC27F82D80C9F70079C478 /* Zip.swift */; };
@@ -333,10 +331,10 @@
EB2F9BFA2DA04B67004010A2 /* Helpers.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = Helpers.swift; sourceTree = "<group>"; };
EB36435F2D73A29F009CDFF8 /* AudioManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AudioManager.swift; sourceTree = "<group>"; };
EB4F9D1E2EC26C7C00A74A0F /* EXIFUtils.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = EXIFUtils.swift; sourceTree = "<group>"; };
EB4F9D232EC27CF000A74A0F /* FileManager+DocumentsOpener.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "FileManager+DocumentsOpener.swift"; sourceTree = "<group>"; };
EB5C36922E34EBBD0052EFD7 /* Localizable.xcstrings */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; path = Localizable.xcstrings; sourceTree = "<group>"; };
EB5C36952E34EBBD0052EFD7 /* mul */ = {isa = PBXFileReference; lastKnownFileType = text.json.xcstrings; name = mul; path = mul.lproj/ShareViewController.xcstrings; sourceTree = "<group>"; };
EB5C36E32E34EC0E0052EFD7 /* License.txt */ = {isa = PBXFileReference; lastKnownFileType = text; path = License.txt; sourceTree = "<group>"; };
EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = SaveFilesManager.swift; sourceTree = "<group>"; };
EB5C36E82E3546100052EFD7 /* DataModel.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DataModel.swift; sourceTree = "<group>"; };
EB5C37E22E377C2E0052EFD7 /* QuickDropiOS.app */ = {isa = PBXFileReference; explicitFileType = wrapper.application; includeInIndex = 0; path = QuickDropiOS.app; sourceTree = BUILT_PRODUCTS_DIR; };
EB5C38382E38C1F60052EFD7 /* justMaybe.mp3 */ = {isa = PBXFileReference; lastKnownFileType = audio.mp3; path = justMaybe.mp3; sourceTree = "<group>"; };
@@ -540,9 +538,9 @@
EBCC27F82D80C9F70079C478 /* Zip.swift */,
EB16B7762E59BA9F007D9792 /* AppGroup.swift */,
EB886E512E4E19390052B7D9 /* HKDF.swift */,
EB4F9D232EC27CF000A74A0F /* FileManager+DocumentsOpener.swift */,
EB4F9D1E2EC26C7C00A74A0F /* EXIFUtils.swift */,
EB7CD1442D6FB0C100CB0CB5 /* Logger.swift */,
EB5C36E62E3544FB0052EFD7 /* SaveFilesManager.swift */,
EB2F9BFA2DA04B67004010A2 /* Helpers.swift */,
);
path = Util;
@@ -960,7 +958,6 @@
EB1325EB2E7025D900FD749C /* ApIsolationTester.swift in Sources */,
EB1325EC2E7025D900FD749C /* ActiveNetworkInterface.swift in Sources */,
EB1325ED2E7025D900FD749C /* ApIsolation6Tester.swift in Sources */,
EB5C37BE2E377A700052EFD7 /* SaveFilesManager.swift in Sources */,
EB9925822E45D53E00357055 /* sharing_enums.pb.swift in Sources */,
);
runOnlyForDeploymentPostprocessing = 0;
@@ -974,7 +971,6 @@
EB5C38412E38C49D0052EFD7 /* LaunchAtLogin.swift in Sources */,
EB5C37B82E377A6F0052EFD7 /* SleepManager.swift in Sources */,
EB869DB52E92AA3A009AAAD4 /* QuickDropToastView.swift in Sources */,
EB5C37B92E377A6F0052EFD7 /* SaveFilesManager.swift in Sources */,
EB5C37BA2E377A6F0052EFD7 /* Helpers.swift in Sources */,
EB05DFBE2E59FC8900EF567A /* TrustedDevicesView.swift in Sources */,
EB16B6182E58B4C8007D9792 /* TrustStore.swift in Sources */,
@@ -1026,11 +1022,11 @@
isa = PBXSourcesBuildPhase;
buildActionMask = 2147483647;
files = (
EB4F9D272EC27CFB00A74A0F /* FileManager+DocumentsOpener.swift in Sources */,
EB821C992E76FE5300036963 /* SmallSheetView.swift in Sources */,
EB05DFBF2E59FD0A00EF567A /* TrustedDevicesView.swift in Sources */,
EB5C37F12E377CE70052EFD7 /* ukey.pb.swift in Sources */,
EB5C37F22E377CE70052EFD7 /* NearbyConnection.swift in Sources */,
EB5C37F32E377CE70052EFD7 /* SaveFilesManager.swift in Sources */,
EB16B6142E58B4B9007D9792 /* IdentityManager.swift in Sources */,
EB886E672E4F2F800052B7D9 /* License.swift in Sources */,
EB5C37F42E377CE70052EFD7 /* NearbyConnectionManager.swift in Sources */,
@@ -1093,7 +1089,6 @@
EB950B532E520436002D68FA /* ukey.pb.swift in Sources */,
EB05DFC02E5A2EAA00EF567A /* TrustedDevicesView.swift in Sources */,
EB950B542E520436002D68FA /* HKDF.swift in Sources */,
EB950B552E520436002D68FA /* SaveFilesManager.swift in Sources */,
EB1325D52E701D8E00FD749C /* Settings.swift in Sources */,
EB950B562E520436002D68FA /* DataModel.swift in Sources */,
EB821C9A2E76FE5300036963 /* SmallSheetView.swift in Sources */,
@@ -1102,6 +1097,7 @@
EB16B6192E58B4C8007D9792 /* TrustStore.swift in Sources */,
EB16B7772E59BAA1007D9792 /* AppGroup.swift in Sources */,
EB950B3F2E52038A002D68FA /* FilePicker.swift in Sources */,
EB4F9D252EC27CFB00A74A0F /* FileManager+DocumentsOpener.swift in Sources */,
EB950B402E52038A002D68FA /* Configuration.swift in Sources */,
EB950B412E52038A002D68FA /* PieProgressView.swift in Sources */,
EB950B592E520649002D68FA /* AudioManager.swift in Sources */,
+38 -38
View File
@@ -30745,229 +30745,229 @@
"ar" : {
"stringUnit" : {
"state" : "translated",
"value" : "يمكنك الآن متابعة استلام الملفات بدون قيود. أي ملفات حاولت إرسالها سابقًا تم حفظها بالفعل على جهازك."
"value" : "يمكنك الآن استقبال الملفات بدون أي قيود. يرجى إعادة إرسال الملف أو الملفات التي حاولت إرسالها سابقًا."
}
},
"ca" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ara pots continuar rebent arxius sense restriccions. Qualsevol arxiu que hagis intentat enviar ja s'ha desat al teu dispositiu."
"value" : "Ara pots rebre fitxers sense cap restricció. Torna a enviar el(s) fitxer(s) que vas intentar enviar abans."
}
},
"cs" : {
"stringUnit" : {
"state" : "translated",
"value" : "Nyní můžete pokračovat v přijímání souborů bez omezení. Všechny soubory, které jste se pokusili odeslat, byly již uloženy na vašem zařízení."
"value" : "Nyní můžete přijímat soubory bez omezení. Prosím, znovu odešlete soubory, které jste se pokusili poslat dříve."
}
},
"da" : {
"stringUnit" : {
"state" : "translated",
"value" : "Du kan nu fortsætte med at modtage filer uden restriktioner. Alle filer, du forsøgte at sende, blev allerede gemt på din enhed."
"value" : "Du kan nu modtage filer uden begrænsninger. Send venligst filerne igen, som du tidligere forsøgte at sende."
}
},
"de" : {
"stringUnit" : {
"state" : "translated",
"value" : "Du kannst nun weiterhin Dateien ohne Einschränkungen empfangen. Alle Dateien, welche gerade gesendet wurden, wurden bereits gespeichert."
"value" : "Du kannst jetzt Dateien ohne Einschränkungen empfangen. Bitte sende die Datei(en), die du zuvor senden wolltest, erneut."
}
},
"el" : {
"stringUnit" : {
"state" : "translated",
"value" : "Μπορείτε τώρα να συνεχίσετε να λαμβάνετε αρχεία χωρίς περιορισμούς. Τυχόν αρχεία που δοκιμάσατε να στείλετε έχουν ήδη αποθηκευτεί στη συσκευή σας."
"value" : "Τώρα μπορείτε να λαμβάνετε αρχεία χωρίς περιορισμούς. Παρακαλούμε στείλτε ξανά το(α) αρχείο(α) που προσπαθήσατε να στείλετε νωρίτερα."
}
},
"en" : {
"stringUnit" : {
"state" : "translated",
"value" : "You can now continue receiving files without restrictions. Any files you tried to send were already saved on your device."
"value" : "You can now receive files without any restrictions. Please resend any files you tried to send earlier."
}
},
"en-AU" : {
"stringUnit" : {
"state" : "translated",
"value" : "Continue unlimited file reception. Attempts were saved."
"value" : "You can now receive files without any restrictions. Please resend the file(s) you tried to send earlier."
}
},
"en-CA" : {
"stringUnit" : {
"state" : "translated",
"value" : "File reception unrestricted. Previously sent files were saved on your device."
"value" : "You can now receive files without any restrictions. Please resend the file(s) you tried to send earlier."
}
},
"en-GB" : {
"stringUnit" : {
"state" : "translated",
"value" : "You can now continue receiving files unrestricted. Any sent files have already been saved."
"value" : "You can now receive files without any restrictions. Please resend the file(s) you tried to send earlier."
}
},
"es" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ahora puedes seguir recibiendo archivos sin restricciones. Todos los archivos que intentaste enviar antes ya se han guardado en tu dispositivo."
"value" : "Ahora puedes recibir archivos sin restricciones. Por favor, vuelve a enviar el/los archivo(s) que intentaste enviar antes."
}
},
"es-419" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ahora puedes continuar recibiendo archivos sin restricciones. Cualquier archivo que intentaste enviar ya se guardó en tu dispositivo."
"value" : "Ahora puedes recibir archivos sin restricciones. Por favor, vuelve a enviar el/los archivo(s) que intentaste enviar antes."
}
},
"fi" : {
"stringUnit" : {
"state" : "translated",
"value" : "Voit nyt jatkaa tiedostojen vastaanottamista ilman rajoituksia. Kaikki lähetettävät tiedostot on jo tallennettu laitteellesi."
"value" : "Voit nyt vastaanottaa tiedostoja ilman rajoituksia. Lähetäthän aiemmin yrittämäsi tiedoston/tiedostot uudelleen."
}
},
"fr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Vous pouvez maintenant continuer à recevoir des fichiers sans restrictions. Tous les fichiers que vous avez essayé d'envoyer précédemment ont déjà été enregistrés sur votre appareil."
"value" : "Vous pouvez maintenant recevoir des fichiers sans aucune restriction. Veuillez renvoyer le(s) fichier(s) que vous avez tenté d’envoyer plus tôt."
}
},
"fr-CA" : {
"stringUnit" : {
"state" : "translated",
"value" : "Vous pouvez maintenant continuer à recevoir des fichiers sans restrictions. Tous les fichiers que vous avez essayé d'envoyer ont déjà été enregistrés sur votre appareil."
"value" : "Vous pouvez maintenant recevoir des fichiers sans aucune restriction. Veuillez renvoyer le(s) fichier(s) que vous avez essayé d’envoyer plus tôt."
}
},
"he" : {
"stringUnit" : {
"state" : "translated",
"value" : "ניתן להמשיך לקבל קבצים ללא מגבלות. כל הקבצים שניסית לשלוח נשמרו כבר במכשיר שלך."
"value" : "ניתן כעת לקבל קבצים ללא מגבלות. אנא שלח שוב את הקובץ או הקבצים שניסית לשלוח קודם."
}
},
"hi" : {
"stringUnit" : {
"state" : "translated",
"value" : "आप अब बिना किसी प्रतिबंध के फ़ाइलें प्राप्त कर सकते हैं। आपने जो फ़ाइलें भेजने की कोशिश की थीं, वे आपकी डिवाइस पर पहले ही सहेजी जा चुकी हैं।"
"value" : "अब आप बिना किसी प्रतिबंध के फाइलें प्राप्त कर सकते हैं। कृपया वे फाइलें फिर से भेजें जिन्हें आपने पहले भेजने की कोशिश की थी।"
}
},
"hr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Sada možete nastaviti primati datoteke bez ograničenja. Sve datoteke koje ste pokušali poslati već su spremljene na vaš uređaj."
"value" : "Sada možete primati datoteke bez ikakvih ograničenja. Molimo ponovno pošaljite datoteke koje ste ranije pokušali poslati."
}
},
"hu" : {
"stringUnit" : {
"state" : "translated",
"value" : "Mostantól korlátozások nélkül fogadhat fájlokat. Az összes fájl, amelyet korábban próbált elküldeni, már el lett mentve az eszközére."
"value" : "Mostantól korlátozás nélkül fogadhat fájlokat. Kérjük, küldje el újra a korábban próbált fájl(oka)t."
}
},
"id" : {
"stringUnit" : {
"state" : "translated",
"value" : "Anda sekarang dapat terus menerima file tanpa batasan. File apa pun yang Anda coba kirim telah disimpan di perangkat Anda."
"value" : "Anda sekarang dapat menerima file tanpa batasan. Silakan kirim ulang file yang sebelumnya Anda coba kirim."
}
},
"it" : {
"stringUnit" : {
"state" : "translated",
"value" : "Ora puoi continuare a ricevere file senza restrizioni. Tutti i file che hai provato a inviare in precedenza sono già stati salvati sul tuo dispositivo."
"value" : "Ora puoi ricevere file senza restrizioni. Invia di nuovo il/i file che hai provato a mandare prima."
}
},
"ja" : {
"stringUnit" : {
"state" : "translated",
"value" : "これで、制限なくファイルを受信し続けることができます。以前送信しようとしたファイルはすでにデバイスに保存されています。"
"value" : "これで制限なくファイルを受信できます。先ほど送信しようとしたファイルをもう一度送信してください。"
}
},
"ko" : {
"stringUnit" : {
"state" : "translated",
"value" : "이제 파일을 제한 없이 계속 받을 수 있습니다. 이전에 전송을 시도한 모든 파일이 이미 기기에 저장되었습니다."
"value" : "이제 아무 제한 없이 파일을 받을 수 있습니다. 이전에 보내려던 파일을 다시 보내주세요."
}
},
"ms" : {
"stringUnit" : {
"state" : "translated",
"value" : "Anda kini boleh terus menerima fail tanpa sekatan. Sebarang fail yang anda cuba hantar telah disimpan pada peranti anda."
"value" : "Anda kini boleh menerima fail tanpa sebarang sekatan. Sila hantar semula fail yang anda cuba hantar sebelum ini."
}
},
"nb" : {
"stringUnit" : {
"state" : "translated",
"value" : "Du kan nå fortsette å motta filer uten begrensninger. Alle filer du prøvde å sende ble allerede lagret på enheten din."
"value" : "Du kan nå motta filer uten begrensninger. Vennligst send filene du prøvde å sende tidligere på nytt."
}
},
"nl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Je kunt nu bestanden blijven ontvangen zonder beperkingen. Alle bestanden die je eerder probeerde te verzenden, zijn al opgeslagen op je apparaat."
"value" : "Je kunt nu bestanden zonder beperkingen ontvangen. Stuur het bestand of de bestanden die je eerder probeerde te verzenden opnieuw."
}
},
"pl" : {
"stringUnit" : {
"state" : "translated",
"value" : "Możesz teraz kontynuować odbieranie plików bez ograniczeń. Wszystkie pliki, które próbowałeś wysłać, zostały już zapisane na Twoim urządzeniu."
"value" : "Możesz teraz odbierać pliki bez ograniczeń. Wyślij ponownie plik(i), które próbowałeś wcześniej wysłać."
}
},
"pt-BR" : {
"stringUnit" : {
"state" : "translated",
"value" : "Agora você pode continuar recebendo arquivos sem restrições. Todos os arquivos que você tentou enviar antes já foram salvos no seu dispositivo."
"value" : "Agora você pode receber arquivos sem restrições. Por favor, envie novamente o(s) arquivo(s) que tentou enviar anteriormente."
}
},
"pt-PT" : {
"stringUnit" : {
"state" : "translated",
"value" : "Agora pode continuar a receber ficheiros sem restrições. Todos os ficheiros que tentou enviar antes já foram guardados no seu dispositivo."
"value" : "Agora pode receber ficheiros sem quaisquer restrições. Por favor, reenvie os ficheiros que tentou enviar anteriormente."
}
},
"ro" : {
"stringUnit" : {
"state" : "translated",
"value" : "Acum puteți continua să primiți fișiere fără restricții. Toate fișierele pe care ați încercat să le trimiteți anterior au fost deja salvate pe dispozitivul dvs."
"value" : "Acum poți primi fișiere fără restricții. Te rugăm să retrimiți fișierul(ele) pe care ai încercat să le trimiți mai devreme."
}
},
"ru" : {
"stringUnit" : {
"state" : "translated",
"value" : "Теперь вы можете продолжать получать файлы без ограничений. Все файлы, которые вы пытались отправить ранее, уже сохранены на вашем устройстве."
"value" : "Теперь вы можете получать файлы без ограничений. Пожалуйста, отправьте файл(ы) заново."
}
},
"sv" : {
"stringUnit" : {
"state" : "translated",
"value" : "Du kan nu fortsätta ta emot filer utan begränsningar. Alla filer du försökte skicka sparades redan på din enhet."
"value" : "Du kan nu ta emot filer utan några begränsningar. Vänligen skicka filen/filerna igen som du försökte skicka tidigare."
}
},
"th" : {
"stringUnit" : {
"state" : "translated",
"value" : "คุณสามารถรับไฟล์ต่อไปได้โดยไม่มีข้อจำกัด ไฟล์ทั้งหมดที่คุณพยายามส่งก่อนหน้านี้ถูกบันทึกไว้ในอุปกรณ์ของคุณแล้ว"
"value" : "คุณสามารถรับไฟล์ได้โดยไม่มีข้อจำกัดแล้ว โปรดส่งไฟล์ที่คุณพยายามส่งมาก่อนหน้านี้อีกครั้ง"
}
},
"tr" : {
"stringUnit" : {
"state" : "translated",
"value" : "Artık sınırsız dosya alımına devam edebilirsiniz. Göndermeye çalıştığınız tüm dosyalar zaten cihazınıza kaydedildi."
"value" : "Artık herhangi bir kısıtlama olmadan dosya alabilirsiniz. Lütfen daha önce göndermeye çalıştığınız dosyaları tekrar gönderin."
}
},
"uk" : {
"stringUnit" : {
"state" : "translated",
"value" : "Тепер ви можете продовжувати отримувати файли без обмежень. Усі файли, які ви намагалися надіслати раніше, вже збережено на вашому пристрої."
"value" : "Тепер ви можете отримувати файли без обмежень. Будь ласка, надішліть файл(и) ще раз."
}
},
"vi" : {
"stringUnit" : {
"state" : "translated",
"value" : "Bạn có thể tiếp tục nhận tệp không giới hạn. Bất kỳ tệp nào bạn đã cố gắng gửi đều đã được lưu vào thiết bị của bạn."
"value" : "Bạn hiện có thể nhận tệp mà không bị hạn chế. Vui lòng gửi lại tệp mà bạn đã cố gửi trước đó."
}
},
"zh-Hans" : {
"stringUnit" : {
"state" : "translated",
"value" : "您现在可以继续无限制地接收文件。您之前尝试发送的所有文件已保存到您的设备上。"
"value" : "您现在可以无限制地接收文件了。请重新发送您之前尝试发送的文件。"
}
},
"zh-Hant" : {
"stringUnit" : {
"state" : "translated",
"value" : "您現在可以繼續無限制地接收檔案。您嘗試傳送的所有檔案均已保存到裝置上。"
"value" : "您現在可以無限制地接收檔案。請重新傳送您先前嘗試傳送的檔案。"
}
}
}
+1 -9
View File
@@ -27,15 +27,7 @@ struct PlusView: View {
@State var warning = false
@State var buying = false
@State var errorText = ""
@State var boughtSuccessAlert = false {
didSet {
if boughtSuccessAlert {
log("User bought plus version. Moving pending files...")
SaveFilesManager.shared.movePendingFilesToTarget()
}
}
}
@State var boughtSuccessAlert = false
var body: some View {
+1 -1
View File
@@ -21,7 +21,7 @@ struct ContentView: View {
}, settingsView: {
CustomSection(header: settings.gotPlus ? "" : "General", footer: "TrustedDevicesFooterShort") {
LUIButton {
SaveFilesManager.shared.openDownloadedFilesFolder()
FileManager.default.openDocumentFolder()
} label: {
NavigationLinkLabel(imageName: "folder.fill", text: "BrowseDownloadedFiles")
}