Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
67 changes: 31 additions & 36 deletions Nextcloud.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

44 changes: 27 additions & 17 deletions Share/NCShareExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -49,6 +49,7 @@ class NCShareExtension: UIViewController {
let global = NCGlobal.shared
var maintenanceMode: Bool = false
var token: Int?
var banner: LucidBanner?
var sceneIdentifier: String = UUID().uuidString

// MARK: - View Life Cycle
Expand Down Expand Up @@ -114,6 +115,10 @@ class NCShareExtension: UIViewController {
}

NCNetworking.shared.setupScene(sceneIdentifier: sceneIdentifier, controller: self)

if let windowScene = view.window?.windowScene {
banner = LucidBannerRegistry.shared.banner(for: windowScene)
}
}

override func viewWillAppear(_ animated: Bool) {
Expand Down Expand Up @@ -383,10 +388,10 @@ extension NCShareExtension {
vPosition: .center,
horizontalLayout: horizontalLayout,
blocksTouches: true)
token = showUploadBanner(scene: window.windowScene,
payload: payload,
allowMinimizeOnTap: false,
onButtonTap: {
(token, banner) = showUploadBanner(windowScene: window.windowScene,
payload: payload,
allowMinimizeOnTap: false,
onButtonTap: {
self.cancel()
})

Expand All @@ -397,7 +402,7 @@ extension NCShareExtension {
systemImage: "arrowshape.up.circle",
imageAnimation: .breathe,
progress: 0)
LucidBanner.shared.update(payload: payloadUpdate)
banner?.update(payload: payloadUpdate)

error = await self.upload(metadata: metadata)
if error != .success {
Expand All @@ -406,12 +411,12 @@ extension NCShareExtension {
}

if error == .success {
LucidBanner.shared.update(payload: LucidBannerPayload.Update(stage: .success, horizontalLayout: .centered(width: 100)), for: self.token)
banner?.update(payload: LucidBannerPayload.Update(stage: .success, horizontalLayout: .centered(width: 100)), for: self.token)
} else {
LucidBanner.shared.update(payload: LucidBannerPayload.Update(subtitle: error?.errorDescription, stage: .error), for: self.token)
banner?.update(payload: LucidBannerPayload.Update(subtitle: error?.errorDescription, stage: .error), for: self.token)
}

LucidBanner.shared.dismiss(after: 2) {
banner?.dismiss(after: 2) {
self.cancel()
}
}
Expand Down Expand Up @@ -444,35 +449,40 @@ extension NCShareExtension {
self.counterUploaded += 1

if metadata.isDirectoryE2EE {
error = await NCNetworkingE2EEUpload().upload(metadata: metadata, session: session, controller: self, stageBanner: nil, tokenBanner: self.token)
error = await NCNetworkingE2EEUpload().upload(metadata: metadata,
session: session,
controller: self,
banner: banner,
stageBanner: nil,
tokenBanner: self.token)
} else if metadata.chunk > 0 {
LucidBanner.shared.update(payload: LucidBannerPayload.Update(systemImage: "gearshape.arrow.triangle.2.circlepath",
imageAnimation: .rotate),
banner?.update(payload: LucidBannerPayload.Update(systemImage: "gearshape.arrow.triangle.2.circlepath",
imageAnimation: .rotate),
for: self.token)
let task = Task { () -> (account: String, file: NKFile?, error: NKError) in
let results = await NCNetworking.shared.uploadChunkFile(metadata: metadata) { total, counter in
Task {@MainActor in
LucidBanner.shared.update(payload: LucidBannerPayload.Update(progress: Double(counter) / Double(total)), for: self.token)
self.banner?.update(payload: LucidBannerPayload.Update(progress: Double(counter) / Double(total)), for: self.token)
}
} uploadStart: { _ in
Task {@MainActor in
let payload = LucidBannerPayload.Update(
systemImage: "arrowshape.up.circle",
imageAnimation: .breathe
)
LucidBanner.shared.update(payload: payload, for: self.token)
self.banner?.update(payload: payload, for: self.token)
}
} uploadProgressHandler: { _, _, progress in
Task {@MainActor in
LucidBanner.shared.update(payload: LucidBannerPayload.Update(progress: progress), for: self.token)
self.banner?.update(payload: LucidBannerPayload.Update(progress: progress), for: self.token)
}
} assembling: {
Task {@MainActor in
let payload = LucidBannerPayload.Update(
systemImage: "gearshape.arrow.triangle.2.circlepath",
imageAnimation: .rotate
)
LucidBanner.shared.update(payload: payload, for: self.token)
self.banner?.update(payload: payload, for: self.token)
}
}

Expand All @@ -494,8 +504,8 @@ extension NCShareExtension {
dateModificationFile: metadata.date as Date) { _ in
} progressHandler: { _, _, fractionCompleted in
Task {@MainActor in
LucidBanner.shared.update(payload: LucidBannerPayload.Update(progress: fractionCompleted),
for: self.token)
self.banner?.update(payload: LucidBannerPayload.Update(progress: fractionCompleted),
for: self.token)
}
}
error = results.error
Expand Down
4 changes: 2 additions & 2 deletions iOSClient/Account/NCAccount.swift
Original file line number Diff line number Diff line change
Expand Up @@ -193,8 +193,8 @@ class NCAccount: NSObject {
guard let tblAccount = await NCManageDatabase.shared.getTableAccountAsync(predicate: NSPredicate(format: "account == %@", account)) else {
return
}

await showErrorBanner(controller: controller, text: String(format: NSLocalizedString("_account_unauthorized_", comment: ""), account), errorCode: NCGlobal.shared.errorUnauthorized401)
let windowScene = SceneManager.shared.getWindowScene(controller: controller)
await showErrorBanner(windowScene: windowScene, text: String(format: NSLocalizedString("_account_unauthorized_", comment: ""), account), errorCode: NCGlobal.shared.errorUnauthorized401)

let resultsWipe = await NextcloudKit.shared.getRemoteWipeStatusAsync(serverUrl: tblAccount.urlBase, token: token, account: account) { task in
Task {
Expand Down
13 changes: 11 additions & 2 deletions iOSClient/Activity/NCActivity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -44,6 +44,11 @@ class NCActivity: UIViewController, NCSharePagingContent {
}
}

@MainActor
internal var windowScene: UIWindowScene? {
SceneManager.shared.getWindowScene(controller: self.tabBarController as? NCMainTabBarController)
}

// MARK: - View Life Cycle

override func viewDidLoad() {
Expand Down Expand Up @@ -83,7 +88,9 @@ class NCActivity: UIViewController, NCSharePagingContent {
self.loadComments()
} else {
Task {
await showErrorBanner(controller: self.tabBarController, text: error.errorDescription, errorCode: error.errorCode)
await showErrorBanner(windowScene: self.windowScene,
text: error.errorDescription,
errorCode: error.errorCode)
}
}
}
Expand Down Expand Up @@ -424,7 +431,9 @@ extension NCActivity {
self.database.addComments(comments, account: metadata.account, objectId: metadata.fileId)
} else if error.errorCode != NCGlobal.shared.errorResourceNotFound {
Task {
await showErrorBanner(controller: self.tabBarController, text: error.errorDescription, errorCode: error.errorCode)
await showErrorBanner(windowScene: self.windowScene,
text: error.errorDescription,
errorCode: error.errorCode)
}
}

Expand Down
4 changes: 3 additions & 1 deletion iOSClient/Activity/NCActivityTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -82,7 +82,9 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
(responder as? UIViewController)!.navigationController?.pushViewController(viewController, animated: true)
} else {
Task {
await showErrorBanner(controller: viewController.controller, text: "_trash_file_not_found_", errorCode: 0)
await showErrorBanner(windowScene: viewController.windowScene,
text: "_trash_file_not_found_",
errorCode: NCGlobal.shared.errorInternalError)
}
}
}
Expand Down
13 changes: 9 additions & 4 deletions iOSClient/Assistant/Chat/NCAssistantChatModel.swift
Original file line number Diff line number Diff line change
Expand Up @@ -4,7 +4,9 @@

import NextcloudKit

@Observable class NCAssistantChatModel {
@MainActor
@Observable
class NCAssistantChatModel {
var messages: [AssistantChatMessage] = []
var isSending: Bool = false
var isThinking: Bool = false
Expand All @@ -22,6 +24,9 @@ import NextcloudKit

@ObservationIgnored var controller: NCMainTabBarController?
@ObservationIgnored private var chatMessageTaskId: Int?
@ObservationIgnored var windowScene: UIWindowScene? {
SceneManager.shared.getWindowScene(controller: controller)
}

init(controller: NCMainTabBarController?, messages: [AssistantChatMessage] = []) {
self.controller = controller
Expand Down Expand Up @@ -96,7 +101,7 @@ import NextcloudKit
if result.error == .success {
messages = result.chatMessages ?? []
} else {
await showErrorBanner(controller: controller, title: "_error_", text: "_assistant_error_load_messages_", errorCode: result.error.errorCode)
await showErrorBanner(windowScene: windowScene, title: "_error_", text: "_assistant_error_load_messages_", errorCode: result.error.errorCode)
}
}

Expand All @@ -107,7 +112,7 @@ import NextcloudKit

if result.error != .success {
stopPolling()
await showErrorBanner(controller: controller, title: "_error_", text: "_assistant_error_generate_response_", errorCode: result.error.errorCode)
await showErrorBanner(windowScene: windowScene, title: "_error_", text: "_assistant_error_generate_response_", errorCode: result.error.errorCode)
return
}

Expand All @@ -134,7 +139,7 @@ import NextcloudKit
await generateChatSession()
startPollingForResponse()
} else {
await showErrorBanner(controller: controller, title: "_error_", text: "_assistant_error_send_message_", errorCode: 20)
await showErrorBanner(windowScene: windowScene, title: "_error_", text: "_assistant_error_send_message_", errorCode: NCGlobal.shared.errorInternalError)
}

isSending = false
Expand Down
8 changes: 5 additions & 3 deletions iOSClient/DeepLink/NCDeepLinkHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -140,9 +140,11 @@ class NCDeepLinkHandler {
controller.selectedIndex = ControllerConstants.moreIndex
guard let navigationController = controller.viewControllers?[controller.selectedIndex] as? UINavigationController else { return }

let settingsView = NCSettingsView(model: NCSettingsModel(controller: controller))
let settingsController = UIHostingController(rootView: settingsView)
navigationController.pushViewController(settingsController, animated: true)
Task { @MainActor in
let settingsView = NCSettingsView(model: NCSettingsModel(controller: controller))
let settingsController = UIHostingController(rootView: settingsView)
navigationController.pushViewController(settingsController, animated: true)
}
}

private func navigateToAutoUpload(controller: NCMainTabBarController) {
Expand Down
27 changes: 4 additions & 23 deletions iOSClient/Extensions/UIViewController+Extension.swift
Original file line number Diff line number Diff line change
@@ -1,30 +1,11 @@
//
// UIViewController+Extension.swift
// Nextcloud
//
// Created by Marino Faggiana on 02/08/2022.
// Copyright © 2022 Marino Faggiana. All rights reserved.
//
// Author Marino Faggiana <marino.faggiana@nextcloud.com>
//
// 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 <http://www.gnu.org/licenses/>.
//
// SPDX-FileCopyrightText: Nextcloud GmbH
// SPDX-FileCopyrightText: 2022-2026 Marino Faggiana
// SPDX-License-Identifier: GPL-3.0-or-later

import Foundation
import UIKit
import MessageUI
import NextcloudKit
import LucidBanner

extension UIViewController {
// https://stackoverflow.com/questions/6131205/how-to-find-topmost-view-controller-on-ios
Expand Down
13 changes: 6 additions & 7 deletions iOSClient/Files/NCFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -314,16 +314,15 @@ class NCFiles: NCCollectionViewCommon {
guard results.error == .success,
let e2eMetadata = results.e2eMetadata,
let version = results.version else {

// No metadata fount, re-send it
if results.error.errorCode == NCGlobal.shared.errorResourceNotFound {
await showInfoBanner(controller: self.controller, text: "Metadata not found")
await showInfoBanner(windowScene: windowScene, text: "Metadata not found")
let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, account: account)
if error != .success {
await showErrorBanner(controller: self.controller, text: error.errorDescription, errorCode: error.errorCode)
await showErrorBanner(windowScene: windowScene, text: error.errorDescription, errorCode: error.errorCode)
}
} else {
await showErrorBanner(controller: self.controller, text: error.errorDescription, errorCode: error.errorCode)
await showErrorBanner(windowScene: windowScene, text: error.errorDescription, errorCode: error.errorCode)
}

return(metadatas, error, reloadRequired)
Expand All @@ -335,20 +334,20 @@ class NCFiles: NCCollectionViewCommon {
if errorDecodeMetadata == .success {
let capabilities = await NKCapabilities.shared.getCapabilities(for: self.session.account)
if version == "v1", NCGlobal.shared.isE2eeVersion2(capabilities.e2EEApiVersion) {
await showInfoBanner(controller: self.controller, text: "Conversion metadata v1 to v2 required, please wait...")
await showInfoBanner(windowScene: windowScene, text: "Conversion metadata v1 to v2 required, please wait...")
nkLog(tag: self.global.logTagE2EE, message: "Conversion v1 to v2")
NCActivityIndicator.shared.start()

let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, updateVersionV1V2: true, account: account)
if error != .success {
await showErrorBanner(controller: self.controller, text: error.errorDescription, errorCode: error.errorCode)
await showErrorBanner(windowScene: windowScene, text: error.errorDescription, errorCode: error.errorCode)
}
NCActivityIndicator.shared.stop()
}
} else {
// Client Diagnostic
await self.database.addDiagnosticAsync(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors)
await showErrorBanner(controller: self.controller, text: error.errorDescription, errorCode: error.errorCode)
await showErrorBanner(windowScene: windowScene, text: errorDecodeMetadata.errorDescription, errorCode: errorDecodeMetadata.errorCode)
}

return (metadatas, error, reloadRequired)
Expand Down
18 changes: 10 additions & 8 deletions iOSClient/GUI/Lucid Banner/AlertActionBannerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,11 +6,14 @@ import SwiftUI
import LucidBanner

@MainActor
func showAlertActionBannerView(scene: UIWindowScene?,
func showAlertActionBannerView(lucidBanner: LucidBanner?,
title: String? = nil,
subtitle: String? = nil,
onConfirm: (() -> Void)? = nil) {
let isPad = scene?.traitCollection.userInterfaceIdiom == .pad
guard let lucidBanner else {
return
}
let isPad = lucidBanner.windowScene.traitCollection.userInterfaceIdiom == .pad
let horizontalLayout: LucidBanner.HorizontalLayout =
isPad
? .centered(width: 450)
Expand All @@ -24,19 +27,18 @@ func showAlertActionBannerView(scene: UIWindowScene?,
swipeToDismiss: true
)

LucidBanner.shared.show(scene: scene,
payload: payload,
policy: .replace) { _, _ in
LucidBanner.shared.dismiss()
lucidBanner.show(payload: payload,
policy: .replace) { _, _ in
lucidBanner.dismiss()
} content: { state in
AlertActionBannerView(
state: state,
onConfirm: {
onConfirm?()
LucidBanner.shared.dismiss()
lucidBanner.dismiss()
},
onCancel: {
LucidBanner.shared.dismiss()
lucidBanner.dismiss()
}
)
}
Expand Down
Loading
Loading