feat(bookmarks): add bulk open links functionality#2631
feat(bookmarks): add bulk open links functionality#2631xingzihai wants to merge 5 commits intokarakeep-app:mainfrom
Conversation
- Add 'Open Links' button to bulk actions toolbar - Allow users to open multiple link-type bookmarks in new tabs - Handle popup blocker with user-friendly error message - Add open_links translation to English locale
- Add ExternalLink to lucide-react imports - Implement openLinks() function with: - URL validation (http/https only) - Maximum 10 links limit - Popup blocker detection - User-friendly toast messages
WalkthroughAdds a bulk "Open Links" action to the bookmarks dashboard that filters selected bookmarks to link-type entries, validates http(s) URLs, defers confirmation when above a warning threshold, opens eligible URLs via window.open, reports opened/blocked/skipped counts via toasts, and adds English localization keys. Changes
Estimated code review effort🎯 3 (Moderate) | ⏱️ ~20 minutes 🚥 Pre-merge checks | ✅ 5✅ Passed checks (5 passed)
✏️ Tip: You can configure your own custom pre-merge checks in the settings. Thanks for using CodeRabbit! It's free for OSS, and your support helps us grow. If you like it, consider giving us a shout-out. Comment |
Greptile SummaryThis PR adds a "Open Links" bulk action that opens all selected link-type bookmarks in new browser tabs, with URL validation (http/https only), a 10-link safety cap, and popup-blocker detection. The implementation is clean and integrates naturally into the existing action list pattern.\n\nKey changes:\n- New Confidence Score: 5/5Safe to merge — no runtime errors or data-integrity issues; all findings are minor style and UX polish. All three findings are P2 (style/UX suggestions): i18n coverage for toast strings, extracting a magic number, and making the skipped-URL count visible. None of these cause incorrect behaviour or data loss. The core logic — URL validation, tab opening, popup-blocker detection, and security flags — is correct. No files require special attention; all concerns are in BulkBookmarksAction.tsx and are non-blocking. Important Files Changed
Prompt To Fix All With AIThis is a comment left during a code review.
Path: apps/web/components/dashboard/BulkBookmarksAction.tsx
Line: 171-214
Comment:
**Toast messages bypass the i18n translation system**
All four toast descriptions in `openLinks` are hard-coded English strings, yet the action's button label (`t("actions.open_links")`) and the key in `translation.json` already use the i18n system. This means the feature is partially localised — the button label translates, but the feedback messages won't.
For consistency with the rest of the file, it would be better to add translation keys for these messages and use `t(...)`:
```
"open_links_none_selected": "No links selected",
"open_links_too_many": "Cannot open more than {{max}} links at once. You selected {{count}}.",
"open_links_success": "Opened {{count}} links in new tabs.",
"open_links_blocked": "Opened {{opened}} links. {{blocked}} were blocked by popup blocker."
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: apps/web/components/dashboard/BulkBookmarksAction.tsx
Line: 178-184
Comment:
**Magic number `10` should be a named constant**
The limit of 10 is used in two places (the condition and the toast message) without explanation. Extracting it to a named constant at the top of the file makes the intent clearer and keeps both occurrences in sync if the limit ever changes.
```suggestion
const MAX_OPEN_LINKS = 10;
// Limit maximum number of links
if (links.length > MAX_OPEN_LINKS) {
toast({
variant: "destructive",
description: `Cannot open more than ${MAX_OPEN_LINKS} links at once. You selected ${links.length}.`,
});
return;
}
```
How can I resolve this? If you propose a fix, please make it concise.
---
This is a comment left during a code review.
Path: apps/web/components/dashboard/BulkBookmarksAction.tsx
Line: 186-215
Comment:
**Silently-skipped URLs make the success count misleading**
Links whose URLs are either invalid or use a non-http/https protocol are caught and silently discarded — they are counted in neither `opened` nor `blocked`. If a user selects 5 links and 2 have non-http URLs (e.g. `ftp://` or an empty string from a partially-saved bookmark), the toast would say "Opened 3 links in new tabs", which is confusing.
Consider tracking skipped URLs and surfacing them in the toast, or at minimum including them in the count so the numbers add up to `links.length`.
```ts
let opened = 0;
let blocked = 0;
let skipped = 0;
links.forEach((item) => {
const url = item.content.url;
try {
const parsed = new URL(url);
if (["http:", "https:"].includes(parsed.protocol)) {
const win = window.open(url, "_blank", "noopener,noreferrer");
if (win) { opened++; } else { blocked++; }
} else {
skipped++;
}
} catch {
skipped++;
}
});
```
How can I resolve this? If you propose a fix, please make it concise.Reviews (1): Last reviewed commit: "fix: implement openLinks function and ad..." | Re-trigger Greptile |
| toast({ | ||
| description: "No links selected", | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // Limit maximum number of links | ||
| if (links.length > 10) { | ||
| toast({ | ||
| variant: "destructive", | ||
| description: `Cannot open more than 10 links at once. You selected ${links.length}.`, | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| let opened = 0; | ||
| let blocked = 0; | ||
|
|
||
| links.forEach((item) => { | ||
| const url = item.content.url; | ||
| try { | ||
| const parsed = new URL(url); | ||
| if (["http:", "https:"].includes(parsed.protocol)) { | ||
| const win = window.open(url, "_blank", "noopener,noreferrer"); | ||
| if (win) { | ||
| opened++; | ||
| } else { | ||
| blocked++; | ||
| } | ||
| } | ||
| } catch { | ||
| // Ignore invalid URLs | ||
| } | ||
| }); | ||
|
|
||
| if (blocked > 0) { | ||
| toast({ | ||
| variant: "destructive", | ||
| description: `Opened ${opened} links. ${blocked} were blocked by popup blocker.`, | ||
| }); | ||
| } else { | ||
| toast({ | ||
| description: `Opened ${opened} links in new tabs.`, | ||
| }); |
There was a problem hiding this comment.
Toast messages bypass the i18n translation system
All four toast descriptions in openLinks are hard-coded English strings, yet the action's button label (t("actions.open_links")) and the key in translation.json already use the i18n system. This means the feature is partially localised — the button label translates, but the feedback messages won't.
For consistency with the rest of the file, it would be better to add translation keys for these messages and use t(...):
"open_links_none_selected": "No links selected",
"open_links_too_many": "Cannot open more than {{max}} links at once. You selected {{count}}.",
"open_links_success": "Opened {{count}} links in new tabs.",
"open_links_blocked": "Opened {{opened}} links. {{blocked}} were blocked by popup blocker."
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/components/dashboard/BulkBookmarksAction.tsx
Line: 171-214
Comment:
**Toast messages bypass the i18n translation system**
All four toast descriptions in `openLinks` are hard-coded English strings, yet the action's button label (`t("actions.open_links")`) and the key in `translation.json` already use the i18n system. This means the feature is partially localised — the button label translates, but the feedback messages won't.
For consistency with the rest of the file, it would be better to add translation keys for these messages and use `t(...)`:
```
"open_links_none_selected": "No links selected",
"open_links_too_many": "Cannot open more than {{max}} links at once. You selected {{count}}.",
"open_links_success": "Opened {{count}} links in new tabs.",
"open_links_blocked": "Opened {{opened}} links. {{blocked}} were blocked by popup blocker."
```
How can I resolve this? If you propose a fix, please make it concise.| if (links.length > 10) { | ||
| toast({ | ||
| variant: "destructive", | ||
| description: `Cannot open more than 10 links at once. You selected ${links.length}.`, | ||
| }); | ||
| return; | ||
| } |
There was a problem hiding this comment.
Magic number
10 should be a named constant
The limit of 10 is used in two places (the condition and the toast message) without explanation. Extracting it to a named constant at the top of the file makes the intent clearer and keeps both occurrences in sync if the limit ever changes.
| if (links.length > 10) { | |
| toast({ | |
| variant: "destructive", | |
| description: `Cannot open more than 10 links at once. You selected ${links.length}.`, | |
| }); | |
| return; | |
| } | |
| const MAX_OPEN_LINKS = 10; | |
| // Limit maximum number of links | |
| if (links.length > MAX_OPEN_LINKS) { | |
| toast({ | |
| variant: "destructive", | |
| description: `Cannot open more than ${MAX_OPEN_LINKS} links at once. You selected ${links.length}.`, | |
| }); | |
| return; | |
| } |
Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/components/dashboard/BulkBookmarksAction.tsx
Line: 178-184
Comment:
**Magic number `10` should be a named constant**
The limit of 10 is used in two places (the condition and the toast message) without explanation. Extracting it to a named constant at the top of the file makes the intent clearer and keeps both occurrences in sync if the limit ever changes.
```suggestion
const MAX_OPEN_LINKS = 10;
// Limit maximum number of links
if (links.length > MAX_OPEN_LINKS) {
toast({
variant: "destructive",
description: `Cannot open more than ${MAX_OPEN_LINKS} links at once. You selected ${links.length}.`,
});
return;
}
```
How can I resolve this? If you propose a fix, please make it concise.Note: If this suggestion doesn't match your team's coding style, reply to this and let me know. I'll remember it for next time!
| let opened = 0; | ||
| let blocked = 0; | ||
|
|
||
| links.forEach((item) => { | ||
| const url = item.content.url; | ||
| try { | ||
| const parsed = new URL(url); | ||
| if (["http:", "https:"].includes(parsed.protocol)) { | ||
| const win = window.open(url, "_blank", "noopener,noreferrer"); | ||
| if (win) { | ||
| opened++; | ||
| } else { | ||
| blocked++; | ||
| } | ||
| } | ||
| } catch { | ||
| // Ignore invalid URLs | ||
| } | ||
| }); | ||
|
|
||
| if (blocked > 0) { | ||
| toast({ | ||
| variant: "destructive", | ||
| description: `Opened ${opened} links. ${blocked} were blocked by popup blocker.`, | ||
| }); | ||
| } else { | ||
| toast({ | ||
| description: `Opened ${opened} links in new tabs.`, | ||
| }); | ||
| } |
There was a problem hiding this comment.
Silently-skipped URLs make the success count misleading
Links whose URLs are either invalid or use a non-http/https protocol are caught and silently discarded — they are counted in neither opened nor blocked. If a user selects 5 links and 2 have non-http URLs (e.g. ftp:// or an empty string from a partially-saved bookmark), the toast would say "Opened 3 links in new tabs", which is confusing.
Consider tracking skipped URLs and surfacing them in the toast, or at minimum including them in the count so the numbers add up to links.length.
let opened = 0;
let blocked = 0;
let skipped = 0;
links.forEach((item) => {
const url = item.content.url;
try {
const parsed = new URL(url);
if (["http:", "https:"].includes(parsed.protocol)) {
const win = window.open(url, "_blank", "noopener,noreferrer");
if (win) { opened++; } else { blocked++; }
} else {
skipped++;
}
} catch {
skipped++;
}
});Prompt To Fix With AI
This is a comment left during a code review.
Path: apps/web/components/dashboard/BulkBookmarksAction.tsx
Line: 186-215
Comment:
**Silently-skipped URLs make the success count misleading**
Links whose URLs are either invalid or use a non-http/https protocol are caught and silently discarded — they are counted in neither `opened` nor `blocked`. If a user selects 5 links and 2 have non-http URLs (e.g. `ftp://` or an empty string from a partially-saved bookmark), the toast would say "Opened 3 links in new tabs", which is confusing.
Consider tracking skipped URLs and surfacing them in the toast, or at minimum including them in the count so the numbers add up to `links.length`.
```ts
let opened = 0;
let blocked = 0;
let skipped = 0;
links.forEach((item) => {
const url = item.content.url;
try {
const parsed = new URL(url);
if (["http:", "https:"].includes(parsed.protocol)) {
const win = window.open(url, "_blank", "noopener,noreferrer");
if (win) { opened++; } else { blocked++; }
} else {
skipped++;
}
} catch {
skipped++;
}
});
```
How can I resolve this? If you propose a fix, please make it concise.There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/components/dashboard/BulkBookmarksAction.tsx`:
- Around line 189-214: The toast currently only reports opened and blocked
counts while invalid/unsupported URLs are ignored; update the links processing
in BulkBookmarksAction.tsx (the loop that calls links.forEach and the
surrounding opened/blocked counters) to also track a skipped counter: increment
skipped inside the catch for invalid URLs and whenever parsed.protocol is not
"http:" or "https:"; finally include the skipped count in the toast messages
(both the destructive and success branches) so users see how many links were
skipped due to invalid or non-HTTP(S) URLs.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 29a04d71-9fef-47f1-a389-5e681ee36f08
📒 Files selected for processing (2)
apps/web/components/dashboard/BulkBookmarksAction.tsxapps/web/lib/i18n/locales/en/translation.json
- Add i18n translation for openLinks toast messages - Define MAX_OPEN_LINKS constant to replace magic number 10 - Track and report skipped URLs (invalid or non-HTTP) in toast - Include all statistics (opened, blocked, skipped) in toast summary
There was a problem hiding this comment.
Actionable comments posted: 1
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/components/dashboard/BulkBookmarksAction.tsx`:
- Around line 167-187: The current cap uses links =
selectedBookmarks.filter(item => item.content.type === BookmarkTypes.LINK) and
checks links.length against MAX_OPEN_LINKS before validating URLs, so
non-HTTP(S) LINK items can incorrectly inflate the count; change the logic in
BulkBookmarksAction to first derive an openableLinks array by filtering links
for valid HTTP(S) URLs (e.g., test content.url with a URL parser or
startsWith('http://')/('https://')), then apply the MAX_OPEN_LINKS check and
toast using openableLinks.length, while preserving the original links/open
behavior to only attempt opening URLs from openableLinks.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: 4948622a-5c86-4401-a52b-c23e8642b531
📒 Files selected for processing (2)
apps/web/components/dashboard/BulkBookmarksAction.tsxapps/web/lib/i18n/locales/en/translation.json
🚧 Files skipped from review as they are similar to previous changes (1)
- apps/web/lib/i18n/locales/en/translation.json
There was a problem hiding this comment.
♻️ Duplicate comments (1)
apps/web/components/dashboard/BulkBookmarksAction.tsx (1)
182-187:⚠️ Potential issue | 🟡 MinorHandle the “all skipped” branch with a skipped-aware message.
At Line 182,
openableLinks.length === 0currently maps totoasts.bookmarks.no_links_selected, which is inaccurate when links were selected but all were invalid/non-HTTP(S). This also hides skipped-count feedback in that path.💡 Suggested patch
- if (openableLinks.length === 0) { - toast({ - description: t("toasts.bookmarks.no_links_selected"), - }); - return; - } + if (openableLinks.length === 0) { + toast({ + variant: "destructive", + description: t("toasts.bookmarks.links_opened_skipped", { + opened: 0, + skipped: links.length, + }), + }); + return; + }🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/components/dashboard/BulkBookmarksAction.tsx` around lines 182 - 187, Replace the current single check for openableLinks.length === 0 with a branch that distinguishes "nothing selected" vs "all selected were skipped": use the selection count from the surrounding scope (e.g., selectedIds/selectedBookmarks or props) together with openableLinks to decide the toast; if no items were selected keep using t("toasts.bookmarks.no_links_selected"), but if items were selected and openableLinks.length === 0 emit a skipped-aware toast (e.g., t("toasts.bookmarks.all_links_skipped", { skipped: selectedCount }) or t("toasts.bookmarks.no_openable_links", { skipped: selectedCount })), ensuring you call toast(...) with the appropriate message and include the skipped count.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Duplicate comments:
In `@apps/web/components/dashboard/BulkBookmarksAction.tsx`:
- Around line 182-187: Replace the current single check for openableLinks.length
=== 0 with a branch that distinguishes "nothing selected" vs "all selected were
skipped": use the selection count from the surrounding scope (e.g.,
selectedIds/selectedBookmarks or props) together with openableLinks to decide
the toast; if no items were selected keep using
t("toasts.bookmarks.no_links_selected"), but if items were selected and
openableLinks.length === 0 emit a skipped-aware toast (e.g.,
t("toasts.bookmarks.all_links_skipped", { skipped: selectedCount }) or
t("toasts.bookmarks.no_openable_links", { skipped: selectedCount })), ensuring
you call toast(...) with the appropriate message and include the skipped count.
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: ea67f3eb-9726-48be-92e1-533763124a9b
📒 Files selected for processing (1)
apps/web/components/dashboard/BulkBookmarksAction.tsx
|
Thanks for writing this improvement! It can make a big difference for managing research projects.
Unfortunately, this is profoundly limiting and prevents the restoration of complex sessions. My browser does lazy loading, so there are no issues with opening many more tabs. Can you please make this limit configurable/optional? |
Previously, opening more than 10 links would show an error and refuse. Now it shows a confirmation dialog allowing users to proceed if they want. Addresses user feedback requesting configurable/optional limit. Users with lazy-loading browsers can now open more links if they choose.
There was a problem hiding this comment.
Actionable comments posted: 1
🧹 Nitpick comments (1)
apps/web/components/dashboard/BulkBookmarksAction.tsx (1)
65-69: Consider clearingpendingOpenableLinkswhen dialog is cancelled.If the user closes the confirmation dialog without confirming (e.g., clicks outside or presses Escape),
pendingOpenableLinksretains stale references. While this doesn't affect correctness (the state is only read inconfirmOpenManyLinks), clearing it improves hygiene.♻️ Suggested change
<ActionConfirmingDialog open={isTooManyLinksDialogOpen} - setOpen={setIsTooManyLinksDialogOpen} + setOpen={(open) => { + setIsTooManyLinksDialogOpen(open); + if (!open) { + setPendingOpenableLinks([]); + } + }} title={"Open Many Links"}Also applies to: 252-256
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed. In `@apps/web/components/dashboard/BulkBookmarksAction.tsx` around lines 65 - 69, The confirmation dialog leaves stale references in pendingOpenableLinks when the user cancels/escapes; update the dialog close/cancel handler (where you call setIsTooManyLinksDialogOpen(false)) to also call setPendingOpenableLinks([]) so pendingOpenableLinks is cleared on non-confirm closes, and do the same for the other dialog instance referenced around lines 252-256; ensure confirmOpenManyLinks still uses pendingOpenableLinks before clearing it after a successful confirm.
🤖 Prompt for all review comments with AI agents
Verify each finding against the current code and only fix it if needed.
Inline comments:
In `@apps/web/components/dashboard/BulkBookmarksAction.tsx`:
- Around line 171-207: In openLinks, avoid re-computing link lists from
selectedBookmarks when linksToOpen is provided: treat linksToOpen as the source
of truth by skipping the initial filter steps (the creation of links and
openableLinks) when linksToOpen is passed, set targetLinks = linksToOpen
directly, and compute skipped based on the difference between the original
provided list length and the final targetLinks length (e.g., skipped =
linksToOpen.length - targetLinks.length or similar) so
confirmOpenManyLinks/pendingOpenableLinks are honored and counts remain
accurate; update references to links, openableLinks, targetLinks, skipped,
pendingOpenableLinks and the early "no links selected" toast to use the provided
linksToOpen path.
---
Nitpick comments:
In `@apps/web/components/dashboard/BulkBookmarksAction.tsx`:
- Around line 65-69: The confirmation dialog leaves stale references in
pendingOpenableLinks when the user cancels/escapes; update the dialog
close/cancel handler (where you call setIsTooManyLinksDialogOpen(false)) to also
call setPendingOpenableLinks([]) so pendingOpenableLinks is cleared on
non-confirm closes, and do the same for the other dialog instance referenced
around lines 252-256; ensure confirmOpenManyLinks still uses
pendingOpenableLinks before clearing it after a successful confirm.
🪄 Autofix (Beta)
Fix all unresolved CodeRabbit comments on this PR:
- Push a commit to this branch (recommended)
- Create a new PR with the fixes
ℹ️ Review info
⚙️ Run configuration
Configuration used: Organization UI
Review profile: CHILL
Plan: Pro
Run ID: f991bfb3-317b-45c5-b089-942de01488fe
📒 Files selected for processing (1)
apps/web/components/dashboard/BulkBookmarksAction.tsx
| const openLinks = (linksToOpen?: typeof selectedBookmarks) => { | ||
| const links = selectedBookmarks.filter( | ||
| (item) => item.content.type === BookmarkTypes.LINK, | ||
| ); | ||
|
|
||
| // First filter for valid HTTP(S) URLs that can actually be opened | ||
| const openableLinks = links.filter((item) => { | ||
| const url = item.content.url; | ||
| try { | ||
| const parsed = new URL(url); | ||
| return ["http:", "https:"].includes(parsed.protocol); | ||
| } catch { | ||
| return false; | ||
| } | ||
| }); | ||
|
|
||
| if (openableLinks.length === 0) { | ||
| toast({ | ||
| description: t("toasts.bookmarks.no_links_selected"), | ||
| }); | ||
| return; | ||
| } | ||
|
|
||
| // If provided links to open (from confirmation dialog), use those | ||
| const targetLinks = linksToOpen ?? openableLinks; | ||
|
|
||
| // Show warning dialog if trying to open more than threshold | ||
| // (unless we're already in the confirmed path) | ||
| if (!linksToOpen && targetLinks.length > MAX_OPEN_LINKS_WARNING) { | ||
| setPendingOpenableLinks(targetLinks); | ||
| setIsTooManyLinksDialogOpen(true); | ||
| return; | ||
| } | ||
|
|
||
| let opened = 0; | ||
| let blocked = 0; | ||
| const skipped = links.length - openableLinks.length; |
There was a problem hiding this comment.
Logic inconsistency when called from confirmation dialog.
When openLinks(pendingOpenableLinks) is called from confirmOpenManyLinks, the function re-computes links and openableLinks from the current selectedBookmarks state, not from the provided linksToOpen. This creates two issues:
-
Early return check (lines 187-192): If selection changes while dialog is open,
openableLinkscould be empty, causing the function to show "no links selected" and abort—even though validlinksToOpenwere passed. -
Skipped count (line 207):
skippedis calculated from re-computedlinks.length - openableLinks.length, which may differ fromtargetLinks. The reported skipped count could be incorrect.
💡 Proposed fix: skip re-computation when linksToOpen is provided
const openLinks = (linksToOpen?: typeof selectedBookmarks) => {
+ // If links are already provided (from confirmation), use them directly
+ if (linksToOpen && linksToOpen.length > 0) {
+ let opened = 0;
+ let blocked = 0;
+
+ linksToOpen.forEach((item) => {
+ const win = window.open(item.content.url, "_blank", "noopener,noreferrer");
+ if (win) {
+ opened++;
+ } else {
+ blocked++;
+ }
+ });
+
+ if (blocked > 0) {
+ toast({
+ variant: "destructive",
+ description: t("toasts.bookmarks.links_opened_blocked", {
+ opened,
+ blocked,
+ }),
+ });
+ } else {
+ toast({
+ description: t("toasts.bookmarks.links_opened", {
+ count: opened,
+ }),
+ });
+ }
+ return;
+ }
+
const links = selectedBookmarks.filter(
(item) => item.content.type === BookmarkTypes.LINK,
);
-
- // First filter for valid HTTP(S) URLs that can actually be opened
- const openableLinks = links.filter((item) => {
- const url = item.content.url;
- try {
- const parsed = new URL(url);
- return ["http:", "https:"].includes(parsed.protocol);
- } catch {
- return false;
- }
- });
-
- if (openableLinks.length === 0) {
- toast({
- description: t("toasts.bookmarks.no_links_selected"),
- });
- return;
- }
-
- // If provided links to open (from confirmation dialog), use those
- const targetLinks = linksToOpen ?? openableLinks;
-
- // Show warning dialog if trying to open more than threshold
- // (unless we're already in the confirmed path)
- if (!linksToOpen && targetLinks.length > MAX_OPEN_LINKS_WARNING) {
- setPendingOpenableLinks(targetLinks);
- setIsTooManyLinksDialogOpen(true);
- return;
- }
+ // ... rest of original logic for the initial (non-confirmation) pathAlternatively, consider extracting the actual "open windows" logic into a separate function that doesn't depend on selectedBookmarks, making the data flow clearer.
🤖 Prompt for AI Agents
Verify each finding against the current code and only fix it if needed.
In `@apps/web/components/dashboard/BulkBookmarksAction.tsx` around lines 171 -
207, In openLinks, avoid re-computing link lists from selectedBookmarks when
linksToOpen is provided: treat linksToOpen as the source of truth by skipping
the initial filter steps (the creation of links and openableLinks) when
linksToOpen is passed, set targetLinks = linksToOpen directly, and compute
skipped based on the difference between the original provided list length and
the final targetLinks length (e.g., skipped = linksToOpen.length -
targetLinks.length or similar) so confirmOpenManyLinks/pendingOpenableLinks are
honored and counts remain accurate; update references to links, openableLinks,
targetLinks, skipped, pendingOpenableLinks and the early "no links selected"
toast to use the provided linksToOpen path.
Fixes #2618
Summary
Add bulk open links functionality to bookmark actions.
Changes
Testing