feat(dashboard): mark episodes watched from Continue Watching and Upcoming

Co-Authored-By: Claude Sonnet 5.5 <noreply@anthropic.com>
This commit is contained in:
jakeandClaude Sonnet 5.5 committed 2026-10-05 14:20:09 -04:00
1 parent 899be96316
commit 04bcb66436
21 files changed
+432 -6

No files matched your search

@@ -21,6 +21,7 @@ export interface ContinueWatchingItem {
watchedEpisodes: number;
totalEpisodes: number;
nextEpisode?: {
id: string;
seasonNumber: number;
episodeNumber: number;
name: string | null;
@@ -43,6 +44,7 @@ export const ContinueWatchingCard = memo(function ContinueWatchingCard({
const seasonNum = item.nextEpisode?.seasonNumber;
const epNum = item.nextEpisode?.episodeNumber;
const nextEpLabel = item.nextEpisode ? t`Next: S${seasonNum} E${epNum}` : undefined;
const nextEpisodeId = item.nextEpisode?.id;
const cardLabel = [item.title.title, progressLabel, nextEpLabel].filter(Boolean).join(", ");
return (
@@ -107,6 +109,13 @@ export const ContinueWatchingCard = memo(function ContinueWatchingCard({
</Link.Trigger>
<Link.Preview />
<Link.Menu>
{nextEpisodeId && (
<Link.MenuAction
title={t`Mark S${seasonNum} E${epNum} Watched`}
icon="checkmark.circle"
onPress={() => titleActions.watchEpisode(nextEpisodeId)}
/>
)}
<Link.MenuAction
title={t`Remove from library`}
icon="trash"
@@ -10,6 +10,7 @@ import { ScaledIcon } from "@/components/ui/scaled-icon";
import { Text } from "@/components/ui/text";
import { titleActions } from "@/lib/title-actions";
import type { UpcomingItem } from "@sofa/api/schemas";
import { formatLocalDate } from "@sofa/i18n/date-buckets";
const statusColors = {
in_watchlist: "--color-status-watchlist",
@@ -27,6 +28,10 @@ function formatShortDate(dateStr: string): string {
}).format(d);
}
function hasAired(date: string): boolean {
return date <= formatLocalDate(new Date());
}
export function UpcomingRow({ item }: { item: UpcomingItem }) {
const { t } = useLingui();
const statusColor = useCSSVariable(statusColors[item.userStatus]) as string;
@@ -140,7 +145,7 @@ export function UpcomingRow({ item }: { item: UpcomingItem }) {
onPress={() => titleActions.markMovieWatched(item.titleId, item.titleName)}
/>
)}
{item.titleType === "tv" && item.episodeId && (
{item.titleType === "tv" && item.episodeId && hasAired(item.date) && (
<Link.MenuAction
title={t`Mark Episode Watched`}
icon="checkmark.circle"
@@ -63,6 +63,7 @@ export const continueWatching = os.library.continueWatching.use(authed).handler(
},
nextEpisode: item.nextEpisode
? {
id: item.nextEpisode.id,
seasonNumber: item.nextEpisode.seasonNumber,
episodeNumber: item.nextEpisode.episodeNumber,
name: item.nextEpisode.name,
+52
View File
@@ -0,0 +1,52 @@
import { call } from "@orpc/server";
import { beforeEach, describe, expect, test, vi } from "vitest";
import { auth } from "@sofa/auth/server";
import {
clearAllTables,
insertEpisodeWatch,
insertStatus,
insertTvShow,
insertUser,
} from "@sofa/test/db";
import { implementedRouter } from "../src/orpc/router";
vi.mock("@sofa/auth/server", () => ({
auth: {
api: { getSession: vi.fn<() => void>() },
handler: vi.fn<() => void>(),
},
}));
const getSession = vi.mocked(auth.api.getSession);
const ctx = { context: { headers: new Headers() } };
function sessionFor(userId: string) {
return {
session: { id: `session-${userId}`, userId },
user: { id: userId, role: "user", name: "U", email: `${userId}@example.com` },
} as never;
}
beforeEach(() => {
clearAllTables();
insertUser("user-1");
getSession.mockReset();
getSession.mockResolvedValue(sessionFor("user-1"));
});
describe("library.continueWatching", () => {
test("includes the next episode id", async () => {
const { titleId, episodeIds } = insertTvShow("tv-1", 1001, 1, 3, {
airDates: ["2020-01-01", "2020-01-08", "2020-01-15"],
});
insertStatus("user-1", titleId, "in_progress");
insertEpisodeWatch("user-1", episodeIds[0]!);
const result = await call(implementedRouter.library.continueWatching, undefined, ctx);
expect(result.items).toHaveLength(1);
expect(result.items[0]?.nextEpisode?.id).toBe(episodeIds[1]);
});
});
@@ -1,10 +1,14 @@
import { plural } from "@lingui/core/macro";
import { useLingui } from "@lingui/react/macro";
import { IconPlayerPlay } from "@tabler/icons-react";
import { IconCheck, IconPlayerPlay } from "@tabler/icons-react";
import { Link } from "@tanstack/react-router";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { thumbHashToUrl } from "@/lib/thumbhash";
import { useQuickWatchEpisode } from "./use-quick-watch";
export interface ContinueWatchingItemProps {
title: {
id: string;
@@ -13,6 +17,7 @@ export interface ContinueWatchingItemProps {
backdropThumbHash?: string | null;
};
nextEpisode: {
id: string;
seasonNumber: number;
episodeNumber: number;
name: string | null;
@@ -25,16 +30,25 @@ export interface ContinueWatchingItemProps {
export function ContinueWatchingCard({ item }: { item: ContinueWatchingItemProps }) {
const { t } = useLingui();
const { watchEpisode, pendingId } = useQuickWatchEpisode();
const stillUrl = item.nextEpisode?.stillPath ?? item.title.backdropPath ?? null;
const progress = item.totalEpisodes > 0 ? (item.watchedEpisodes / item.totalEpisodes) * 100 : 0;
const watchedEpisodes = item.watchedEpisodes;
const totalEpisodes = item.totalEpisodes;
const nextEpisode = item.nextEpisode;
const quickWatchSeason = nextEpisode?.seasonNumber;
const quickWatchEpisode = nextEpisode?.episodeNumber;
const quickWatchLabel = nextEpisode
? t`Mark S${quickWatchSeason} E${quickWatchEpisode} watched`
: null;
return (
<div className="relative w-64 shrink-0 sm:w-72">
<Link
to="/titles/$id"
params={{ id: item.title.id }}
className="group bg-card/50 relative inline-block w-64 shrink-0 overflow-hidden rounded-xl ring-1 ring-white/[0.06] transition-shadow hover:shadow-lg hover:shadow-black/25 sm:w-72"
className="group bg-card/50 relative block w-full overflow-hidden rounded-xl ring-1 ring-white/[0.06] transition-shadow hover:shadow-lg hover:shadow-black/25"
>
<div
className="bg-muted relative aspect-video overflow-hidden rounded-t-xl"
@@ -86,9 +100,37 @@ export function ContinueWatchingCard({ item }: { item: ContinueWatchingItemProps
</div>
{progress > 0 && (
<div className="bg-muted absolute right-0 bottom-0 left-0 h-0.5">
<div className="bg-primary h-full transition-[width]" style={{ width: `${progress}%` }} />
<div
className="bg-primary h-full transition-[width]"
style={{ width: `${progress}%` }}
/>
</div>
)}
</Link>
{nextEpisode && quickWatchLabel && (
<Tooltip>
<TooltipTrigger
render={
<Button
variant="secondary"
size="icon"
aria-label={quickWatchLabel}
disabled={pendingId === nextEpisode.id}
className="absolute top-2 right-2 rounded-full bg-black/50 text-white backdrop-blur-sm hover:bg-black/70"
onClick={() =>
watchEpisode(
nextEpisode.id,
`S${nextEpisode.seasonNumber} E${nextEpisode.episodeNumber}`,
)
}
/>
}
>
<IconCheck aria-hidden={true} />
</TooltipTrigger>
<TooltipContent side="bottom">{quickWatchLabel}</TooltipContent>
</Tooltip>
)}
</div>
);
}
@@ -1,11 +1,15 @@
import { plural } from "@lingui/core/macro";
import { useLingui } from "@lingui/react/macro";
import { IconMovie } from "@tabler/icons-react";
import { IconCheck, IconMovie } from "@tabler/icons-react";
import { Link } from "@tanstack/react-router";
import { Button } from "@/components/ui/button";
import { Tooltip, TooltipContent, TooltipTrigger } from "@/components/ui/tooltip";
import { thumbHashToUrl } from "@/lib/thumbhash";
import type { UpcomingItem } from "@sofa/api/schemas";
import { formatLocalDate } from "@sofa/i18n/date-buckets";
import { useQuickWatchEpisode } from "./use-quick-watch";
const statusColorClass = {
in_watchlist: "bg-status-watchlist",
@@ -25,8 +29,19 @@ function formatShortDate(dateStr: string): string {
}).format(d);
}
function hasAired(date: string): boolean {
return date <= formatLocalDate(new Date());
}
export function UpcomingRow({ item }: { item: UpcomingItem }) {
const { t } = useLingui();
const { watchEpisode, pendingId } = useQuickWatchEpisode();
const quickWatchEpisodeId =
item.titleType === "tv" && item.episodeId && hasAired(item.date) ? item.episodeId : null;
const quickWatchSeason = item.seasonNumber ?? 0;
const quickWatchEpisode = item.episodeNumber ?? 0;
const quickWatchLabel = t`Mark S${quickWatchSeason} E${quickWatchEpisode} watched`;
const statusLabels = {
in_watchlist: t`On Watchlist`,
@@ -51,10 +66,11 @@ export function UpcomingRow({ item }: { item: UpcomingItem }) {
}
return (
<div className="relative">
<Link
to="/titles/$id"
params={{ id: item.titleId }}
className="group bg-card/40 hover:bg-card/60 hover:shadow-primary/5 hover:ring-primary/25 flex items-center gap-4 rounded-xl px-3 py-3 ring-1 ring-white/[0.06] transition-[background,box-shadow,ring-color] duration-200 hover:shadow-lg"
className={`group bg-card/40 hover:bg-card/60 hover:shadow-primary/5 hover:ring-primary/25 flex items-center gap-4 rounded-xl py-3 ps-3 ring-1 ring-white/[0.06] transition-[background,box-shadow,ring-color] duration-200 hover:shadow-lg ${quickWatchEpisodeId ? "pe-12" : "pe-3"}`}
>
{/* Poster */}
<div className="relative h-[66px] w-11 shrink-0 overflow-hidden rounded-lg ring-1 ring-white/[0.06]">
@@ -129,5 +145,27 @@ export function UpcomingRow({ item }: { item: UpcomingItem }) {
)}
</div>
</Link>
{quickWatchEpisodeId && (
<Tooltip>
<TooltipTrigger
render={
<Button
variant="ghost"
size="icon"
aria-label={quickWatchLabel}
disabled={pendingId === quickWatchEpisodeId}
className="absolute end-3 top-1/2 -translate-y-1/2 rounded-full"
onClick={() =>
watchEpisode(quickWatchEpisodeId, `S${quickWatchSeason} E${quickWatchEpisode}`)
}
/>
}
>
<IconCheck aria-hidden={true} />
</TooltipTrigger>
<TooltipContent side="top">{quickWatchLabel}</TooltipContent>
</Tooltip>
)}
</div>
);
}
@@ -0,0 +1,33 @@
import { useLingui } from "@lingui/react/macro";
import { useMutation, useQueryClient } from "@tanstack/react-query";
import { toast } from "sonner";
import { getErrorMessage } from "@/lib/error-messages";
import { orpc } from "@/lib/orpc/client";
import { invalidateTrackingQueries } from "@/lib/orpc/invalidate";
/** Mark a single episode watched from a dashboard card, with toast + cache refresh. */
export function useQuickWatchEpisode() {
const { t } = useLingui();
const queryClient = useQueryClient();
const { mutate, variables, isPending } = useMutation(orpc.tracking.watch.mutationOptions());
function watchEpisode(episodeId: string, label: string) {
mutate(
{ scope: "episode", ids: [episodeId] },
{
onSuccess: () => {
toast.success(t`Marked ${label} as watched`);
void invalidateTrackingQueries(queryClient);
},
onError: (err) => {
toast.error(getErrorMessage(err, t`Failed to mark episode`));
},
},
);
}
const pendingId = isPending ? (variables?.ids?.[0] ?? null) : null;
return { watchEpisode, pendingId };
}
+5
View File
@@ -2816,6 +2816,10 @@
{
"type": "object",
"properties": {
"id": {
"type": "string",
"description": "Episode ID"
},
"seasonNumber": {
"type": "number",
"description": "Season number"
@@ -2859,6 +2863,7 @@
}
},
"required": [
"id",
"seasonNumber",
"episodeNumber",
"name",
+1
View File
@@ -560,6 +560,7 @@ export const ContinueWatchingOutput = z
}),
nextEpisode: z
.object({
id: z.string().describe("Episode ID"),
seasonNumber: z.number().describe("Season number"),
episodeNumber: z.number().describe("Episode number"),
name: z.string().nullable().describe("Episode title"),
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "فشل التحديد كمشاهدة"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "فشل تحديد الحلقة"
@@ -1867,6 +1868,21 @@ msgstr "تمييز الحلقة {epNum} كمشاهَدة"
msgid "Mark Episode Watched"
msgstr "تمييز الحلقة كمشاهَدة"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "تم تحديد \"{titleName}\" كمشاهدة"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "Konnte nicht als gesehen markiert werden"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "Episode konnte nicht markiert werden"
@@ -1867,6 +1868,21 @@ msgstr "Episode {epNum} als gesehen markieren"
msgid "Mark Episode Watched"
msgstr "Episode als gesehen markieren"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "\"{titleName}\" als gesehen markiert"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr ""
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr ""
@@ -1867,6 +1868,21 @@ msgstr "Mark episode {epNum} watched"
msgid "Mark Episode Watched"
msgstr "Mark Episode Watched"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr "Mark S{0} E{1} watched"
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr "Mark S{seasonNum} E{epNum} Watched"
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr "Marked \"{titleName}\" as unwatched"
msgid "Marked \"{titleName}\" as watched"
msgstr ""
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr "Marked {label} as watched"
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "Error al marcar como visto"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "Error al marcar el episodio"
@@ -1867,6 +1868,21 @@ msgstr "Marcar episodio {epNum} como visto"
msgid "Mark Episode Watched"
msgstr "Marcar episodio como visto"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "\"{titleName}\" marcado como visto"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "Échec du marquage comme visionné"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "Échec du marquage de l'épisode"
@@ -1867,6 +1868,21 @@ msgstr "Marquer l'épisode {epNum} comme regardé"
msgid "Mark Episode Watched"
msgstr "Marquer l'épisode comme regardé"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "« {titleName} » marqué comme visionné"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "סימון כנצפה נכשל"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "סימון הפרק נכשל"
@@ -1867,6 +1868,21 @@ msgstr "סמן פרק {epNum} כנצפה"
msgid "Mark Episode Watched"
msgstr "סמן פרק כנצפה"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "\"{titleName}\" סומן כנצפה"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "Impossibile segnare come visto"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "Impossibile segnare l'episodio"
@@ -1867,6 +1868,21 @@ msgstr "Segna episodio {epNum} come visto"
msgid "Mark Episode Watched"
msgstr "Segna episodio come visto"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "Segnato \"{titleName}\" come visto"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "視聴済みにするのに失敗しました"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "エピソードのマークに失敗しました"
@@ -1867,6 +1868,21 @@ msgstr "エピソード{epNum}を視聴済みにする"
msgid "Mark Episode Watched"
msgstr "エピソードを視聴済みにする"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "「{titleName}」を視聴済みにしました"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "시청 처리 실패"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "에피소드 처리 실패"
@@ -1867,6 +1868,21 @@ msgstr "에피소드 {epNum} 시청함으로 표시"
msgid "Mark Episode Watched"
msgstr "에피소드 시청함으로 표시"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "\"{titleName}\"을(를) 시청 완료로 표시했습니다"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "Markeren als bekeken mislukt"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "Aflevering markeren mislukt"
@@ -1867,6 +1868,21 @@ msgstr "Aflevering {epNum} als bekeken markeren"
msgid "Mark Episode Watched"
msgstr "Aflevering als bekeken markeren"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "\"{titleName}\" gemarkeerd als bekeken"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "Falha ao marcar como assistido"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "Falha ao marcar episódio"
@@ -1867,6 +1868,21 @@ msgstr "Marcar episódio {epNum} como assistido"
msgid "Mark Episode Watched"
msgstr "Marcar Episódio como Assistido"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "Marcado \"{titleName}\" como assistido"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"
+20
View File
@@ -1308,6 +1308,7 @@ msgstr "标记为已观看失败"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/dashboard/use-quick-watch.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Failed to mark episode"
msgstr "标记单集失败"
@@ -1867,6 +1868,21 @@ msgstr "将第 {epNum} 集标记为已观看"
msgid "Mark Episode Watched"
msgstr "标记剧集为已观看"
#. placeholder {0}: nextEpisode.seasonNumber
#. placeholder {1}: nextEpisode.episodeNumber
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#~ msgid "Mark S{0} E{1} watched"
#~ msgstr ""
#: apps/web/src/components/dashboard/continue-watching-card.tsx
#: apps/web/src/components/dashboard/upcoming-item.tsx
msgid "Mark S{quickWatchSeason} E{quickWatchEpisode} watched"
msgstr ""
#: apps/native/src/components/dashboard/continue-watching-card.tsx
msgid "Mark S{seasonNum} E{epNum} Watched"
msgstr ""
#: apps/native/src/app/title/[id].tsx
#: apps/native/src/components/dashboard/upcoming-row.tsx
#: apps/native/src/components/ui/poster-card.tsx
@@ -1907,6 +1923,10 @@ msgstr ""
msgid "Marked \"{titleName}\" as watched"
msgstr "已将\"{titleName}\"标记为已观看"
#: apps/web/src/components/dashboard/use-quick-watch.ts
msgid "Marked {label} as watched"
msgstr ""
#: apps/native/src/lib/title-actions.ts
#: apps/web/src/components/titles/use-title-actions.ts
msgid "Marked all episodes as watched"