Add shared collection feature: implement sharing functionality with copyable links and display for shared collections - #22
Conversation
…opyable links and display for shared collections
|
The latest updates on your projects. Learn more about Vercel for GitHub.
|
|
Warning Review limit reached
Next review available in: 18 minutes Enable usage-based reviews in Billing to review now. Otherwise, wait until the next included review is available. How can I continue?After more reviews become available, a review can be triggered using the To avoid repeated limits, reduce automatic review volume by pausing incremental auto-reviews earlier, using label-based review opt-in, excluding WIP or generated PR titles, or requesting reviews manually when the PR is ready. If your team needs uninterrupted high-volume reviews, an organization admin can enable usage-based reviews. How do review limits work?CodeRabbit enforces per-developer PR review limits for each organization. Most developers receive the normal plan review availability. For paid Pro and Pro+ PR reviews, CodeRabbit uses adaptive limits for sustained high-volume activity. When a developer's recent PR review activity reaches the 95th percentile or higher among CodeRabbit users, additional reviews become available more gradually as earlier reviews age out of the rolling window. Please refer docs for additional details. Review details⚙️ Run configurationConfiguration used: defaults Review profile: CHILL Plan: Pro Plus Run ID: 📒 Files selected for processing (5)
📝 WalkthroughWalkthroughAdds public slug-based collection APIs, client data access, collection sharing controls, clipboard feedback, localized messages, and a shared collection page with link cards. ChangesPublic collection sharing
Estimated code review effort: 3 (Moderate) | ~20 minutes Sequence Diagram(s)sequenceDiagram
participant Visitor
participant SharedPage
participant useSharedCollection
participant SharedAPI
participant Database
Visitor->>SharedPage: Open shared collection slug
SharedPage->>useSharedCollection: Fetch collection data
useSharedCollection->>SharedAPI: Request collection metadata and links
SharedAPI->>Database: Query shared collection and public links
Database-->>SharedAPI: Return selected records
SharedAPI-->>useSharedCollection: Return API responses
useSharedCollection-->>SharedPage: Expose data and state
SharedPage-->>Visitor: Render collection and link cards
Possibly related PRs
🚥 Pre-merge checks | ✅ 4 | ❌ 1❌ Failed checks (1 warning)
✅ Passed checks (4 passed)
✨ Finishing Touches 💡 1📝 Generate docstrings 💡
🧪 Generate unit tests (beta)
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 |
There was a problem hiding this comment.
Actionable comments posted: 3
🧹 Nitpick comments (4)
app/shared/api/use-shared-collection.ts (1)
1-1: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winImport the shared type through the API barrel.
Replace the sibling import with
import type { SharedCollection } from "~/shared/api";. Keepimport typeso this change does not add a runtime barrel dependency.As per coding guidelines, "Import shared modules through their segment barrels (
~/shared/api,~/shared/ui, or~/shared/lib) rather than deep paths."🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/shared/api/use-shared-collection.ts` at line 1, Update the SharedCollection type import in use-shared-collection.ts to use the shared API barrel via the ~/shared/api alias instead of the sibling deep path, while preserving import type so no runtime dependency is introduced.Source: Coding guidelines
app/features/shared-collection/ui/shared-link-card.vue (1)
13-16: 📐 Maintainability & Code Quality | 🔵 Trivial | ⚡ Quick winAdd alt text to the link preview image.
v-imgat Line 13 has noaltattribute. Screen readers cannot describe the preview image. UsedisplayTitleor the link URL as alt text.♿ Proposed fix
- <v-img v-if="link.imageUrl" :src="link.imageUrl" height="160" cover /> + <v-img v-if="link.imageUrl" :src="link.imageUrl" :alt="displayTitle" height="160" cover />🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/features/shared-collection/ui/shared-link-card.vue` around lines 13 - 16, Update the v-img element in the shared link card to include alt text, using displayTitle when available and the link URL as the fallback, while preserving the existing conditional rendering and image properties.app/pages/shared/[slug].vue (2)
11-20: 🚀 Performance & Scalability | 🔵 Trivial | ⚡ Quick winSkip the links request when the collection fetch already failed.
Line 15 awaits
useSharedCollection(slug)before Line 18 starts the linksuseAsyncDatacall. WhencollectionErroris set (404 or otherwise), the page still issues a request to/api/shared/${slug}/links. That request hits an endpoint scoped to the same unusable slug and wastes a round trip.Skip the links fetch when the collection already failed.
⚡ Proposed fix
const requestFetch = useRequestFetch(); -const { data: links, error: linksError } = await useAsyncData(`shared-links-${slug}`, () => - requestFetch<SharedLinkItem[]>(`/api/shared/${slug}/links`) -); +const { data: links, error: linksError } = await useAsyncData(`shared-links-${slug}`, () => + collectionError.value ? Promise.resolve([]) : requestFetch<SharedLinkItem[]>(`/api/shared/${slug}/links`) +);🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/pages/shared/`[slug].vue around lines 11 - 20, Update the links-loading flow after useSharedCollection(slug) so useAsyncData for shared-links-${slug} is not invoked when collectionError is set. Preserve the existing links request and response handling when the collection fetch succeeds, and keep linksError available to the page.
9-9: 🎯 Functional Correctness | 🔵 Trivial | 💤 Low valueValidate the
slugroute parameter type.Line 9 casts
route.params.slug as stringwithout runtime validation. For a single dynamic segment[slug].vue, Vue Router always returns a string here, so this is a low-risk assumption, but the cast itself provides no safety if the route structure changes later.🤖 Prompt for AI Agents
Verify each finding against current code. Fix only still-valid issues, skip the rest with a brief reason, keep changes minimal, and validate. In `@app/pages/shared/`[slug].vue at line 9, Update the slug handling near the route parameter extraction to validate that route.params.slug is a string at runtime instead of relying solely on the `as string` cast, while preserving the existing behavior for valid single-segment routes and handling invalid values safely.
🤖 Prompt for all review comments with AI agents
Verify each finding against current code. Fix only still-valid issues, skip the
rest with a brief reason, keep changes minimal, and validate.
Inline comments:
In `@app/features/collection-share-link/ui/collection-share-link.vue`:
- Around line 6-13: Move share URL construction and clipboard orchestration from
the collection-share-link UI component into a model composable, then have the
component consume its exposed values and actions while remaining presentational.
Export the model composable alongside the UI component from
app/features/collection-share-link/index.ts at line 1, and update
app/features/collection-share-link/ui/collection-share-link.vue lines 6-13
accordingly.
- Around line 11-13: Update onCopyClick to be asynchronous, await
copy(shareUrl.value), and catch rejected clipboard writes. On failure, set a
distinct non-copied failure state or provide the existing manual-copy fallback
so the rejection is handled without becoming unhandled.
In `@server/api/shared/`[slug]/links/index.get.ts:
- Around line 17-31: Replace the separate collection lookup and link query with
one query rooted at collections, filtering by both parsedSlug.data and
collections.shared, then left-joining collectionItems and urls. Update the
result handling so no matching collection still throws the existing 404, while a
valid shared collection with no links returns an empty array; preserve
sharedLinkSelection and position ordering.
---
Nitpick comments:
In `@app/features/shared-collection/ui/shared-link-card.vue`:
- Around line 13-16: Update the v-img element in the shared link card to include
alt text, using displayTitle when available and the link URL as the fallback,
while preserving the existing conditional rendering and image properties.
In `@app/pages/shared/`[slug].vue:
- Around line 11-20: Update the links-loading flow after
useSharedCollection(slug) so useAsyncData for shared-links-${slug} is not
invoked when collectionError is set. Preserve the existing links request and
response handling when the collection fetch succeeds, and keep linksError
available to the page.
- Line 9: Update the slug handling near the route parameter extraction to
validate that route.params.slug is a string at runtime instead of relying solely
on the `as string` cast, while preserving the existing behavior for valid
single-segment routes and handling invalid values safely.
In `@app/shared/api/use-shared-collection.ts`:
- Line 1: Update the SharedCollection type import in use-shared-collection.ts to
use the shared API barrel via the ~/shared/api alias instead of the sibling deep
path, while preserving import type so no runtime dependency is introduced.
🪄 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: defaults
Review profile: CHILL
Plan: Pro Plus
Run ID: e5004634-5b50-40d1-903e-3f46610eef55
📒 Files selected for processing (16)
app/features/collection-share-link/index.tsapp/features/collection-share-link/ui/collection-share-link.vueapp/features/shared-collection/index.tsapp/features/shared-collection/ui/shared-link-card.vueapp/pages/collections/[id]/index.vueapp/pages/shared/[slug].vueapp/shared/api/index.tsapp/shared/api/shared-collections.tsapp/shared/api/use-shared-collection.tsapp/shared/lib/index.tsapp/shared/lib/use-clipboard.tsi18n/locales/en.jsoni18n/locales/ru.jsonserver/api/shared/[slug].get.tsserver/api/shared/[slug]/links/index.get.tsserver/utils/shared-link-select.ts
…race - Move share-URL/clipboard orchestration out of collection-share-link.vue into a model composable (FSD boundary), handling clipboard write rejections with an error snackbar instead of an unhandled rejection. - Rewrite the public shared-links endpoint as a single left-joined query rooted at collections, so the shared check and link read are atomic - closes a race where unsharing between two separate queries could leak links to an anonymous caller. - Add alt text to the shared link card's preview image. Co-Authored-By: Claude Sonnet 5 <noreply@anthropic.com>
Summary by CodeRabbit