diff --git a/Package.swift b/Package.swift index 617ab061294b661012d646258bf8cde4c3cf4aef..2b920178afe74017dacd2c1029d75211267f7ea7 100644 --- a/Package.swift +++ b/Package.swift @@ -27,6 +27,7 @@ let package = Package( .library(name: "Integration", targets: ["Integration"]), .library(name: "ChatFeature", targets: ["ChatFeature"]), .library(name: "PushFeature", targets: ["PushFeature"]), + .library(name: "SFTPFeature", targets: ["SFTPFeature"]), .library(name: "CrashService", targets: ["CrashService"]), .library(name: "Presentation", targets: ["Presentation"]), .library(name: "BackupFeature", targets: ["BackupFeature"]), @@ -81,6 +82,7 @@ let package = Package( "ChatFeature", "MenuFeature", "PushFeature", + "SFTPFeature", "ToastFeature", "CrashService", "BackupFeature", @@ -208,6 +210,15 @@ let package = Package( ] ), + // MARK: - SFTPFeature + + .target( + name: "SFTPFeature", + dependencies: [ + + ] + ), + // MARK: - GoogleDriveFeature .target( @@ -398,6 +409,7 @@ let package = Package( dependencies: [ "HUD", "Shared", + "SFTPFeature", "Integration", "Presentation", "iCloudFeature", @@ -612,6 +624,7 @@ let package = Package( "Shared", "Models", "InputField", + "SFTPFeature", "Presentation", "iCloudFeature", "DrawerFeature", diff --git a/Sources/App/DependencyRegistrator.swift b/Sources/App/DependencyRegistrator.swift index 9102274af7c0c37ce986b8aba6b2607a33cd66ca..776a17b10af562e1b4aadc0e1e7305db8bf52c78 100644 --- a/Sources/App/DependencyRegistrator.swift +++ b/Sources/App/DependencyRegistrator.swift @@ -18,6 +18,7 @@ import Voxophone import Integration import Permissions import PushFeature +import SFTPFeature import CrashService import ToastFeature import iCloudFeature @@ -86,6 +87,7 @@ struct DependencyRegistrator { /// Restore / Backup + container.register(SFTPService.live) container.register(iCloudService() as iCloudInterface) container.register(DropboxService() as DropboxInterface) container.register(GoogleDriveService() as GoogleDriveInterface) diff --git a/Sources/BackupFeature/Controllers/BackupConfigController.swift b/Sources/BackupFeature/Controllers/BackupConfigController.swift index c61bd74d6c971c544087d7020db72ce16d11e763..a901e6b9668fc89df2cd1ae1184ca655fa8bdb1b 100644 --- a/Sources/BackupFeature/Controllers/BackupConfigController.swift +++ b/Sources/BackupFeature/Controllers/BackupConfigController.swift @@ -105,6 +105,11 @@ final class BackupConfigController: UIViewController { .sink { [unowned self] in viewModel.didToggleService(self, .dropbox, screenView.dropboxButton.switcherView.isOn) } .store(in: &cancellables) + screenView.sftpButton.switcherView + .publisher(for: .valueChanged) + .sink { [unowned self] in viewModel.didToggleService(self, .sftp, screenView.sftpButton.switcherView.isOn) } + .store(in: &cancellables) + screenView.iCloudButton.switcherView .publisher(for: .valueChanged) .sink { [unowned self] in viewModel.didToggleService(self, .icloud, screenView.iCloudButton.switcherView.isOn) } @@ -115,6 +120,11 @@ final class BackupConfigController: UIViewController { .sink { [unowned self] in viewModel.didTapService(.dropbox, self) } .store(in: &cancellables) + screenView.sftpButton + .publisher(for: .touchUpInside) + .sink { [unowned self] in viewModel.didTapService(.sftp, self) } + .store(in: &cancellables) + screenView.iCloudButton .publisher(for: .touchUpInside) .sink { [unowned self] in viewModel.didTapService(.icloud, self) } @@ -128,16 +138,17 @@ final class BackupConfigController: UIViewController { case .none: break case .icloud: - serviceName = "iCloud" + serviceName = Localized.Backup.iCloud button = screenView.iCloudButton - case .dropbox: - serviceName = "Dropbox" + serviceName = Localized.Backup.dropbox button = screenView.dropboxButton - case .drive: - serviceName = "Google Drive" + serviceName = Localized.Backup.googleDrive button = screenView.googleDriveButton + case .sftp: + serviceName = Localized.Backup.sftp + button = screenView.sftpButton } screenView.enabledSubtitleLabel.text @@ -146,10 +157,12 @@ final class BackupConfigController: UIViewController { = Localized.Backup.Config.frequency(serviceName).uppercased() guard let button = button else { + screenView.sftpButton.isHidden = false screenView.iCloudButton.isHidden = false screenView.dropboxButton.isHidden = false screenView.googleDriveButton.isHidden = false + screenView.sftpButton.switcherView.isOn = false screenView.iCloudButton.switcherView.isOn = false screenView.dropboxButton.switcherView.isOn = false screenView.googleDriveButton.switcherView.isOn = false @@ -166,11 +179,13 @@ final class BackupConfigController: UIViewController { screenView.latestBackupDetailView.isHidden = false screenView.infrastructureDetailView.isHidden = false - [screenView.iCloudButton, screenView.dropboxButton, screenView.googleDriveButton] - .forEach { - $0.isHidden = $0 != button - $0.switcherView.isOn = $0 == button - } + [screenView.iCloudButton, + screenView.dropboxButton, + screenView.googleDriveButton, + screenView.sftpButton].forEach { + $0.isHidden = $0 != button + $0.switcherView.isOn = $0 == button + } } private func decorate(connectedServices: Set<CloudService>) { @@ -191,6 +206,12 @@ final class BackupConfigController: UIViewController { } else { screenView.googleDriveButton.showChevron() } + + if connectedServices.contains(.sftp) { + screenView.sftpButton.showSwitcher(enabled: false) + } else { + screenView.sftpButton.showChevron() + } } private func presentInfrastructureDrawer(wifiOnly: Bool) { diff --git a/Sources/BackupFeature/Service/BackupService.swift b/Sources/BackupFeature/Service/BackupService.swift index 8b82a8f27c40af85acd836182144d05c945008e1..0c163e4d92d470a7e3bcaf86cac1c9be0bf26176 100644 --- a/Sources/BackupFeature/Service/BackupService.swift +++ b/Sources/BackupFeature/Service/BackupService.swift @@ -2,6 +2,7 @@ import UIKit import Models import Combine import Defaults +import SFTPFeature import iCloudFeature import DropboxFeature import NetworkMonitor @@ -9,10 +10,11 @@ import GoogleDriveFeature import DependencyInjection public final class BackupService { + @Dependency private var sftpService: SFTPService @Dependency private var icloudService: iCloudInterface @Dependency private var dropboxService: DropboxInterface - @Dependency private var driveService: GoogleDriveInterface @Dependency private var networkManager: NetworkMonitoring + @Dependency private var driveService: GoogleDriveInterface @KeyObject(.backupSettings, defaultValue: Data()) private var storedSettings: Data @@ -149,6 +151,10 @@ extension BackupService { self.refreshBackups() }.store(in: &cancellables) } + case .sftp: + if !sftpService.isAuthorized() { + // TODO + } } } } @@ -167,6 +173,12 @@ extension BackupService { settings.value.connectedServices.remove(.dropbox) } + if sftpService.isAuthorized() && !settings.value.connectedServices.contains(.sftp) { + settings.value.connectedServices.insert(.sftp) + } else if !sftpService.isAuthorized() && settings.value.connectedServices.contains(.sftp) { + settings.value.connectedServices.remove(.sftp) + } + driveService.isAuthorized { [weak settings] isAuthorized in guard let settings = settings else { return } @@ -196,6 +208,10 @@ extension BackupService { } } + if sftpService.isAuthorized() { + // TODO + } + if dropboxService.isAuthorized() { dropboxService.downloadMetadata { [weak settings] in guard let settings = settings else { return } @@ -293,6 +309,8 @@ extension BackupService { // try? FileManager.default.removeItem(at: url) } + case .sftp: + break // TODO } } } diff --git a/Sources/BackupFeature/Views/BackupConfigView.swift b/Sources/BackupFeature/Views/BackupConfigView.swift index 8c400b3ff9162f380548d0478f2356454910833e..1c65f58f7222f1c069284c717518904dc2e596ca 100644 --- a/Sources/BackupFeature/Views/BackupConfigView.swift +++ b/Sources/BackupFeature/Views/BackupConfigView.swift @@ -7,6 +7,7 @@ final class BackupConfigView: UIView { let actionView = BackupActionView() let stackView = UIStackView() + let sftpButton = BackupSwitcherButton() let iCloudButton = BackupSwitcherButton() let dropboxButton = BackupSwitcherButton() let googleDriveButton = BackupSwitcherButton() @@ -44,6 +45,9 @@ final class BackupConfigView: UIView { subtitleLabel.numberOfLines = 0 subtitleLabel.attributedText = attString + sftpButton.titleLabel.text = Localized.Backup.sftp + sftpButton.logoImageView.image = Asset.restoreSFTP.image + iCloudButton.titleLabel.text = Localized.Backup.iCloud iCloudButton.logoImageView.image = Asset.restoreIcloud.image @@ -65,6 +69,7 @@ final class BackupConfigView: UIView { stackView.addArrangedSubview(googleDriveButton) stackView.addArrangedSubview(iCloudButton) stackView.addArrangedSubview(dropboxButton) + stackView.addArrangedSubview(sftpButton) stackView.addArrangedSubview(enabledSubtitleView) stackView.addArrangedSubview(latestBackupDetailView) stackView.addArrangedSubview(frequencyDetailView) @@ -75,36 +80,36 @@ final class BackupConfigView: UIView { addSubview(actionView) addSubview(stackView) - titleLabel.snp.makeConstraints { make in - make.top.equalToSuperview().offset(15) - make.left.equalToSuperview().offset(38) - make.right.equalToSuperview().offset(-41) + titleLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(15) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-41) } - enabledSubtitleLabel.snp.makeConstraints { make in - make.top.equalToSuperview().offset(-10) - make.left.equalToSuperview().offset(92) - make.right.equalToSuperview().offset(-48) - make.bottom.equalToSuperview() + enabledSubtitleLabel.snp.makeConstraints { + $0.top.equalToSuperview().offset(-10) + $0.left.equalToSuperview().offset(92) + $0.right.equalToSuperview().offset(-48) + $0.bottom.equalToSuperview() } - subtitleLabel.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(8) - make.left.equalToSuperview().offset(38) - make.right.equalToSuperview().offset(-41) + subtitleLabel.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(8) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-41) } - actionView.snp.makeConstraints { make in - make.top.equalTo(subtitleLabel.snp.bottom).offset(15) - make.left.equalToSuperview().offset(38) - make.right.equalToSuperview().offset(-38) + actionView.snp.makeConstraints { + $0.top.equalTo(subtitleLabel.snp.bottom).offset(15) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-38) } - stackView.snp.makeConstraints { make in - make.top.equalTo(actionView.snp.bottom).offset(28) - make.left.equalToSuperview() - make.right.equalToSuperview() - make.bottom.lessThanOrEqualToSuperview() + stackView.snp.makeConstraints { + $0.top.equalTo(actionView.snp.bottom).offset(28) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.lessThanOrEqualToSuperview() } } diff --git a/Sources/Models/CloudService.swift b/Sources/Models/CloudService.swift index 5daba7238d74388dac591b63220dbbc8b1c9f911..d217dca853e6ecf22f119520d9805567f7ead3a5 100644 --- a/Sources/Models/CloudService.swift +++ b/Sources/Models/CloudService.swift @@ -2,4 +2,5 @@ public enum CloudService: Equatable, Codable { case drive case icloud case dropbox + case sftp } diff --git a/Sources/RestoreFeature/Controllers/RestoreListController.swift b/Sources/RestoreFeature/Controllers/RestoreListController.swift index 041717b6dde907568db4872827957d487010d006..68a5edcaf45b7f3d052d2fee89322c0b1f5d93e9 100644 --- a/Sources/RestoreFeature/Controllers/RestoreListController.swift +++ b/Sources/RestoreFeature/Controllers/RestoreListController.swift @@ -80,6 +80,11 @@ public final class RestoreListController: UIViewController { .publisher(for: .touchUpInside) .sink { [unowned self] in viewModel.didTapCloud(.dropbox, from: self) } .store(in: &cancellables) + + screenView.sftpButton + .publisher(for: .touchUpInside) + .sink { [unowned self] in viewModel.didTapCloud(.sftp, from: self) } + .store(in: &cancellables) } @objc private func didTapBack() { diff --git a/Sources/RestoreFeature/Service/MockRestoreService.swift b/Sources/RestoreFeature/Service/MockRestoreService.swift deleted file mode 100644 index 1a013434211580df1c99ac1fa3842ab0fc797d59..0000000000000000000000000000000000000000 --- a/Sources/RestoreFeature/Service/MockRestoreService.swift +++ /dev/null @@ -1,30 +0,0 @@ -import UIKit -import Models -import Combine -import Foundation -import GoogleDriveFeature -import DependencyInjection - -public struct RestoreServiceMock: RestoreServiceType { - public var inProgress: AnyPublisher<Void, Never> { - fatalError() - } - - public var settings: AnyPublisher<RestoreSettings, Never> { - fatalError() - } - - public init() {} - - public func didSelectBackup(at url: URL) {} - - public func authorize(service: CloudService, from: UIViewController) {} - - public func download( - from settings: RestoreSettings, - progress: @escaping RestoreProgress, - whenFinished: @escaping RestoreDownloadFinished - ) { - fatalError() - } -} diff --git a/Sources/RestoreFeature/Service/RestoreServiceType.swift b/Sources/RestoreFeature/Service/RestoreServiceType.swift deleted file mode 100644 index 78a32e9f6d9e199d78fa9c17a4a06b7ca1de51f3..0000000000000000000000000000000000000000 --- a/Sources/RestoreFeature/Service/RestoreServiceType.swift +++ /dev/null @@ -1,20 +0,0 @@ -import UIKit -import Models -import Combine - -public typealias RestoreProgress = (Float) -> Void -public typealias RestoreDownloadFinished = (Result<Data, Error>) -> Void - -public protocol RestoreServiceType { - var inProgress: AnyPublisher<Void, Never> { get } - - var settings: AnyPublisher<RestoreSettings, Never> { get } - - func authorize(service: CloudService, from: UIViewController) - - func download( - from settings: RestoreSettings, - progress: @escaping RestoreProgress, - whenFinished: @escaping RestoreDownloadFinished - ) -} diff --git a/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift index 3e91448aed07d65e2cdf650033be9d15be08fb4d..1d86eabc20a3bb6b21eaea30f942c2a2323e089b 100644 --- a/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift +++ b/Sources/RestoreFeature/ViewModels/RestoreListViewModel.swift @@ -6,14 +6,16 @@ import Combine import BackupFeature import DependencyInjection +import SFTPFeature import iCloudFeature import DropboxFeature import GoogleDriveFeature final class RestoreListViewModel { - @Dependency private var icloud: iCloudInterface - @Dependency private var dropbox: DropboxInterface - @Dependency private var drive: GoogleDriveInterface + @Dependency private var sftpService: SFTPService + @Dependency private var icloudService: iCloudInterface + @Dependency private var dropboxService: DropboxInterface + @Dependency private var googleDriveService: GoogleDriveInterface var hud: AnyPublisher<HUDStatus, Never> { hudSubject.eraseToAnyPublisher() } var didFetchBackup: AnyPublisher<RestoreSettings, Never> { backupSubject.eraseToAnyPublisher() } @@ -31,15 +33,17 @@ final class RestoreListViewModel { didRequestICloudAuthorization() case .dropbox: didRequestDropboxAuthorization(from: parent) + case .sftp: + didRequestSFTPAuthorization() } } private func didRequestDriveAuthorization(from controller: UIViewController) { - drive.authorize(presenting: controller) { authResult in + googleDriveService.authorize(presenting: controller) { authResult in switch authResult { case .success: self.hudSubject.send(.on(nil)) - self.drive.downloadMetadata { downloadResult in + self.googleDriveService.downloadMetadata { downloadResult in switch downloadResult { case .success(let metadata): var backup: Backup? @@ -62,10 +66,10 @@ final class RestoreListViewModel { } private func didRequestICloudAuthorization() { - if icloud.isAuthorized() { + if icloudService.isAuthorized() { self.hudSubject.send(.on(nil)) - icloud.downloadMetadata { result in + icloudService.downloadMetadata { result in switch result { case .success(let metadata): var backup: Backup? @@ -83,12 +87,12 @@ final class RestoreListViewModel { } else { /// This could be an alert controller asking if user wants to enable/deeplink /// - icloud.openSettings() + icloudService.openSettings() } } private func didRequestDropboxAuthorization(from controller: UIViewController) { - dropboxAuthCancellable = dropbox.authorize(presenting: controller) + dropboxAuthCancellable = dropboxService.authorize(presenting: controller) .receive(on: DispatchQueue.main) .sink { [unowned self] authResult in switch authResult { @@ -96,7 +100,7 @@ final class RestoreListViewModel { guard bool == true else { return } self.hudSubject.send(.on(nil)) - dropbox.downloadMetadata { metadataResult in + dropboxService.downloadMetadata { metadataResult in switch metadataResult { case .success(let metadata): var backup: Backup? diff --git a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift index 37a5bbe469665e83df59f9a5c3215c4223e426d5..663d3529b44012f24408c11ff1ad223cb5a58a7d 100644 --- a/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift +++ b/Sources/RestoreFeature/ViewModels/RestoreViewModel.swift @@ -8,6 +8,7 @@ import Integration import BackupFeature import DependencyInjection +import SFTPFeature import iCloudFeature import DropboxFeature import GoogleDriveFeature @@ -38,6 +39,7 @@ extension RestorationStep: Equatable { } final class RestoreViewModel { + @Dependency private var sftpService: SFTPService @Dependency private var iCloudService: iCloudInterface @Dependency private var dropboxService: DropboxInterface @Dependency private var googleService: GoogleDriveInterface @@ -80,9 +82,15 @@ final class RestoreViewModel { downloadBackupForDropbox(backup) case .icloud: downloadBackupForiCloud(backup) + case .sftp: + downloadBackupForSFTP(backup) } } + private func downloadBackupForSFTP(_ backup: Backup) { + // TODO + } + private func downloadBackupForDropbox(_ backup: Backup) { dropboxService.downloadBackup(backup.id) { [weak self] in guard let self = self else { return } diff --git a/Sources/RestoreFeature/Views/RestoreListView.swift b/Sources/RestoreFeature/Views/RestoreListView.swift index 2a688760bc68cd509775fa97d28a37538dc44d3a..a955173a742b7c0b94f95601f90634353ada327f 100644 --- a/Sources/RestoreFeature/Views/RestoreListView.swift +++ b/Sources/RestoreFeature/Views/RestoreListView.swift @@ -6,6 +6,7 @@ final class RestoreListView: UIView { let stackView = UIStackView() let firstSubtitleLabel = UILabel() let secondSubtitleLabel = UILabel() + let sftpButton = RowButton() let driveButton = RowButton() let icloudButton = RowButton() let dropboxButton = RowButton() @@ -34,6 +35,7 @@ final class RestoreListView: UIView { secondSubtitleLabel.numberOfLines = 0 secondSubtitleLabel.attributedText = attrString + sftpButton.setup(title: Localized.Backup.sftp, icon: Asset.restoreSFTP.image) icloudButton.setup(title: Localized.Backup.iCloud, icon: Asset.restoreIcloud.image) dropboxButton.setup(title: Localized.Backup.dropbox, icon: Asset.restoreDropbox.image) driveButton.setup(title: Localized.Backup.googleDrive, icon: Asset.restoreDrive.image) @@ -41,9 +43,11 @@ final class RestoreListView: UIView { cancelButton.set(style: .seeThrough, title: Localized.AccountRestore.List.cancel) stackView.axis = .vertical + stackView.distribution = .fillEqually stackView.addArrangedSubview(driveButton) stackView.addArrangedSubview(icloudButton) stackView.addArrangedSubview(dropboxButton) + stackView.addArrangedSubview(sftpButton) addSubview(titleLabel) addSubview(firstSubtitleLabel) @@ -51,35 +55,35 @@ final class RestoreListView: UIView { addSubview(stackView) addSubview(cancelButton) - titleLabel.snp.makeConstraints { make in - make.top.equalTo(safeAreaLayoutGuide).offset(15) - make.left.equalToSuperview().offset(38) - make.right.equalToSuperview().offset(-41) + titleLabel.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).offset(15) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-41) } - firstSubtitleLabel.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(8) - make.left.equalToSuperview().offset(38) - make.right.equalToSuperview().offset(-41) + firstSubtitleLabel.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(8) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-41) } - secondSubtitleLabel.snp.makeConstraints { make in - make.top.equalTo(firstSubtitleLabel.snp.bottom).offset(8) - make.left.equalToSuperview().offset(38) - make.right.equalToSuperview().offset(-41) + secondSubtitleLabel.snp.makeConstraints { + $0.top.equalTo(firstSubtitleLabel.snp.bottom).offset(8) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-41) } - stackView.snp.makeConstraints { make in - make.top.equalTo(secondSubtitleLabel.snp.bottom).offset(28) - make.left.equalToSuperview().offset(24) - make.right.equalToSuperview().offset(-24) + stackView.snp.makeConstraints { + $0.top.equalTo(secondSubtitleLabel.snp.bottom).offset(28) + $0.left.equalToSuperview().offset(24) + $0.right.equalToSuperview().offset(-24) } - cancelButton.snp.makeConstraints { make in - make.top.greaterThanOrEqualTo(stackView.snp.bottom).offset(20) - make.left.equalToSuperview().offset(40) - make.right.equalToSuperview().offset(-40) - make.bottom.equalTo(safeAreaLayoutGuide).offset(-50) + cancelButton.snp.makeConstraints { + $0.top.greaterThanOrEqualTo(stackView.snp.bottom).offset(20) + $0.left.equalToSuperview().offset(40) + $0.right.equalToSuperview().offset(-40) + $0.bottom.equalTo(safeAreaLayoutGuide).offset(-50) } } diff --git a/Sources/RestoreFeature/Views/RestoreView.swift b/Sources/RestoreFeature/Views/RestoreView.swift index e36e5d5b1dd97cb735ac84f9d1c5c56fdb19b5c3..ba2e643cafc4dcca652ac7daac8ad14d22f52d15 100644 --- a/Sources/RestoreFeature/Views/RestoreView.swift +++ b/Sources/RestoreFeature/Views/RestoreView.swift @@ -39,36 +39,36 @@ final class RestoreView: UIView { bottomStackView.addArrangedSubview(cancelButton) bottomStackView.addArrangedSubview(backButton) - titleLabel.snp.makeConstraints { make in - make.top.equalTo(safeAreaLayoutGuide).offset(20) - make.left.equalToSuperview().offset(38) - make.right.equalToSuperview().offset(-38) + titleLabel.snp.makeConstraints { + $0.top.equalTo(safeAreaLayoutGuide).offset(20) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-38) } - subtitleLabel.snp.makeConstraints { make in - make.top.equalTo(titleLabel.snp.bottom).offset(20) - make.left.equalToSuperview().offset(38) - make.right.equalToSuperview().offset(-38) + subtitleLabel.snp.makeConstraints { + $0.top.equalTo(titleLabel.snp.bottom).offset(20) + $0.left.equalToSuperview().offset(38) + $0.right.equalToSuperview().offset(-38) } - detailsView.snp.makeConstraints { make in - make.top.equalTo(subtitleLabel.snp.bottom).offset(40) - make.left.equalToSuperview() - make.right.equalToSuperview() + detailsView.snp.makeConstraints { + $0.top.equalTo(subtitleLabel.snp.bottom).offset(40) + $0.left.equalToSuperview() + $0.right.equalToSuperview() } - progressView.snp.makeConstraints { make in - make.top.greaterThanOrEqualTo(detailsView.snp.bottom) - make.left.equalToSuperview() - make.right.equalToSuperview() - make.bottom.lessThanOrEqualTo(bottomStackView.snp.top) + progressView.snp.makeConstraints { + $0.top.greaterThanOrEqualTo(detailsView.snp.bottom) + $0.left.equalToSuperview() + $0.right.equalToSuperview() + $0.bottom.lessThanOrEqualTo(bottomStackView.snp.top) } - bottomStackView.snp.makeConstraints { make in - make.top.greaterThanOrEqualTo(detailsView.snp.bottom).offset(10) - make.left.equalToSuperview().offset(40) - make.right.equalToSuperview().offset(-40) - make.bottom.equalTo(safeAreaLayoutGuide).offset(-20) + bottomStackView.snp.makeConstraints { + $0.top.greaterThanOrEqualTo(detailsView.snp.bottom).offset(10) + $0.left.equalToSuperview().offset(40) + $0.right.equalToSuperview().offset(-40) + $0.bottom.equalTo(safeAreaLayoutGuide).offset(-20) } } @@ -151,6 +151,8 @@ private extension CloudService { return Localized.Backup.iCloud case .dropbox: return Localized.Backup.dropbox + case .sftp: + return Localized.Backup.sftp } } @@ -162,6 +164,8 @@ private extension CloudService { return Asset.restoreIcloud.image case .dropbox: return Asset.restoreDropbox.image + case .sftp: + return Asset.restoreSFTP.image } } } diff --git a/Sources/SFTPFeature/SFTPController.swift b/Sources/SFTPFeature/SFTPController.swift new file mode 100644 index 0000000000000000000000000000000000000000..332d7079e6be9d77061a0b499a41878746d6897e --- /dev/null +++ b/Sources/SFTPFeature/SFTPController.swift @@ -0,0 +1,8 @@ +// +// File.swift +// +// +// Created by Bruno Muniz on 6/30/22. +// + +import Foundation diff --git a/Sources/SFTPFeature/SFTPService.swift b/Sources/SFTPFeature/SFTPService.swift new file mode 100644 index 0000000000000000000000000000000000000000..7181c915a13c1e8fb9f6f849763cdf315ce9b369 --- /dev/null +++ b/Sources/SFTPFeature/SFTPService.swift @@ -0,0 +1,15 @@ +public struct SFTPService { + public var isAuthorized: () -> Bool + public var downloadMetadata: (@escaping (String) -> Void) -> Void +} + +public extension SFTPService { + static var live = SFTPService( + isAuthorized: { + true + }, + downloadMetadata: { completion in + completion("MOCK") + } + ) +} diff --git a/Sources/Shared/AutoGenerated/Assets.swift b/Sources/Shared/AutoGenerated/Assets.swift index 2b2248fb83ad430ec95d9378a5d5762e8d74c046..852b60a160080fa6b35f53882e5635ff6602b7c8 100644 --- a/Sources/Shared/AutoGenerated/Assets.swift +++ b/Sources/Shared/AutoGenerated/Assets.swift @@ -97,6 +97,7 @@ public enum Asset { public static let requestsTabReceived = ImageAsset(name: "requests_tab_received") public static let requestsTabSent = ImageAsset(name: "requests_tab_sent") public static let requestsVerificationFailed = ImageAsset(name: "requests_verification_failed") + public static let restoreSFTP = ImageAsset(name: "restore_SFTP") public static let restoreDrive = ImageAsset(name: "restore_drive") public static let restoreDropbox = ImageAsset(name: "restore_dropbox") public static let restoreIcloud = ImageAsset(name: "restore_icloud") diff --git a/Sources/Shared/AutoGenerated/Strings.swift b/Sources/Shared/AutoGenerated/Strings.swift index 755693f39ac7ba661a99e93b8d64360590ed299b..26455e8689ac10078ac41cc5ccc8feb24d1bb835 100644 --- a/Sources/Shared/AutoGenerated/Strings.swift +++ b/Sources/Shared/AutoGenerated/Strings.swift @@ -236,6 +236,8 @@ public enum Localized { public static let header = Localized.tr("Localizable", "backup.header") /// iCloud public static let iCloud = Localized.tr("Localizable", "backup.iCloud") + /// SFTP + public static let sftp = Localized.tr("Localizable", "backup.SFTP") /// Back up your account to a cloud storage service, you can restore it along with only your contacts when you reinstall xx Messenger on another device. public static let subtitle = Localized.tr("Localizable", "backup.subtitle") public enum Config { diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_SFTP.imageset/Contents.json b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_SFTP.imageset/Contents.json new file mode 100644 index 0000000000000000000000000000000000000000..5c2f805eb3317d819659b5d73f14b6a1b249804f --- /dev/null +++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_SFTP.imageset/Contents.json @@ -0,0 +1,15 @@ +{ + "images" : [ + { + "filename" : "Icon.pdf", + "idiom" : "universal" + } + ], + "info" : { + "author" : "xcode", + "version" : 1 + }, + "properties" : { + "preserves-vector-representation" : true + } +} diff --git a/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_SFTP.imageset/Icon.pdf b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_SFTP.imageset/Icon.pdf new file mode 100644 index 0000000000000000000000000000000000000000..08fcf11943d754dfc4e7427d290def530dc7dbcb --- /dev/null +++ b/Sources/Shared/Resources/Assets.xcassets/AssetsRestore/restore_SFTP.imageset/Icon.pdf @@ -0,0 +1,447 @@ +%PDF-1.7 + +1 0 obj + << >> +endobj + +2 0 obj + << /Length 3 0 R >> +stream +/DeviceRGB CS +/DeviceRGB cs +q +1.000000 0.000000 -0.000000 1.000000 15.426392 33.381592 cm +0.693500 0.711750 0.730000 scn +23.717567 7.403564 m +1.429677 7.403564 l +0.639939 7.403564 0.000000 6.763626 0.000000 5.973887 c +0.000000 0.000000 l +25.147245 0.000000 l +25.147245 5.973887 l +25.147245 6.763626 24.507305 7.403564 23.717567 7.403564 c +h +3.436188 1.708138 m +2.466323 1.708138 l +2.293242 1.708138 2.152940 1.848440 2.152940 2.021521 c +2.152940 2.194602 2.293242 2.334904 2.466323 2.334904 c +3.436188 2.334904 l +3.609268 2.334904 3.749571 2.194602 3.749571 2.021521 c +3.749265 1.848440 3.608962 1.708138 3.436188 1.708138 c +h +3.436188 3.388399 m +2.466323 3.388399 l +2.293242 3.388399 2.152940 3.528702 2.152940 3.701782 c +2.152940 3.874863 2.293242 4.015165 2.466323 4.015165 c +3.436188 4.015165 l +3.609268 4.015165 3.749571 3.874863 3.749571 3.701782 c +3.749265 3.528702 3.608962 3.388399 3.436188 3.388399 c +h +3.436188 5.069273 m +2.466323 5.069273 l +2.293242 5.069273 2.152940 5.209575 2.152940 5.382656 c +2.152940 5.555737 2.293242 5.696039 2.466323 5.696039 c +3.436188 5.696039 l +3.609268 5.696039 3.749571 5.555737 3.749571 5.382656 c +3.749571 5.209575 3.608962 5.069273 3.436188 5.069273 c +h +6.515186 1.708138 m +5.545321 1.708138 l +5.372241 1.708138 5.231938 1.848440 5.231938 2.021521 c +5.231938 2.194602 5.372241 2.334904 5.545321 2.334904 c +6.515186 2.334904 l +6.688267 2.334904 6.828569 2.194602 6.828569 2.021521 c +6.828263 1.848440 6.687960 1.708138 6.515186 1.708138 c +h +6.515186 3.388399 m +5.545321 3.388399 l +5.372241 3.388399 5.231938 3.528702 5.231938 3.701782 c +5.231938 3.874863 5.372241 4.015165 5.545321 4.015165 c +6.515186 4.015165 l +6.688267 4.015165 6.828569 3.874863 6.828569 3.701782 c +6.828263 3.528702 6.687960 3.388399 6.515186 3.388399 c +h +6.515186 5.069273 m +5.545321 5.069273 l +5.372241 5.069273 5.231938 5.209575 5.231938 5.382656 c +5.231938 5.555737 5.372241 5.696039 5.545321 5.696039 c +6.515186 5.696039 l +6.688267 5.696039 6.828569 5.555737 6.828569 5.382656 c +6.828569 5.209575 6.687960 5.069273 6.515186 5.069273 c +h +9.594184 1.708138 m +8.624320 1.708138 l +8.451240 1.708138 8.310936 1.848440 8.310936 2.021521 c +8.310936 2.194602 8.451240 2.334904 8.624320 2.334904 c +9.594184 2.334904 l +9.767264 2.334904 9.907569 2.194602 9.907569 2.021521 c +9.907262 1.848440 9.766958 1.708138 9.594184 1.708138 c +h +9.594184 3.388399 m +8.624320 3.388399 l +8.451240 3.388399 8.310936 3.528702 8.310936 3.701782 c +8.310936 3.874863 8.451240 4.015165 8.624320 4.015165 c +9.594184 4.015165 l +9.767264 4.015165 9.907569 3.874863 9.907569 3.701782 c +9.907262 3.528702 9.766958 3.388399 9.594184 3.388399 c +h +9.594184 5.069273 m +8.624320 5.069273 l +8.451240 5.069273 8.310936 5.209575 8.310936 5.382656 c +8.310936 5.555737 8.451240 5.696039 8.624320 5.696039 c +9.594184 5.696039 l +9.767264 5.696039 9.907569 5.555737 9.907569 5.382656 c +9.907569 5.209575 9.766958 5.069273 9.594184 5.069273 c +h +12.673183 1.708138 m +11.703625 1.708138 l +11.530544 1.708138 11.390241 1.848440 11.390241 2.021521 c +11.390241 2.194602 11.530544 2.334904 11.703625 2.334904 c +12.673183 2.334904 l +12.846264 2.334904 12.986566 2.194602 12.986566 2.021521 c +12.986259 1.848440 12.845958 1.708138 12.673183 1.708138 c +h +12.673183 3.388399 m +11.703625 3.388399 l +11.530544 3.388399 11.390241 3.528702 11.390241 3.701782 c +11.390241 3.874863 11.530544 4.015165 11.703625 4.015165 c +12.673183 4.015165 l +12.846264 4.015165 12.986566 3.874863 12.986566 3.701782 c +12.986259 3.528702 12.845958 3.388399 12.673183 3.388399 c +h +12.673183 5.069273 m +11.703625 5.069273 l +11.530544 5.069273 11.390241 5.209575 11.390241 5.382656 c +11.390241 5.555737 11.530544 5.696039 11.703625 5.696039 c +12.673183 5.696039 l +12.846264 5.696039 12.986566 5.555737 12.986566 5.382656 c +12.986566 5.209575 12.845958 5.069273 12.673183 5.069273 c +h +16.798935 2.525446 m +16.149193 2.525446 15.622601 3.052040 15.622601 3.701782 c +15.622601 4.351524 16.149193 4.878118 16.798935 4.878118 c +17.448677 4.878118 17.975273 4.351524 17.975273 3.701782 c +17.975273 3.052040 17.448677 2.525446 16.798935 2.525446 c +h +21.504585 2.525446 m +20.854843 2.525446 20.328251 3.052040 20.328251 3.701782 c +20.328251 4.351524 20.854843 4.878118 21.504585 4.878118 c +22.154327 4.878118 22.680923 4.351524 22.680923 3.701782 c +22.680923 3.052040 22.154327 2.525446 21.504585 2.525446 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 15.426392 23.298096 cm +0.693500 0.711750 0.730000 scn +0.000000 7.403564 m +0.000000 0.000000 l +25.147245 0.000000 l +25.147245 7.403564 l +0.000000 7.403564 l +h +3.436188 1.707832 m +2.466323 1.707832 l +2.293242 1.707832 2.152940 1.847827 2.152940 2.021214 c +2.152940 2.194602 2.293242 2.334598 2.466323 2.334598 c +3.436188 2.334598 l +3.609268 2.334598 3.749571 2.194602 3.749571 2.021214 c +3.749571 1.847827 3.608962 1.707832 3.436188 1.707832 c +h +3.436188 3.388399 m +2.466323 3.388399 l +2.293242 3.388399 2.152940 3.528702 2.152940 3.701782 c +2.152940 3.874863 2.293242 4.015165 2.466323 4.015165 c +3.436188 4.015165 l +3.609268 4.015165 3.749571 3.874863 3.749571 3.701782 c +3.749265 3.528702 3.608962 3.388399 3.436188 3.388399 c +h +3.436188 5.068967 m +2.466323 5.068967 l +2.293242 5.068967 2.152940 5.209269 2.152940 5.382350 c +2.152940 5.555430 2.293242 5.695733 2.466323 5.695733 c +3.436188 5.695733 l +3.609268 5.695733 3.749571 5.555430 3.749571 5.382350 c +3.749265 5.209269 3.608962 5.068967 3.436188 5.068967 c +h +6.515186 1.707832 m +5.545321 1.707832 l +5.372241 1.707832 5.231938 1.847827 5.231938 2.021214 c +5.231938 2.194602 5.372241 2.334598 5.545321 2.334598 c +6.515186 2.334598 l +6.688267 2.334598 6.828569 2.194602 6.828569 2.021214 c +6.828569 1.847827 6.687960 1.707832 6.515186 1.707832 c +h +6.515186 3.388399 m +5.545321 3.388399 l +5.372241 3.388399 5.231938 3.528702 5.231938 3.701782 c +5.231938 3.874863 5.372241 4.015165 5.545321 4.015165 c +6.515186 4.015165 l +6.688267 4.015165 6.828569 3.874863 6.828569 3.701782 c +6.828263 3.528702 6.687960 3.388399 6.515186 3.388399 c +h +6.515186 5.068967 m +5.545321 5.068967 l +5.372241 5.068967 5.231938 5.209269 5.231938 5.382350 c +5.231938 5.555430 5.372241 5.695733 5.545321 5.695733 c +6.515186 5.695733 l +6.688267 5.695733 6.828569 5.555430 6.828569 5.382350 c +6.828263 5.209269 6.687960 5.068967 6.515186 5.068967 c +h +9.594184 1.707832 m +8.624320 1.707832 l +8.451240 1.707832 8.310936 1.847827 8.310936 2.021214 c +8.310936 2.194602 8.451240 2.334598 8.624320 2.334598 c +9.594184 2.334598 l +9.767264 2.334598 9.907569 2.194602 9.907569 2.021214 c +9.907569 1.847827 9.766958 1.707832 9.594184 1.707832 c +h +9.594184 3.388399 m +8.624320 3.388399 l +8.451240 3.388399 8.310936 3.528702 8.310936 3.701782 c +8.310936 3.874863 8.451240 4.015165 8.624320 4.015165 c +9.594184 4.015165 l +9.767264 4.015165 9.907569 3.874863 9.907569 3.701782 c +9.907262 3.528702 9.766958 3.388399 9.594184 3.388399 c +h +9.594184 5.068967 m +8.624320 5.068967 l +8.451240 5.068967 8.310936 5.209269 8.310936 5.382350 c +8.310936 5.555430 8.451240 5.695733 8.624320 5.695733 c +9.594184 5.695733 l +9.767264 5.695733 9.907569 5.555430 9.907569 5.382350 c +9.907262 5.209269 9.766958 5.068967 9.594184 5.068967 c +h +12.673183 1.707832 m +11.703625 1.707832 l +11.530544 1.707832 11.390241 1.847827 11.390241 2.021214 c +11.390241 2.194602 11.530544 2.334598 11.703625 2.334598 c +12.673183 2.334598 l +12.846264 2.334598 12.986566 2.194602 12.986566 2.021214 c +12.986566 1.847827 12.845958 1.707832 12.673183 1.707832 c +h +12.673183 3.388399 m +11.703625 3.388399 l +11.530544 3.388399 11.390241 3.528702 11.390241 3.701782 c +11.390241 3.874863 11.530544 4.015165 11.703625 4.015165 c +12.673183 4.015165 l +12.846264 4.015165 12.986566 3.874863 12.986566 3.701782 c +12.986259 3.528702 12.845958 3.388399 12.673183 3.388399 c +h +12.673183 5.068967 m +11.703625 5.068967 l +11.530544 5.068967 11.390241 5.209269 11.390241 5.382350 c +11.390241 5.555430 11.530544 5.695733 11.703625 5.695733 c +12.673183 5.695733 l +12.846264 5.695733 12.986566 5.555430 12.986566 5.382350 c +12.986259 5.209269 12.845958 5.068967 12.673183 5.068967 c +h +16.798935 2.525446 m +16.149193 2.525446 15.622601 3.052040 15.622601 3.701782 c +15.622601 4.351524 16.149193 4.878119 16.798935 4.878119 c +17.448677 4.878119 17.975273 4.351524 17.975273 3.701782 c +17.975273 3.052040 17.448677 2.525446 16.798935 2.525446 c +h +21.504585 2.525446 m +20.854843 2.525446 20.328251 3.052040 20.328251 3.701782 c +20.328251 4.351524 20.854843 4.878119 21.504585 4.878119 c +22.154327 4.878119 22.680923 4.351524 22.680923 3.701782 c +22.680923 3.052040 22.154327 2.525446 21.504585 2.525446 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 15.426392 13.214844 cm +0.693500 0.711750 0.730000 scn +0.000000 7.403564 m +0.000000 1.429677 l +0.000000 0.639939 0.639939 0.000000 1.429677 0.000000 c +23.717876 0.000000 l +24.507307 0.000000 25.147552 0.639939 25.147552 1.429677 c +25.147552 7.403564 l +0.000000 7.403564 l +h +3.436188 1.707830 m +2.466323 1.707830 l +2.293243 1.707830 2.152940 1.847827 2.152940 2.021214 c +2.152940 2.194295 2.293243 2.334599 2.466323 2.334599 c +3.436188 2.334599 l +3.609269 2.334599 3.749571 2.194602 3.749571 2.021214 c +3.749265 1.848134 3.608963 1.707830 3.436188 1.707830 c +h +3.436188 3.388398 m +2.466323 3.388398 l +2.293243 3.388398 2.152940 3.528395 2.152940 3.701782 c +2.152940 3.875169 2.293243 4.015166 2.466323 4.015166 c +3.436188 4.015166 l +3.609269 4.015166 3.749571 3.875169 3.749571 3.701782 c +3.749571 3.528395 3.608963 3.388398 3.436188 3.388398 c +h +3.436188 5.068966 m +2.466323 5.068966 l +2.293243 5.068966 2.152940 5.208962 2.152940 5.382350 c +2.152940 5.555430 2.293243 5.695734 2.466323 5.695734 c +3.436188 5.695734 l +3.609269 5.695734 3.749571 5.555737 3.749571 5.382350 c +3.749265 5.209269 3.608963 5.068966 3.436188 5.068966 c +h +6.515187 1.707830 m +5.545322 1.707830 l +5.372241 1.707830 5.231939 1.847827 5.231939 2.021214 c +5.231939 2.194295 5.372241 2.334599 5.545322 2.334599 c +6.515187 2.334599 l +6.688267 2.334599 6.828570 2.194602 6.828570 2.021214 c +6.828264 1.848134 6.687961 1.707830 6.515187 1.707830 c +h +6.515187 3.388398 m +5.545322 3.388398 l +5.372241 3.388398 5.231939 3.528395 5.231939 3.701782 c +5.231939 3.875169 5.372241 4.015166 5.545322 4.015166 c +6.515187 4.015166 l +6.688267 4.015166 6.828570 3.875169 6.828570 3.701782 c +6.828570 3.528395 6.687961 3.388398 6.515187 3.388398 c +h +6.515187 5.068966 m +5.545322 5.068966 l +5.372241 5.068966 5.231939 5.208962 5.231939 5.382350 c +5.231939 5.555430 5.372241 5.695734 5.545322 5.695734 c +6.515187 5.695734 l +6.688267 5.695734 6.828570 5.555737 6.828570 5.382350 c +6.828264 5.209269 6.687961 5.068966 6.515187 5.068966 c +h +9.594185 1.707830 m +8.624321 1.707830 l +8.451241 1.707830 8.310937 1.847827 8.310937 2.021214 c +8.310937 2.194295 8.451241 2.334599 8.624321 2.334599 c +9.594185 2.334599 l +9.767265 2.334599 9.907570 2.194602 9.907570 2.021214 c +9.907263 1.848134 9.766959 1.707830 9.594185 1.707830 c +h +9.594185 3.388398 m +8.624321 3.388398 l +8.451241 3.388398 8.310937 3.528395 8.310937 3.701782 c +8.310937 3.875169 8.451241 4.015166 8.624321 4.015166 c +9.594185 4.015166 l +9.767265 4.015166 9.907570 3.875169 9.907570 3.701782 c +9.907570 3.528395 9.766959 3.388398 9.594185 3.388398 c +h +9.594185 5.068966 m +8.624321 5.068966 l +8.451241 5.068966 8.310937 5.208962 8.310937 5.382350 c +8.310937 5.555430 8.451241 5.695734 8.624321 5.695734 c +9.594185 5.695734 l +9.767265 5.695734 9.907570 5.555737 9.907570 5.382350 c +9.907263 5.209269 9.766959 5.068966 9.594185 5.068966 c +h +12.673184 1.707830 m +11.703626 1.707830 l +11.530545 1.707830 11.390242 1.847827 11.390242 2.021214 c +11.390242 2.194295 11.530545 2.334599 11.703626 2.334599 c +12.673184 2.334599 l +12.846266 2.334599 12.986567 2.194602 12.986567 2.021214 c +12.986260 1.848134 12.845959 1.707830 12.673184 1.707830 c +h +12.673184 3.388398 m +11.703626 3.388398 l +11.530545 3.388398 11.390242 3.528395 11.390242 3.701782 c +11.390242 3.875169 11.530545 4.015166 11.703626 4.015166 c +12.673184 4.015166 l +12.846266 4.015166 12.986567 3.875169 12.986567 3.701782 c +12.986567 3.528395 12.845959 3.388398 12.673184 3.388398 c +h +12.673184 5.068966 m +11.703626 5.068966 l +11.530545 5.068966 11.390242 5.208962 11.390242 5.382350 c +11.390242 5.555430 11.530545 5.695734 11.703626 5.695734 c +12.673184 5.695734 l +12.846266 5.695734 12.986567 5.555737 12.986567 5.382350 c +12.986260 5.209269 12.845959 5.068966 12.673184 5.068966 c +h +16.798937 2.525447 m +16.149195 2.525447 15.622602 3.052040 15.622602 3.701782 c +15.622602 4.351524 16.149195 4.878119 16.798937 4.878119 c +17.448679 4.878119 17.975275 4.351524 17.975275 3.701782 c +17.975275 3.052040 17.448679 2.525447 16.798937 2.525447 c +h +21.504587 2.525447 m +20.854845 2.525447 20.328253 3.052040 20.328253 3.701782 c +20.328253 4.351524 20.854845 4.878119 21.504587 4.878119 c +22.154329 4.878119 22.680925 4.351524 22.680925 3.701782 c +22.680925 3.052040 22.154329 2.525447 21.504587 2.525447 c +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 17.400757 21.498291 cm +0.693500 0.711750 0.730000 scn +21.201620 0.918945 m +0.000000 0.918945 l +0.000000 -0.000067 l +21.201620 -0.000067 l +21.201620 0.918945 l +h +f +n +Q +q +1.000000 0.000000 -0.000000 1.000000 17.400757 31.582764 cm +0.693500 0.711750 0.730000 scn +21.201620 0.918945 m +0.000000 0.918945 l +0.000000 -0.000067 l +21.201620 -0.000067 l +21.201620 0.918945 l +h +f +n +Q + +endstream +endobj + +3 0 obj + 13265 +endobj + +4 0 obj + << /Annots [] + /Type /Page + /MediaBox [ 0.000000 0.000000 56.000000 56.000000 ] + /Resources 1 0 R + /Contents 2 0 R + /Parent 5 0 R + >> +endobj + +5 0 obj + << /Kids [ 4 0 R ] + /Count 1 + /Type /Pages + >> +endobj + +6 0 obj + << /Pages 5 0 R + /Type /Catalog + >> +endobj + +xref +0 7 +0000000000 65535 f +0000000010 00000 n +0000000034 00000 n +0000013355 00000 n +0000013379 00000 n +0000013552 00000 n +0000013626 00000 n +trailer +<< /ID [ (some) (id) ] + /Root 6 0 R + /Size 7 +>> +startxref +13685 +%%EOF \ No newline at end of file diff --git a/Sources/Shared/Resources/en.lproj/Localizable.strings b/Sources/Shared/Resources/en.lproj/Localizable.strings index 774e9759242a913fc93e1d9b025ced9ca2b847b9..7b68750b8d5018d2067245a01698fb67ec95df02 100644 --- a/Sources/Shared/Resources/en.lproj/Localizable.strings +++ b/Sources/Shared/Resources/en.lproj/Localizable.strings @@ -620,6 +620,8 @@ = "Dropbox"; "backup.googleDrive" = "Google Drive"; +"backup.SFTP" += "SFTP"; // Settings - Delete Account