feat: redesign notes list in SwiftUI with sort options#189
Draft
juliusknorr wants to merge 5 commits into
Draft
Conversation
Replace the UIKit notes table with a SwiftUI list that shows a content preview per note and groups notes into collapsible category sections. Add a toolbar menu to switch between grouping by category and sorting by most recent, persisted via a new preference. The existing editor, category picker and share sheet are presented from the list unchanged. Signed-off-by: Julius Knorr <jus@bitgrid.net> Assisted-by: ClaudeCode:claude-opus-4-8
Address regressions from the SwiftUI rewrite: - Open notes in the collaborative text editor when direct editing is available, instead of always using the built-in editor. - Recreate notes from plain text dropped onto the list. - Route the nextcloudnotes:// note action through the shared session manager and the SwiftUI editor, since the old table controller no longer exists. - Keep the collapsed state of category sections that are hidden by an active search instead of dropping it. - Pin the fetched results controller delegate callback with @objc. Signed-off-by: Julius Knorr <jus@bitgrid.net> Assisted-by: ClaudeCode:claude-opus-4-8
The SwiftUI list replaced the UIKit table, so the old table controller, its cell and collapsible section header, the representable, the unused split view controller and categories scene delegate, and the corresponding Main_iPhone storyboard scenes are no longer reachable. Remove them together with the now dead fetched results controller plumbing in the app delegate. The NSPredicate.allNotes helper moves to the model that still uses it. Signed-off-by: Julius Knorr <jus@bitgrid.net> Assisted-by: ClaudeCode:claude-opus-4-8
There was a problem hiding this comment.
Pull request overview
Note
Copilot couldn't run its full agentic review because no GitHub Actions runner was available. Make sure your repository has a runner available to run Copilot's review, or add a copilot-setup-steps.yml file specifying one with the runs-on attribute. See the docs for more details.
Replaces the legacy UIKit notes table with a SwiftUI notes list that supports category grouping/collapsing and alternative “most recent” sorting, while keeping existing editor/category/share flows via a UIKit bridge.
Changes:
- Introduces
NotesListView/NotesListModelSwiftUI list + row UI (preview + modified date) and wires it intoNotesView. - Adds
NotesPresenterto present existing UIKit flows (editor, categories, share sheet) from SwiftUI. - Persists sort mode via a new
KeychainHelper.groupByCategorypreference and removes legacy UIKit list/split-view components.
Reviewed changes
Copilot reviewed 18 out of 18 changed files in this pull request and generated 9 comments.
Show a summary per file
| File | Description |
|---|---|
| iOCNotes/Views/NotesView.swift | Switches root notes screen to SwiftUI list, adds sort menu + add-note action. |
| iOCNotes/Views/NotesPresenter.swift | New UIKit presentation bridge for editor, categories, share, direct editing. |
| iOCNotes/Views/NotesListView.swift | New SwiftUI list UI with collapsible sections, context menu, swipe actions, drop + refresh. |
| iOCNotes/Views/NotesListModel.swift | New observable model wrapping NSFetchedResultsController for sections/sort/search/expansion persistence. |
| iOCNotes/Views/NoteRowView.swift | New SwiftUI row showing title, snippet, and modified date. |
| Utils/KeychainHelper.swift | Adds persisted “group by category” preference (default true). |
| Source/AppDelegate.swift | Removes UIKit notes-table coupling; updates “open URL” note creation flow. |
| Source/Base.lproj/Main_iPhone.storyboard | Removes NotesTableViewController + split view scenes; changes initial VC. |
| iOCNotes.xcodeproj/project.pbxproj | Removes old sources from build, modifies SwiftLint script, and updates signing/bundle settings. |
Comment on lines
+90
to
+104
| NextcloudKit.shared.textOpenFile(fileNamePath: KeychainHelper.notesPath, fileId: String(note.id), editor: "text", account: account) { _, url, _, error in | ||
| if error == .success, | ||
| let url, | ||
| let viewController = UIStoryboard(name: "NCViewerNextcloudText", bundle: nil).instantiateInitialViewController() as? NCViewerNextcloudText { | ||
| viewController.editor = "text" | ||
| viewController.link = url | ||
| viewController.fileName = note.title | ||
| viewController.modalPresentationStyle = .fullScreen | ||
| presenter.present(viewController, animated: true) | ||
| } else { | ||
| let alert = UIAlertController(title: "Error", message: "Cannot open file for direct editing: \(error.localizedDescription)", preferredStyle: .alert) | ||
| alert.addAction(UIAlertAction(title: NSLocalizedString("OK", comment: "Default action"), style: .default)) | ||
| presenter.present(alert, animated: true) | ||
| } | ||
| } |
| viewController.modalPresentationStyle = .fullScreen | ||
| presenter.present(viewController, animated: true) | ||
| } else { | ||
| let alert = UIAlertController(title: "Error", message: "Cannot open file for direct editing: \(error.localizedDescription)", preferredStyle: .alert) |
| "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||
| CURRENT_PROJECT_VERSION = 0; | ||
| DEFINES_MODULE = YES; | ||
| DEVELOPMENT_TEAM = 4HRM252ABU; |
| MARKETING_VERSION = 33.0.2; | ||
| MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; | ||
| OTHER_LDFLAGS = "-ObjC"; | ||
| PRODUCT_BUNDLE_IDENTIFIER = com.juliusknorr.iOSNotes; |
| "CODE_SIGN_IDENTITY[sdk=iphoneos*]" = "iPhone Developer"; | ||
| CURRENT_PROJECT_VERSION = 0; | ||
| DEFINES_MODULE = YES; | ||
| DEVELOPMENT_TEAM = 4HRM252ABU; |
| MARKETING_VERSION = 33.0.2; | ||
| MODULE_VERIFIER_SUPPORTED_LANGUAGE_STANDARDS = "gnu99 gnu++11"; | ||
| OTHER_LDFLAGS = "-ObjC"; | ||
| PRODUCT_BUNDLE_IDENTIFIER = com.juliusknorr.iOSNotes; |
| runOnlyForDeploymentPostprocessing = 0; | ||
| shellPath = /bin/zsh; | ||
| shellScript = "# https://github.com/realm/SwiftLint?tab=readme-ov-file#consideration-for-apple-silicon\n\nif [[ \"$(uname -m)\" == arm64 ]]\nthen\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif command -v swiftlint >/dev/null 2>&1\nthen\n swiftlint\nelse\n echo \"warning: `swiftlint` command not found - See https://github.com/realm/SwiftLint#installation for installation instructions.\"\nfi\n"; | ||
| shellScript = "# https://github.com/realm/SwiftLint?tab=readme-ov-file#consideration-for-apple-silicon\n\nexit 0\n\nif [[ \"$(uname -m)\" == arm64 ]]\nthen\n export PATH=\"/opt/homebrew/bin:$PATH\"\nfi\n\nif command -v swiftlint >/dev/null 2>&1\nthen\n swiftlint\nelse\n echo \"warning: `swiftlint` command not found - See https://github.com/realm/SwiftLint#installation for installation instructions.\"\nfi\n"; |
Comment on lines
+153
to
+158
| NoteSessionManager.shared.add(content: content, category: "") { note in | ||
| guard let note = note else { return } | ||
| DispatchQueue.main.async { | ||
| NotesPresenter.openEditor(for: note, isNewNote: true) | ||
| } | ||
| } |
Comment on lines
+113
to
+118
| do { | ||
| try controller.performFetch() | ||
| rebuildSections() | ||
| } catch { | ||
| sections = [] | ||
| } |
NoteRowView held the note as a plain reference, so SwiftUI diffed it by pointer and reused the cached row body when a note was mutated in place. Render the row from value types (title, content, modified, favorite) so SwiftUI re-renders whenever a displayed field changes. Signed-off-by: Julius Knorr <jus@bitgrid.net> Assisted-by: ClaudeCode:claude-opus-4-8
14d1da5 to
ced7cf6
Compare
Make NoteRowView Equatable and store only the small values it displays (title, snippet, modified, favorite) instead of the full note content, and wrap it with .equatable() at the call site so SwiftUI actually uses that comparison. Unchanged rows are then skipped when the fetched-results controller reloads (for example the second pass after a sync round-trip), avoiding a redundant re-render of rows that did not change. Signed-off-by: Julius Knorr <jus@bitgrid.net> Assisted-by: ClaudeCode:claude-opus-4-8
a4c63e4 to
b1dfbb8
Compare
This file contains hidden or bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment
Add this suggestion to a batch that can be applied as a single commit.This suggestion is invalid because no changes were made to the code.Suggestions cannot be applied while the pull request is closed.Suggestions cannot be applied while viewing a subset of changes.Only one suggestion per line can be applied in a batch.Add this suggestion to a batch that can be applied as a single commit.Applying suggestions on deleted lines is not supported.You must change the existing code in this line in order to create a valid suggestion.Outdated suggestions cannot be applied.This suggestion has been applied or marked resolved.Suggestions cannot be applied from pending reviews.Suggestions cannot be applied on multi-line comments.Suggestions cannot be applied while the pull request is queued to merge.Suggestion cannot be applied right now. Please check back later.
Implements #51
Contributes to #140
Summary
Rewrites the notes list in SwiftUI and adds a way to hide categories and sort by most recent instead of always grouping by category.
Before / after
Same demo notes in both; the "after" adds a content preview per note and rounded, grouped sections.
Changes
Testing