fix(tracking): report when a title was already in the library

updateStatus now returns { alreadyAdded }. Native toasts say so instead of
claiming success, and the Explore hero hides Add to Watchlist for library titles.

Co-Authored-By: Claude Opus 5.5 <noreply@anthropic.com>
This commit is contained in:
jakeandClaude Opus 5.5 committed 2026-10-05 22:50:04 -04:00
1 parent d6abcbd2bb
commit 8da4cbbe52
19 files changed
+167 -18

No files matched your search

@@ -70,7 +70,9 @@ export default function ExploreScreen() {
refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />} refreshControl={<RefreshControl refreshing={refreshing} onRefresh={onRefresh} />}
> >
<View className="gap-8"> <View className="gap-8">
{heroItem && <HeroBanner item={heroItem} />} {heroItem && (
<HeroBanner item={{ ...heroItem, userStatus: trendingStatuses[heroItem.id] ?? null }} />
)}
<Animated.View entering={FadeInDown.duration(300).delay(100)}> <Animated.View entering={FadeInDown.duration(300).delay(100)}>
<FilterableTitleRow <FilterableTitleRow
@@ -21,6 +21,7 @@ export interface HeroBannerItem {
overview?: string | null; overview?: string | null;
voteAverage?: number | null; voteAverage?: number | null;
releaseDate?: string | null; releaseDate?: string | null;
userStatus?: string | null;
} }
export function HeroBanner({ item }: { item: HeroBannerItem }) { export function HeroBanner({ item }: { item: HeroBannerItem }) {
@@ -127,13 +128,15 @@ export function HeroBanner({ item }: { item: HeroBannerItem }) {
</Pressable> </Pressable>
</Link.Trigger> </Link.Trigger>
<Link.Preview /> <Link.Preview />
<Link.Menu> {!item.userStatus && (
<Link.MenuAction <Link.Menu>
title={t`Add to Watchlist`} <Link.MenuAction
icon="bookmark" title={t`Add to Watchlist`}
onPress={() => titleActions.addToWatchlist(item.id, item.title)} icon="bookmark"
/> onPress={() => titleActions.addToWatchlist(item.id, item.title)}
</Link.Menu> />
</Link.Menu>
)}
</Link> </Link>
</Animated.View> </Animated.View>
</GestureDetector> </GestureDetector>
+5 -1
View File
@@ -44,7 +44,11 @@ export function useTitleActions(options?: UseTitleActionsOptions) {
const updateStatus = useMutation( const updateStatus = useMutation(
orpc.tracking.updateStatus.mutationOptions({ orpc.tracking.updateStatus.mutationOptions({
onSuccess: (_data, input) => { onSuccess: (data, input) => {
if (input.status === "watchlist" && data?.alreadyAdded) {
toast.info(t`Already in your library`);
return invalidateTitleQueries();
}
const statusMessages: Record<string, string> = { const statusMessages: Record<string, string> = {
watchlist: t`Added to watchlist`, watchlist: t`Added to watchlist`,
}; };
+14 -6
View File
@@ -41,12 +41,20 @@ export function invalidateTitleQueries(): Promise<unknown> {
export const titleActions = { export const titleActions = {
async addToWatchlist(id: string, titleName?: string) { async addToWatchlist(id: string, titleName?: string) {
try { try {
await client.tracking.updateStatus({ id, status: "watchlist" }); const result = await client.tracking.updateStatus({ id, status: "watchlist" });
toast.success( if (result.alreadyAdded) {
titleName toast.info(
? i18n._(msg`Added "${titleName}" to watchlist`) titleName
: i18n._(msg`Added to watchlist`), ? i18n._(msg`"${titleName}" is already in your library`)
); : i18n._(msg`Already in your library`),
);
} else {
toast.success(
titleName
? i18n._(msg`Added "${titleName}" to watchlist`)
: i18n._(msg`Added to watchlist`),
);
}
invalidateTitleQueries(); invalidateTitleQueries();
} catch { } catch {
toast.error(i18n._(msg`Failed to add to watchlist`)); toast.error(i18n._(msg`Failed to add to watchlist`));
+3 -1
View File
@@ -90,7 +90,7 @@ export const updateStatus = os.tracking.updateStatus
.handler(async ({ input, context }) => { .handler(async ({ input, context }) => {
if (input.status === null) { if (input.status === null) {
removeTitleStatus(context.user.id, input.id); removeTitleStatus(context.user.id, input.id);
return; return { alreadyAdded: false };
} }
// Auto-import from TMDB if the title is a shell (absorbs quickAdd logic) // Auto-import from TMDB if the title is a shell (absorbs quickAdd logic)
@@ -106,6 +106,8 @@ export const updateStatus = os.tracking.updateStatus
log.warn(`Failed to import ${result.type} TMDB ${result.tmdbId}:`, err); log.warn(`Failed to import ${result.type} TMDB ${result.tmdbId}:`, err);
}); });
} }
return { alreadyAdded: result.alreadyAdded };
}); });
export const rate = os.tracking.rate.use(authed).handler(({ input, context }) => { export const rate = os.tracking.rate.use(authed).handler(({ input, context }) => {
+17 -1
View File
@@ -1983,7 +1983,23 @@
}, },
"responses": { "responses": {
"200": { "200": {
"description": "OK" "description": "OK",
"content": {
"application/json": {
"schema": {
"type": "object",
"properties": {
"alreadyAdded": {
"type": "boolean",
"description": "True when the title was already in the library, so nothing changed"
}
},
"required": [
"alreadyAdded"
]
}
}
}
}, },
"404": { "404": {
"description": "404", "description": "404",
+7 -1
View File
@@ -146,7 +146,13 @@ export const contract = {
"Set the user's tracking status for a title. Use null to remove the title from the library entirely.", "Set the user's tracking status for a title. Use null to remove the title from the library entirely.",
}) })
.input(UpdateStatusInput) .input(UpdateStatusInput)
.output(z.void()) .output(
z.object({
alreadyAdded: z
.boolean()
.describe("True when the title was already in the library, so nothing changed"),
}),
)
.errors({ .errors({
NOT_FOUND: { NOT_FOUND: {
message: "Title not found", message: "Title not found",
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...و{remainingErrors} أخرى" msgstr "...و{remainingErrors} أخرى"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "هل لديك حساب بالفعل؟"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "هل لديك حساب بالفعل؟ تسجيل الدخول" msgstr "هل لديك حساب بالفعل؟ تسجيل الدخول"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "متاح أيضاً على" msgstr "متاح أيضاً على"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...und {remainingErrors} weitere" msgstr "...und {remainingErrors} weitere"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "Bereits ein Konto?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "Bereits ein Konto? Anmelden" msgstr "Bereits ein Konto? Anmelden"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "Auch verfügbar auf" msgstr "Auch verfügbar auf"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...and {remainingErrors} more" msgstr "...and {remainingErrors} more"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr "\"{titleName}\" is already in your library"
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr ""
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "" msgstr ""
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr "Already in your library"
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "Also available on" msgstr "Also available on"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...y {remainingErrors} más" msgstr "...y {remainingErrors} más"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "¿Ya tienes una cuenta?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "¿Ya tienes una cuenta? Inicia sesión" msgstr "¿Ya tienes una cuenta? Inicia sesión"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "También disponible en" msgstr "También disponible en"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...et {remainingErrors} de plus" msgstr "...et {remainingErrors} de plus"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "Vous avez déjà un compte ?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "Vous avez déjà un compte ? Se connecter" msgstr "Vous avez déjà un compte ? Se connecter"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "Aussi disponible sur" msgstr "Aussi disponible sur"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...ועוד {remainingErrors}" msgstr "...ועוד {remainingErrors}"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "כבר יש לך חשבון?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "כבר יש לך חשבון? התחבר" msgstr "כבר יש לך חשבון? התחבר"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "זמין גם ב" msgstr "זמין גם ב"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...e altri {remainingErrors}" msgstr "...e altri {remainingErrors}"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "Hai già un account?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "Hai già un account? Accedi" msgstr "Hai già un account? Accedi"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "Disponibile anche su" msgstr "Disponibile anche su"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...他{remainingErrors}件" msgstr "...他{remainingErrors}件"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "すでにアカウントをお持ちですか?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "すでにアカウントをお持ちの方はサインイン" msgstr "すでにアカウントをお持ちの方はサインイン"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "他でも視聴可能" msgstr "他でも視聴可能"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...그 외 {remainingErrors}개 더" msgstr "...그 외 {remainingErrors}개 더"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "이미 계정이 있으신가요?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "이미 계정이 있으신가요? 로그인" msgstr "이미 계정이 있으신가요? 로그인"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "다음에서도 이용 가능" msgstr "다음에서도 이용 가능"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...en {remainingErrors} meer" msgstr "...en {remainingErrors} meer"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "Al een account?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "Al een account? Aanmelden" msgstr "Al een account? Aanmelden"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "Ook beschikbaar op" msgstr "Ook beschikbaar op"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...e mais {remainingErrors}" msgstr "...e mais {remainingErrors}"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "Já tem uma conta?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "Já tem uma conta? Entrar" msgstr "Já tem uma conta? Entrar"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "Também disponível em" msgstr "Também disponível em"
+9
View File
@@ -26,6 +26,10 @@ msgstr ""
msgid "...and {remainingErrors} more" msgid "...and {remainingErrors} more"
msgstr "...及另外 {remainingErrors} 个" msgstr "...及另外 {remainingErrors} 个"
#: apps/native/src/lib/title-actions.ts
msgid "\"{titleName}\" is already in your library"
msgstr ""
#. placeholder {0}: systemHealth.data.imageCache.imageCount #. placeholder {0}: systemHealth.data.imageCache.imageCount
#: apps/native/src/app/(tabs)/(settings)/index.tsx #: apps/native/src/app/(tabs)/(settings)/index.tsx
msgid "{0, plural, one {# image} other {# images}}" msgid "{0, plural, one {# image} other {# images}}"
@@ -407,6 +411,11 @@ msgstr "已有账户?"
msgid "Already have an account? Sign in" msgid "Already have an account? Sign in"
msgstr "已有账户?立即登录" msgstr "已有账户?立即登录"
#: apps/native/src/hooks/use-title-actions.ts
#: apps/native/src/lib/title-actions.ts
msgid "Already in your library"
msgstr ""
#: apps/web/src/components/titles/title-availability.tsx #: apps/web/src/components/titles/title-availability.tsx
msgid "Also available on" msgid "Also available on"
msgstr "同样在以下平台可用" msgstr "同样在以下平台可用"