diff --git a/kDrive/Resources/de.lproj/Localizable.strings b/kDrive/Resources/de.lproj/Localizable.strings index ab76ffb78..944d2cb43 100644 --- a/kDrive/Resources/de.lproj/Localizable.strings +++ b/kDrive/Resources/de.lproj/Localizable.strings @@ -3,8 +3,8 @@ * Project: kDrive * Locale: de, German * Tagged: ios - * Exported by: Matthieu Déglon - * Exported at: Thu, 03 Oct 2024 08:22:20 +0200 + * Exported by: Baptiste Griva + * Exported at: Fri, 20 Dec 2024 14:32:53 +0100 */ /* loco:610a8791fa12ab20713c09e4 */ @@ -1540,6 +1540,12 @@ /* loco:63846ac1f8786f515b61f752 */ "photoLibraryAccessLimitedTitle" = "Eingeschränkter oder verweigerter Zugang"; +/* loco:6765711b1f6cfc8c5408ee33 */ +"photoSyncSuspended" = "Verbinden Sie sich mit einem Wi-Fi-Netzwerk oder erlauben Sie die mobile Datennutzung"; + +/* loco:67657012f62b39abc50deb62 */ +"photoSyncSuspendedTitle" = "Fotosicherung ausgesetzt"; + /* loco:627cf782146a6d748e5e7262 */ "photosHeaderDateFormat" = "MMMM yyyy"; @@ -2155,6 +2161,9 @@ /* loco:6049df4d5c2c3a04bc397a8c */ "uploadNetworkErrorWifiRequired" = "Warten auf WLAN"; +/* loco:66fbba74682fe9a0990df012 */ +"uploadOverDataRestrictedError" = "Warten auf Wi-Fi"; + /* loco:60928fcf72952001716b86d2 */ "uploadPausedDescription" = "Einige Uploads sind in kDrive noch im Gange. Öffnen Sie die App und lassen Sie das Gerät verbunden, um den Upload zu beschleunigen."; diff --git a/kDrive/Resources/en.lproj/Localizable.strings b/kDrive/Resources/en.lproj/Localizable.strings index 792b04078..60dc9e704 100644 --- a/kDrive/Resources/en.lproj/Localizable.strings +++ b/kDrive/Resources/en.lproj/Localizable.strings @@ -1,10 +1,10 @@ /* - * Loco ios export: iOS Localizable.strings + * Loco ios export: Xcode Strings (legacy) * Project: kDrive * Locale: en, English * Tagged: ios * Exported by: Baptiste Griva - * Exported at: Thu, 26 Sep 2024 10:46:53 +0200 + * Exported at: Fri, 20 Dec 2024 14:32:53 +0100 */ /* loco:610a8791fa12ab20713c09e4 */ @@ -1540,6 +1540,12 @@ /* loco:63846ac1f8786f515b61f752 */ "photoLibraryAccessLimitedTitle" = "Restricted or denied access"; +/* loco:6765711b1f6cfc8c5408ee33 */ +"photoSyncSuspended" = "Connect to a Wi-Fi network or allow mobile data usage"; + +/* loco:67657012f62b39abc50deb62 */ +"photoSyncSuspendedTitle" = "Photo backup suspended"; + /* loco:627cf782146a6d748e5e7262 */ "photosHeaderDateFormat" = "MMMM yyyy"; @@ -2155,6 +2161,9 @@ /* loco:6049df4d5c2c3a04bc397a8c */ "uploadNetworkErrorWifiRequired" = "Waiting for WiFi network"; +/* loco:66fbba74682fe9a0990df012 */ +"uploadOverDataRestrictedError" = "Waiting for Wi-Fi"; + /* loco:60928fcf72952001716b86d2 */ "uploadPausedDescription" = "Some uploads are still in progress in kDrive. Open the app and keep the device connected to speed up the upload."; diff --git a/kDrive/Resources/es.lproj/Localizable.strings b/kDrive/Resources/es.lproj/Localizable.strings index 62fd6e3dc..e156f2a3a 100644 --- a/kDrive/Resources/es.lproj/Localizable.strings +++ b/kDrive/Resources/es.lproj/Localizable.strings @@ -3,8 +3,8 @@ * Project: kDrive * Locale: es, Spanish * Tagged: ios - * Exported by: Matthieu Déglon - * Exported at: Thu, 03 Oct 2024 08:22:20 +0200 + * Exported by: Baptiste Griva + * Exported at: Fri, 20 Dec 2024 14:32:53 +0100 */ /* loco:610a8791fa12ab20713c09e4 */ @@ -1540,6 +1540,12 @@ /* loco:63846ac1f8786f515b61f752 */ "photoLibraryAccessLimitedTitle" = "Acceso restringido o denegado"; +/* loco:6765711b1f6cfc8c5408ee33 */ +"photoSyncSuspended" = "Conectarse a una red Wi-Fi o permitir el uso de datos móviles"; + +/* loco:67657012f62b39abc50deb62 */ +"photoSyncSuspendedTitle" = "Suspendida la copia de seguridad de fotos"; + /* loco:627cf782146a6d748e5e7262 */ "photosHeaderDateFormat" = "MMMM yyyy"; @@ -2155,6 +2161,9 @@ /* loco:6049df4d5c2c3a04bc397a8c */ "uploadNetworkErrorWifiRequired" = "A la espera de red WiFi"; +/* loco:66fbba74682fe9a0990df012 */ +"uploadOverDataRestrictedError" = "A la espera de Wi-Fi"; + /* loco:60928fcf72952001716b86d2 */ "uploadPausedDescription" = "Algunas subidas siguen en curso en kDrive. Abre la aplicación y mantén el dispositivo conectado para acelerar la carga."; diff --git a/kDrive/Resources/fr.lproj/Localizable.strings b/kDrive/Resources/fr.lproj/Localizable.strings index aa9d63a19..05efc0ef6 100644 --- a/kDrive/Resources/fr.lproj/Localizable.strings +++ b/kDrive/Resources/fr.lproj/Localizable.strings @@ -3,8 +3,8 @@ * Project: kDrive * Locale: fr, French * Tagged: ios - * Exported by: Matthieu Déglon - * Exported at: Thu, 03 Oct 2024 08:22:20 +0200 + * Exported by: Baptiste Griva + * Exported at: Fri, 20 Dec 2024 14:32:53 +0100 */ /* loco:610a8791fa12ab20713c09e4 */ @@ -1540,6 +1540,12 @@ /* loco:63846ac1f8786f515b61f752 */ "photoLibraryAccessLimitedTitle" = "Accès limité ou refusé"; +/* loco:6765711b1f6cfc8c5408ee33 */ +"photoSyncSuspended" = "Se connecter à un réseau Wi-Fi ou autoriser l’utilisation de données mobiles"; + +/* loco:67657012f62b39abc50deb62 */ +"photoSyncSuspendedTitle" = "Sauvegarde des photos suspendue"; + /* loco:627cf782146a6d748e5e7262 */ "photosHeaderDateFormat" = "MMMM yyyy"; @@ -2155,6 +2161,9 @@ /* loco:6049df4d5c2c3a04bc397a8c */ "uploadNetworkErrorWifiRequired" = "En attente de réseau Wi-Fi"; +/* loco:66fbba74682fe9a0990df012 */ +"uploadOverDataRestrictedError" = "En attente de Wi-Fi"; + /* loco:60928fcf72952001716b86d2 */ "uploadPausedDescription" = "Des importations sont toujours en cours dans kDrive. Ouvrez l’app et gardez l’appareil branché pour accélérer l’upload."; diff --git a/kDrive/Resources/it.lproj/Localizable.strings b/kDrive/Resources/it.lproj/Localizable.strings index 59cf14ba1..1562bbbf0 100644 --- a/kDrive/Resources/it.lproj/Localizable.strings +++ b/kDrive/Resources/it.lproj/Localizable.strings @@ -3,8 +3,8 @@ * Project: kDrive * Locale: it, Italian * Tagged: ios - * Exported by: Matthieu Déglon - * Exported at: Thu, 03 Oct 2024 08:22:20 +0200 + * Exported by: Baptiste Griva + * Exported at: Fri, 20 Dec 2024 14:32:53 +0100 */ /* loco:610a8791fa12ab20713c09e4 */ @@ -1540,6 +1540,12 @@ /* loco:63846ac1f8786f515b61f752 */ "photoLibraryAccessLimitedTitle" = "Accesso limitato o negato"; +/* loco:6765711b1f6cfc8c5408ee33 */ +"photoSyncSuspended" = "Collegarsi a una rete Wi-Fi o consentire l’utilizzo di dati mobili"; + +/* loco:67657012f62b39abc50deb62 */ +"photoSyncSuspendedTitle" = "Backup delle foto sospeso"; + /* loco:627cf782146a6d748e5e7262 */ "photosHeaderDateFormat" = "MMMM yyyy"; @@ -2155,6 +2161,9 @@ /* loco:6049df4d5c2c3a04bc397a8c */ "uploadNetworkErrorWifiRequired" = "In attesa di connessione alla rete Wi-Fi"; +/* loco:66fbba74682fe9a0990df012 */ +"uploadOverDataRestrictedError" = "In attesa del Wi-Fi"; + /* loco:60928fcf72952001716b86d2 */ "uploadPausedDescription" = "Alcuni caricamenti sono ancora in corso in kDrive. Aprite l’app e mantenete il dispositivo connesso per accelerare il caricamento."; diff --git a/kDrive/UI/Controller/Files/Upload/UploadQueueViewController.swift b/kDrive/UI/Controller/Files/Upload/UploadQueueViewController.swift index 42d95ddba..e85e23a64 100644 --- a/kDrive/UI/Controller/Files/Upload/UploadQueueViewController.swift +++ b/kDrive/UI/Controller/Files/Upload/UploadQueueViewController.swift @@ -42,6 +42,7 @@ final class UploadQueueViewController: UIViewController { navigationItem.hideBackButtonText() tableView.register(cellView: UploadTableViewCell.self) + tableView.register(cellView: ErrorUploadTableViewCell.self) retryButton.accessibilityLabel = KDriveResourcesStrings.Localizable.buttonRetry cancelButton.accessibilityLabel = KDriveResourcesStrings.Localizable.buttonCancel @@ -58,6 +59,7 @@ final class UploadQueueViewController: UIViewController { override func viewDidAppear(_ animated: Bool) { super.viewDidAppear(animated) MatomoUtils.track(view: [MatomoUtils.Views.uploadQueue.displayName, "Main"]) + } deinit { @@ -134,21 +136,35 @@ extension UploadQueueViewController: UITableViewDataSource { } func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { - let cell = tableView.dequeueReusableCell(type: UploadTableViewCell.self, for: indexPath) - cell.initWithPositionAndShadow(isFirst: indexPath.row == 0, - isLast: indexPath.row == self.tableView( - tableView, - numberOfRowsInSection: indexPath.section - ) - 1) - - /// Make sure the file is valid - let file = uploadingFiles[indexPath.row] - if !file.isInvalidated { - let progress: CGFloat? = (file.progress != nil) ? CGFloat(file.progress!) : nil - cell.configureWith(uploadFile: file, progress: progress) + if indexPath.row == 0 && UserDefaults.shared.isWifiOnly && ReachabilityListener.instance.currentStatus == .cellular { + let cell = tableView.dequeueReusableCell(type: ErrorUploadTableViewCell.self, for: indexPath) + cell.initWithPositionAndShadow(isFirst: true, + isLast: true) + cell.delegate = self + return cell + } else { + let cell = tableView.dequeueReusableCell(type: UploadTableViewCell.self, for: indexPath) + cell.initWithPositionAndShadow(isFirst: indexPath.row == 0, + isLast: indexPath.row == self.tableView( + tableView, + numberOfRowsInSection: indexPath.section + ) - 1) + + /// Make sure the file is valid + let file = uploadingFiles[indexPath.row] + if !file.isInvalidated { + let progress: CGFloat? = (file.progress != nil) ? CGFloat(file.progress!) : nil + cell.configureWith(uploadFile: file, progress: progress) + } + + cell.selectionStyle = .none + return cell } + } +} - cell.selectionStyle = .none - return cell +extension UploadQueueViewController: AccessParametersDelegate { + func parameterButtonTapped() { + navigationController?.pushViewController(PhotoSyncSettingsViewController(), animated: true) } } diff --git a/kDrive/UI/Controller/Home/RootMenuHeaderView.swift b/kDrive/UI/Controller/Home/RootMenuHeaderView.swift index 89b305334..a8db7a6cb 100644 --- a/kDrive/UI/Controller/Home/RootMenuHeaderView.swift +++ b/kDrive/UI/Controller/Home/RootMenuHeaderView.swift @@ -35,6 +35,10 @@ class RootMenuHeaderView: UICollectionReusableView { var onUploadCardViewTapped: (() -> Void)? + deinit { + NotificationCenter.default.removeObserver(self) + } + override func awakeFromNib() { super.awakeFromNib() @@ -45,9 +49,7 @@ class RootMenuHeaderView: UICollectionReusableView { radius: 10 ) - uploadCardView.titleLabel.text = KDriveResourcesStrings.Localizable.uploadInProgressTitle - uploadCardView.progressView.setInfomaniakStyle() - uploadCardView.progressView.enableIndeterminate() + updateWifiView() uploadCardView.isHidden = true offlineView.isHidden = true @@ -55,6 +57,13 @@ class RootMenuHeaderView: UICollectionReusableView { let tapGestureRecognizer = UITapGestureRecognizer(target: self, action: #selector(didTapOnUploadCardView)) uploadCardView.addGestureRecognizer(tapGestureRecognizer) + + NotificationCenter.default.addObserver( + self, + selector: #selector(reloadWifiView), + name: .reloadWifiView, + object: nil + ) } func configureInCollectionView( @@ -82,6 +91,11 @@ class RootMenuHeaderView: UICollectionReusableView { } } + @objc func reloadWifiView(_ notification: Notification) { + print("Call observeNetworkChange") + updateWifiView() + } + @objc func didTapOnUploadCardView() { onUploadCardViewTapped?() } @@ -113,11 +127,34 @@ class RootMenuHeaderView: UICollectionReusableView { guard let self else { return } offlineView.isHidden = status != .offline - reloadHeader() + + updateWifiView() } } } + private func updateWifiView() { + if UserDefaults.shared.isWifiOnly && ReachabilityListener.instance.currentStatus == .cellular { + uploadCardView.titleLabel.text = KDriveResourcesStrings.Localizable.uploadPausedTitle + uploadCardView.progressView.isHidden = true + uploadCardView.iconView.image = UIImage(systemName: "exclamationmark.arrow.triangle.2.circlepath") + uploadCardView.iconView.isHidden = false + uploadCardView.iconView.translatesAutoresizingMaskIntoConstraints = false + NSLayoutConstraint.activate([ + uploadCardView.iconView.widthAnchor.constraint(equalToConstant: 24), + uploadCardView.iconView.heightAnchor.constraint(equalToConstant: 24) + ]) + uploadCardView.iconView.tintColor = .gray + } else { + uploadCardView.titleLabel.text = KDriveResourcesStrings.Localizable.uploadInProgressTitle + uploadCardView.progressView.isHidden = false + uploadCardView.iconView.isHidden = true + uploadCardView.progressView.setInfomaniakStyle() + uploadCardView.progressView.enableIndeterminate() + } + reloadHeader() + } + private func reloadHeader() { hideIfNeeded() diff --git a/kDrive/UI/Controller/Home/RootMenuHeaderView.xib b/kDrive/UI/Controller/Home/RootMenuHeaderView.xib index a4f4a3155..5b7319805 100644 --- a/kDrive/UI/Controller/Home/RootMenuHeaderView.xib +++ b/kDrive/UI/Controller/Home/RootMenuHeaderView.xib @@ -172,7 +172,7 @@ - + diff --git a/kDrive/UI/Controller/Menu/MenuViewController.swift b/kDrive/UI/Controller/Menu/MenuViewController.swift index ae38221e6..bbab5491e 100644 --- a/kDrive/UI/Controller/Menu/MenuViewController.swift +++ b/kDrive/UI/Controller/Menu/MenuViewController.swift @@ -84,6 +84,10 @@ final class MenuViewController: UITableViewController, SelectSwitchDriveDelegate fatalError("init(coder:) has not been implemented") } + deinit { + NotificationCenter.default.removeObserver(self) + } + override func viewDidLoad() { super.viewDidLoad() @@ -92,12 +96,27 @@ final class MenuViewController: UITableViewController, SelectSwitchDriveDelegate tableView.register(cellView: MenuTableViewCell.self) tableView.register(cellView: MenuTopTableViewCell.self) tableView.register(cellView: UploadsInProgressTableViewCell.self) + tableView.register(cellView: UploadsPausedTableViewCell.self) tableView.contentInset = UIEdgeInsets(top: 0, left: 0, bottom: UIConstants.listPaddingBottom, right: 0) updateTableContent() navigationItem.title = KDriveResourcesStrings.Localizable.menuTitle navigationItem.hideBackButtonText() + + NotificationCenter.default.addObserver( + self, + selector: #selector(reloadWifiView), + name: .reloadWifiView, + object: nil + ) + + ReachabilityListener.instance.observeNetworkChange(self) { [weak self] _ in + Task { @MainActor in + let indexPath = IndexPath(row: 0, section: 1) + self?.tableView.reloadRows(at: [indexPath], with: .automatic) + } + } } override func viewWillAppear(_ animated: Bool) { @@ -163,6 +182,10 @@ final class MenuViewController: UITableViewController, SelectSwitchDriveDelegate sections.insert(.uploads, at: 1) } } + + @objc func reloadWifiView(_ notification: Notification) { + reloadData() + } } // MARK: - Table view delegate @@ -194,11 +217,18 @@ extension MenuViewController { cell.switchDriveButton.addTarget(self, action: #selector(switchDriveButtonPressed(_:)), for: .touchUpInside) return cell } else if section == .uploads { - let cell = tableView.dequeueReusableCell(type: UploadsInProgressTableViewCell.self, for: indexPath) - cell.initWithPositionAndShadow(isFirst: true, isLast: true) - cell.progressView.enableIndeterminate() - cell.setUploadCount(uploadCountManager?.uploadCount ?? 0) - return cell + if UserDefaults.shared.isWifiOnly && ReachabilityListener.instance.currentStatus == .cellular { + let cell = tableView.dequeueReusableCell(type: UploadsPausedTableViewCell.self, for: indexPath) + cell.initWithPositionAndShadow(isFirst: true, isLast: true) + cell.setUploadCount(uploadCountManager?.uploadCount ?? 0) + return cell + } else { + let cell = tableView.dequeueReusableCell(type: UploadsInProgressTableViewCell.self, for: indexPath) + cell.initWithPositionAndShadow(isFirst: true, isLast: true) + cell.progressView.enableIndeterminate() + cell.setUploadCount(uploadCountManager?.uploadCount ?? 0) + return cell + } } else { let action = section.actions[indexPath.row] let cell = tableView.dequeueReusableCell(type: MenuTableViewCell.self, for: indexPath) diff --git a/kDrive/UI/Controller/Menu/OfflineSyncSettingsViewController.swift b/kDrive/UI/Controller/Menu/OfflineSyncSettingsViewController.swift new file mode 100644 index 000000000..460fa401d --- /dev/null +++ b/kDrive/UI/Controller/Menu/OfflineSyncSettingsViewController.swift @@ -0,0 +1,64 @@ +/* + Infomaniak kDrive - iOS App + Copyright (C) 2021 Infomaniak Network SA + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +import kDriveCore +import UIKit + +class OfflineSyncSettingsViewController: BaseGroupedTableViewController { + private var tableContent: [SyncMode] = SyncMode.allCases + private var selectedOfflineMode: SyncMode = UserDefaults.shared.syncOfflineMode + + weak var delegate: SelectPhotoFormatDelegate? + + override func viewDidLoad() { + super.viewDidLoad() + tableView.register(cellView: ParameterSyncTableViewCell.self) + } + + override func viewDidAppear(_ animated: Bool) { + super.viewDidAppear(animated) + MatomoUtils.track(view: [MatomoUtils.Views.menu.displayName, MatomoUtils.Views.settings.displayName, "selectOfflineMode"]) + } + + override func numberOfSections(in tableView: UITableView) -> Int { + return 1 + } + + override func tableView(_ tableView: UITableView, numberOfRowsInSection section: Int) -> Int { + return tableContent.count + } + + override func tableView(_ tableView: UITableView, cellForRowAt indexPath: IndexPath) -> UITableViewCell { + let cell = tableView.dequeueReusableCell(type: ParameterSyncTableViewCell.self, for: indexPath) + cell.initWithPositionAndShadow(isFirst: true, isLast: true) + let currentMode = tableContent[indexPath.row] + cell.syncTitleLabel.text = currentMode.title + cell.syncDetailLabel.text = currentMode.selectionTitle + if currentMode == selectedOfflineMode { + tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none) + } + return cell + } + + override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { + let mode = tableContent[indexPath.row] + MatomoUtils.track(eventWithCategory: .settings, name: "mod\(mode.rawValue.capitalized)") + UserDefaults.shared.syncOfflineMode = mode + navigationController?.popViewController(animated: true) + } +} diff --git a/kDrive/UI/Controller/Menu/ParameterTableViewController.swift b/kDrive/UI/Controller/Menu/ParameterTableViewController.swift index e7119e165..c3a5a5946 100644 --- a/kDrive/UI/Controller/Menu/ParameterTableViewController.swift +++ b/kDrive/UI/Controller/Menu/ParameterTableViewController.swift @@ -35,7 +35,7 @@ class ParameterTableViewController: BaseGroupedTableViewController { case theme case notifications case security - case wifi + case offlineSync case storage case about case deleteAccount @@ -50,7 +50,7 @@ class ParameterTableViewController: BaseGroupedTableViewController { return KDriveResourcesStrings.Localizable.notificationTitle case .security: return KDriveResourcesStrings.Localizable.securityTitle - case .wifi: + case .offlineSync: return KDriveResourcesStrings.Localizable.syncWifiSettingsTitle case .storage: return KDriveResourcesStrings.Localizable.manageStorageTitle @@ -137,11 +137,11 @@ class ParameterTableViewController: BaseGroupedTableViewController { cell.titleLabel.text = row.title return cell - case .wifi: + case .offlineSync: let cell = tableView.dequeueReusableCell(type: AboutDetailTableViewCell.self, for: indexPath) cell.initWithPositionAndShadow(isFirst: indexPath.row == 0, isLast: indexPath.row == tableContent.count - 1) - cell.titleLabel.text = UserDefaults.shared.syncMode.title - cell.detailLabel.text = UserDefaults.shared.syncMode.selectionTitle + cell.titleLabel.text = KDriveResourcesStrings.Localizable.syncWifiSettingsTitle + cell.detailLabel.text = UserDefaults.shared.syncOfflineMode.title return cell } } @@ -161,8 +161,8 @@ class ParameterTableViewController: BaseGroupedTableViewController { navigationController?.pushViewController(NotificationsSettingsTableViewController(), animated: true) case .security: navigationController?.pushViewController(SecurityTableViewController(), animated: true) - case .wifi: - navigationController?.pushViewController(WifiSyncSettingsViewController(), animated: true) + case .offlineSync: + navigationController?.pushViewController(OfflineSyncSettingsViewController(), animated: true) case .about: navigationController?.pushViewController(AboutTableViewController(), animated: true) case .deleteAccount: diff --git a/kDrive/UI/Controller/Menu/PhotoSyncSettingsViewController.swift b/kDrive/UI/Controller/Menu/PhotoSyncSettingsViewController.swift index 9fb79f767..2258ded18 100644 --- a/kDrive/UI/Controller/Menu/PhotoSyncSettingsViewController.swift +++ b/kDrive/UI/Controller/Menu/PhotoSyncSettingsViewController.swift @@ -434,7 +434,7 @@ extension PhotoSyncSettingsViewController { let cell = tableView.dequeueReusableCell(type: AboutDetailTableViewCell.self, for: indexPath) cell.initWithPositionAndShadow(isFirst: indexPath.row == 0, isLast: indexPath.row == settingsRows.count - 1) cell.titleLabel.text = KDriveResourcesStrings.Localizable.syncWifiPicturesTitle - cell.detailLabel.text = UserDefaults.shared.syncMode.title + cell.detailLabel.text = newSyncSettings.wifiSync.title return cell } case .syncDenied: @@ -584,9 +584,16 @@ extension PhotoSyncSettingsViewController: PhotoSyncSettingsTableViewCellDelegat extension PhotoSyncSettingsViewController: WifiSyncSettingsDelegate { func didSelectSyncMode(_ mode: SyncMode) { newSyncSettings.wifiSync = mode + if mode == .onlyWifi { + UserDefaults.shared.isWifiOnly = true + } else { + UserDefaults.shared.isWifiOnly = false + } + updateSaveButtonState() tableView.reloadRows( at: [IndexPath(row: PhotoSyncSettingsRows.wifiSync.rawValue, section: PhotoSyncSection.syncSettings.rawValue)], with: .fade ) + NotificationCenter.default.post(name: .reloadWifiView, object: nil) } } diff --git a/kDrive/UI/Controller/Menu/WifiSyncSettingsViewController.swift b/kDrive/UI/Controller/Menu/WifiSyncSettingsViewController.swift index c07094505..1eb8c8587 100644 --- a/kDrive/UI/Controller/Menu/WifiSyncSettingsViewController.swift +++ b/kDrive/UI/Controller/Menu/WifiSyncSettingsViewController.swift @@ -40,8 +40,6 @@ class WifiSyncSettingsViewController: BaseGroupedTableViewController { tableView.register(cellView: ParameterSyncTableViewCell.self) tableView.allowsMultipleSelection = false - - selectedMode = UserDefaults.shared.syncMode } static func instantiate(selectedMode: SyncMode) -> WifiSyncSettingsViewController { @@ -67,18 +65,17 @@ class WifiSyncSettingsViewController: BaseGroupedTableViewController { let cell = tableView.dequeueReusableCell(type: ParameterSyncTableViewCell.self, for: indexPath) cell.initWithPositionAndShadow(isFirst: true, isLast: true) let currentMode = tableContent[indexPath.row] + cell.syncTitleLabel.text = currentMode.title + cell.syncDetailLabel.text = currentMode.selectionTitle if currentMode == selectedMode { tableView.selectRow(at: indexPath, animated: true, scrollPosition: .none) } - cell.syncTitleLabel.text = currentMode.title - cell.syncDetailLabel.text = currentMode.selectionTitle return cell } override func tableView(_ tableView: UITableView, didSelectRowAt indexPath: IndexPath) { let mode = tableContent[indexPath.row] - MatomoUtils.track(eventWithCategory: .settings, name: "mode\(mode.rawValue.capitalized)") - UserDefaults.shared.syncMode = mode + MatomoUtils.track(eventWithCategory: .settings, name: "mod\(mode.rawValue.capitalized)") delegate?.didSelectSyncMode(tableContent[indexPath.row]) navigationController?.popViewController(animated: true) } diff --git a/kDrive/UI/View/Files/Upload/ErrorUploadTableViewCell.swift b/kDrive/UI/View/Files/Upload/ErrorUploadTableViewCell.swift new file mode 100644 index 000000000..ea76aa6a3 --- /dev/null +++ b/kDrive/UI/View/Files/Upload/ErrorUploadTableViewCell.swift @@ -0,0 +1,40 @@ +/* + Infomaniak kDrive - iOS App + Copyright (C) 2024 Infomaniak Network SA + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +import InfomaniakCoreUIKit +import kDriveCore +import kDriveResources +import UIKit + +protocol AccessParametersDelegate: AnyObject { + func parameterButtonTapped() +} + +class ErrorUploadTableViewCell: InsetTableViewCell { + @IBOutlet var bannerView: UIView! + @IBOutlet var errorIconImageView: UIImageView! + @IBOutlet var errorTitleLabel: UILabel! + @IBOutlet var errorDetailLabel: UILabel! + @IBOutlet var settingButton: UIButton! + + weak var delegate: AccessParametersDelegate? + + @IBAction func updateButtonPressed(_ sender: UIButton) { + delegate?.parameterButtonTapped() + } +} diff --git a/kDrive/UI/View/Files/Upload/ErrorUploadTableViewCell.xib b/kDrive/UI/View/Files/Upload/ErrorUploadTableViewCell.xib new file mode 100644 index 000000000..3bf5e9028 --- /dev/null +++ b/kDrive/UI/View/Files/Upload/ErrorUploadTableViewCell.xib @@ -0,0 +1,164 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kDrive/UI/View/Files/Upload/UploadTableViewCell.swift b/kDrive/UI/View/Files/Upload/UploadTableViewCell.swift index c751dbb69..247f1fcd2 100644 --- a/kDrive/UI/View/Files/Upload/UploadTableViewCell.swift +++ b/kDrive/UI/View/Files/Upload/UploadTableViewCell.swift @@ -74,8 +74,13 @@ final class UploadTableViewCell: InsetTableViewCell { if let error = uploadFile.error, error != .taskRescheduled { cardContentView.retryButton?.isHidden = false - cardContentView.detailsLabel.text = KDriveResourcesStrings.Localizable - .errorUpload + " (\(error.localizedDescription))" + if error.localizedDescription == KDriveResourcesStrings.Localizable.uploadOverDataRestrictedError { + cardContentView.detailsLabel.text = error.localizedDescription + } else { + cardContentView.detailsLabel.text = KDriveResourcesStrings.Localizable + .errorUpload + " (\(error.localizedDescription))" + } + } else { cardContentView.retryButton? .isHidden = (uploadFile.maxRetryCount > 0) // Display retry for uploads that reached automatic retry limit diff --git a/kDrive/UI/View/Home/UploadsPausedTableViewCell.swift b/kDrive/UI/View/Home/UploadsPausedTableViewCell.swift new file mode 100644 index 000000000..82c72c3ff --- /dev/null +++ b/kDrive/UI/View/Home/UploadsPausedTableViewCell.swift @@ -0,0 +1,30 @@ +/* + Infomaniak kDrive - iOS App + Copyright (C) 2024 Infomaniak Network SA + + This program is free software: you can redistribute it and/or modify + it under the terms of the GNU General Public License as published by + the Free Software Foundation, either version 3 of the License, or + (at your option) any later version. + + This program is distributed in the hope that it will be useful, + but WITHOUT ANY WARRANTY; without even the implied warranty of + MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the + GNU General Public License for more details. + + You should have received a copy of the GNU General Public License + along with this program. If not, see . + */ + +import InfomaniakCoreUIKit +import kDriveCore +import kDriveResources +import UIKit + +class UploadsPausedTableViewCell: InsetTableViewCell { + @IBOutlet var subtitleLabel: IKLabel! + + func setUploadCount(_ count: Int) { + subtitleLabel.text = KDriveResourcesStrings.Localizable.uploadInProgressNumberFile(count) + } +} diff --git a/kDrive/UI/View/Home/UploadsPausedTableViewCell.xib b/kDrive/UI/View/Home/UploadsPausedTableViewCell.xib new file mode 100644 index 000000000..f01b4c9ff --- /dev/null +++ b/kDrive/UI/View/Home/UploadsPausedTableViewCell.xib @@ -0,0 +1,126 @@ + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + + diff --git a/kDrive/UI/View/Menu/Parameters/ParameterSyncTableViewCell.swift b/kDrive/UI/View/Menu/Parameters/ParameterSyncTableViewCell.swift index e576a8a0f..1544217af 100644 --- a/kDrive/UI/View/Menu/Parameters/ParameterSyncTableViewCell.swift +++ b/kDrive/UI/View/Menu/Parameters/ParameterSyncTableViewCell.swift @@ -17,9 +17,22 @@ */ import InfomaniakCoreUIKit +import kDriveResources import UIKit class ParameterSyncTableViewCell: InsetTableViewCell { @IBOutlet var syncTitleLabel: UILabel! @IBOutlet var syncDetailLabel: UILabel! + + override func setSelected(_ selected: Bool, animated: Bool) { + super.setSelected(selected, animated: animated) + + contentInsetView.backgroundColor = KDriveResourcesAsset.backgroundCardViewColor.color + if selected { + contentInsetView.borderColor = KDriveResourcesAsset.infomaniakColor.color + contentInsetView.borderWidth = 2 + } else { + contentInsetView.borderWidth = 0 + } + } } diff --git a/kDriveCore/Data/Cache/DriveFileManager/DriveFileManagerConstants.swift b/kDriveCore/Data/Cache/DriveFileManager/DriveFileManagerConstants.swift index 2fbe346f1..95f14f048 100644 --- a/kDriveCore/Data/Cache/DriveFileManager/DriveFileManagerConstants.swift +++ b/kDriveCore/Data/Cache/DriveFileManager/DriveFileManagerConstants.swift @@ -207,7 +207,7 @@ public class DriveFileManagerConstants { // Migration to add syncWifi if oldSchemaVersion < 22 { - migration.enumerateObjects(ofType: UploadFile.className()) { _, newObject in + migration.enumerateObjects(ofType: PhotoSyncSettings.className()) { _, newObject in guard let newObject else { return } diff --git a/kDriveCore/Data/Models/DriveError.swift b/kDriveCore/Data/Models/DriveError.swift index f85b04a66..5f18f157d 100644 --- a/kDriveCore/Data/Models/DriveError.swift +++ b/kDriveCore/Data/Models/DriveError.swift @@ -141,6 +141,10 @@ public struct DriveError: Error, Equatable { localizedString: KDriveResourcesStrings.Localizable.errorCache) public static let unknownError = DriveError(type: .localError, code: "unknownError") + public static let uploadOverDataRestrictedError = DriveError(type: .localError, + code: "uploadOverDataRestrictedError", + localizedString: KDriveResourcesStrings.Localizable.uploadOverDataRestrictedError) + // MARK: - Server public static let refreshToken = DriveError(type: .serverError, code: "refreshToken") @@ -261,7 +265,8 @@ public struct DriveError: Error, Equatable { uploadTokenIsNotValid, fileAlreadyExistsError, errorDeviceStorage, - limitExceededError] + limitExceededError, + uploadOverDataRestrictedError] private static let encoder = JSONEncoder() private static let decoder = JSONDecoder() diff --git a/kDriveCore/Data/UploadQueue/Operation/UploadOperation+Error.swift b/kDriveCore/Data/UploadQueue/Operation/UploadOperation+Error.swift index ef974c081..07e93f2a2 100644 --- a/kDriveCore/Data/UploadQueue/Operation/UploadOperation+Error.swift +++ b/kDriveCore/Data/UploadQueue/Operation/UploadOperation+Error.swift @@ -127,6 +127,8 @@ extension UploadOperation { // Silently stop if an UploadFile is no longer in base // _not_ overriding file.error self.cancel() + case .uploadOverDataRestrictedError: + file.error = DriveError.uploadOverDataRestrictedError } errorHandled = true diff --git a/kDriveCore/Data/UploadQueue/Operation/UploadOperation+PHAsset.swift b/kDriveCore/Data/UploadQueue/Operation/UploadOperation+PHAsset.swift index 5ae8f002c..c4dfaca69 100644 --- a/kDriveCore/Data/UploadQueue/Operation/UploadOperation+PHAsset.swift +++ b/kDriveCore/Data/UploadQueue/Operation/UploadOperation+PHAsset.swift @@ -17,6 +17,7 @@ */ import Foundation +import InfomaniakCore extension UploadOperation { func getPhAssetIfNeeded() async throws { @@ -56,4 +57,24 @@ extension UploadOperation { file.pathURL = url } } + + func checkForRestrictedUploadOverDataMode() throws { + let file = try readOnlyFile() + + guard file.type == .phAsset else { + // This UploadFile is not a PHAsset, return silently + return + } + + let status = ReachabilityListener.instance.currentStatus + let canUpload = !(status == .cellular && UserDefaults.shared.isWifiOnly) + + guard !canUpload else { + return + } + + uploadQueue.cancelRunningOperations() + uploadQueue.suspendAllOperations() + throw ErrorDomain.uploadOverDataRestrictedError + } } diff --git a/kDriveCore/Data/UploadQueue/Operation/UploadOperation.swift b/kDriveCore/Data/UploadQueue/Operation/UploadOperation.swift index 69f00cec7..6b6ec781f 100644 --- a/kDriveCore/Data/UploadQueue/Operation/UploadOperation.swift +++ b/kDriveCore/Data/UploadQueue/Operation/UploadOperation.swift @@ -58,6 +58,8 @@ public final class UploadOperation: AsynchronousOperation, UploadOperationable { case operationFinished /// Cannot decrease further retry count, already zero case retryCountIsZero + /// Cannot upload image because we are not in wifi + case uploadOverDataRestrictedError } // MARK: - Attributes @@ -127,6 +129,9 @@ public final class UploadOperation: AsynchronousOperation, UploadOperationable { // Clean existing error if any try self.cleanUploadFileError() + // Pause the upload depending on the status + try self.checkForRestrictedUploadOverDataMode() + // Fetch content from local library if needed try await self.getPhAssetIfNeeded() diff --git a/kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift b/kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift index e1ad324e8..8850db104 100644 --- a/kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift +++ b/kDriveCore/Data/UploadQueue/Queue/UploadQueue+Queue.swift @@ -88,7 +88,7 @@ extension UploadQueue: UploadQueueable { return lazyCollection.filter(uploadFileQuery) .sorted(byKeyPath: "taskCreationDate") } - let uploadingFileIds = uploadingFiles.map(\.id) + let uploadingFileIds = Array(uploadingFiles.map(\.id)) Log.uploadQueue("rebuildUploadQueueFromObjectsInRealm uploads to restart:\(uploadingFileIds.count)") let batches = uploadingFileIds.chunks(ofCount: 100) diff --git a/kDriveCore/Data/UploadQueue/Queue/UploadQueue.swift b/kDriveCore/Data/UploadQueue/Queue/UploadQueue.swift index 56fa6f94a..8054f82c9 100644 --- a/kDriveCore/Data/UploadQueue/Queue/UploadQueue.swift +++ b/kDriveCore/Data/UploadQueue/Queue/UploadQueue.swift @@ -102,7 +102,7 @@ public final class UploadQueue: ParallelismHeuristicDelegate { } let status = ReachabilityListener.instance.currentStatus - return status == .offline || (status != .wifi && UserDefaults.shared.isWifiOnly) + return status == .offline } /// Should suspend operation queue based on explicit `suspendAllOperations()` call @@ -139,6 +139,9 @@ public final class UploadQueue: ParallelismHeuristicDelegate { let isSuspended = (shouldSuspendQueue || forceSuspendQueue) operationQueue.isSuspended = isSuspended Log.uploadQueue("observeNetworkChange :\(isSuspended)") + if ReachabilityListener.instance.currentStatus == .wifi || !UserDefaults.shared.isWifiOnly { + self.resumeAllOperations() + } } observeMemoryWarnings() diff --git a/kDriveCore/Utils/DriveUserDefaults+Extension.swift b/kDriveCore/Utils/DriveUserDefaults+Extension.swift index 1a0682295..5c89598b7 100644 --- a/kDriveCore/Utils/DriveUserDefaults+Extension.swift +++ b/kDriveCore/Utils/DriveUserDefaults+Extension.swift @@ -49,7 +49,7 @@ extension UserDefaults.Keys { static let selectedHomeIndex = UserDefaults.Keys(rawValue: "selectedHomeIndex") static let fpStorageVersion = UserDefaults.Keys(rawValue: "fpStorageVersion") static let importPhotoFormat = UserDefaults.Keys(rawValue: "importPhotoFormat") - static let syncMode = UserDefaults.Keys(rawValue: "syncMode") + static let syncOfflineMode = UserDefaults.Keys(rawValue: "syncOfflineMod") } public extension UserDefaults { @@ -338,16 +338,16 @@ public extension UserDefaults { } } - var syncMode: SyncMode { + var syncOfflineMode: SyncMode { get { - if let rawValue = object(forKey: key(.syncMode)) as? String, + if let rawValue = object(forKey: key(.syncOfflineMode)) as? String, let mode = SyncMode(rawValue: rawValue) { return mode } return .onlyWifi } set { - set(newValue.rawValue, forKey: key(.syncMode)) + set(newValue.rawValue, forKey: key(.syncOfflineMode)) } } } diff --git a/kDriveCore/Utils/NotificationName+Extension.swift b/kDriveCore/Utils/NotificationName+Extension.swift index a4454d7e0..1711bbc9c 100644 --- a/kDriveCore/Utils/NotificationName+Extension.swift +++ b/kDriveCore/Utils/NotificationName+Extension.swift @@ -21,4 +21,5 @@ import Foundation public extension Notification.Name { static let locateUploadActionTapped = Notification.Name(rawValue: "kDriveLocateUploadActionTapped") static let reloadDrive = Notification.Name(rawValue: "kDriveReloadDrive") + static let reloadWifiView = Notification.Name(rawValue: "kDriveReloadWifiView") }