Skip to content

feat(folders): add folder management - #729

Merged
farfromrefug merged 8 commits into
mainfrom
feat/684-folder-management
Sep 14, 2026
Merged

farfromrefug merged 8 commits into
mainfrom
feat/684-folder-management

Conversation

@farfromrefug

Copy link
Copy Markdown
Collaborator

Summary

  • Settings > Folders > manage folders: lists every folder with its document count, including the empty ones the main list hides, and deletes them from a selection toolbar or in bulk with "delete empty folders". Deleting a folder cascades to its subfolders, and when documents are involved the confirmation lets the user keep them (they go back to the root list) or delete them, honouring the trash setting.
  • Folder deletion in the data layer: DocumentsFolders rows are removed explicitly, since foreign keys are off on this database and the declared ON DELETE CASCADE never fires.
  • Deleted folders now stick across devices: folder sync was a plain union, so a folder missing locally was recreated from the remote manifest on the next both-ways sync. Folders get tombstones mirroring the existing deleted_documents.json mechanism.
  • Fix: moving a document to a subfolder in one step (work/2024) created only that row, and since the folder list groups by first path component and drops names containing a slash, neither the folder nor the document showed up anywhere. Missing ancestors are now created, with a migration backfilling existing databases.
  • Patch ui-collectionview 6.0.26 so replacing the items array from inside onBindViewHolder no longer throws IllegalStateException (upstream: fix(android): do not refresh while the RecyclerView computes its layout nativescript-community/ui-collectionview#106). Drop the patch once that ships.

Testing

  • yarn test (307 passing) — new unit tests cover the folder cascade/ancestor helpers and the folder tombstone merge; each was mutation-checked.
  • yarn svelte-check clean, eslint clean on the changed files.
  • Visual check still required. The screen, the delete dialogs and the sync path were not verified on device in this branch, and the collectionview patch adds a native API to the whitelist, so it needs a full rebuild (ns run android --clean), not a livesync. Worth exercising: delete an empty folder, delete one holding documents with both answers, delete a parent holding subfolders, and a two-device sync where a folder deleted on A disappears on B.

Refs #684

🤖 Generated with Claude Code

farfromrefug and others added 8 commits September 14, 2026 15:12
Folders are tags: emptying one leaves its row forever with no way to remove it.
Add FolderRepository.findAllFolders (flat list with document count, subfolders
kept as their own rows) and DocumentsService.deleteFolders, which drops the
DocumentsFolders rows explicitly since foreign keys are off on this database,
then notifies EVENT_FOLDER_DELETED so the main list refreshes.

folderUtils holds the pure cascade rule ("work" also takes "work/2024", never
"workshop") so it can be unit-tested outside the NativeScript runtime.

Refs #684

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Add Settings > Folders > "manage folders": lists every folder with its document
count, including the empty ones the main list hides, and deletes them from a
selection toolbar or in bulk through "delete empty folders".

Deleting cascades to subfolders since they only exist through their name. When
documents are involved the confirmation lets the user keep them — they go back
to the root list — or delete them, honouring the trash setting.

Refs #684

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Folder sync was a plain union: a folder missing locally was recreated from the
remote manifest, so deleting one always came back on the next both-ways sync.

Mirror the document tombstones for folders: deleting a folder queues its id per
service, the ids land in a deleted_folders.json manifest, and both sync
directions drop tombstoned folders from the remote list instead of resurrecting
them - the both-ways pass also deletes them locally, which propagates a
deletion made on another device.

Generalise mergeDeletedDocumentTombstones into mergeTombstones so folders reuse
it with numeric ids, keeping the document wrapper and its behavior unchanged.

Refs #684

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Moving a document to "a/b" only created the "a/b" row, but the folder list
groups by first path component and drops every entry whose name still contains
a slash - so the group had no row to render and the document disappeared from
both the folder list and the root list.

Create the missing ancestors whenever a folder name introduces them, on move
and on rename, and backfill existing databases through a migration so already
orphaned documents show up again.

Refs #684

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Deleting a folder while keeping its documents unfiles them, so they belong to
the root list, but the list only rebuilt its folder rows and the documents
stayed hidden until the next app start. Refresh the whole list instead, and
leave the folder view when the folder it shows is the one being deleted.

Refs #684

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
Replacing the items array reaches the collectionview refresh() from inside
onBindViewHolder, where notifyDataSetChanged throws IllegalStateException:
the list crashed when a document was moved between folders while cells were
binding.

Patch ui-collectionview 6.0.26 (upstream PR nativescript-community/ui-collectionview#106)
to defer such a refresh to the layout callback fired after super.onLayout, with
a one-shot OnPreDrawListener for the scroll case, and to keep serving the data
the running pass started with. Follows the svelte-check patch already in this
repo; drop it once the fix ships upstream.

Guard itemTemplateSpanSize against a missing item, like the CardsList template
selector already does.

Refs #684

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
The one-way folder sync pushed a folder as its JSON string, so the manifest
ended up holding a string where every other path stores an object. Such an
entry matches no local folder by id: it is pushed again on every sync, it
cannot be matched by the deleted folder tombstones, and a both-ways sync tries
to create a local folder out of it.

Refs #684

Co-Authored-By: Claude Opus 5 <noreply@anthropic.com>
@farfromrefug
farfromrefug marked this pull request as ready for review September 14, 2026 15:31
@farfromrefug
farfromrefug merged commit 6d9ec73 into main Sep 14, 2026
1 check passed
@farfromrefug
farfromrefug deleted the feat/684-folder-management branch September 14, 2026 15:31
Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

None yet

Projects

None yet

Development

Successfully merging this pull request may close these issues.

1 participant