fix: address review feedback on API reorganization

- Fix updateRating invalidation in native app — was only invalidating
  title queries, now calls invalidateTitleQueries() to also refresh
  tracking.userInfo (drives the rating UI)
- Make unwatchMovie status revert consistent with unwatchSeries — revert
  any non-watchlist status, not just "completed"
- Add sync invariant comment on handleWatch/handleUnwatch loops
- Remove unused queryClient import in native use-title-actions

Co-Authored-By: Claude Opus 4.6 (1M context) <noreply@anthropic.com>
This commit is contained in:
2026-03-26 12:58:26 -04:00
co-authored by Claude Opus 4.6
parent 73e5c96bd8
commit d2ddc0fd34
23 changed files with 188 additions and 386 deletions
+12 -32
View File
@@ -11,7 +11,6 @@ import {
ContinueWatchingOutput,
CreateImportJobInput,
CreateIntegrationInput,
DashboardStatsOutput,
DiscoverInput,
DiscoverOutput,
DiscoverRecommendationsOutput,
@@ -26,6 +25,7 @@ import {
LibraryGenresOutput,
LibraryListInput,
LibraryListOutput,
LibraryStatsOutput,
MediaTypeParam,
PageParam,
PaginatedInput,
@@ -38,7 +38,6 @@ import {
PublicInfoOutput,
PurgeImageCacheOutput,
PurgeMetadataCacheOutput,
QuickAddOutput,
RestoreBackupInput,
SearchInput,
SearchOutput,
@@ -166,24 +165,6 @@ export const contract = {
})
.input(IdParam)
.output(UserInfoOutput),
quickAdd: oc
.route({
method: "POST",
path: "/tracking/titles/{id}/quick-add",
tags: ["Tracking"],
summary: "Quick add title to library",
description:
"Add a title to the user's watchlist and trigger a full TMDB import if needed. If the title already exists in the user's library, returns alreadyAdded: true.",
successDescription: "Title ID and whether it was already in the library",
})
.input(IdParam)
.output(QuickAddOutput)
.errors({
NOT_FOUND: {
message: "Title not found",
data: appErrorData(AppErrorCode.TITLE_NOT_FOUND),
},
}),
stats: oc
.route({
method: "GET",
@@ -191,18 +172,7 @@ export const contract = {
tags: ["Tracking"],
summary: "Get watch statistics",
description:
"Fetch summary counts for the current user: movies watched this month, episodes this week, library size, and completed titles.",
successDescription: "Aggregate watch statistics",
})
.output(DashboardStatsOutput),
history: oc
.route({
method: "GET",
path: "/tracking/history",
tags: ["Tracking"],
summary: "Get watch history",
description:
"Fetch the user's watch counts grouped by time period. Useful for rendering activity charts.",
"Fetch the user's watch counts grouped by time period. Useful for rendering activity charts and dashboard counters.",
successDescription: "Watch counts bucketed by time period",
})
.input(WatchHistoryInput)
@@ -234,6 +204,16 @@ export const contract = {
successDescription: "Genres present in the library",
})
.output(LibraryGenresOutput),
stats: oc
.route({
method: "GET",
path: "/library/stats",
tags: ["Library"],
summary: "Get library statistics",
description: "Fetch aggregate library counts: total titles and completed titles.",
successDescription: "Library size and completed count",
})
.output(LibraryStatsOutput),
continueWatching: oc
.route({
method: "GET",
+4 -17
View File
@@ -537,14 +537,12 @@ export const PersonDetailOutput = z
// ─── Dashboard outputs ─────────────────────────────────────────
export const DashboardStatsOutput = z
export const LibraryStatsOutput = z
.object({
moviesThisMonth: z.number().describe("Movies watched in the current calendar month"),
episodesThisWeek: z.number().describe("Episodes watched in the current calendar week"),
librarySize: z.number().describe("Total titles in the user's library"),
size: z.number().describe("Total titles in the user's library"),
completed: z.number().describe("Total titles with completed status"),
})
.meta({ description: "Aggregate watch statistics for the dashboard" });
.meta({ description: "Aggregate library statistics" });
export const ContinueWatchingOutput = z
.object({
@@ -1000,17 +998,6 @@ export const PurgeImageCacheOutput = z
})
.meta({ description: "Result of purging the image cache from disk" });
// ─── Quick add output ──────────────────────────────────────────
export const QuickAddOutput = z
.object({
id: z.string().describe("Internal title ID"),
alreadyAdded: z.boolean().describe("True if the title was already in the user's library"),
})
.meta({
description: "Result of a quick-add operation",
});
// ─── System outputs ───────────────────────────────────────────
export const PublicInfoOutput = z
@@ -1225,7 +1212,7 @@ export type BackupInfo = z.infer<typeof BackupSchema>;
export type CastMember = z.infer<typeof CastMemberSchema>;
export type ColorPalette = z.infer<typeof ColorPaletteSchema>;
export type CronJobName = z.infer<typeof cronJobName>;
export type DashboardStats = z.infer<typeof DashboardStatsOutput>;
export type LibraryStats = z.infer<typeof LibraryStatsOutput>;
export type Episode = z.infer<typeof EpisodeSchema>;
export type HistoryBucket = z.infer<typeof HistoryBucketSchema>;
export type PersonCredit = z.infer<typeof PersonCreditSchema>;