Skip to content

Commit fe920b4

Browse files
Version 6.2.3 (#3274)
1 parent 5142d4a commit fe920b4

File tree

144 files changed

+1075
-482
lines changed

Some content is hidden

Large Commits have some content hidden by default. Use the searchbox below for content that may be hidden.

144 files changed

+1075
-482
lines changed

.swiftlint.yml

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -37,6 +37,7 @@ disabled_rules:
3737
- type_name
3838
- void_function_in_ternary
3939
- switch_case_alignment
40+
- unavailable_condition
4041
excluded:
4142
- Carthage
4243
- Pods

Brand/Database.swift

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -26,4 +26,4 @@ import Foundation
2626
// Database Realm
2727
//
2828
let databaseName = "nextcloud.realm"
29-
let databaseSchemaVersion: UInt64 = 367
29+
let databaseSchemaVersion: UInt64 = 370

Brand/NCBrand.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -73,6 +73,7 @@ let userAgent: String = {
7373
var doNotAskPasscodeAtStartup: Bool = false
7474
var disable_source_code_in_settings: Bool = false
7575
var enforce_passcode_lock = false
76+
var use_in_app_browser_for_login = false
7677

7778
// (name: "Name 1", url: "https://cloud.nextcloud.com"),(name: "Name 2", url: "https://cloud.nextcloud.com")
7879
var enforce_servers: [(name: String, url: String)] = []

Nextcloud.xcodeproj/project.pbxproj

Lines changed: 56 additions & 12 deletions
Large diffs are not rendered by default.

iOSClient/Data/NCManageDatabase+Capabilities.swift

Lines changed: 9 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -100,6 +100,7 @@ extension NCManageDatabase {
100100
let groupfolders: GroupFolders?
101101
let securityguard: SecurityGuard?
102102
let assistant: Assistant?
103+
let recommendations: Recommendations?
103104

104105
enum CodingKeys: String, CodingKey {
105106
case filessharing = "files_sharing"
@@ -110,6 +111,7 @@ extension NCManageDatabase {
110111
case external, groupfolders
111112
case securityguard = "security_guard"
112113
case assistant
114+
case recommendations
113115
}
114116

115117
struct FilesSharing: Codable {
@@ -280,6 +282,10 @@ extension NCManageDatabase {
280282
let enabled: Bool?
281283
let version: String?
282284
}
285+
286+
struct Recommendations: Codable {
287+
let enabled: Bool?
288+
}
283289
}
284290
}
285291
}
@@ -383,6 +389,9 @@ extension NCManageDatabase {
383389
capabilities.capabilityForbiddenFileNameCharacters = data.capabilities.files?.forbiddenFileNameCharacters ?? []
384390
capabilities.capabilityForbiddenFileNameExtensions = data.capabilities.files?.forbiddenFileNameExtensions ?? []
385391

392+
// TODO: not yet available (IN TEST)
393+
// capabilities.capabilityRecommendations = data.capabilities.recommendations?.enabled ?? false
394+
386395
NCCapabilities.shared.appendCapabilities(account: account, capabilities: capabilities)
387396

388397
return capabilities
Lines changed: 71 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,71 @@
1+
// SPDX-FileCopyrightText: Nextcloud GmbH
2+
// SPDX-FileCopyrightText: 2024 Marino Faggiana
3+
// SPDX-License-Identifier: GPL-3.0-or-later
4+
5+
import Foundation
6+
import RealmSwift
7+
import NextcloudKit
8+
9+
class tableRecommendedFiles: Object {
10+
@Persisted var account = ""
11+
@Persisted var id = ""
12+
@Persisted(primaryKey: true) var primaryKey = ""
13+
@Persisted var timestamp: Date?
14+
@Persisted var name: String = ""
15+
@Persisted var directory: String = ""
16+
@Persisted var extensionType: String = ""
17+
@Persisted var mimeType: String = ""
18+
@Persisted var hasPreview: Bool = false
19+
@Persisted var reason: String = ""
20+
21+
convenience init(account: String, id: String, timestamp: Date?, name: String, directory: String, extensionType: String, mimeType: String, hasPreview: Bool, reason: String) {
22+
self.init()
23+
24+
self.account = account
25+
self.id = id
26+
self.primaryKey = account + id
27+
self.timestamp = timestamp
28+
self.name = name
29+
self.directory = directory
30+
self.extensionType = extensionType
31+
self.mimeType = mimeType
32+
self.hasPreview = hasPreview
33+
self.reason = reason
34+
}
35+
}
36+
37+
extension NCManageDatabase {
38+
func createRecommendedFiles(account: String, recommendations: [NKRecommendation]) {
39+
do {
40+
let realm = try Realm()
41+
42+
try realm.write {
43+
// Removed all objct for account
44+
let results = realm.objects(tableRecommendedFiles.self).filter("account == %@", account)
45+
46+
realm.delete(results)
47+
48+
// Added the new recommendations
49+
for recommendation in recommendations {
50+
let recommendedFile = tableRecommendedFiles(account: account, id: recommendation.id, timestamp: recommendation.timestamp, name: recommendation.name, directory: recommendation.directory, extensionType: recommendation.extensionType, mimeType: recommendation.mimeType, hasPreview: recommendation.hasPreview, reason: recommendation.reason)
51+
realm.add(recommendedFile)
52+
}
53+
}
54+
} catch let error {
55+
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not write to database: \(error)")
56+
}
57+
}
58+
59+
func getRecommendedFiles(account: String) -> [tableRecommendedFiles] {
60+
do {
61+
let realm = try Realm()
62+
let results = realm.objects(tableRecommendedFiles.self).filter("account == %@", account)
63+
64+
return Array(results.map { tableRecommendedFiles.init(value: $0) })
65+
} catch let error {
66+
NextcloudKit.shared.nkCommonInstance.writeLog("[ERROR] Could not access database: \(error)")
67+
}
68+
69+
return []
70+
}
71+
}

iOSClient/Data/NCManageDatabase.swift

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -197,6 +197,7 @@ class NCManageDatabase: NSObject {
197197
self.clearTable(tableTrash.self, account: account)
198198
self.clearTable(tableUserStatus.self, account: account)
199199
self.clearTable(tableVideo.self, account: account)
200+
self.clearTable(tableRecommendedFiles.self, account: account)
200201
}
201202

202203
func clearTablesE2EE(account: String?) {
Lines changed: 40 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,40 @@
1+
//
2+
// UIButton+Extension.swift
3+
// Nextcloud
4+
//
5+
// Created by Milen Pivchev on 17.12.24.
6+
// Copyright © 2024 Marino Faggiana. All rights reserved.
7+
//
8+
9+
extension UIButton {
10+
func hideButtonAndShowSpinner(tint: UIColor = .white) {
11+
self.isHidden = true
12+
13+
let spinnerTag = Int(bitPattern: Unmanaged.passUnretained(self).toOpaque())
14+
if self.superview?.subviews.first(where: { view -> Bool in
15+
return view.isKind(of: UIActivityIndicatorView.self) && view.tag == spinnerTag
16+
}) != nil {
17+
return
18+
}
19+
20+
let spinner = UIActivityIndicatorView(style: .medium)
21+
spinner.tag = spinnerTag
22+
spinner.color = tint
23+
spinner.startAnimating()
24+
spinner.center = self.center
25+
self.superview?.addSubview(spinner)
26+
spinner.translatesAutoresizingMaskIntoConstraints = false
27+
spinner.centerXAnchor.constraint(equalTo: self.centerXAnchor).isActive = true
28+
spinner.centerYAnchor.constraint(equalTo: self.centerYAnchor).isActive = true
29+
}
30+
31+
func hideSpinnerAndShowButton() {
32+
let spinnerTag = Int(bitPattern: Unmanaged.passUnretained(self).toOpaque())
33+
let spinner = self.superview?.subviews.first(where: { view -> Bool in
34+
return view.isKind(of: UIActivityIndicatorView.self) && view.tag == spinnerTag
35+
})
36+
37+
spinner?.removeFromSuperview()
38+
self.isHidden = false
39+
}
40+
}

iOSClient/Files/NCFiles.swift

Lines changed: 72 additions & 17 deletions
Original file line numberDiff line numberDiff line change
@@ -27,11 +27,10 @@ import RealmSwift
2727
import SwiftUI
2828

2929
class NCFiles: NCCollectionViewCommon {
30-
internal var isRoot: Bool = true
3130
internal var fileNameBlink: String?
3231
internal var fileNameOpen: String?
3332
internal var matadatasHash: String = ""
34-
internal var reloadDataSourceInProgress: Bool = false
33+
internal var semaphoreReloadDataSource = DispatchSemaphore(value: 1)
3534

3635
required init?(coder aDecoder: NSCoder) {
3736
super.init(coder: aDecoder)
@@ -50,7 +49,14 @@ class NCFiles: NCCollectionViewCommon {
5049
override func viewDidLoad() {
5150
super.viewDidLoad()
5251

53-
if isRoot {
52+
if self.serverUrl.isEmpty {
53+
54+
///
55+
/// Set ServerURL when start (isEmpty)
56+
///
57+
self.serverUrl = utilityFileSystem.getHomeServer(session: session)
58+
self.titleCurrentFolder = getNavigationTitle()
59+
5460
NotificationCenter.default.addObserver(forName: NSNotification.Name(rawValue: NCGlobal.shared.notificationCenterChangeUser), object: nil, queue: nil) { notification in
5561

5662
if let userInfo = notification.userInfo, let account = userInfo["account"] as? String {
@@ -88,10 +94,6 @@ class NCFiles: NCCollectionViewCommon {
8894
}
8995

9096
override func viewWillAppear(_ animated: Bool) {
91-
if isRoot {
92-
serverUrl = utilityFileSystem.getHomeServer(session: session)
93-
titleCurrentFolder = getNavigationTitle()
94-
}
9597
super.viewWillAppear(animated)
9698

9799
reloadDataSource()
@@ -122,12 +124,16 @@ class NCFiles: NCCollectionViewCommon {
122124
// MARK: - DataSource
123125

124126
override func reloadDataSource() {
125-
guard !isSearchingMode,
126-
!reloadDataSourceInProgress
127+
guard !isSearchingMode
127128
else {
128129
return super.reloadDataSource()
129130
}
130-
reloadDataSourceInProgress = true
131+
132+
// Watchdog: this is only a fail safe "dead lock", I don't think the timeout will ever be called but at least nothing gets stuck, if after 5 sec. (which is a long time in this routine), the semaphore is still locked
133+
//
134+
if self.semaphoreReloadDataSource.wait(timeout: .now() + 5) == .timedOut {
135+
self.semaphoreReloadDataSource.signal()
136+
}
131137

132138
var predicate = self.defaultPredicate
133139
let predicateDirectory = NSPredicate(format: "account == %@ AND serverUrl == %@", session.account, self.serverUrl)
@@ -145,14 +151,16 @@ class NCFiles: NCCollectionViewCommon {
145151
self.dataSource = NCCollectionViewDataSource(metadatas: metadatas, layoutForView: layoutForView)
146152

147153
if metadatas.isEmpty {
148-
reloadDataSourceInProgress = false
154+
self.semaphoreReloadDataSource.signal()
149155
return super.reloadDataSource()
150156
}
151157

152158
self.dataSource.caching(metadatas: metadatas, dataSourceMetadatas: dataSourceMetadatas) { updated in
153-
self.reloadDataSourceInProgress = false
154-
if updated || self.isNumberOfItemsInAllSectionsNull || self.numberOfItemsInAllSections != metadatas.count {
155-
super.reloadDataSource()
159+
self.semaphoreReloadDataSource.signal()
160+
DispatchQueue.main.async {
161+
if updated || self.isNumberOfItemsInAllSectionsNull || self.numberOfItemsInAllSections != metadatas.count {
162+
super.reloadDataSource()
163+
}
156164
}
157165
}
158166
}
@@ -178,6 +186,26 @@ class NCFiles: NCCollectionViewCommon {
178186
return false
179187
}
180188

189+
///
190+
/// Recommended files
191+
///
192+
if self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session),
193+
NCCapabilities.shared.getCapabilities(account: self.session.account).capabilityRecommendations {
194+
let options = NKRequestOptions(queue: NextcloudKit.shared.nkCommonInstance.backgroundQueue)
195+
196+
NextcloudKit.shared.getRecommendedFiles(account: self.session.account, options: options) { _, recommendations, _, error in
197+
if error == .success,
198+
let recommendations,
199+
!recommendations.isEmpty {
200+
Task.detached {
201+
await self.createRecommendations(recommendations)
202+
}
203+
} else {
204+
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadRecommendedFiles, userInfo: nil)
205+
}
206+
}
207+
}
208+
181209
DispatchQueue.global().async {
182210
self.networkReadFolder { metadatas, isChanged, error in
183211
DispatchQueue.main.async {
@@ -340,6 +368,33 @@ class NCFiles: NCCollectionViewCommon {
340368
}
341369
}
342370

371+
private func createRecommendations(_ recommendations: [NKRecommendation]) async {
372+
let home = self.utilityFileSystem.getHomeServer(session: self.session)
373+
var recommendationsToInsert: [NKRecommendation] = []
374+
375+
for recommendation in recommendations {
376+
var metadata = database.getResultMetadataFromFileId(recommendation.id)
377+
if metadata == nil || metadata?.fileName != recommendation.name {
378+
let serverUrlFileName = home + recommendation.directory + recommendation.name
379+
let results = await NCNetworking.shared.readFileOrFolder(serverUrlFileName: serverUrlFileName, depth: "0", showHiddenFiles: NCKeychain().showHiddenFiles, account: session.account)
380+
381+
if results.error == .success, let file = results.files?.first {
382+
let isDirectoryE2EE = self.utilityFileSystem.isDirectoryE2EE(file: file)
383+
let metadataConverted = self.database.convertFileToMetadata(file, isDirectoryE2EE: isDirectoryE2EE)
384+
metadata = metadataConverted
385+
386+
self.database.addMetadata(metadataConverted)
387+
recommendationsToInsert.append(recommendation)
388+
}
389+
} else {
390+
recommendationsToInsert.append(recommendation)
391+
}
392+
}
393+
394+
self.database.createRecommendedFiles(account: session.account, recommendations: recommendationsToInsert)
395+
NotificationCenter.default.postOnMainThread(name: NCGlobal.shared.notificationCenterReloadRecommendedFiles, userInfo: nil)
396+
}
397+
343398
// MARK: - NCAccountSettingsModelDelegate
344399

345400
override func accountSettingsDidDismiss(tableAccount: tableAccount?, controller: NCMainTabBarController?) {
@@ -349,9 +404,9 @@ class NCFiles: NCCollectionViewCommon {
349404
appDelegate.openLogin(selector: NCGlobal.shared.introLogin)
350405
} else if let account = tableAccount?.account, account != currentAccount {
351406
NCAccount().changeAccount(account, userProfile: nil, controller: controller) { }
352-
} else if isRoot {
353-
titleCurrentFolder = getNavigationTitle()
354-
navigationItem.title = titleCurrentFolder
407+
} else if self.serverUrl == self.utilityFileSystem.getHomeServer(session: self.session) {
408+
self.titleCurrentFolder = getNavigationTitle()
409+
navigationItem.title = self.titleCurrentFolder
355410
}
356411

357412
setNavigationLeftItems()

0 commit comments

Comments
 (0)