feat: add anonymous mode toggle to hide reading progress and tracking

Adds a toggleable anonymous mode (eye icon in header) that:
- Stops syncing read progress to the server while reading
- Hides mark as read/unread buttons on book covers and lists
- Hides reading status badges on series and books
- Hides progress bars on series and book covers
- Hides "continue reading" and "continue series" sections on home
- Persists the setting server-side in user preferences (anonymousMode)

Co-Authored-By: Claude Opus 4.6 <noreply@anthropic.com>
This commit is contained in:
2026-03-20 13:35:22 +01:00
parent a82ce024ee
commit c5da33d6b2
19 changed files with 197 additions and 90 deletions

View File

@@ -14,6 +14,7 @@ import { StatusBadge } from "@/components/ui/status-badge";
import { IconButton } from "@/components/ui/icon-button";
import logger from "@/lib/logger";
import { addToFavorites, removeFromFavorites } from "@/app/actions/favorites";
import { useAnonymous } from "@/contexts/AnonymousContext";
interface SeriesHeaderProps {
series: NormalizedSeries;
@@ -23,6 +24,7 @@ interface SeriesHeaderProps {
export const SeriesHeader = ({ series, refreshSeries, initialIsFavorite }: SeriesHeaderProps) => {
const { toast } = useToast();
const { isAnonymous } = useAnonymous();
const [isFavorite, setIsFavorite] = useState(initialIsFavorite);
const [isDescriptionExpanded, setIsDescriptionExpanded] = useState(false);
const { t } = useTranslate();
@@ -100,7 +102,7 @@ export const SeriesHeader = ({ series, refreshSeries, initialIsFavorite }: Serie
};
};
const statusInfo = getReadingStatusInfo();
const statusInfo = isAnonymous ? null : getReadingStatusInfo();
const authorsText = series.authors?.length
? series.authors.map((a) => a.name).join(", ")
: null;
@@ -152,9 +154,11 @@ export const SeriesHeader = ({ series, refreshSeries, initialIsFavorite }: Serie
</div>
)}
<div className="flex items-center gap-4 mt-4 justify-center md:justify-start flex-wrap">
<StatusBadge status={statusInfo.status} icon={statusInfo.icon}>
{statusInfo.label}
</StatusBadge>
{statusInfo && (
<StatusBadge status={statusInfo.status} icon={statusInfo.icon}>
{statusInfo.label}
</StatusBadge>
)}
<span className="text-sm text-white/80">
{series.bookCount === 1
? t("series.header.books", { count: series.bookCount })