Skip to content
Merged
Show file tree
Hide file tree
Changes from 1 commit
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
Prev Previous commit
Next Next commit
payload
Signed-off-by: Marino Faggiana <marino.faggiana@nextcloud.com>
  • Loading branch information
marinofaggiana committed Jan 21, 2026
commit 231101f6f3b293354168eec1451b26884a020380
4 changes: 2 additions & 2 deletions Nextcloud.xcodeproj/project.pbxproj
Original file line number Diff line number Diff line change
Expand Up @@ -6015,7 +6015,7 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/marinofaggiana/LucidBanner";
requirement = {
branch = main;
branch = payload;
kind = branch;
};
};
Expand Down Expand Up @@ -6143,7 +6143,7 @@
isa = XCRemoteSwiftPackageReference;
repositoryURL = "https://github.com/nextcloud/NextcloudKit";
requirement = {
branch = "declarative-ui";
branch = main;
kind = branch;
};
};
Expand Down
48 changes: 31 additions & 17 deletions Share/NCShareExtension.swift
Original file line number Diff line number Diff line change
Expand Up @@ -371,12 +371,12 @@ extension NCShareExtension {

for metadata in self.uploadMetadata {
// BANNER
LucidBanner.shared.update(
let payload = LucidBannerPayload.Update(
title: NSLocalizedString("_upload_file_", comment: "") + " \(self.counterUploaded + 1) " + NSLocalizedString("_of_", comment: "") + " \(self.filesName.count)",
systemImage: "arrowshape.up.circle",
imageAnimation: .breathe,
progress: 0
)
progress: 0)
LucidBanner.shared.update(payload: payload)

error = await self.upload(metadata: metadata)
if error != .success {
Expand All @@ -385,9 +385,14 @@ extension NCShareExtension {
}

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

LucidBanner.shared.dismiss(after: 2) {
Expand Down Expand Up @@ -425,32 +430,39 @@ extension NCShareExtension {
if metadata.isDirectoryE2EE {
error = await NCNetworkingE2EEUpload().upload(metadata: metadata, session: session, controller: self, stageBanner: nil, tokenBanner: self.token)
} else if metadata.chunk > 0 {
LucidBanner.shared.update(
let payload = LucidBannerPayload.Update(
systemImage: "gearshape.arrow.triangle.2.circlepath",
imageAnimation: .rotate,
for: self.token)
imageAnimation: .rotate
)
LucidBanner.shared.update(payload: payload, 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(progress: Double(counter / total), for: self.token)
let progress = Double(counter) / Double(total)
LucidBanner.shared.update(payload: LucidBannerPayload.Update(progress: progress), for: self.token)
}
} uploadStart: { _ in
Task {@MainActor in
LucidBanner.shared.update(
let payload = LucidBannerPayload.Update(
systemImage: "arrowshape.up.circle",
imageAnimation: .breathe,
for: self.token)
imageAnimation: .breathe
)
LucidBanner.shared.update(payload: payload, for: self.token)
}
} uploadProgressHandler: { _, _, progress in
Task {@MainActor in
LucidBanner.shared.update(progress: progress, for: self.token)
LucidBanner.shared.update(
payload: LucidBannerPayload.Update(progress: progress),
for: self.token)
LucidBanner.shared.update(payload: payload, for: self.token)
}
} assembling: {
Task {@MainActor in
LucidBanner.shared.update(
let payload = LucidBannerPayload.Update(
systemImage: "gearshape.arrow.triangle.2.circlepath",
imageAnimation: .rotate,
for: self.token)
imageAnimation: .rotate
)
LucidBanner.shared.update(payload: payload, for: self.token)
}
}

Expand All @@ -472,7 +484,9 @@ extension NCShareExtension {
dateModificationFile: metadata.date as Date) { _ in
} progressHandler: { _, _, fractionCompleted in
Task {@MainActor in
LucidBanner.shared.update(progress: fractionCompleted, for: self.token)
LucidBanner.shared.update(
payload: LucidBannerPayload.Update(progress: fractionCompleted),
for: self.token)
}
}
error = results.error
Expand Down
63 changes: 19 additions & 44 deletions iOSClient/GUI/Lucid Banner/ErrorBannerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -22,13 +22,16 @@ func showErrorBanner(scene: UIWindowScene?, errorDescription: String, footnote:
scene = UIApplication.shared.mainAppWindow?.windowScene
}

LucidBanner.shared.show(
scene: scene,
let payload = LucidBannerPayload(
subtitle: NSLocalizedString(errorDescription, comment: ""),
footnote: NSLocalizedString(footnote ?? "", comment: ""),
vPosition: .top,
autoDismissAfter: NCGlobal.shared.dismissAfterSecond,
swipeToDismiss: true,
)
LucidBanner.shared.show(
scene: scene,
payload: payload,
onTap: { _, _ in
LucidBanner.shared.dismiss()
}
Expand All @@ -44,10 +47,10 @@ struct ErrorBannerView: View {
let textColor = Color(.label)

var body: some View {
let showSubtitle = !(state.subtitle?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true)
let showFootnote = !(state.footnote?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true)
let showSubtitle = !(state.payload.subtitle?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true)
let showFootnote = !(state.payload.footnote?.trimmingCharacters(in: .whitespacesAndNewlines).isEmpty ?? true)

containerView {
containerView(state: state, allowMinimizeOnTap: false) {
VStack(spacing: 15) {
HStack(alignment: .top, spacing: 10) {
Image(systemName: "xmark.circle.fill")
Expand All @@ -61,14 +64,14 @@ struct ErrorBannerView: View {
.truncationMode(.tail)
.foregroundStyle(textColor)

if showSubtitle, let subtitle = state.subtitle {
if showSubtitle, let subtitle = state.payload.subtitle {
Text(subtitle)
.font(.subheadline)
.multilineTextAlignment(.leading)
.truncationMode(.tail)
.foregroundStyle(textColor)
}
if showFootnote, let footnote = state.footnote {
if showFootnote, let footnote = state.payload.footnote {
Text(footnote)
.font(.caption)
.multilineTextAlignment(.leading)
Expand All @@ -83,36 +86,6 @@ struct ErrorBannerView: View {
.frame(maxWidth: .infinity, alignment: .leading)
}
}

// MARK: - Container

@ViewBuilder
func containerView<Content: View>(@ViewBuilder _ content: () -> Content) -> some View {
let cornerRadius: CGFloat = 22
let errorColor = Color.red.opacity(0.75)
let contentBase = content()
.contentShape(Rectangle())
.frame(maxWidth: 500)

if #available(iOS 26, *) {
contentBase
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(errorColor)
)
.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
Expand All @@ -128,13 +101,15 @@ struct ErrorBannerView: View {
.foregroundStyle(.primary)
.padding()

ErrorBannerView(
state: LucidBannerState(
title: "Error",
subtitle: "Not avalilable",
footnote: "ErroCode. 12",
imageAnimation: .breathe)
)
let state = LucidBannerState(payload: LucidBannerPayload(
title: "Error",
subtitle: "Not avalilable",
footnote: "ErroCode. 12",
imageAnimation: .breathe,
stage: .error
))

ErrorBannerView(state: state)
.padding()
}
}
54 changes: 54 additions & 0 deletions iOSClient/GUI/Lucid Banner/HelperBanner.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,60 @@ import SwiftUI
import LucidBanner

public extension View {
@ViewBuilder
func containerView<Content: View>(state: LucidBannerState,
allowMinimizeOnTap: Bool,
bgColor: UIColor = .systemBackground,
@ViewBuilder _ content: () -> Content) -> some View {
let isError = state.payload.stage == .error
let isSuccess = state.payload.stage == .success
let isMinimized = state.isMinimized

let cornerRadius: CGFloat = isMinimized ? 15 : 25
let maxWidth: CGFloat? = (isMinimized || isSuccess) ? nil : 500

let backgroundColor = isError
? Color.red.opacity(0.8)
: Color(bgColor).opacity(0.7)

let base = content()
.contentShape(Rectangle())
.onTapGesture {
guard allowMinimizeOnTap else { return }
LucidBannerMinimizeCoordinator.shared.handleTap(state)
}
.frame(maxWidth: maxWidth)

if #available(iOS 26, *) {
base
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(backgroundColor)
)
.glassEffect(.clear, in: RoundedRectangle(cornerRadius: cornerRadius))
.shadow(color: .black.opacity(0.5), radius: 10, x: 0, y: 4)
.frame(maxWidth: .infinity, alignment: .center)

} else {
base
.background(
RoundedRectangle(cornerRadius: cornerRadius)
.fill(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)
.frame(maxWidth: .infinity, alignment: .center)
}
}

@ViewBuilder
func applyBannerAnimation(_ style: LucidBanner.LucidBannerAnimationStyle) -> some View {
switch style {
Expand Down
Loading