feat: message bookmarks — star important messages and jump back to them

- Add bookmark button on message hover (amber star icon)
- Bookmarked messages show a small star indicator in the timestamp row
- Floating bookmarks panel to list and jump to bookmarked messages
- Bookmarks persisted in localStorage per session
- i18n support (EN/FR)
This commit is contained in:
Nicolas Varrot
2026-02-14 09:56:37 +00:00
parent 56fccc1e62
commit aa9680cad6
4 changed files with 132 additions and 5 deletions

68
src/hooks/useBookmarks.ts Normal file
View File

@@ -0,0 +1,68 @@
import { useState, useCallback } from 'react';
const STORAGE_KEY = 'pinchchat-bookmarks';
export interface Bookmark {
messageId: string;
sessionKey: string;
preview: string;
timestamp: number;
bookmarkedAt: number;
}
function loadBookmarks(): Bookmark[] {
try {
const raw = localStorage.getItem(STORAGE_KEY);
if (raw) return JSON.parse(raw) as Bookmark[];
} catch { /* noop */ }
return [];
}
function saveBookmarks(bookmarks: Bookmark[]) {
try {
localStorage.setItem(STORAGE_KEY, JSON.stringify(bookmarks));
} catch { /* noop */ }
}
export function useBookmarks() {
const [bookmarks, setBookmarks] = useState<Bookmark[]>(loadBookmarks);
const toggle = useCallback((messageId: string, sessionKey: string, preview: string, timestamp: number) => {
setBookmarks(prev => {
const exists = prev.some(b => b.messageId === messageId);
const next = exists
? prev.filter(b => b.messageId !== messageId)
: [...prev, { messageId, sessionKey, preview: preview.slice(0, 120), timestamp, bookmarkedAt: Date.now() }];
saveBookmarks(next);
return next;
});
}, []);
const isBookmarked = useCallback((messageId: string) => {
return bookmarks.some(b => b.messageId === messageId);
}, [bookmarks]);
const getForSession = useCallback((sessionKey: string) => {
return bookmarks
.filter(b => b.sessionKey === sessionKey)
.sort((a, b) => a.timestamp - b.timestamp);
}, [bookmarks]);
const remove = useCallback((messageId: string) => {
setBookmarks(prev => {
const next = prev.filter(b => b.messageId !== messageId);
saveBookmarks(next);
return next;
});
}, []);
const clearSession = useCallback((sessionKey: string) => {
setBookmarks(prev => {
const next = prev.filter(b => b.sessionKey !== sessionKey);
saveBookmarks(next);
return next;
});
}, []);
return { bookmarks, toggle, isBookmarked, getForSession, remove, clearSession };
}