feat(sidebar): Add conversation sections and sort options#17368
feat(sidebar): Add conversation sections and sort options#17368Rikdekker wants to merge 12 commits intonextcloud:mainfrom
Conversation
19bd9ad to
ba23a61
Compare
Allow users to organize conversations into custom sections with drag-and-drop reordering, and sort conversations by recent activity, alphabetical order, or groups first. Resolves nextcloud#12025 Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Add type assertions in template to satisfy TypeScript type checker for union type ListItem (Conversation | SectionHeaderItem). Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
Fix import ordering, member delimiter style, missing JSDoc comments, unnecessary parentheses, and sorted named exports. Add mock for conversationSectionsService in LeftSidebar tests to prevent network errors during test execution. Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Rikdekker <Rikdekker@users.noreply.github.com>
Revert version back to 24.0.0-dev.2 (matching main branch) to fix "App not compatible with this version" error in integration tests. Add missing section_id key to ChatManagerTest attendee data arrays to fix "Undefined array key" PHP warning. Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Rikdekker <Rikdekker@users.noreply.github.com>
Update min-version and max-version to 34 to match the main branch target, fixing integration test failures. Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Rikdekker <Rikdekker@users.noreply.github.com>
293f1aa to
224cbd8
Compare
nickvergessen
left a comment
There was a problem hiding this comment.
This is a huge step 😎
However it will prevent a conversation from being in multiple sections, which we had planned. Not sure if it's better to merge like this already, or if we should try a second PR based on the current one that tries to make it possible to have 1toN mapping available
|
Also to be able to review easier, we'd appreciate if you address comments in individual commits going forward. we can rebase and combine them at the end, but it avoids having to recheck the full diff all the time :) |
|
Thanks for the review! 🙏 Will address all comments in individual commits. Regarding 1:1 vs 1:N section mapping: I believe 1:1 is the right model for sections — they behave like folders, not labels. A few observations:
If cross-cutting organization is ever needed, a separate tagging system would be more appropriate than multi-section — tags and folders solve fundamentally different problems. Happy to discuss further if you see use cases I'm missing! |
Add userId to findById() and clearSectionFromAttendees() to ensure sections can only be accessed/modified by their owner at the query level, preventing insecure direct object reference vulnerabilities. Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Rikdekker <Rikdekker@users.noreply.github.com>
Replace autoincrement with Snowflake ID pattern for the talk_conversation_sections table, matching the scheduled messages table design. Rename index to tcs_user_id. Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Rikdekker <Rikdekker@users.noreply.github.com>
Remove docs/conversation-sections.md (documentation is in OpenAPI specs). Revert CHANGELOG.md changes (done manually on release). Move capabilities entries from 23.x to section 24. Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com> Signed-off-by: Rikdekker <Rikdekker@users.noreply.github.com>
Well that is basically exactly the problem 😅
yeah, but you can have "folder behaviour" with tag API, but not the other way around.
This can be adjusted of course. I'll discuss it with the team next week |
nickvergessen
left a comment
There was a problem hiding this comment.
Frontend
-
we are migrating away from Vuex store, sortMode implementation can be moved to Pinia store settings.ts
-
UI: Filter + Sort buttons taking too much space, we can merge them into single entry point, with NcActionSeparator inside. Shared icon could be https://pictogrammers.com/library/mdi/icon/filter-cog-outline/ or https://pictogrammers.com/library/mdi/icon/filter-variant/
Design
-
Having a separate "Manage sections" entry at the bottom is lessening the already reduced vertical space, instead we can have a 3 dot menu button that appears on hover, with edit/reorder/delete which leads to the modal with that section in an editable state
Sample from mail app: https://github.com/user-attachments/assets/41903e40-116c-4871-a288-e3442ef9749c -
It seems the headers are not using our components
-
In the "Manage sections" modal

- each item seems to be taller than usual? Possibly it's 44px instead of default clickable area.
- To create a new section feels not accessible and inconsistent with how it's usually done in NC (lack of a button, only a text field with a placeholder, etc), we should add an explicit button, and then a text field
- There should also be "move up" and "move down" buttons in the 3 dot menu for a11y
-
Wording should be category / tag / label instead of "section"
We can also take over the Pull Request to address the feedback ourselves in case it's too much for you.
|
|
||
| $attendeesTable = $schema->getTable('talk_attendees'); | ||
| if (!$attendeesTable->hasColumn('section_id')) { | ||
| $attendeesTable->addColumn('section_id', Types::BIGINT, [ |
There was a problem hiding this comment.
Our plan would be instead of int make this a TEXT type column and store a json encoded array with the applicable section ids
docs/capabilities.md
Outdated
|
|
||
| ## 24 | ||
| * `conversation-sections` (local) - Whether the user can create custom sections to organize conversations in the sidebar | ||
| * `config => conversations => sort-mode` (local) - User selected sort mode for conversations (`activity`, `alphabetical`, or `type-first`) |
There was a problem hiding this comment.
This capability is just here in the docs but not available in the Capabilities.php
Either we clean it up and keep sorting BrowserStorage-only, or implement it via user preferences like config => chat => style currently works.
| <NcActionButton | ||
| closeAfterClick | ||
| type="radio" | ||
| :modelValue="sortMode === 'type-first'" |
There was a problem hiding this comment.
The team brought up that it feel like activity + alphabetical and type-first are two different categories - sorting and grouping, and users e.g. might want to combine 'groups first' or 'private first' with 'alphabetical' or 'activity'. Maybe we can store them as separate preferences, or a tuple.
Addresses all review feedback from nextcloud#17368: **Rename & terminology:** - Rename "sections" to "categories" throughout (backend, frontend, API) - API endpoints: /sections → /categories **Backend:** - IDOR fix: userId parameter on mapper queries - Snowflake ID for primary key - category_ids as TEXT/JSON column (single category per conversation) - Sort order and group mode as server-side user preferences - Consistent string-based ID handling for Snowflake IDs **Frontend:** - Merge filter + sort into single dropdown with NcActionSeparator - Split sort (activity/alphabetical) and group (type-first) into separate settings - Migrate sortMode from Vuex to Pinia settings store - Use NcAppNavigationItem with allowCollapse for category headers - Inline category management via 3-dot menu (rename, move up/down, delete) - New category creation via filter/sort dropdown - Delete confirmation using Nextcloud ConfirmDialog - Move up/down disabled on first/last category - Conversations move to "Other" when category is deleted (instant UI update) **Cleanup:** - Remove ManageSectionsDialog (replaced by inline management) - Remove standalone docs (use OpenAPI specs) - Revert CHANGELOG.md and appinfo/info.xml changes - Regenerate OpenAPI specs and TypeScript types Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
|
Thank you for the thorough review @nickvergessen! We've addressed all feedback points in this commit. Inline comment responses
Review body responses
ScreenshotsCreating a new category via the filter/sort dropdown:
Assigning a conversation to a category and the result:
|
Addresses all review feedback from nextcloud#17368: **Rename & terminology:** - Rename "sections" to "categories" throughout (backend, frontend, API) - API endpoints: /sections → /categories **Backend:** - IDOR fix: userId parameter on mapper queries - Snowflake ID for primary key - category_ids as TEXT/JSON column (single category per conversation) - Sort order and group mode as server-side user preferences - Consistent string-based ID handling for Snowflake IDs **Frontend:** - Merge filter + sort into single dropdown with NcActionSeparator - Split sort (activity/alphabetical) and group (type-first) into separate settings - Migrate sortMode from Vuex to Pinia settings store - Use NcAppNavigationItem with allowCollapse for category headers - Inline category management via 3-dot menu (rename, move up/down, delete) - New category creation via filter/sort dropdown - Delete confirmation using Nextcloud ConfirmDialog - Move up/down disabled on first/last category - Conversations move to "Other" when category is deleted (instant UI update) **Cleanup:** - Remove ManageSectionsDialog (replaced by inline management) - Remove standalone docs (use OpenAPI specs) - Revert CHANGELOG.md and appinfo/info.xml changes - Regenerate OpenAPI specs and TypeScript types Signed-off-by: Rikdekker <Rikdekker@users.noreply.github.com> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
1ba676a to
b500d87
Compare
Addresses all review feedback from nextcloud#17368: **Rename & terminology:** - Rename "sections" to "categories" throughout (backend, frontend, API) - API endpoints: /sections → /categories **Backend:** - IDOR fix: userId parameter on mapper queries - Snowflake ID for primary key - category_ids as TEXT/JSON column (single category per conversation) - Sort order and group mode as server-side user preferences - Consistent string-based ID handling for Snowflake IDs **Frontend:** - Merge filter + sort into single dropdown with NcActionSeparator - Split sort (activity/alphabetical) and group (type-first) into separate settings - Migrate sortMode from Vuex to Pinia settings store - Use NcAppNavigationItem with allowCollapse for category headers - Inline category management via 3-dot menu (rename, move up/down, delete) - New category creation via filter/sort dropdown - Delete confirmation using Nextcloud ConfirmDialog - Move up/down disabled on first/last category - Conversations move to "Other" when category is deleted (instant UI update) **Cleanup:** - Remove ManageSectionsDialog (replaced by inline management) - Remove standalone docs (use OpenAPI specs) - Revert CHANGELOG.md and appinfo/info.xml changes - Regenerate OpenAPI specs and TypeScript types Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
- Fix arrow function spacing: `fn($id)` → `fn ($id)` (php-cs-fixer) - Wrap array_filter/array_map in array_values() for psalm list type - Add sort-order and group-mode to CapabilitiesTest expectations and mocks Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
…arning) - Add psalm type annotation for json_decode result in ConversationCategoryMapper - Handle missing category_ids key in AttendeeMapper::createAttendeeFromRow Signed-off-by: Rikdekker <rik@shalution.nl> Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
b500d87 to
b37ee63
Compare



Summary
Resolves #12025
Backend
talk_conversation_sectionstable withid,user_id,name,sort_order,collapsedcolumnsConversationSectionControllerwith full CRUD + reorder API (/api/v4/sections)sectionIdfield onAttendeemodel to assign conversations to sectionsconversation-sectionsFrontend
ConversationSectionHeadercomponent for collapsible section headersManageSectionsDialogwith drag-and-drop reorderingconversationSectionsPinia storeScreenshots
Test plan
🤖 Generated with Claude Code