feat: Favorites for projects & tasks - EXO-88316 - #622
Conversation
Adds Favorites support for Task projects and tasks, reusing the platform favorites framework end to end A "Make it a favorite" / "Remove from favorite" row in the 3-dots menu of project cards and the task drawer.Projects and Tasks sections in the global top-bar Favorites drawer, with items resolving their own title/icon/link No new REST/DAO/schema — favorites ride the generic /v1/social/favorites API, and the existing ProjectAclPlugin/TaskAclPlugin (object types project/task) already gate access. (cherry picked from commit 949d1f6)
|
There was a problem hiding this comment.
(Review par Claude Code)
Reviewed the diff, source of the modified files on backportdev-88316, and the CI/SonarCloud results. Overall the feature itself (favorites for projects/tasks via the generic /v1/social/favorites API, no new REST/DAO/schema) looks solid, but a few things should be addressed before merging.
Out-of-scope file
This PR adds a 63-line CLAUDE.md at the repo root, unrelated to the Favorites feature. Traced it to commit 9c341e4c ("feat: add favorite star to the project board header") on the source branch — looks like it was committed accidentally (AI coding assistant artifact) and swept into this squashed cherry-pick. It doesn't exist on develop yet. Suggest dropping it from this PR (or landing it separately if it's wanted).
Confirmed by SonarCloud (5 new MAJOR issues)
ProjectFavoriteMenuAction.vue:93andTaskFavoriteMenuItem.vue:93mutate theproject/taskprop directly (this.project.favorite = this.isFavorite) instead of emitting an update to the parent — a real Vue anti-pattern that can cause reactivity bugs if the parent reuses the same object elsewhere.removedevent triggered but not declared inemitsonProjectFavoriteItem.vue/TaskFavoriteItem.vue.TaskRestService's constructor now has 9 parameters (>7 allowed) — see duplication note below, which is the root cause.
Duplication
isFavorite(objectType, objectId)is copy-pasted verbatim in bothProjectRestServiceandTaskRestService. Worth factoring into a shared helper — would also fix the constructor param-count warning above.ProjectFavoriteMenuAction.vueandTaskFavoriteMenuItem.vueduplicate ~90% of their toggle/alert/event logic, whereas the drawer'sfavorite-buttonis a shared generic component. These two could follow the same pattern.
Performance — N+1 risk
ProjectRestService.buildJSON() loops over the project list and calls buildJsonProject() per project, which now calls isFavorite() per project — i.e., one identity resolution + one FavoriteService.isFavorite() call per project on every GET project list. FavoriteService has no batch "is-favorite" check, but exposes getFavoriteItemsByCreatorAndType(objectType, creatorId, offset, limit) — fetching the current user's favorite project ids once and checking membership in a Set would avoid the per-row round trip.
Minor
- No test asserts the new
favoriteflag is actually populated in the JSON/DTO output — the updated tests only thread the new mocked dependencies through existing constructors. Coverage on new code is 66.7% per Sonar, consistent with this gap.
Nothing here blocks the underlying feature design (ACL reuse, ProjectAclPlugin/TaskAclPlugin scoping, and the current-user-only isFavorite check all look correct and safe) — these are polish/cleanup items, plus the stray CLAUDE.md which shouldn't ship in this PR.
…88316 - Remove out-of-scope CLAUDE.md accidentally swept into the squashed cherry-pick - Stop mutating the project/task prop in ProjectFavoriteMenuAction.vue and TaskFavoriteMenuItem.vue (Sonar javascript:S8951); the shared favorite-button pattern already syncs state via the metadata.favorite.updated event - Declare the removed event in emits on ProjectFavoriteItem.vue/TaskFavoriteItem.vue (Sonar javascript:S8961) - Extract the duplicated isFavorite() logic from ProjectRestService and TaskRestService into a shared org.exoplatform.task.util.FavoriteUtil - Fix N+1 favorite lookup in ProjectRestService.buildJSON(): batch-fetch the current user's favorite project ids once via FavoriteService#getFavoriteItemsByCreatorAndType instead of calling isFavorite() per project in the list - Add assertions in TestProjectRestService/TestTaskRestService covering the new favorite field enrichment Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
|
Thanks for the review — all points have been addressed in 37733a9:
CI on 37733a9: PR Build ✅. Sonar Analysis had failed on a transient SonarCloud infra timeout (504 on JRE metadata fetch, before any code analysis) — re-triggered the job. Ready for re-review. |
- Update copyright end year (2025 -> 2026) in all files touched by the previous fix commit, matching the convention already used elsewhere in the repo for files edited in 2026 - Collapse FavoriteUtil call sites and signatures that were wrapped across multiple lines despite fitting well within the 200-char limit, for consistency with the existing single-line style used throughout the util/rest packages (e.g. TaskRestService's own FavoriteUtil call) Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Copyright end year should only move to the current year on files that didn't exist before (new files). Revert the previous commit's header bump on files that already existed and were merely modified (ProjectRestService, TaskRestService, TestProjectRestService, TestTaskRestService), and apply the 2026 bump to the remaining new files from the original squashed commit that still showed 2025 (favorite-extensions/task-favorite-menu main.js/extensions.js/ initComponents.js and ProjectFavoriteBoardHeaderAction.vue). Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
## What Backport of #619, #624, #628, #629 onto `develop`. Adds a **"Favorites only"** filter for tasks (follow-up to Favorites #615 / EXO-88316), with the fixes that followed after real-world testing: - **#619** — `favorite` query param on `GET /tasks/filter` (batched via `FavoriteService`) + a "Favorites only" toggle in the Sort & Filter drawer, persisted in localStorage. - **#624** — un-bookmarking a task now removes it from the favorites-filtered list immediately (list rows carry `favorite` state, front-end listens for `metadata.favorite.updated`); drawer star recolored to neutral grey. - **#628** — the persisted "Favorites only" toggle is now honored on the *first* fetch after a page reload in My Tasks (was previously applied only after the initial unfiltered fetch). - **#629** — same reload fix extended to the project/space board view, which #628 had missed. ## Backport notes Cherry-picked the 4 commits onto `develop`. Two conflicts, both resolved: - `TaskRestService.java`: import conflict (develop already had `FavoriteService` imported for an unrelated feature); also dropped a private `isFavorite(String, long)` helper introduced by #624 that duplicated `FavoriteUtil.isFavorite`, already present on `develop` since #622 (`Favorites for projects & tasks - EXO-88316`) — it was unused here since `getTaskById` on `develop` already goes through `FavoriteUtil`. - `TasksViewDashboard.vue`: #629's diff assumed `favorite` had been dropped from this file by an intermediate commit that only exists on `feature/ai-contribution`, not `develop` — on `develop` the field was already being carried through (from #619/#624), so the "fix" was already effectively in place; resolved by keeping upstream's exact wording where it was purely a stylistic difference (`!!x` vs `x || false`), for consistency. ## Test - `mvn -pl services -am -Dcheckstyle.skip=true -DskipTests install` → BUILD SUCCESS - `mvn -pl services -Dcheckstyle.skip=true -Dtest=TestTaskRestService test` → 20 tests, 0 failures/errors 🤖 Generated with [Claude Code](https://claude.com/claude-code)



Adds Favorites support for Task projects and tasks, reusing the platform favorites framework end to end A "Make it a favorite" / "Remove from favorite" row in the 3-dots menu of project cards and the task drawer.Projects and Tasks sections in the global top-bar Favorites drawer, with items resolving their own title/icon/link No new REST/DAO/schema — favorites ride the generic /v1/social/favorites API, and the existing ProjectAclPlugin/TaskAclPlugin (object types project/task) already gate access.
(cherry picked from commit 949d1f6)