Skip to content

feat: Favorites for projects & tasks - EXO-88316 - #622

Merged
Jihed525 merged 4 commits into
developfrom
backportdev-88316
Jul 16, 2026
Merged

Jihed525 merged 4 commits into
developfrom
backportdev-88316

Conversation

@Jihed525

Copy link
Copy Markdown
Contributor

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)

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)
@Jihed525
Jihed525 requested a review from boubaker July 16, 2026 07:29
@Jihed525
Jihed525 requested a review from ahamdi July 16, 2026 07:29
@sonarqubecloud

Copy link
Copy Markdown

@boubaker boubaker changed the title feat: Favorites for projects & tasks - EXO-88316 #615 feat: Favorites for projects & tasks - EXO-88316 Jul 16, 2026
@Jihed525
Jihed525 removed request for ahamdi and boubaker July 16, 2026 07:37

@boubaker boubaker left a comment

Copy link
Copy Markdown
Member

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

(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:93 and TaskFavoriteMenuItem.vue:93 mutate the project/task prop 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.
  • removed event triggered but not declared in emits on ProjectFavoriteItem.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 both ProjectRestService and TaskRestService. Worth factoring into a shared helper — would also fix the constructor param-count warning above.
  • ProjectFavoriteMenuAction.vue and TaskFavoriteMenuItem.vue duplicate ~90% of their toggle/alert/event logic, whereas the drawer's favorite-button is 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 favorite flag 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>
@boubaker
boubaker self-requested a review July 16, 2026 08:14
@Jihed525

Copy link
Copy Markdown
Contributor Author

Thanks for the review — all points have been addressed in 37733a9:

  • Removed the stray CLAUDE.md swept in from the squashed cherry-pick.
  • ProjectFavoriteMenuAction.vue / TaskFavoriteMenuItem.vue no longer mutate the project/task prop directly (Sonar S8951); state sync relies on the existing metadata.favorite.updated event like the shared favorite-button.
  • Declared the removed event in emits on ProjectFavoriteItem.vue / TaskFavoriteItem.vue (Sonar S8961).
  • Extracted the duplicated isFavorite() logic from ProjectRestService/TaskRestService into a shared org.exoplatform.task.util.FavoriteUtil, which also resolves the >7-parameter constructor warning.
  • Fixed the N+1 favorite lookup in ProjectRestService.buildJSON(): now batch-fetches the current user's favorite project ids once via FavoriteService#getFavoriteItemsByCreatorAndType instead of one isFavorite() call per project.
  • Added assertions in TestProjectRestService/TestTaskRestService covering the new favorite field enrichment.

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.

boubaker and others added 2 commits July 16, 2026 09:25
- 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>
@Jihed525
Jihed525 merged commit f666f76 into develop Jul 16, 2026
4 of 5 checks passed
@Jihed525
Jihed525 deleted the backportdev-88316 branch July 16, 2026 08:35
Jihed525 added a commit that referenced this pull request Jul 22, 2026
## 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)
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.

3 participants