Skip to content

Commit 2ce8f80

Browse files
committed
Merge remote-tracking branch 'origin/master' into nmc/master/custom_main_window
Signed-off-by: Jyrki Gadinger <nilsding@nilsding.org>
2 parents 206ea43 + 715ba36 commit 2ce8f80

162 files changed

Lines changed: 19753 additions & 21460 deletions

File tree

Some content is hidden

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

.github/workflows/clang-format.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -9,7 +9,7 @@ jobs:
99
runs-on: ubuntu-22.04
1010
steps:
1111
- uses: actions/checkout@de0fac2e4500dabe0009e67214ff5f5447ce83dd # v6.0.2
12-
- uses: cpp-linter/cpp-linter-action@b6edc0625e3941baa1797f4b4326adeab6890c97 # v2.16.7
12+
- uses: cpp-linter/cpp-linter-action@24467985494bed9bfc398489b6ec12469beaf4da # v2.17.0
1313
id: linter
1414
env:
1515
GITHUB_TOKEN: ${{ secrets.GITHUB_TOKEN }}

.github/workflows/linux-appimage.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -24,7 +24,7 @@ jobs:
2424
2525
- name: Upload AppImage artifact
2626
id: upload-appimage
27-
uses: actions/upload-artifact@b7c566a772e6b6bfb58ed0dc250532a479d7789f # v6.0.0
27+
uses: actions/upload-artifact@bbbca2ddaa5d8feaa63e36b76fdaad77386f024f # v7.0.0
2828
with:
2929
name: nextcloud-appimage-pr-${{ github.event.number }}
3030
path: ${{ steps.build-appimage.outputs.APPIMAGE_NAME }}

.github/workflows/stale.yml

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -17,7 +17,7 @@ jobs:
1717
issues: write
1818

1919
steps:
20-
- uses: actions/stale@997185467fa4f803885201cee163a9f38240193d # v10.1.1
20+
- uses: actions/stale@b5d41d4e1d5dceea10e7104786b73624c18a190f # v10.2.0
2121
with:
2222
operations-per-run: 1500
2323
days-before-stale: 28

NEXTCLOUD.cmake

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -31,7 +31,7 @@ set( APPLICATION_ICON_SET "SVG" )
3131
set( APPLICATION_SERVER_URL "" CACHE STRING "URL for the server to use. If entered, the UI field will be pre-filled with it" )
3232
set( APPLICATION_SERVER_URL_ENFORCE ON ) # If set and APPLICATION_SERVER_URL is defined, the server can only connect to the pre-defined URL
3333
set( APPLICATION_REV_DOMAIN "com.nextcloud.desktopclient" )
34-
set( DEVELOPMENT_TEAM "NKUJUXUJ3B" CACHE STRING "Apple Development Team ID for code signing" )
34+
set( DEVELOPMENT_TEAM "NKUJUXUJ3B" CACHE STRING "Apple Development Team ID" )
3535
set( APPLICATION_VIRTUALFILE_SUFFIX "nextcloud" CACHE STRING "Virtual file suffix (not including the .)")
3636
set( APPLICATION_OCSP_STAPLING_ENABLED OFF )
3737
set( APPLICATION_FORBID_BAD_SSL OFF )

Nextcloud Desktop Client.xcworkspace/contents.xcworkspacedata

Lines changed: 3 additions & 0 deletions
Some generated files are not rendered by default. Learn more about customizing how changed files appear on GitHub.

README.md

Lines changed: 12 additions & 10 deletions
Original file line numberDiff line numberDiff line change
@@ -7,25 +7,26 @@
77

88
[![REUSE status](https://api.reuse.software/badge/github.com/nextcloud/desktop)](https://api.reuse.software/info/github.com/nextcloud/desktop)
99

10-
The Nextcloud Desktop Client is a tool to synchronize files from Nextcloud Server with your computer.
10+
The Nextcloud Desktop Client is an app to synchronize files from Nextcloud Server with your computer.
1111

1212
<p align="center">
1313
<img src="doc/images/main_dialog_christine.png" alt="Desktop Client on Windows" width="450">
1414
</p>
1515

16-
## Releases 🚀
17-
For the latest stable recommended version, please refer to the [download page https://nextcloud.com/install/#install-clients](https://nextcloud.com/install/#install-clients)
16+
## Downloads 🚀
17+
For the latest stable and recommended version, please refer to [the official download page](https://nextcloud.com/install/#install-clients).
1818

19-
## Contributing to the desktop client 🫴
19+
## Contributing 🫴
2020
:v: Please read the [Code of Conduct](https://nextcloud.com/community/code-of-conduct/). This document offers some guidance to ensure Nextcloud participants can cooperate effectively in a positive and inspiring atmosphere and to explain how together we can strengthen and support each other.
2121

2222
## Join the team 👪
2323
There are many ways to contribute, of which development is only one! Find out [how to get involved](https://nextcloud.com/contribute/), including as a translator, designer, tester, helping others, and much more! 😍
2424

2525
## Help testing 🔬
26-
Download and install the client:<br>
27-
[🔽 All releases](https://github.com/nextcloud-releases/desktop/releases)<br>
28-
[🔽 Daily master builds](https://download.nextcloud.com/desktop/daily)
26+
Download and install the client:
27+
28+
- [All releases](https://github.com/nextcloud-releases/desktop/releases)<br>
29+
- [Daily builds](https://download.nextcloud.com/desktop/daily)
2930

3031
## Reporting issues 🐛
3132
If you find any bugs or have any suggestion for improvement, please
@@ -100,11 +101,12 @@ This simple test server already suffices in the most cases. For more advanced se
100101

101102
## Get in touch 💬
102103
* [📋 Forum](https://help.nextcloud.com)
103-
* [👥 Facebook](https://www.facebook.com/nextclouders)
104-
* [🐣 Twitter](https://twitter.com/Nextclouders)
105104
* [🐘 Mastodon](https://mastodon.xyz/@nextcloud)
105+
* [🔗 LinkedIn](https://www.linkedin.com/company/nextcloud-gmbh/)
106+
* [🦋 Bluesky](https://bsky.app/profile/nextcloud.bsky.social)
107+
* [👥 Facebook](https://www.facebook.com/nextclouders)
106108

107-
You can also [get support for Nextcloud](https://nextcloud.com/support)!
109+
You can also [get professional support for Nextcloud and the desktop client](https://nextcloud.com/support)!
108110

109111
## License 📜
110112

admin/osx/mac-crafter/Sources/Commands/Build.swift

Lines changed: 25 additions & 6 deletions
Original file line numberDiff line numberDiff line change
@@ -91,6 +91,9 @@ struct Build: AsyncParsableCommand {
9191
@Flag(help: "Build File Provider Module.")
9292
var buildFileProviderModule = false
9393

94+
@Flag(help: "Build without QtWebEngine.")
95+
var withoutWebEngine = false
96+
9497
@Flag(help: "Build without Sparkle auto-updater.")
9598
var disableAutoUpdater = false
9699

@@ -202,7 +205,8 @@ struct Build: AsyncParsableCommand {
202205
"\(craftBlueprintName).osxArchs=\(arch)",
203206
"\(craftBlueprintName).buildTests=\(buildTests ? "True" : "False")",
204207
"\(craftBlueprintName).buildMacOSBundle=\(disableAppBundle ? "False" : "True")",
205-
"\(craftBlueprintName).buildFileProviderModule=\(buildFileProviderModule ? "True" : "False")"
208+
"\(craftBlueprintName).buildFileProviderModule=\(buildFileProviderModule ? "True" : "False")",
209+
"\(craftBlueprintName).buildWithWebEngine=\(withoutWebEngine ? "False" : "True")"
206210
]
207211

208212
if let overrideServerUrl {
@@ -303,18 +307,32 @@ struct Build: AsyncParsableCommand {
303307
// which allows breakpoints in extension source files to be resolved correctly.
304308

305309
if dev {
306-
Log.info("Copying extension dSYM bundles into product app bundle for debugging...")
310+
let dSYM = clientBuildURL
311+
.appendingPathComponent("image-\(buildType)-master-dbg")
312+
.appendingPathComponent("\(appName).app.dSYM")
313+
314+
let binaryLocation = clientAppURL
315+
.appendingPathComponent("Contents")
316+
.appendingPathComponent("MacOS")
317+
.appendingPathComponent("\(appName).app.dSYM")
318+
319+
Log.info("Copying main dSYM bundle at \"\(dSYM.path)\" into product app bundle \"\(binaryLocation.path)\" for debugging...")
307320

308-
// XCODE_TARGET_CONFIGURATION in CMakeLists.txt is always "Debug" when NEXTCLOUD_DEV=ON,
309-
// regardless of the CMake build type passed to Craft.
310-
let xcodeTargetConfiguration = "Debug"
321+
if fm.fileExists(atPath: binaryLocation.path) {
322+
Log.info("Removing already existing main dSYM bundle at \"\(binaryLocation.path)\"...")
323+
try fm.removeItem(at: binaryLocation)
324+
}
325+
326+
try fm.copyItem(at: dSYM, to: binaryLocation)
327+
328+
Log.info("Copying extension dSYM bundles into product app bundle for debugging...")
311329

312330
let shellIntegrationBuildDir = clientBuildURL
313331
.appendingPathComponent("work")
314332
.appendingPathComponent("build")
315333
.appendingPathComponent("shell_integration")
316334
.appendingPathComponent("MacOSX")
317-
.appendingPathComponent(xcodeTargetConfiguration)
335+
.appendingPathComponent(buildType)
318336

319337
let plugInsDir = clientAppURL
320338
.appendingPathComponent("Contents")
@@ -332,6 +350,7 @@ struct Build: AsyncParsableCommand {
332350
let destination = plugInsDir.appendingPathComponent(dSYM.lastPathComponent)
333351

334352
if fm.fileExists(atPath: destination.path) {
353+
Log.info("Removing already existing extension dSYM bundle at \"\(destination.path)\"...")
335354
try fm.removeItem(at: destination)
336355
}
337356

shell_integration/MacOSX/NextcloudFileProviderKit/Sources/NextcloudFileProviderKit/Database/FilesDatabaseManager.swift

Lines changed: 103 additions & 42 deletions
Original file line numberDiff line numberDiff line change
@@ -447,21 +447,53 @@ public final class FilesDatabaseManager: Sendable {
447447
}
448448
}
449449

450+
///
451+
/// Mark an item as deleted.
452+
///
453+
/// This is a soft delete and does not actually delete data for which there is ``removeItemMetadata(ocId:)``.
454+
///
455+
/// - Parameters:
456+
/// - ocId: The unique identifier of the item.
457+
///
450458
@discardableResult public func deleteItemMetadata(ocId: String) -> Bool {
451459
do {
452460
let results = itemMetadatas.where { $0.ocId == ocId }
453461
let database = ncDatabase()
462+
454463
try database.write {
455-
logger.debug("Deleting item metadata. \(ocId)")
456464
results.forEach { $0.deleted = true }
465+
logger.debug("Marked item as deleted.", [.item: ocId])
457466
}
467+
458468
return true
459469
} catch {
460-
logger.error("Could not delete item metadata with ocId \(ocId).", [.error: error])
470+
logger.error("Could not mark item as deleted.", [.item: ocId, .error: error])
461471
return false
462472
}
463473
}
464474

475+
///
476+
/// Hard delete an item.
477+
///
478+
/// Unlike ``deleteItemMetadata(ocId:)``, this actually deletes a data record.
479+
///
480+
/// - Parameters:
481+
/// - ocId: The unique identifier of the item.
482+
///
483+
public func removeItemMetadata(ocId: String) {
484+
do {
485+
let database = ncDatabase()
486+
let results = itemMetadatas.where { $0.ocId == ocId }
487+
488+
try database.write {
489+
database.delete(results)
490+
logger.debug("Removed item metadata from database.", [.item: ocId])
491+
}
492+
} catch {
493+
logger.error("Could not remove item metadata.", [.item: ocId, .error: error])
494+
}
495+
}
496+
465497
public func renameItemMetadata(ocId: String, newServerUrl: String, newFileName: String) {
466498
guard let itemMetadata = itemMetadatas.where({ $0.ocId == ocId }).first else {
467499
logger.error("Could not find an item with ocID \(ocId) to rename to \(newFileName)")
@@ -538,13 +570,12 @@ public final class FilesDatabaseManager: Sendable {
538570
return NSFileProviderItemIdentifier(parentMetadata.ocId)
539571
}
540572

541-
private func managedMaterialisedItemMetadatas(account: String) -> Results<RealmItemMetadata> {
573+
private func managedMaterialisedItemMetadatas() -> Results<RealmItemMetadata> {
542574
itemMetadatas.where { candidate in
543-
let belongsToAccount = candidate.account == account
544575
let isVisitedDirectory = candidate.directory && candidate.visitedDirectory
545576
let isDownloadedFile = candidate.directory == false && candidate.downloaded
546577

547-
return belongsToAccount && (isVisitedDirectory || isDownloadedFile)
578+
return isVisitedDirectory || isDownloadedFile
548579
}
549580
}
550581

@@ -556,67 +587,97 @@ public final class FilesDatabaseManager: Sendable {
556587
///
557588
/// - Returns: An array of sendable metadata objects.
558589
///
559-
public func materialisedItemMetadatas(account: String) -> [SendableItemMetadata] {
560-
managedMaterialisedItemMetadatas(account: account).toUnmanagedResults()
590+
public func materialisedItemMetadatas(account _: String) -> [SendableItemMetadata] {
591+
managedMaterialisedItemMetadatas().toUnmanagedResults()
561592
}
562593

563-
public func pendingWorkingSetChanges(account: Account, since date: Date) -> (updated: [SendableItemMetadata], deleted: [SendableItemMetadata]) {
564-
let accId = account.ncKitAccount
565-
let pending = managedMaterialisedItemMetadatas(account: accId).where { $0.syncTime > date }
566-
var updated = pending.where { !$0.deleted }.toUnmanagedResults()
567-
var deleted = pending.where { $0.deleted }.toUnmanagedResults()
568-
var handledUpdateOcIds = Set(updated.map(\.ocId))
594+
///
595+
/// Look up the not yet synchronized changes and deletions in the materialized items since the last given synchronization time.
596+
///
597+
/// - Parameters:
598+
/// - date: All items with a synchronization time later than this are considered.
599+
///
600+
/// - Returns: Locally changed items in the working set grouped by "updated" and "deleted".
601+
///
602+
public func pendingWorkingSetChanges(since date: Date) -> (updated: [SendableItemMetadata], deleted: [SendableItemMetadata]) {
603+
logger.debug("Gathering pending working set changes...")
604+
let pendingChanges = managedMaterialisedItemMetadatas().where { $0.syncTime > date }
605+
var updatedItems = pendingChanges.where { !$0.deleted }.toUnmanagedResults()
606+
var deletedItems = pendingChanges.where { $0.deleted }.toUnmanagedResults()
607+
608+
for item in updatedItems {
609+
logger.debug("Found updated item.", [.item: item.ocId, .name: item.fileName])
610+
}
569611

570-
updated
571-
.map { $0.remotePath() }
572-
.forEach { serverUrl in
573-
logger.debug("Checking updated item...", [.url: serverUrl])
612+
for item in deletedItems {
613+
logger.debug("Found deleted item.", [.item: item.ocId, .name: item.fileName])
614+
}
574615

616+
var updatedItemIdentifiers = Set(updatedItems.map(\.ocId))
617+
var deletedItemIdentifiers = Set(deletedItems.map(\.ocId))
618+
619+
updatedItems // Look for changed children
620+
.filter {
621+
$0.directory // files do not have any children to look for
622+
}
623+
.map {
624+
$0.remotePath()
625+
}
626+
.forEach { serverUrl in
575627
itemMetadatas
576-
.where { $0.serverUrl == serverUrl && $0.syncTime > date }
577-
.forEach { metadata in
578-
guard !handledUpdateOcIds.contains(metadata.ocId) else {
579-
return
580-
}
628+
.where {
629+
$0.serverUrl == serverUrl && $0.syncTime > date
630+
}
631+
.forEach { child in
632+
let sendableMetadata = SendableItemMetadata(value: child)
581633

582-
handledUpdateOcIds.insert(metadata.ocId)
583-
let sendableMetadata = SendableItemMetadata(value: metadata)
634+
if child.deleted {
635+
guard deletedItemIdentifiers.contains(child.ocId) == false else {
636+
return
637+
}
584638

585-
if metadata.deleted {
586-
deleted.append(sendableMetadata)
587-
logger.debug("Appended deleted item to working set changes.", [.item: metadata.ocId, .url: serverUrl])
639+
deletedItemIdentifiers.insert(child.ocId)
640+
deletedItems.append(sendableMetadata)
641+
logger.debug("Appended deleted item to working set changes.", [.item: child.ocId, .url: serverUrl])
588642
} else {
589-
updated.append(sendableMetadata)
590-
logger.debug("Appended updated item to working set changes.", [.item: metadata.ocId, .url: serverUrl])
643+
guard updatedItemIdentifiers.contains(child.ocId) == false else {
644+
return
645+
}
646+
647+
updatedItemIdentifiers.insert(child.ocId)
648+
updatedItems.append(sendableMetadata)
649+
logger.debug("Appended updated item to working set changes.", [.item: child.ocId, .url: serverUrl])
591650
}
592651
}
593652
}
594653

595-
let handledDeleteOcIds = Set(deleted.map(\.ocId))
596-
597-
deleted
598-
.map { $0.remotePath() }
654+
deletedItems // Look for deleted children recursively
655+
.filter {
656+
$0.directory // files do not have any children to look for
657+
}
658+
.map {
659+
$0.remotePath()
660+
}
599661
.forEach { serverUrl in
600-
logger.debug("Verifying deleted item...", [.url: serverUrl])
601-
602662
itemMetadatas.where {
603663
$0.serverUrl.starts(with: serverUrl) && $0.syncTime > date
604-
}.forEach { metadata in
605-
guard metadata.isLockFileOfLocalOrigin == false else {
606-
logger.info("Excluding item from deletion because it is a lock file from local origin.", [.item: metadata.ocId])
664+
}.forEach { child in
665+
guard child.isLockFileOfLocalOrigin == false else {
666+
logger.info("Excluding item from deletion because it is a lock file from local origin.", [.item: child.ocId, .name: child.fileName])
607667
return
608668
}
609669

610-
guard !handledDeleteOcIds.contains(metadata.ocId) else {
670+
guard !deletedItemIdentifiers.contains(child.ocId) else {
611671
return
612672
}
613673

614-
deleted.append(SendableItemMetadata(value: metadata))
615-
logger.debug("Appended deleted item to working set changes.", [.item: metadata.ocId, .url: serverUrl])
674+
deletedItemIdentifiers.insert(child.ocId)
675+
deletedItems.append(SendableItemMetadata(value: child))
676+
logger.debug("Appended deleted item to working set changes.", [.item: child.ocId, .url: serverUrl])
616677
}
617678
}
618679

619-
return (updated, deleted)
680+
return (updatedItems, deletedItems)
620681
}
621682

622683
public func itemsMetadataByFileNameSuffix(suffix: String) -> [SendableItemMetadata] {

shell_integration/MacOSX/NextcloudFileProviderKit/Sources/NextcloudFileProviderKit/Enumeration/Enumerator+SyncEngine.swift

Lines changed: 2 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -196,7 +196,7 @@ extension Enumerator {
196196
metadata.downloaded = existing?.downloaded == true
197197
metadata.keepDownloaded = existing?.keepDownloaded == true
198198
dbManager.addItemMetadata(metadata)
199-
return ([metadata], newItems, updatedItems, nil, nextPage, nil)
199+
return ([metadata], newItems, updatedItems, [], nextPage, nil)
200200
}
201201
}
202202

@@ -211,7 +211,7 @@ extension Enumerator {
211211
metadata.keepDownloaded = existing?.keepDownloaded == true
212212
dbManager.addItemMetadata(metadata)
213213

214-
return ([metadata], newMetadatas, updatedMetadatas, nil, nextPage, nil)
214+
return ([metadata], newMetadatas, updatedMetadatas, [], nextPage, nil)
215215
} else if depth == .targetAndDirectChildren {
216216
let (allMetadatas, newMetadatas, updatedMetadatas, deletedMetadatas, readError) = await handleDepth1ReadFileOrFolder(
217217
serverUrl: serverUrl,

0 commit comments

Comments
 (0)