mirror of
https://github.com/bookorbit/bookorbit.git
synced 2026-11-03 00:11:37 -06:00
feat(achievements): rebuild the page as a trophy cabinet with hints
Replace the card grid with medals, a showcase, category shelves, a record book and a detail sheet. Add GET /achievements/hints, which explains how to unlock what is still locked from the user's own library and reading history, backed by a new page-count index. Year-Round Reader now reports its months read as progress.
This commit is contained in:
@@ -49,3 +49,77 @@ export interface AchievementCelebrationClaim {
|
||||
expiresAt: string;
|
||||
achievement: AchievementItem;
|
||||
}
|
||||
|
||||
/**
|
||||
* What a "how to get it" hint says. The list grows: a client renders the kinds it knows and skips the rest.
|
||||
* - `reading_pace`: hours read in the last `days` days, and how long the next tier takes at that pace.
|
||||
* - `period_unread`: unfinished books from a decade or century not finished yet; `period` is its first year.
|
||||
* - `stalled_book`: an in-progress book started long enough ago to qualify once finished.
|
||||
* - `nearly_finished`: in-progress books past `minPercent`.
|
||||
* - `unrated_finished`: finished books with no rating.
|
||||
* - `short_books` / `long_books`: unfinished books under or over a page count.
|
||||
* - `old_books`: unfinished books published before `year`.
|
||||
* - `author_unread`: unfinished books by the author already read most among those with enough books.
|
||||
* - `language_unread`: unfinished books in a language not finished yet.
|
||||
* - `months_left`: months of `year` still needed, all of them still ahead.
|
||||
* - `unavailable`: nothing in the library or calendar can earn it now; `reason` says why.
|
||||
*/
|
||||
export type AchievementHintKind =
|
||||
| "reading_pace"
|
||||
| "period_unread"
|
||||
| "stalled_book"
|
||||
| "nearly_finished"
|
||||
| "unrated_finished"
|
||||
| "short_books"
|
||||
| "long_books"
|
||||
| "old_books"
|
||||
| "author_unread"
|
||||
| "language_unread"
|
||||
| "months_left"
|
||||
| "unavailable";
|
||||
|
||||
export type AchievementHintUnavailableReason = "no_long_book" | "nothing_before_year" | "too_few_periods" | "missed_month";
|
||||
|
||||
/** A book a hint can open in the reader. */
|
||||
export interface AchievementHintResumeTarget {
|
||||
bookId: number;
|
||||
fileId: number;
|
||||
format: string | null;
|
||||
}
|
||||
|
||||
interface AchievementHintBase {
|
||||
/** The achievement the hint is for: the next unearned tier of a set, or a single achievement. */
|
||||
key: string;
|
||||
/** How many books (or months) the hint counts. */
|
||||
count?: number;
|
||||
/** Up to three books the user can open, best first. */
|
||||
bookIds?: number[];
|
||||
/** Titles of `bookIds`, in the same order. */
|
||||
bookTitles?: (string | null)[];
|
||||
}
|
||||
|
||||
export type AchievementHint = AchievementHintBase &
|
||||
(
|
||||
| { kind: "reading_pace"; values: { hours: number; days: number; remaining: number; etaDays: number } }
|
||||
| { kind: "period_unread"; values: { period: number; years: 10 | 100 } }
|
||||
| {
|
||||
kind: "stalled_book";
|
||||
values: { title: string | null; percent: number | null; startedAt: string; resume: AchievementHintResumeTarget | null };
|
||||
}
|
||||
| { kind: "nearly_finished"; values: { minPercent: number; resume: AchievementHintResumeTarget | null } }
|
||||
| { kind: "unrated_finished"; values: Record<string, never> }
|
||||
| { kind: "short_books"; values: { maxPages: number; inProgress: number } }
|
||||
| { kind: "long_books"; values: { minPages: number } }
|
||||
| { kind: "old_books"; values: { year: number } }
|
||||
| { kind: "author_unread"; values: { authorId: number; author: string; finished: number } }
|
||||
| { kind: "language_unread"; values: { language: string } }
|
||||
| { kind: "months_left"; values: { year: number; monthsRead: number; months: number[] } }
|
||||
| {
|
||||
kind: "unavailable";
|
||||
values: { reason: AchievementHintUnavailableReason; pages?: number; year?: number; years?: 10 | 100; reachable?: number; month?: number };
|
||||
}
|
||||
);
|
||||
|
||||
export interface AchievementHintsResponse {
|
||||
hints: AchievementHint[];
|
||||
}
|
||||
|
||||
Reference in New Issue
Block a user