Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
Show all changes
71 commits
Select commit Hold shift + click to select a range
a4ee593
Add menu
mpivchev Aug 8, 2025
5205bea
Merge branch 'master' of https://github.com/nextcloud/ios into declar…
mpivchev Sep 18, 2025
fd87337
WIP
mpivchev Sep 22, 2025
ed8a527
WIP
mpivchev Sep 22, 2025
70e7521
Merge branch 'master' of https://github.com/nextcloud/ios into declar…
mpivchev Sep 22, 2025
965977e
Replace sheet with native context menu
mpivchev Sep 23, 2025
3bd98c6
Merge branch 'master' of https://github.com/nextcloud/ios into declar…
mpivchev Sep 23, 2025
c1adab5
WIP
mpivchev Sep 23, 2025
0a793b1
WIP
mpivchev Sep 25, 2025
3fc8c0f
Merge branch 'master' of https://github.com/nextcloud/ios into declar…
mpivchev Oct 1, 2025
783822b
WIP
mpivchev Oct 1, 2025
b98c61c
Merge branch 'master' of https://github.com/nextcloud/ios into declar…
mpivchev Dec 2, 2025
1eba37b
Refactor
mpivchev Dec 2, 2025
9c26f98
Use relative file path
mpivchev Dec 2, 2025
4d9c211
Refactor
mpivchev Dec 3, 2025
75b014f
API
mpivchev Dec 3, 2025
ea1ed18
Error handling
mpivchev Dec 3, 2025
ab9d2d4
WIP
mpivchev Dec 3, 2025
3568ee9
WIP
mpivchev Dec 8, 2025
f25edde
Merge branch 'master' of https://github.com/nextcloud/ios into declar…
mpivchev Dec 9, 2025
4e65ec0
WIP
mpivchev Dec 9, 2025
c3dee5a
WIP
mpivchev Dec 9, 2025
22eae10
WIP
mpivchev Dec 9, 2025
476a9bb
WIP
mpivchev Dec 10, 2025
ff7b7a0
Sendable fix
mpivchev Dec 10, 2025
2a05902
Refactor
mpivchev Dec 10, 2025
6b234df
Refactor
mpivchev Dec 10, 2025
1a1a903
Refactor
mpivchev Dec 10, 2025
471f8ad
WIP
mpivchev Dec 11, 2025
668ad4d
Merge branch 'master' of https://github.com/nextcloud/ios into declar…
mpivchev Jan 6, 2026
4821a42
Bring back more button in list
mpivchev Jan 6, 2026
93852f6
Revert more button in grid
mpivchev Jan 6, 2026
54013b7
WIP
mpivchev Jan 6, 2026
aed33b1
WIP
mpivchev Jan 7, 2026
2c033d1
WIP
mpivchev Jan 8, 2026
55161d4
WIP
mpivchev Jan 8, 2026
b8d710c
Refactor
mpivchev Jan 8, 2026
c4297f0
Recommendations menu
mpivchev Jan 8, 2026
c11f075
Photo cell fix
mpivchev Jan 8, 2026
8b3e673
Resize icons
mpivchev Jan 9, 2026
51e208a
Refactor
mpivchev Jan 9, 2026
a232309
Merge branch 'master' of https://github.com/nextcloud/ios into declar…
mpivchev Jan 12, 2026
e86d947
remove old code
marinofaggiana Jan 13, 2026
7480d68
remove old code
marinofaggiana Jan 13, 2026
a3fe9e1
cleaning
marinofaggiana Jan 13, 2026
3dc9004
added onMenuIntent
marinofaggiana Jan 13, 2026
878fade
normalized
marinofaggiana Jan 13, 2026
479ec6d
normalized
marinofaggiana Jan 13, 2026
047eb43
TransferDelegate
marinofaggiana Jan 13, 2026
109ec19
transferReloadData
marinofaggiana Jan 13, 2026
6b703a2
test
marinofaggiana Jan 13, 2026
9fd0a7b
incredible strategy
marinofaggiana Jan 13, 2026
634b895
code
marinofaggiana Jan 14, 2026
29247a9
Media
marinofaggiana Jan 14, 2026
fc1825c
License
marinofaggiana Jan 14, 2026
584c3e8
add Helper
marinofaggiana Jan 14, 2026
3dd3fe4
Improved code
marinofaggiana Jan 14, 2026
b3c1eaf
fix
marinofaggiana Jan 14, 2026
770a0be
hide button more in photo cell
marinofaggiana Jan 15, 2026
670eb6c
improved
marinofaggiana Jan 15, 2026
4063199
2026
marinofaggiana Jan 15, 2026
1a83ec6
2026
marinofaggiana Jan 15, 2026
f966d58
cell protocol improvements
marinofaggiana Jan 15, 2026
8ddd746
cleaning code
marinofaggiana Jan 15, 2026
c777766
lint
marinofaggiana Jan 15, 2026
c6a65db
cleaning (Lint)
marinofaggiana Jan 15, 2026
a1cb0f9
lint
marinofaggiana Jan 15, 2026
a87d76a
NCContextMenu
marinofaggiana Jan 15, 2026
63c835f
NCContextMenu
marinofaggiana Jan 15, 2026
7aeadcc
doc
marinofaggiana Jan 16, 2026
79ee468
code
marinofaggiana Jan 16, 2026
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
2 changes: 1 addition & 1 deletion Brand/NCBrand.swift
Original file line number Diff line number Diff line change
Expand Up @@ -25,7 +25,7 @@ final class NCBrandOptions: @unchecked Sendable {

var brand: String = "Nextcloud"
var brandUserAgent: String = ""
var textCopyrightNextcloudiOS: String = "Nextcloud Matheria for iOS %@ © 2025"
var textCopyrightNextcloudiOS: String = "Nextcloud Matheria for iOS %@ © 2026"
var textCopyrightNextcloudServer: String = "Nextcloud Server %@"
var loginBaseUrl: String = "https://cloud.nextcloud.com"
var pushNotificationServerProxy: String = ""
Expand Down
Original file line number Diff line number Diff line change
Expand Up @@ -207,7 +207,7 @@ extension FileProviderExtension {
}

if (favorite == true && !metadata.favorite) || (!favorite && metadata.favorite) {
let fileNamePath = NCUtilityFileSystem().getFileNamePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId)
let fileNamePath = NCUtilityFileSystem().getRelativeFilePath(metadata.fileName, serverUrl: metadata.serverUrl, urlBase: metadata.urlBase, userId: metadata.userId)
let resultsFavorite = await NextcloudKit.shared.setFavoriteAsync(fileName: fileNamePath, favorite: favorite, account: metadata.account)

if resultsFavorite.error == .success {
Expand Down
52 changes: 43 additions & 9 deletions Nextcloud.xcodeproj/project.pbxproj

Large diffs are not rendered by default.

6 changes: 2 additions & 4 deletions Share/NCShareExtension+DataSource.swift
Original file line number Diff line number Diff line change
Expand Up @@ -76,14 +76,12 @@ extension NCShareExtension: UICollectionViewDataSource {
}

func collectionView(_ collectionView: UICollectionView, cellForItemAt indexPath: IndexPath) -> UICollectionViewCell {
let cell = (collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell)!
var cell = (collectionView.dequeueReusableCell(withReuseIdentifier: "listCell", for: indexPath) as? NCListCell)!
guard let metadata = self.dataSource.getMetadata(indexPath: indexPath) else {
return cell
}

cell.fileOcId = metadata.ocId
cell.fileOcIdTransfer = metadata.ocIdTransfer
cell.fileUser = metadata.ownerId
cell.metadata = metadata
cell.labelTitle.text = metadata.fileNameView
cell.labelTitle.textColor = NCBrandColor.shared.textColor
cell.imageSelect.image = nil
Expand Down
24 changes: 8 additions & 16 deletions iOSClient/Activity/NCActivity.swift
Original file line number Diff line number Diff line change
Expand Up @@ -84,9 +84,7 @@ class NCActivity: UIViewController, NCSharePagingContent {
self.loadComments()
} else {
Task {@MainActor in
await showErrorBanner(controller: self.tabBarController,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: self.tabBarController, errorDescription: error.errorDescription)
}
}
}
Expand Down Expand Up @@ -225,9 +223,9 @@ extension NCActivity: UITableViewDataSource {
let results = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName)

if results.image == nil {
cell.fileAvatarImageView?.image = utility.loadUserImage(for: comment.actorId, displayName: comment.actorDisplayName, urlBase: NCSession.shared.getSession(account: account).urlBase)
cell.avatarImageView?.image = utility.loadUserImage(for: comment.actorId, displayName: comment.actorDisplayName, urlBase: NCSession.shared.getSession(account: account).urlBase)
} else {
cell.fileAvatarImageView?.image = results.image
cell.avatarImageView?.image = results.image
}

if let tblAvatar = results.tblAvatar,
Expand Down Expand Up @@ -313,9 +311,9 @@ extension NCActivity: UITableViewDataSource {
let results = NCManageDatabase.shared.getImageAvatarLoaded(fileName: fileName)

if results.image == nil {
cell.fileAvatarImageView?.image = utility.loadUserImage(for: activity.user, displayName: nil, urlBase: session.urlBase)
cell.avatarImageView?.image = utility.loadUserImage(for: activity.user, displayName: nil, urlBase: session.urlBase)
} else {
cell.fileAvatarImageView?.image = results.image
cell.avatarImageView?.image = results.image
}

if !(results.tblAvatar?.loaded ?? false),
Expand Down Expand Up @@ -441,9 +439,7 @@ extension NCActivity {
self.database.addComments(comments, account: metadata.account, objectId: metadata.fileId)
} else if error.errorCode != NCGlobal.shared.errorResourceNotFound {
Task {@MainActor in
await showErrorBanner(controller: self.tabBarController,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: self.tabBarController, errorDescription: error.errorDescription)
}
}

Expand Down Expand Up @@ -583,9 +579,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
self.loadComments()
} else {
Task {@MainActor in
await showErrorBanner(controller: self.tabBarController,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: self.tabBarController, errorDescription: error.errorDescription)
}
}
}
Expand Down Expand Up @@ -617,9 +611,7 @@ extension NCActivity: NCShareCommentsCellDelegate {
self.loadComments()
} else {
Task {@MainActor in
await showErrorBanner(controller: self.tabBarController,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: self.tabBarController, errorDescription: error.errorDescription)
}
}
}
Expand Down
6 changes: 2 additions & 4 deletions iOSClient/Activity/NCActivityTableViewCell.swift
Original file line number Diff line number Diff line change
Expand Up @@ -37,7 +37,7 @@ class NCActivityTableViewCell: UITableViewCell, NCCellProtocol {
get { return index }
set { index = newValue }
}
var fileAvatarImageView: UIImageView? {
var avatarImageView: UIImageView? {
return avatar
}
var fileUser: String? {
Expand Down Expand Up @@ -86,9 +86,7 @@ extension NCActivityTableViewCell: UICollectionViewDelegate {
} else {
let error = NKError(errorCode: NCGlobal.shared.errorInternalError, errorDescription: "_trash_file_not_found_")
Task {@MainActor in
await showErrorBanner(controller: viewController.controller,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: viewController.controller, errorDescription: error.errorDescription)
}
}
}
Expand Down
3 changes: 1 addition & 2 deletions iOSClient/AppDelegate.swift
Original file line number Diff line number Diff line change
Expand Up @@ -14,7 +14,6 @@ import EasyTipView
import SwiftUI
import RealmSwift

@UIApplicationMain
class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterDelegate {
var backgroundSessionCompletionHandler: (() -> Void)?
var isUiTestingEnabled: Bool {
Expand Down Expand Up @@ -408,7 +407,7 @@ class AppDelegate: UIResponder, UIApplicationDelegate, UNUserNotificationCenterD
Task {
await NCNetworking.shared.transferDispatcher.notifyAllDelegatesAsync { delegate in
try? await Task.sleep(nanoseconds: 500_000_000)
delegate.transferReloadData(serverUrl: nil, requestData: true, status: nil)
delegate.transferReloadDataSource(serverUrl: nil, requestData: true, status: nil)
}
}
} else if let navigationController = UIStoryboard(name: "NCNotification", bundle: nil).instantiateInitialViewController() as? UINavigationController,
Expand Down
83 changes: 83 additions & 0 deletions iOSClient/Client Integration/ClientIntegrationUIViewer.swift
Original file line number Diff line number Diff line change
@@ -0,0 +1,83 @@
// SPDX-FileCopyrightText: Nextcloud GmbH
// SPDX-FileCopyrightText: 2025 Milen Pivchev
// SPDX-License-Identifier: GPL-3.0-or-later

import SwiftUI

struct ClientIntegrationUIViewer: View {
@Environment(\.openURL) private var openURL

struct Row: Identifiable {
let id = UUID()
let element: String
let title: String?
let urlString: String
}

// Configurable inputs
let rows: [Row]
let baseURL: String

var body: some View {
ScrollView {
LazyVStack(alignment: .leading, spacing: 12) {
ForEach(rows) { row in
VStack(alignment: .leading, spacing: 8) {
HStack {
Text(row.element)
.font(.subheadline)
.fontWeight(.semibold)
.foregroundStyle(.secondary)
Spacer()
}

if let title = row.title {
Text(title).font(.headline)
}

let finalUrl = baseURL + row.urlString

Text(finalUrl)
.font(.footnote)
.foregroundStyle(.secondary)
.textSelection(.enabled)

HStack(spacing: 12) {
Button {
openURL(URL(string: finalUrl)!)
} label: {
Label("Open", systemImage: "safari")
}

Button {
UIPasteboard.general.string = row.urlString
} label: {
Label("Copy", systemImage: "doc.on.doc")
}
}
.buttonStyle(.borderedProminent)
}
.padding()
.background(.thinMaterial, in: RoundedRectangle(cornerRadius: 12))
}
}
.padding()
}
}

// private func resolvedURL(for row: Row) -> URL? {
// // If the string is already an absolute URL with a scheme, use it.
// if let absolute = URL(string: row.urlString), absolute.scheme != nil {
// return absolute
// }
// // Otherwise, resolve relative to the provided base URL if available.
// if let baseURL {
// return URL(string: row.urlString, relativeTo: baseURL)?.absoluteURL
// }
// return nil
// }
}

#Preview {
ClientIntegrationUIViewer(rows: [.init(element: "URL", title: "Test", urlString: "/test")], baseURL: "test.com")
}
7 changes: 7 additions & 0 deletions iOSClient/Data/NCManageDatabase+Metadata.swift
Original file line number Diff line number Diff line change
Expand Up @@ -125,6 +125,13 @@ class tableMetadata: Object {
@objc dynamic var autoUploadServerUrlBase: String?
@objc dynamic var typeIdentifier: String = ""

// =========================
// UI / transient properties
// =========================

/// Used only for UI state (not persisted, not observed by Realm)
var isOffline: Bool = false

override static func primaryKey() -> String {
return "ocId"
}
Expand Down
2 changes: 1 addition & 1 deletion iOSClient/DeepLink/NCDeepLinkHandler.swift
Original file line number Diff line number Diff line change
Expand Up @@ -116,7 +116,7 @@ class NCDeepLinkHandler {
DispatchQueue.main.asyncAfter(deadline: .now() + 4) {
let serverUrl = controller.currentServerUrl()
let session = NCSession.shared.getSession(controller: controller)
let fileFolderPath = NCUtilityFileSystem().getFileNamePath("", serverUrl: serverUrl, session: session)
let fileFolderPath = NCUtilityFileSystem().getRelativeFilePath("", serverUrl: serverUrl, session: session)
let fileFolderName = (serverUrl as NSString).lastPathComponent
let capabilities = NCNetworking.shared.capabilities[controller.account] ?? NKCapabilities.Capabilities()

Expand Down
16 changes: 4 additions & 12 deletions iOSClient/Files/NCFiles.swift
Original file line number Diff line number Diff line change
Expand Up @@ -321,16 +321,12 @@ class NCFiles: NCCollectionViewCommon {
NCContentPresenter().showInfo(description: "Metadata not found")
let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, account: account)
if error != .success {
await showErrorBanner(controller: self.controller,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription)
}
} else {
// show error
Task {@MainActor in
await showErrorBanner(controller: self.controller,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription)
}
}

Expand All @@ -350,9 +346,7 @@ class NCFiles: NCCollectionViewCommon {
let error = await NCNetworkingE2EE().uploadMetadata(serverUrl: serverUrl, updateVersionV1V2: true, account: account)
if error != .success {
Task {@MainActor in
await showErrorBanner(controller: self.controller,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription)
}
}
NCActivityIndicator.shared.stop()
Expand All @@ -361,9 +355,7 @@ class NCFiles: NCCollectionViewCommon {
// Client Diagnostic
await self.database.addDiagnosticAsync(account: account, issue: NCGlobal.shared.diagnosticIssueE2eeErrors)
Task {@MainActor in
await showErrorBanner(controller: self.controller,
errorDescription: error.errorDescription,
errorCode: error.errorCode)
await showErrorBanner(controller: self.controller, errorDescription: error.errorDescription)
}
}

Expand Down
8 changes: 4 additions & 4 deletions iOSClient/GUI/Lucid Banner/ErrorBannerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -6,13 +6,13 @@ import SwiftUI
import LucidBanner

@MainActor
func showErrorBanner(controller: UITabBarController?, errorDescription: String, errorCode: Int, sleepBefore: Double = 1) async {
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, errorCode: errorCode, sleepBefore: sleepBefore)
await showErrorBanner(scene: scene, errorDescription: errorDescription, footnote: footnote, sleepBefore: sleepBefore)
}

@MainActor
func showErrorBanner(scene: UIWindowScene?, errorDescription: String, errorCode: Int, sleepBefore: Double = 1) async {
func showErrorBanner(scene: UIWindowScene?, errorDescription: String, footnote: String? = nil, sleepBefore: Double = 1) async {
try? await Task.sleep(nanoseconds: UInt64(sleepBefore * 1e9))
var scene = scene
if scene == nil {
Expand All @@ -22,7 +22,7 @@ func showErrorBanner(scene: UIWindowScene?, errorDescription: String, errorCode:
LucidBanner.shared.show(
scene: scene,
subtitle: errorDescription,
footnote: "(Code: \(errorCode))",
footnote: footnote,
vPosition: .top,
autoDismissAfter: NCGlobal.shared.dismissAfterSecond,
swipeToDismiss: true,
Expand Down
1 change: 0 additions & 1 deletion iOSClient/GUI/Lucid Banner/HudBannerView.swift
Original file line number Diff line number Diff line change
Expand Up @@ -208,7 +208,6 @@ struct HudBannerView: View {
@ViewBuilder
func containerView<Content: View>(@ViewBuilder _ content: () -> Content) -> some View {
let cornerRadius: CGFloat = 22
let opacity = 0.65
let backgroundColor = Color(.systemBackground).opacity(0.65)

if #available(iOS 26, *) {
Expand Down
Loading
Loading