Skip to content

[PB-5661] feat: favorite toggle from context menu and F shortcut - #2041

Open
victor-ferro wants to merge 10 commits into
PB-5660-favorites-pagefrom
PB-5661-favorite-toggle
Open

[PB-5661] feat: favorite toggle from context menu and F shortcut#2041
victor-ferro wants to merge 10 commits into
PB-5660-favorites-pagefrom
PB-5661-favorite-toggle

Conversation

@victor-ferro

@victor-ferro victor-ferro commented Jul 14, 2026

Copy link
Copy Markdown

Description

Allows users to favorite/unfavorite files and folders from the item context menu, plus an F keyboard shortcut.

  • New menu entry "Add to Favorites" / "Remove from Favorites" (star / crossed-out star icon, F shortcut label right-aligned) in every single-item menu — right-click and three-dot — across Drive, Recents, Favorites and Shared
  • Excluded from multi-selection menus (ambiguous label with mixed selections), workspace menus (favorites API is personal-scoped) and Trash
  • F with exactly one item selected toggles it; guarded against ⇧F (new folder), open dialogs and Trash
  • Immediate UI update, no reload: setItemFavorite service + toggleFavoriteThunk patch the item in place and keep the favorites listing in sync (patchItem, addFavorites / new removeFavorites reducer); error toast on failure
  • isFavorite added to drive/shared item types; translations in all locales

Related Issues

Relates to PB-5661

Related Pull Requests

Checklist

  • Changes have been tested locally.
  • Unit tests have been written or updated as necessary.
  • The code adheres to the repository's coding standards.
  • Relevant documentation has been added or updated.
  • No new warnings or errors have been introduced.
  • SonarCloud issues have been reviewed and addressed.
  • QA Passed

Testing Process

  • yarn vitest run src/views/Favorites src/app/store/slices/storage/storage.slice.test.ts src/views/Shared/hooks/useSharedContextMenu.test.ts — 6 files, 45 tests passing (against linked SDK)
  • ESLint clean on all touched files; yarn check:i18n in sync
  • Manual testing against the real backend: toggle from both menus on files and folders, F shortcut, immediate update in Drive and Favorites views, label/icon switching with item state

Additional Notes

  • Custom icon: @phosphor-icons/react has no crossed-out star (StarSlash doesn't exist), so src/assets/icons/star-slash.svg was hand-made following Phosphor's stroke style (20×20, currentColor) and imported with ?react like the existing move.svg. If design wants a different one, swapping it is trivial — a single SVG referenced from one place.
  • ⚠️ Same SDK dependency as [PB-5660] feat: favorites page #2038: markFileAsFavorite / unmarkFileAsFavorite / markFolderAsFavorite / unmarkFolderAsFavorite come from unreleased @internxt/sdk changes (developed against a yarn-linked local SDK). CI will fail until that SDK version is published and bumped.

@cloudflare-workers-and-pages

cloudflare-workers-and-pages Bot commented Jul 14, 2026

Copy link
Copy Markdown

Deploying drive-web with  Cloudflare Pages  Cloudflare Pages

Latest commit: 5c64d18
Status: ✅  Deploy successful!
Preview URL: https://ee6916d3.drive-web.pages.dev
Branch Preview URL: https://pb-5661-favorite-toggle.drive-web.pages.dev

View logs

Comment thread src/app/store/slices/storage/index.ts Outdated
},
removeFavorites: (state: StorageState, action: PayloadAction<DriveItemData[]>) => {
state.favorites = state.favorites.filter(
(item) => !action.payload.find((i) => i.uuid === item.uuid && !!i.isFolder === !!item.isFolder),

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

check sonar warning

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done!

@@ -0,0 +1,16 @@
import { StorageTypes } from '@internxt/sdk/dist/drive';

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

To ensure consistency with other files, the name should be ‘xxxx.service.ts’.
If we want to change it, we can discuss it, but ideally, even if it seems redundant, they should all have the same name so we don’t get confused

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Done!

Comment on lines +242 to +252
useHotkeys(
'f',
(e) => {
if (e.shiftKey) return;
e.preventDefault();
if (!disableKeyboardShortcuts && selectedItems.length === 1) {
toggleFavorite(selectedItems);
}
},
[disableKeyboardShortcuts, selectedItems, toggleFavorite],
);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

this hotkey function seems duplicated in DriveExplorer.tsx, maybe we should extract the hotkeys to a hook in order to implement the lib in one point and import in the other places

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

Extracted the duplicated F shortcut into a shared useToggleFavoriteHotkey hook used by both DriveExplorer and SharedItemListContainer. Or were you referring to a hook covering all the useHotkeys we have across the project?

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

No, not for all, for now, let’s stick to this one so we don’t stray from the scope of the task, and we’ll get round to the others later

Comment on lines +16 to +20
await Promise.all(
items.map(async (item) => {
const favorite = !item.isFavorite;

await setItemFavorite(item, favorite);

Copy link
Copy Markdown
Collaborator

Choose a reason for hiding this comment

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

As several favorites can be sent at once, using Promise.all can mean that if one fails, we display as though everything has failed, when that is not the case. I think we should replace Promise.all with Promise.allSettled, so that the dispatches are applied only to those that were successful.
If they all fail, we could display a ‘rejected’ thunk (current toast), and if only some fail, perhaps display a ‘fulfilled’ thunk but with a different toast, such as ‘some favourites could not be updated’

Copy link
Copy Markdown
Author

Choose a reason for hiding this comment

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

You're right. It's a use case that didn't affect this branch yet because we only allowed sending a single item to this function, but it's true that it's better to do it properly for the future. In fact, we already ran into this case in the PB-5662 branch. Done!

@victor-ferro
victor-ferro requested a review from CandelR July 21, 2026 09:40
@victor-ferro victor-ferro self-assigned this Jul 24, 2026
# Conflicts:
#	src/views/Drive/components/DriveExplorer/DriveExplorer.tsx
@sonarqubecloud

Copy link
Copy Markdown

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.

2 participants