Archived
feat: uv config other feats
- add uv configuration for the backend - update frontend to make auth work - add new auth endpoints - add bookmars feat - add reader feat
This commit is contained in:
@@ -0,0 +1,86 @@
|
||||
import { useEffect } from "react";
|
||||
import { useTranslation } from "react-i18n-lite";
|
||||
import { useAnnotations } from "@/context/AnnotationsContext";
|
||||
import type { MarkerEntry } from "@/types";
|
||||
import {
|
||||
CollapsibleMarkerPassage,
|
||||
CollapsibleMarkerThought,
|
||||
} from "@/components/annotations/CollapsibleMarkerText";
|
||||
|
||||
interface BookMarkersPanelProps {
|
||||
ebookId: number;
|
||||
isOpen: boolean;
|
||||
onClose: () => void;
|
||||
onGoToPassage: (marker: MarkerEntry) => void;
|
||||
}
|
||||
|
||||
export function BookMarkersPanel({
|
||||
ebookId,
|
||||
isOpen,
|
||||
onClose,
|
||||
onGoToPassage,
|
||||
}: BookMarkersPanelProps) {
|
||||
const { t } = useTranslation();
|
||||
const { markers, loadBookmarks, removeBookmark, state } = useAnnotations();
|
||||
|
||||
useEffect(() => {
|
||||
if (isOpen) void loadBookmarks(ebookId);
|
||||
}, [isOpen, ebookId, loadBookmarks]);
|
||||
|
||||
if (!isOpen) return null;
|
||||
|
||||
return (
|
||||
<>
|
||||
<div className="reader-panel-backdrop" onClick={onClose} aria-hidden />
|
||||
<aside className="reader-side-panel reader-markers-panel" role="dialog" aria-label={t("annotations.inBookPanel")}>
|
||||
<header className="reader-panel-header">
|
||||
<h2>{t("annotations.inBookPanel")}</h2>
|
||||
<button type="button" className="reader-panel-close" onClick={onClose} aria-label={t("annotations.close")}>
|
||||
×
|
||||
</button>
|
||||
</header>
|
||||
<div className="reader-panel-body">
|
||||
{state.bookmarksLoading ? (
|
||||
<p className="annotations-loading">{t("annotations.loading")}</p>
|
||||
) : markers.length === 0 ? (
|
||||
<p className="annotations-empty">{t("annotations.selectTextHint")}</p>
|
||||
) : (
|
||||
<ul className="marker-thread-list">
|
||||
{markers.map((m) => (
|
||||
<li key={m.id} className="marker-thread">
|
||||
{m.chapter_title && (
|
||||
<span className="marker-thread-chapter">{m.chapter_title}</span>
|
||||
)}
|
||||
{m.location_text && (
|
||||
<CollapsibleMarkerPassage
|
||||
text={m.location_text}
|
||||
highlightColor={m.highlight_color}
|
||||
className="annotation-quote"
|
||||
/>
|
||||
)}
|
||||
{m.content ? (
|
||||
<CollapsibleMarkerThought text={m.content} />
|
||||
) : (
|
||||
<span className="annotation-kind-badge bookmark-badge">{t("annotations.bookmarkOnly")}</span>
|
||||
)}
|
||||
<div className="annotation-actions">
|
||||
<button type="button" className="btn btn-sm" onClick={() => onGoToPassage(m)}>
|
||||
{t("annotations.goToPassage")}
|
||||
</button>
|
||||
<button
|
||||
type="button"
|
||||
className="btn btn-sm btn-danger"
|
||||
onClick={() => removeBookmark(m.id)}
|
||||
>
|
||||
{t("common.delete")}
|
||||
</button>
|
||||
</div>
|
||||
</li>
|
||||
))}
|
||||
</ul>
|
||||
)}
|
||||
</div>
|
||||
</aside>
|
||||
</>
|
||||
);
|
||||
}
|
||||
Reference in New Issue
Block a user