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
10 changes: 7 additions & 3 deletions Nextcloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -292,6 +292,7 @@
F72AD71128C24BBB006CB92D /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = F72AD71028C24BBB006CB92D /* NextcloudKit */; };
F72AD71328C24BCC006CB92D /* NextcloudKit in Frameworks */ = {isa = PBXBuildFile; productRef = F72AD71228C24BCC006CB92D /* NextcloudKit */; };
F72CA0572F5048C3002E2F06 /* UIApplication+Extension.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72CA0562F5048C3002E2F06 /* UIApplication+Extension.swift */; };
F72CA05C2F5051DB002E2F06 /* AlertActionBannerView.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72CA05B2F5051DB002E2F06 /* AlertActionBannerView.swift */; };
F72CD63A25C19EBF00F46F9A /* NCAutoUpload.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72CD63925C19EBF00F46F9A /* NCAutoUpload.swift */; };
F72D1007210B6882009C96B7 /* NCPushNotificationEncryption.m in Sources */ = {isa = PBXBuildFile; fileRef = F72D1005210B6882009C96B7 /* NCPushNotificationEncryption.m */; };
F72D404923D2082500A97FD0 /* NCViewerNextcloudText.swift in Sources */ = {isa = PBXBuildFile; fileRef = F72D404823D2082500A97FD0 /* NCViewerNextcloudText.swift */; };
Expand Down Expand Up @@ -1347,6 +1348,7 @@
F72944F42A8424F800246839 /* NCEndToEndMetadataV1.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCEndToEndMetadataV1.swift; sourceTree = "<group>"; };
F72A17D728B221E300F3F159 /* DashboardWidgetView.swift */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.swift; path = DashboardWidgetView.swift; sourceTree = "<group>"; };
F72CA0562F5048C3002E2F06 /* UIApplication+Extension.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = "UIApplication+Extension.swift"; sourceTree = "<group>"; };
F72CA05B2F5051DB002E2F06 /* AlertActionBannerView.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = AlertActionBannerView.swift; sourceTree = "<group>"; };
F72CD63925C19EBF00F46F9A /* NCAutoUpload.swift */ = {isa = PBXFileReference; lastKnownFileType = sourcecode.swift; path = NCAutoUpload.swift; sourceTree = "<group>"; };
F72D1005210B6882009C96B7 /* NCPushNotificationEncryption.m */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.objc; path = NCPushNotificationEncryption.m; sourceTree = "<group>"; };
F72D1006210B6882009C96B7 /* NCPushNotificationEncryption.h */ = {isa = PBXFileReference; fileEncoding = 4; lastKnownFileType = sourcecode.c.h; path = NCPushNotificationEncryption.h; sourceTree = "<group>"; };
Expand Down Expand Up @@ -2240,6 +2242,7 @@
F70557BC2ED44F1800135623 /* Lucid Banner */ = {
isa = PBXGroup;
children = (
F72CA05B2F5051DB002E2F06 /* AlertActionBannerView.swift */,
F7DF7B3E2F1A2EE400514020 /* BannerView.swift */,
F714A1462ED84AF00050A43B /* HudBannerView.swift */,
F70557BB2ED44F1800135623 /* UploadBannerView.swift */,
Expand Down Expand Up @@ -4683,6 +4686,7 @@
F343A4B32A1E01FF00DDA874 /* PHAsset+Extension.swift in Sources */,
F70968A424212C4E00ED60E5 /* NCLivePhoto.swift in Sources */,
F7C30DFA291BCF790017149B /* NCNetworkingE2EECreateFolder.swift in Sources */,
F72CA05C2F5051DB002E2F06 /* AlertActionBannerView.swift in Sources */,
F722133B2D40EF9D002F7438 /* NCFilesNavigationController.swift in Sources */,
F7BC288026663F85004D46C5 /* NCViewCertificateDetails.swift in Sources */,
F78B87E92B62550800C65ADC /* NCMediaDownloadThumbnail.swift in Sources */,
Expand Down Expand Up @@ -5746,7 +5750,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 Nextcloud. All rights reserved.";
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -5809,7 +5813,7 @@
GCC_WARN_UNUSED_FUNCTION = YES;
GCC_WARN_UNUSED_VARIABLE = YES;
INFOPLIST_KEY_NSHumanReadableCopyright = "Copyright © 2026 Nextcloud. All rights reserved.";
IPHONEOS_DEPLOYMENT_TARGET = 17.6;
IPHONEOS_DEPLOYMENT_TARGET = 17.0;
LD_RUNPATH_SEARCH_PATHS = (
"$(inherited)",
"@executable_path/Frameworks",
Expand Down Expand Up @@ -5968,7 +5972,7 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/marinofaggiana/LucidBanner";
requirement = {
branch = 0.6.0;
branch = main;
kind = branch;
};
};
Expand Down
178 changes: 178 additions & 0 deletions iOSClient/GUI/Lucid Banner/AlertActionBannerView.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,178 @@
// SPDX-FileCopyrightText: Nextcloud GmbH
// SPDX-FileCopyrightText: 2026 Marino Faggiana
// SPDX-License-Identifier: GPL-3.0-or-later

import SwiftUI
import LucidBanner

@MainActor
func showAlertActionBannerView(scene: UIWindowScene?,
title: String? = nil,
subtitle: String? = nil,
onConfirm: (() -> Void)? = nil) {
let isPad = scene?.traitCollection.userInterfaceIdiom == .pad
let horizontalLayout: LucidBanner.HorizontalLayout =
isPad
? .centered(width: 450)
: .stretch(margins: 20)

let payload = LucidBannerPayload(
title: title,
subtitle: subtitle,
vPosition: .top,
horizontalLayout: horizontalLayout,
swipeToDismiss: true
)

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

}

// MARK: - SwiftUI

struct AlertActionBannerView: View {
@ObservedObject var state: LucidBannerState

let onConfirm: (() -> Void)?
let onCancel: (() -> Void)?

init(
state: LucidBannerState,
onConfirm: (() -> Void)? = nil,
onCancel: (() -> Void)? = nil
) {
self.state = state
self.onConfirm = onConfirm
self.onCancel = onCancel
}

var body: some View {
alertActionContainerView {
VStack(spacing: 20) {
// Title
if let title = state.payload.title, !title.isEmpty {
Text(title)
.font(.headline.weight(.semibold))
.foregroundStyle(state.payload.textColor)
.multilineTextAlignment(.center)
}

// Subtitle
if let subtitle = state.payload.subtitle, !subtitle.isEmpty {
Text(subtitle)
.font(.subheadline)
.foregroundStyle(state.payload.textColor)
.multilineTextAlignment(.center)
}

// Buttons
HStack(spacing: 12) {
Button("_cancel_") {
onCancel?()
}
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
.background(
Capsule()
.stroke(Color.secondary.opacity(0.5), lineWidth: 1)
)
.foregroundStyle(.primary)

Button("_ok_") {
onConfirm?()
}
.padding(.vertical, 8)
.frame(maxWidth: .infinity)
.background(
Capsule().fill(Color.accentColor)
)
.foregroundStyle(.white)
}
.frame(maxWidth: .infinity)
}
.padding(20)
}
}

// MARK: - Container

@ViewBuilder
func alertActionContainerView<Content: View>(@ViewBuilder _ content: () -> Content) -> some View {
let cornerRadius: CGFloat = 22
let backgroundColor = Color(.systemBackground).opacity(0.9)

if #available(iOS 26, *) {
content()
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(backgroundColor)
.id(backgroundColor)
)
.glassEffect(.clear, in: RoundedRectangle(cornerRadius: cornerRadius))
.shadow(color: .black.opacity(0.5), radius: 10, x: 0, y: 4)
} else {
content()
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(backgroundColor)
.id(backgroundColor)
)
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(.ultraThinMaterial)
)
.overlay(
RoundedRectangle(cornerRadius: cornerRadius)
.stroke(backgroundColor, lineWidth: 0.6)
.allowsHitTesting(false)
)
.shadow(color: .black.opacity(0.5), radius: 10, x: 0, y: 4)
}
}
}

// MARK: - Preview

#Preview("Alert - Light") {

let payload = LucidBannerPayload(
title: "Title ?",
subtitle: "Subtitle.",
stage: .warning,
vPosition: .center,
blocksTouches: true
)

let state = LucidBannerState(payload: payload)

return ZStack {
Color.black.opacity(0.15)
.ignoresSafeArea()

AlertActionBannerView(
state: state,
onConfirm: {
print("Confirm tapped")
},
onCancel: {
print("Cancel tapped")
}
)
.padding()
}
}
21 changes: 13 additions & 8 deletions iOSClient/Login/NCLogin.swift
Original file line number Diff line number Diff line change
Expand Up @@ -8,6 +8,7 @@ import UIKit
import NextcloudKit
import SwiftUI
import SafariServices
import LucidBanner

class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
@IBOutlet weak var imageBrand: UIImageView!
Expand Down Expand Up @@ -187,20 +188,24 @@ class NCLogin: UIViewController, UITextFieldDelegate, NCLoginQRCodeDelegate {
override func viewDidAppear(_ animated: Bool) {
super.viewDidAppear(animated)

if self.shareAccounts != nil, let image = UIImage(systemName: "person.badge.plus")?.withTintColor(.white, renderingMode: .alwaysOriginal), let backgroundColor = NCBrandColor.shared.customer.lighter(by: 10) {
if self.shareAccounts != nil {
let title = String(format: NSLocalizedString("_apps_nextcloud_detect_", comment: ""), NCBrandOptions.shared.brand)
let description = String(format: NSLocalizedString("_add_existing_account_", comment: ""), NCBrandOptions.shared.brand)
let subtitle = String(format: NSLocalizedString("_add_existing_account_", comment: ""), NCBrandOptions.shared.brand)

/*
NCContentPresenter().alertAction(image: image, contentModeImage: .scaleAspectFit, sizeImage: CGSize(width: 45, height: 45), backgroundColor: backgroundColor, textColor: textColor, title: title, description: description, textCancelButton: "_cancel_", textOkButton: "_ok_", attributes: EKAttributes.topFloat) { identifier in
if identifier == "ok" {
self.openShareAccountsViewController(nil)
}
showAlertActionBannerView(scene: view.window?.windowScene,
title: title,
subtitle: subtitle) {
self.openShareAccountsViewController(nil)
}
*/
}
}

override func viewWillDisappear(_ animated: Bool) {
super.viewWillDisappear(animated)

LucidBanner.shared.dismiss()
}

private func handleLoginWithAppConfig() {
let accountCount = NCManageDatabase.shared.getAccounts()?.count ?? 0

Expand Down
Loading