Skip to content

feat: implement zero-auth offline bookmarking system with glassmorphic drawer and SSR hydration safety (#462)#482

Open
shivani11jadhav wants to merge 3 commits into
devpathindcommunity-india:masterfrom
shivani11jadhav:feature/offline-bookmarks
Open

feat: implement zero-auth offline bookmarking system with glassmorphic drawer and SSR hydration safety (#462)#482
shivani11jadhav wants to merge 3 commits into
devpathindcommunity-india:masterfrom
shivani11jadhav:feature/offline-bookmarks

Conversation

@shivani11jadhav
Copy link
Copy Markdown
Contributor

🧩 Linked Issue

Closes #462

🧠 Feature Overview & Engineering Design

To align with DevPath's standard "no-nonsense, zero-authentication direct learning" model, this PR introduces a production-ready, ultra-lightweight Offline Roadmap Bookmarking System. Users can now persistently save, organize, and toggle their preferred roadmaps and projects instantly across sessions without requiring an authenticated account layer.

🛠️ Core Implementation Details:

  1. useBookmarks.ts (Reactive Offline State Engine):

    • Implements custom client-side caching synchronized with the browser's native localStorage API.
    • Features a structural Next.js SSR Hydration Guard to prevent initialization flashes or layout compilation crashes common during Node.js server to React client handshakes.
  2. BookmarkDrawer.tsx (Premium Glassmorphic Panel):

    • Designed a fully responsive, collapsible sidebar animated cleanly via framer-motion spring mechanics.
    • Uses premium glassmorphism utility classes (backdrop-blur-xl bg-[#0f1419]/95).
  3. Card-Level Trigger Overlays (PremiumCard.tsx & ProjectCard.tsx):

    • Injected sleek floating bookmark action icons into existing grid structures.
    • Utilizes precise e.stopPropagation() handling to ensure bookmark toggling doesn't trigger parent card route modals.

🧪 Verification & Build Status

  • Verified full code compliance via standard TypeScript compiler checks (npx tsc --noEmit checks fully green).

@Aditya948351 Aditya948351 self-requested a review June 2, 2026 06:52
Copy link
Copy Markdown
Collaborator

@Aditya948351 Aditya948351 left a comment

Choose a reason for hiding this comment

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

LGTM! Approved. Great work on this.

@Aditya948351 Aditya948351 added gssoc26 This is a official GirlScript Summer of Code label. type:feature level:intermediate Intermediate level issues labels Jun 2, 2026
@Aditya948351 Aditya948351 added the gssoc:approved give 50+ base points label Jun 2, 2026
@Aditya948351
Copy link
Copy Markdown
Collaborator

@shivani11jadhav Do resolve this Conflicts
And why did you removed .png files that are not supposed to be touched?

@Aditya948351 Aditya948351 removed the gssoc:approved give 50+ base points label Jun 2, 2026
@shivani11jadhav
Copy link
Copy Markdown
Contributor Author

shivani11jadhav commented Jun 2, 2026

@Aditya948351 , absolutely sorry for the trouble with the asset files! I definitely didn't mean to remove or alter any .png files.

It seems my local environment accidentally tracked and staged the public assets and logo pointers incorrectly during the branch setup. I am actively working on cleaning up the branch right away to restore all untouched assets, ensuring only the core bookmarking components and hooks remain. Thanks for pointing it out

@Aditya948351
Copy link
Copy Markdown
Collaborator

Aditya948351 commented Jun 2, 2026

@Aditya948351 , absolutely sorry for the trouble with the asset files! I definitely didn't mean to remove or alter any .png files.

It seems my local environment accidentally tracked and staged the public assets and logo pointers incorrectly during the branch setup. I am actively working on cleaning up the branch right away to restore all untouched assets, ensuring only the core bookmarking components and hooks remain. Thanks for pointing it out

Absolutely Fine @shivani11jadhav , Correct that now and Waiting for the More Contributions of you on this Repo!

@Aditya948351
Copy link
Copy Markdown
Collaborator

@shivani11jadhav Look into those Conflicts again

Copy link
Copy Markdown
Contributor

Copilot AI left a comment

Choose a reason for hiding this comment

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

Pull request overview

Adds a zero-auth, offline bookmarking feature backed by localStorage, including UI affordances on roadmap/project cards and a global bookmark drawer accessible from the navbar, with URL-based deep-linking to open specific roadmaps.

Changes:

  • Introduces a useBookmarks hook to read/write bookmarks from localStorage and sync updates across components/tabs.
  • Adds bookmark toggle buttons to PremiumCard (roadmaps) and ProjectCard (projects).
  • Adds a glassmorphic BookmarkDrawer and a navbar trigger; supports opening a roadmap modal via ?open=roadmap&title=....

Reviewed changes

Copilot reviewed 7 out of 7 changed files in this pull request and generated 12 comments.

Show a summary per file
File Description
src/lib/firebase.ts Adds fallback “mock” Firebase env values (needs production safety guard).
src/hooks/useBookmarks.ts New hook providing bookmark CRUD + hydration guard; localStorage synchronization.
src/components/ui/PremiumCard.tsx Adds optional bookmark overlay button for cards.
src/components/ui/BookmarkDrawer.tsx New animated drawer listing/removing bookmarks and linking to saved items.
src/components/projects/ProjectCard.tsx Adds offline bookmark overlay button to project cards.
src/components/layout/Navbar.tsx Adds navbar bookmark icon and mounts the bookmark drawer.
src/components/home/ResourcesTabs.tsx Adds deep-link handler for opening a specific roadmap modal and passes bookmark metadata into cards.

💡 Add Copilot custom instructions for smarter, more guided reviews. Learn how to get started.

Comment thread src/lib/firebase.ts
Comment on lines +7 to +11
apiKey: process.env.NEXT_PUBLIC_FIREBASE_API_KEY || "mock-api-key-for-local-testing-only-123456",
authDomain: process.env.NEXT_PUBLIC_FIREBASE_AUTH_DOMAIN || "mock-app.firebaseapp.com",
projectId: process.env.NEXT_PUBLIC_FIREBASE_PROJECT_ID || "mock-app-id",
storageBucket: process.env.NEXT_PUBLIC_FIREBASE_STORAGE_BUCKET || "mock-app.appspot.com",
messagingSenderId: process.env.NEXT_PUBLIC_FIREBASE_MESSAGING_SENDER_ID || "1234567890",
Comment thread src/hooks/useBookmarks.ts
Comment on lines +16 to +19
try {
const data = localStorage.getItem('devpath_saved_roadmaps');
return data ? JSON.parse(data) : [];
} catch (e) {
Comment thread src/hooks/useBookmarks.ts
Comment on lines +46 to +59
const addBookmark = (item: BookmarkItem) => {
const current = getSavedBookmarks();
if (current.some(b => b.id === item.id)) return;
const updated = [...current, item];
localStorage.setItem('devpath_saved_roadmaps', JSON.stringify(updated));
listeners.forEach(listener => listener());
};

const removeBookmark = (id: string) => {
const current = getSavedBookmarks();
const updated = current.filter(b => b.id !== id);
localStorage.setItem('devpath_saved_roadmaps', JSON.stringify(updated));
listeners.forEach(listener => listener());
};
Comment thread src/hooks/useBookmarks.ts
Comment on lines +48 to +57
if (current.some(b => b.id === item.id)) return;
const updated = [...current, item];
localStorage.setItem('devpath_saved_roadmaps', JSON.stringify(updated));
listeners.forEach(listener => listener());
};

const removeBookmark = (id: string) => {
const current = getSavedBookmarks();
const updated = current.filter(b => b.id !== id);
localStorage.setItem('devpath_saved_roadmaps', JSON.stringify(updated));
Comment on lines +50 to +62
<button
onClick={(e) => {
e.stopPropagation();
e.preventDefault();
toggleBookmark(bookmarkItem);
}}
className={`absolute top-4 right-4 z-30 p-2.5 rounded-xl border backdrop-blur-md transition-all shadow-md hover:scale-105 active:scale-95 ${
bookmarked
? 'bg-yellow-500/20 dark:bg-yellow-500/30 border-yellow-500/50 text-yellow-500 hover:bg-yellow-500/30'
: 'bg-black/5 dark:bg-white/5 border-black/10 dark:border-white/10 text-muted-foreground hover:text-foreground hover:bg-black/10 dark:hover:bg-white/10'
}`}
title={bookmarked ? "Remove Bookmark" : "Save Bookmark"}
>
Comment on lines +120 to +140
{item.type === 'roadmap' ? (
<Link
href={`/resources?open=roadmap&title=${encodeURIComponent(item.title)}`}
onClick={onClose}
className="inline-flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 bg-primary text-white rounded-lg hover:bg-primary/95 shadow-sm transition-all"
>
<Map size={12} />
View Roadmap
<ExternalLink size={10} />
</Link>
) : (
<Link
href="/profile"
onClick={onClose}
className="inline-flex items-center gap-1.5 text-xs font-semibold px-3 py-1.5 bg-primary text-white rounded-lg hover:bg-primary/95 shadow-sm transition-all"
>
<Code size={12} />
View Project
<ExternalLink size={10} />
</Link>
)}
Comment on lines +52 to +56
<button
onClick={onClose}
className="p-2 hover:bg-black/5 dark:hover:bg-white/5 rounded-full transition-colors text-muted-foreground hover:text-foreground"
>
<X size={20} />
Comment on lines +108 to +113
<button
onClick={() => removeBookmark(item.id)}
className="p-1.5 hover:bg-red-500/10 text-muted-foreground hover:text-red-500 rounded-lg transition-colors"
title="Remove Bookmark"
>
<Trash2 size={16} />
Comment thread src/hooks/useBookmarks.ts
const getSavedBookmarks = (): BookmarkItem[] => {
if (typeof window === 'undefined') return [];
try {
const data = localStorage.getItem('devpath_saved_roadmaps');
Comment on lines +30 to +36
<motion.div
className="fixed top-0 right-0 bottom-0 z-[1600] w-full max-w-md bg-white/95 dark:bg-[#0f1419]/95 backdrop-blur-xl border-l border-black/5 dark:border-white/10 shadow-2xl flex flex-col"
initial={{ x: '100%' }}
animate={{ x: 0 }}
exit={{ x: '100%' }}
transition={{ type: 'spring', damping: 25, stiffness: 200 }}
>
Co-authored-by: Copilot Autofix powered by AI <175728472+Copilot@users.noreply.github.com>
Copy link
Copy Markdown
Collaborator

@Aditya948351 Aditya948351 left a comment

Choose a reason for hiding this comment

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

I've reviewed the latest changes and the Copilot feedback. Please address Copilot's point about src/lib/firebase.ts—make sure to add a production safety guard so those fallback 'mock' Firebase env values are strictly limited to development and don't leak into production.

Also, as mentioned earlier, please make sure all merge conflicts are completely resolved and the accidentally tracked .png assets are reverted so we can finally get this merged in!

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment

Labels

gssoc26 This is a official GirlScript Summer of Code label. level:intermediate Intermediate level issues type:feature type:refactor

Projects

None yet

Development

Successfully merging this pull request may close these issues.

[FEATURE]: Zero-Auth Offline Roadmap Bookmarking System via LocalStorage API

3 participants