diff --git a/Nextcloud.xcodeproj/project.pbxproj b/Nextcloud.xcodeproj/project.pbxproj index cc39c7ec55..910bbdeb9a 100644 --- a/Nextcloud.xcodeproj/project.pbxproj +++ b/Nextcloud.xcodeproj/project.pbxproj @@ -843,6 +843,9 @@ F7D7A7712DCDD437003D2007 /* NCManageDatabase+AutoUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7D7A76B2DCDD437003D2007 /* NCManageDatabase+AutoUpload.swift */; }; F7D7A7722DCDD437003D2007 /* NCManageDatabase+AutoUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7D7A76B2DCDD437003D2007 /* NCManageDatabase+AutoUpload.swift */; }; F7D890752BD25C570050B8A6 /* NCCollectionViewCommon+DragDrop.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7D890742BD25C570050B8A6 /* NCCollectionViewCommon+DragDrop.swift */; }; + F7DF7B3F2F1A2EF900514020 /* MessageBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DF7B3E2F1A2EE400514020 /* MessageBannerView.swift */; }; + F7DF7B422F1A36C100514020 /* HelperBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DF7B412F1A36B600514020 /* HelperBanner.swift */; }; + F7DF7B432F1A373E00514020 /* HelperBanner.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7DF7B412F1A36B600514020 /* HelperBanner.swift */; }; F7E0710128B13BB00001B882 /* DashboardData.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E0710028B13BB00001B882 /* DashboardData.swift */; }; F7E2B64F2DDCC5C30075B4D0 /* NCMedia+TransferDelegate.swift in Sources */ = {isa = PBXBuildFile; fileRef = F7E2B64E2DDCC5C30075B4D0 /* NCMedia+TransferDelegate.swift */; }; F7E402292BA85D1D007E5609 /* PrivacyInfo.xcprivacy in Resources */ = {isa = PBXBuildFile; fileRef = F7E402282BA85D1D007E5609 /* PrivacyInfo.xcprivacy */; }; @@ -1760,6 +1763,8 @@ F7D7A76B2DCDD437003D2007 /* NCManageDatabase+AutoUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCManageDatabase+AutoUpload.swift"; sourceTree = ""; }; F7D890742BD25C570050B8A6 /* NCCollectionViewCommon+DragDrop.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCCollectionViewCommon+DragDrop.swift"; sourceTree = ""; }; F7DE9AB01F482FA5008DFE10 /* sv */ = {isa = PBXFileReference; lastKnownFileType = text.plist.strings; name = sv; path = sv.lproj/Localizable.strings; sourceTree = ""; }; + F7DF7B3E2F1A2EE400514020 /* MessageBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = MessageBannerView.swift; sourceTree = ""; }; + F7DF7B412F1A36B600514020 /* HelperBanner.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = HelperBanner.swift; sourceTree = ""; }; F7E0710028B13BB00001B882 /* DashboardData.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = DashboardData.swift; sourceTree = ""; }; F7E2B64E2DDCC5C30075B4D0 /* NCMedia+TransferDelegate.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "NCMedia+TransferDelegate.swift"; sourceTree = ""; }; F7E402282BA85D1D007E5609 /* PrivacyInfo.xcprivacy */ = {isa = PBXFileReference; lastKnownFileType = text.xml; path = PrivacyInfo.xcprivacy; sourceTree = ""; }; @@ -2239,7 +2244,9 @@ isa = PBXGroup; children = ( F70557BA2ED44F1800135623 /* ErrorBannerView.swift */, + F7DF7B412F1A36B600514020 /* HelperBanner.swift */, F714A1462ED84AF00050A43B /* HudBannerView.swift */, + F7DF7B3E2F1A2EE400514020 /* MessageBannerView.swift */, F70557BB2ED44F1800135623 /* UploadBannerView.swift */, ); path = "Lucid Banner"; @@ -4219,6 +4226,7 @@ F798F0E225880608000DAFFD /* UIColor+Extension.swift in Sources */, F7C9B9202B582F550064EA91 /* NCManageDatabase+SecurityGuard.swift in Sources */, F78E2D6829AF02DB0024D4F3 /* Database.swift in Sources */, + F7DF7B432F1A373E00514020 /* HelperBanner.swift in Sources */, AA8D31562D41052300FE2775 /* NCManageDatabase+DownloadLimit.swift in Sources */, F711A4DF2AF92CAE00095DD8 /* NCUtility+Date.swift in Sources */, F78295311F962EFA00A572F5 /* NCEndToEndEncryption.m in Sources */, @@ -4431,6 +4439,7 @@ F733598125C1C188002ABA72 /* NCAskAuthorization.swift in Sources */, 370D26AF248A3D7A00121797 /* NCCellProtocol.swift in Sources */, F32FADA92D1176E3007035E2 /* UIButton+Extension.swift in Sources */, + F7DF7B3F2F1A2EF900514020 /* MessageBannerView.swift in Sources */, F768822C2C0DD1E7001CF441 /* NCPreferences.swift in Sources */, F7CAFE1D2F17A35F00DB35A5 /* NCNetworking+Actor.swift in Sources */, F71CD6CA2930D7B1006C95C1 /* NCApplicationHandle.swift in Sources */, @@ -4551,6 +4560,7 @@ F7D4BF4D2CA2E8D800A5E746 /* TOPasscodeViewController.m in Sources */, F75C0C4823D1FAE300163CC8 /* NCRichWorkspaceCommon.swift in Sources */, AAE330042D2ED20200B04903 /* NCShareNavigationTitleSetting.swift in Sources */, + F7DF7B422F1A36C100514020 /* HelperBanner.swift in Sources */, F78ACD4A21903F850088454D /* NCTrashListCell.swift in Sources */, F7386E482DA90E0F009A00F6 /* NCAppVersionManager.swift in Sources */, F76882352C0DD1E7001CF441 /* NCWebBrowserView.swift in Sources */, diff --git a/Share/NCShareExtension.swift b/Share/NCShareExtension.swift index c9bc162dca..9e3b2e094b 100644 --- a/Share/NCShareExtension.swift +++ b/Share/NCShareExtension.swift @@ -48,6 +48,7 @@ class NCShareExtension: UIViewController { let global = NCGlobal.shared var maintenanceMode: Bool = false var token: Int? + var sceneIdentifier: String = UUID().uuidString // MARK: - View Life Cycle @@ -110,6 +111,8 @@ class NCShareExtension: UIViewController { if let account = NCShareExtensionData.shared.getTblAccoun()?.account { accountRequestChangeAccount(account: account, controller: nil) } + + NCNetworking.shared.setupScene(sceneIdentifier: sceneIdentifier, controller: self) } override func viewWillAppear(_ animated: Bool) { @@ -330,7 +333,7 @@ extension NCShareExtension { ocId: ocId, serverUrl: serverUrl, session: session, - sceneIdentifier: nil) + sceneIdentifier: self.sceneIdentifier) metadataForUpload.session = NCNetworking.shared.sessionUpload metadataForUpload.sessionSelector = NCGlobal.shared.selectorUploadFileShareExtension diff --git a/iOSClient/Activity/NCActivity.swift b/iOSClient/Activity/NCActivity.swift index adf92a6dd3..d2153bd1c4 100644 --- a/iOSClient/Activity/NCActivity.swift +++ b/iOSClient/Activity/NCActivity.swift @@ -83,7 +83,7 @@ class NCActivity: UIViewController, NCSharePagingContent { self.commentView?.newCommentField.text?.removeAll() self.loadComments() } else { - Task {@MainActor in + Task { await showErrorBanner(controller: self.tabBarController, errorDescription: error.errorDescription) } } @@ -438,7 +438,7 @@ extension NCActivity { if error == .success, let comments = comments { self.database.addComments(comments, account: metadata.account, objectId: metadata.fileId) } else if error.errorCode != NCGlobal.shared.errorResourceNotFound { - Task {@MainActor in + Task { await showErrorBanner(controller: self.tabBarController, errorDescription: error.errorDescription) } } @@ -610,7 +610,7 @@ extension NCActivity: NCShareCommentsCellDelegate { if error == .success { self.loadComments() } else { - Task {@MainActor in + Task { await showErrorBanner(controller: self.tabBarController, errorDescription: error.errorDescription) } } diff --git a/iOSClient/Activity/NCActivityTableViewCell.swift b/iOSClient/Activity/NCActivityTableViewCell.swift index fae6553e84..db17b61320 100644 --- a/iOSClient/Activity/NCActivityTableViewCell.swift +++ b/iOSClient/Activity/NCActivityTableViewCell.swift @@ -85,7 +85,7 @@ extension NCActivityTableViewCell: UICollectionViewDelegate { (responder as? UIViewController)!.navigationController?.pushViewController(viewController, animated: true) } else { let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_trash_file_not_found_") - Task {@MainActor in + Task { await showErrorBanner(controller: viewController.controller, errorDescription: error.errorDescription) } } diff --git a/iOSClient/Files/NCFiles.swift b/iOSClient/Files/NCFiles.swift index fdfafa3923..842e13d3ea 100644 --- a/iOSClient/Files/NCFiles.swift +++ b/iOSClient/Files/NCFiles.swift @@ -325,7 +325,7 @@ class NCFiles: NCCollectionViewCommon { } } else { // show error - Task {@MainActor in + Task { await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription) } } @@ -345,7 +345,7 @@ class NCFiles: NCCollectionViewCommon { let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, updateVersionV1V2: true, account: account) if error != .success { - Task {@MainActor in + Task { await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription) } } @@ -354,7 +354,7 @@ class NCFiles: NCCollectionViewCommon { } else { // Client Diagnostic await self.database.addDiagnosticAsync(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors) - Task {@MainActor in + Task { await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription) } } diff --git a/iOSClient/GUI/Lucid Banner/ErrorBannerView.swift b/iOSClient/GUI/Lucid Banner/ErrorBannerView.swift index a22fc906d6..1a3e0a56a4 100644 --- a/iOSClient/GUI/Lucid Banner/ErrorBannerView.swift +++ b/iOSClient/GUI/Lucid Banner/ErrorBannerView.swift @@ -8,7 +8,10 @@ import LucidBanner @MainActor func showErrorBanner(controller: UITabBarController?, errorDescription: String, footnote: String? = nil, sleepBefore: Double = 1) async { let scene = SceneManager.shared.getWindow(controller: controller)?.windowScene - await showErrorBanner(scene: scene, errorDescription: errorDescription, footnote: footnote, sleepBefore: sleepBefore) + await showErrorBanner(scene: scene, + errorDescription: NSLocalizedString(errorDescription, comment: ""), + footnote: NSLocalizedString(footnote ?? "", comment: ""), + sleepBefore: sleepBefore) } @MainActor @@ -21,8 +24,8 @@ func showErrorBanner(scene: UIWindowScene?, errorDescription: String, footnote: LucidBanner.shared.show( scene: scene, - subtitle: errorDescription, - footnote: footnote, + subtitle: NSLocalizedString(errorDescription, comment: ""), + footnote: NSLocalizedString(footnote ?? "", comment: ""), vPosition: .top, autoDismissAfter: NCGlobal.shared.dismissAfterSecond, swipeToDismiss: true, diff --git a/iOSClient/GUI/Lucid Banner/HelperBanner.swift b/iOSClient/GUI/Lucid Banner/HelperBanner.swift new file mode 100644 index 0000000000..ce0858ee1a --- /dev/null +++ b/iOSClient/GUI/Lucid Banner/HelperBanner.swift @@ -0,0 +1,55 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2026 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import SwiftUI +import LucidBanner + +public extension View { + @ViewBuilder + func applyBannerAnimation(_ style: LucidBanner.LucidBannerAnimationStyle) -> some View { + switch style { + + // ---- iOS 18+ effects ---- + case .rotate, .pulse, .pulsebyLayer, .breathe, .bounce, .wiggle, .scale, .scaleUpbyLayer, .variableColor: + if #available(iOS 18, *) { + switch style { + case .rotate: + self.symbolEffect(.rotate, options: .repeat(.continuous)) + case .pulse: + self.symbolEffect(.pulse, options: .repeat(.continuous)) + case .pulsebyLayer: + self.symbolEffect(.pulse.byLayer, options: .repeat(.continuous)) + case .breathe: + self.symbolEffect(.breathe, options: .repeat(.continuous)) + case .bounce: + self.symbolEffect(.bounce, options: .repeat(.continuous)) + case .wiggle: + self.symbolEffect(.wiggle, options: .repeat(.continuous)) + case .scale: + self.symbolEffect(.scale, options: .repeat(.continuous)) + case .scaleUpbyLayer: + self.symbolEffect(.scale.up.byLayer, options: .repeat(.continuous)) + case .variableColor: + self.symbolEffect(.variableColor, options: .repeat(.continuous)) + default: + self + } + } else { + self + } + + // ---- iOS 26+ effect: drawOn ---- + case .drawOn: + if #available(iOS 26, *) { + self + } else { + self + } + + // ---- no animation ---- + case .none: + self + } + } +} diff --git a/iOSClient/GUI/Lucid Banner/MessageBannerView.swift b/iOSClient/GUI/Lucid Banner/MessageBannerView.swift new file mode 100644 index 0000000000..816d408513 --- /dev/null +++ b/iOSClient/GUI/Lucid Banner/MessageBannerView.swift @@ -0,0 +1,149 @@ +// SPDX-FileCopyrightText: Nextcloud GmbH +// SPDX-FileCopyrightText: 2026 Marino Faggiana +// SPDX-License-Identifier: GPL-3.0-or-later + +import SwiftUI +import LucidBanner + +@MainActor +func showBanner(scene: UIWindowScene?, + title: String?, + subtitle: String? = nil, + footnote: String? = nil, + textColor: UIColor, + image: String?, + imageAnimation: LucidBanner.LucidBannerAnimationStyle, + imageColor: UIColor, + vPosition: LucidBanner.VerticalPosition = .top, + backgroundColor: UIColor) async { + var scene = scene + if scene == nil { + scene = UIApplication.shared.mainAppWindow?.windowScene + } + + LucidBanner.shared.show( + scene: scene, + title: NSLocalizedString(title ?? "", comment: ""), + subtitle: NSLocalizedString(subtitle ?? "", comment: ""), + footnote: NSLocalizedString(footnote ?? "", comment: ""), + textColor: Color(uiColor: textColor), + systemImage: image, + imageAnimation: imageAnimation, + imageColor: Color(uiColor: imageColor), + backgroundColor: Color(uiColor: backgroundColor), + vPosition: vPosition, + autoDismissAfter: NCGlobal.shared.dismissAfterSecond, + swipeToDismiss: true, + onTap: { _, _ in + LucidBanner.shared.dismiss() + } + ) { state in + MessageBannerView(state: state) + } +} + +// MARK: - SwiftUI + +struct MessageBannerView: View { + @ObservedObject var state: LucidBannerState + + var body: some View { + let showTitle = !(state.title?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true) + let showSubtitle = !(state.subtitle?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true) + let showFootnote = !(state.footnote?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true) + + containerView(state: state) { + VStack(spacing: 15) { + HStack(alignment: .top, spacing: 10) { + Image(systemName: state.systemImage ?? "info.circle") + .applyBannerAnimation(state.imageAnimation) + .font(.system(size: 30, weight: .bold)) + .foregroundStyle(state.imageColor) + + VStack(alignment: .leading, spacing: 7) { + if showTitle, let title = state.title { + Text(title) + .font(.subheadline.weight(.bold)) + .multilineTextAlignment(.leading) + .truncationMode(.tail) + .foregroundStyle(state.textColor) + } + + if showSubtitle, let subtitle = state.subtitle { + Text(subtitle) + .font(.subheadline) + .multilineTextAlignment(.leading) + .truncationMode(.tail) + .foregroundStyle(state.textColor) + } + if showFootnote, let footnote = state.footnote { + Text(footnote) + .font(.caption) + .multilineTextAlignment(.leading) + .truncationMode(.tail) + .foregroundStyle(state.textColor) + } + } + } + } + .padding(.horizontal, 12) + .padding(.vertical, 12) + .frame(maxWidth: .infinity, alignment: .leading) + } + } + + // MARK: - Container + + @ViewBuilder + func containerView(state: LucidBannerState, @ViewBuilder _ content: () -> Content) -> some View { + let cornerRadius: CGFloat = 22 + let contentBase = content() + .contentShape(Rectangle()) + .frame(maxWidth: 500) + + if #available(iOS 26, *) { + contentBase + .background( + RoundedRectangle(cornerRadius: cornerRadius) + .fill(state.backgroundColor) + ) + .glassEffect(.clear, in: RoundedRectangle(cornerRadius: 22)) + .frame(maxWidth: .infinity, alignment: .center) + } else { + contentBase + .background(.ultraThinMaterial, in: RoundedRectangle(cornerRadius: cornerRadius)) + .overlay( + RoundedRectangle(cornerRadius: cornerRadius, style: .continuous) + .stroke(.white.opacity(0.9), lineWidth: 0.6) + ) + .shadow(color: .black.opacity(0.5), radius: 10, x: 0, y: 4) + .frame(maxWidth: .infinity, alignment: .center) + } + } +} + +// MARK: - Preview + +#Preview { + ZStack { + Text( + Array(0...500) + .map(String.init) + .joined(separator: " ") + ) + .font(.system(size: 16, design: .monospaced)) + .foregroundStyle(.primary) + .padding() + + MessageBannerView( + state: LucidBannerState( + title: "Title", + subtitle: "Subtitle", + footnote: "footnote", + systemImage: "wifi.circle", + imageAnimation: .variableColor, + ) + ) + .padding() + } +} diff --git a/iOSClient/GUI/Lucid Banner/UploadBannerView.swift b/iOSClient/GUI/Lucid Banner/UploadBannerView.swift index 7d85e1a0b6..1ad8c923fc 100644 --- a/iOSClient/GUI/Lucid Banner/UploadBannerView.swift +++ b/iOSClient/GUI/Lucid Banner/UploadBannerView.swift @@ -300,53 +300,6 @@ struct UploadBannerView: View { } } -public extension View { - @ViewBuilder - func applyBannerAnimation(_ style: LucidBanner.LucidBannerAnimationStyle) -> some View { - switch style { - - // ---- iOS 18+ effects ---- - case .rotate, .pulse, .pulsebyLayer, .breathe, .bounce, .wiggle, .scale, .scaleUpbyLayer: - if #available(iOS 18, *) { - switch style { - case .rotate: - self.symbolEffect(.rotate, options: .repeat(.continuous)) - case .pulse: - self.symbolEffect(.pulse, options: .repeat(.continuous)) - case .pulsebyLayer: - self.symbolEffect(.pulse.byLayer, options: .repeat(.continuous)) - case .breathe: - self.symbolEffect(.breathe, options: .repeat(.continuous)) - case .bounce: - self.symbolEffect(.bounce, options: .repeat(.continuous)) - case .wiggle: - self.symbolEffect(.wiggle, options: .repeat(.continuous)) - case .scale: - self.symbolEffect(.scale, options: .repeat(.continuous)) - case .scaleUpbyLayer: - self.symbolEffect(.scale.up.byLayer, options: .repeat(.continuous)) - default: - self - } - } else { - self - } - - // ---- iOS 26+ effect: drawOn ---- - case .drawOn: - if #available(iOS 26, *) { - self - } else { - self - } - - // ---- no animation ---- - case .none: - self - } - } -} - // MARK: - Preview #Preview { diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift index 6c2499114f..565bbc346d 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon+CollectionViewDelegate.swift @@ -113,8 +113,10 @@ extension NCCollectionViewCommon: UICollectionViewDelegate { self.navigationController?.pushViewController(vc, animated: true) } } else { - let error = NKError(errorCode: global.errorOffline, errorDescription: "_go_online_") - NCContentPresenter().showInfo(error: error) + Task { + await showErrorBanner(controller: controller, errorDescription: "_go_online_") + + } } } } diff --git a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift index db710613e8..273cbaddd0 100644 --- a/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift +++ b/iOSClient/Main/Collection Common/NCCollectionViewCommon.swift @@ -623,7 +623,7 @@ class NCCollectionViewCommon: UIViewController, NCAccountSettingsModelDelegate, } } } else { - Task {@MainActor in + Task { await showErrorBanner(scene: scene, errorDescription: resultsUpload.error.errorDescription) } } @@ -744,7 +744,7 @@ class NCCollectionViewCommon: UIViewController, NCAccountSettingsModelDelegate, } } completion: { _, searchResult, metadatas, error in if error != .success { - Task {@MainActor in + Task { await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription) } } diff --git a/iOSClient/Main/Create/NCCreate.swift b/iOSClient/Main/Create/NCCreate.swift index 26b6de23a6..24bf659578 100644 --- a/iOSClient/Main/Create/NCCreate.swift +++ b/iOSClient/Main/Create/NCCreate.swift @@ -40,7 +40,7 @@ class NCCreate: NSObject { } } guard results.error == .success, let url = results.url else { - Task {@MainActor in + Task { await showErrorBanner(controller: controller, errorDescription: results.error.errorDescription) } return @@ -67,7 +67,7 @@ class NCCreate: NSObject { } } guard results.error == .success, let url = results.url else { - Task {@MainActor in + Task { await showErrorBanner(controller: controller, errorDescription: results.error.errorDescription) } return diff --git a/iOSClient/Main/NCDragDrop.swift b/iOSClient/Main/NCDragDrop.swift index 9fe6217710..970188a05f 100644 --- a/iOSClient/Main/NCDragDrop.swift +++ b/iOSClient/Main/NCDragDrop.swift @@ -143,7 +143,7 @@ class NCDragDrop: NSObject { database.addMetadata(metadataForUpload) } catch { - Task {@MainActor in + Task { let error = NKError(error: error) await showErrorBanner(controller: controller, errorDescription: error.errorDescription) } diff --git a/iOSClient/Main/NCMainTabBarController.swift b/iOSClient/Main/NCMainTabBarController.swift index bc8699fe7a..778a257e61 100644 --- a/iOSClient/Main/NCMainTabBarController.swift +++ b/iOSClient/Main/NCMainTabBarController.swift @@ -43,8 +43,7 @@ class NCMainTabBarController: UITabBarController { super.viewDidLoad() delegate = self - NCNetworking.shared.controller = self - NCImageCache.shared.controller = self + NCNetworking.shared.setupScene(sceneIdentifier: sceneIdentifier, controller: self) tabBar.tintColor = NCBrandColor.shared.getElement(account: account) diff --git a/iOSClient/Main/NCPickerViewController.swift b/iOSClient/Main/NCPickerViewController.swift index a6e20c3411..c411619fa4 100644 --- a/iOSClient/Main/NCPickerViewController.swift +++ b/iOSClient/Main/NCPickerViewController.swift @@ -60,21 +60,21 @@ class NCPhotosPickerViewController: NSObject { pickerVC?.didExceedMaximumNumberOfSelection = { _ in let error = NKError(errorCode: self.global.errorInternalError, errorDescription: "_limited_dimension_") - Task {@MainActor in + Task { await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription) } } pickerVC?.handleNoAlbumPermissions = { _ in let error = NKError(errorCode: self.global.errorInternalError, errorDescription: "_denied_album_") - Task {@MainActor in + Task { await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription) } } pickerVC?.handleNoCameraPermissions = { _ in let error = NKError(errorCode: self.global.errorInternalError, errorDescription: "_denied_camera_") - Task {@MainActor in + Task { await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription) } } diff --git a/iOSClient/Networking/NCNetworking+NextcloudKitDelegate.swift b/iOSClient/Networking/NCNetworking+NextcloudKitDelegate.swift index d5a6e6fb39..54044417c0 100644 --- a/iOSClient/Networking/NCNetworking+NextcloudKitDelegate.swift +++ b/iOSClient/Networking/NCNetworking+NextcloudKitDelegate.swift @@ -9,14 +9,23 @@ import Alamofire extension NCNetworking { -#if !EXTENSION_FILE_PROVIDER_EXTENSION +#if !EXTENSION func networkReachabilityObserver(_ typeReachability: NKTypeReachability) { if typeReachability == NKTypeReachability.reachableCellular || typeReachability == NKTypeReachability.reachableEthernetOrWiFi { lastReachability = true } else { if lastReachability { - let error = NKError(errorCode: global.errorNetworkNotAvailable, errorDescription: "") - NCContentPresenter().messageNotification("_network_not_available_", error: error, delay: global.dismissAfterSecond, type: NCContentPresenter.messageType.info) + let scene = SceneManager.shared.getWindow(sceneIdentifier: sceneIdentifier)?.windowScene + Task { + await showBanner(scene: scene, + title: NSLocalizedString("_info_", comment: ""), + subtitle: NSLocalizedString("_network_not_available_", comment: ""), + textColor: .white, + image: "wifi.exclamationmark.circle", + imageAnimation: .bounce, + imageColor: .white, + backgroundColor: UIColor.lightGray.withAlphaComponent(0.75)) + } } lastReachability = false } diff --git a/iOSClient/Networking/NCNetworking+TransferDelegate.swift b/iOSClient/Networking/NCNetworking+TransferDelegate.swift index 87e113ddff..85a15c796a 100644 --- a/iOSClient/Networking/NCNetworking+TransferDelegate.swift +++ b/iOSClient/Networking/NCNetworking+TransferDelegate.swift @@ -137,7 +137,7 @@ extension NCNetworking: NCTransferDelegate { assetRequest.addResource(with: .photo, data: data, options: nil) }) { success, _ in if !success { - Task {@MainActor in + Task { await showErrorBanner(scene: scene, errorDescription: errorSave.errorDescription) } } @@ -147,19 +147,19 @@ extension NCNetworking: NCTransferDelegate { PHAssetChangeRequest.creationRequestForAssetFromVideo(atFileURL: URL(fileURLWithPath: fileNamePath)) }) { success, _ in if !success { - Task {@MainActor in + Task { await showErrorBanner(scene: scene, errorDescription: errorSave.errorDescription) } } } } else { - Task {@MainActor in + Task { await showErrorBanner(scene: scene, errorDescription: errorSave.errorDescription) } return } } catch { - Task {@MainActor in + Task { await showErrorBanner(scene: scene, errorDescription: errorSave.errorDescription) } } @@ -229,7 +229,7 @@ extension NCNetworking: NCTransferDelegate { } } guard resultsFile.error == .success, let file = resultsFile.file else { - Task {@MainActor in + Task { await showErrorBanner(controller: viewController.tabBarController, errorDescription: resultsFile.error.errorDescription) } return diff --git a/iOSClient/Networking/NCNetworking.swift b/iOSClient/Networking/NCNetworking.swift index 226efc5e2c..a162fcafc1 100644 --- a/iOSClient/Networking/NCNetworking.swift +++ b/iOSClient/Networking/NCNetworking.swift @@ -58,14 +58,15 @@ class NCNetworking: @unchecked Sendable, NextcloudKitDelegate { let global = NCGlobal.shared let backgroundSession = NKBackground(nkCommonInstance: NextcloudKit.shared.nkCommonInstance) - let sceneIdentifier: String = "" var requestsUnifiedSearch: [DataRequest] = [] var lastReachability: Bool = true var networkReachability: NKTypeReachability? weak var certificateDelegate: ClientCertificateDelegate? var p12Data: Data? var p12Password: String? - var controller: UIViewController? + + internal var sceneIdentifier: String = "" + internal var controller: UIViewController? var isOffline: Bool { return networkReachability == NKTypeReachability.notReachable || networkReachability == NKTypeReachability.unknown @@ -98,6 +99,11 @@ class NCNetworking: @unchecked Sendable, NextcloudKitDelegate { init() { } + func setupScene(sceneIdentifier: String, controller: UIViewController?) { + self.sceneIdentifier = sceneIdentifier + self.controller = controller + } + func authenticationChallenge(_ session: URLSession, didReceive challenge: URLAuthenticationChallenge, completionHandler: @escaping (URLSession.AuthChallengeDisposition, URLCredential?) -> Void) {