feat(search): tag-based page search surface + per-page tags & collections
Add the /search surface (Pages / Chapters / Mangas tabs) backed by per-user page tags and per-page collections: schema (migration 0023), backend endpoints for page tags/collections and tagged-page aggregations (with the OCR text-search param reserved at 501), plus the frontend API clients, library Page-tags tab, collection page sections, page context menu / AddTagsSheet, and reader long-press wiring. Includes the continuous-reader navigation fixes (?page=N handling, chapter-reset timing, back-button pops history) and tag-normalization hardening accumulated on the branch. Bump version 0.60.2 -> 0.62.0. Co-Authored-By: Claude Opus 4.8 (1M context) <noreply@anthropic.com>
This commit is contained in:
@@ -33,6 +33,22 @@ pub struct CollectionSummary {
|
||||
pub sample_covers: Vec<String>,
|
||||
}
|
||||
|
||||
/// Row returned by `GET /collections/:id/pages`. Joins through
|
||||
/// `chapters` and `mangas` so the collection detail view can render a
|
||||
/// thumbnail + breadcrumb without per-row follow-up fetches.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct CollectionPageItem {
|
||||
pub page_id: Uuid,
|
||||
pub chapter_id: Uuid,
|
||||
pub manga_id: Uuid,
|
||||
pub page_number: i32,
|
||||
pub chapter_number: i32,
|
||||
pub chapter_title: Option<String>,
|
||||
pub manga_title: String,
|
||||
pub storage_key: String,
|
||||
pub added_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct NewCollection {
|
||||
pub name: String,
|
||||
|
||||
@@ -7,6 +7,7 @@ pub mod collection;
|
||||
pub mod genre;
|
||||
pub mod manga;
|
||||
pub mod page;
|
||||
pub mod page_tag;
|
||||
pub mod patch;
|
||||
pub mod read_progress;
|
||||
pub mod session;
|
||||
@@ -21,10 +22,14 @@ pub use api_token::ApiToken;
|
||||
pub use author::{Author, AuthorRef, AuthorWithCount};
|
||||
pub use bookmark::{Bookmark, BookmarkSummary};
|
||||
pub use chapter::Chapter;
|
||||
pub use collection::{Collection, CollectionSummary};
|
||||
pub use collection::{Collection, CollectionPageItem, CollectionSummary};
|
||||
pub use genre::{Genre, GenreRef};
|
||||
pub use manga::{Manga, MangaCard, MangaDetail};
|
||||
pub use page::Page;
|
||||
pub use page_tag::{
|
||||
NewPageTag, PageTagSummary, TaggedChapterAggregate, TaggedMangaAggregate,
|
||||
TaggedPageItem,
|
||||
};
|
||||
pub use patch::Patch;
|
||||
pub use read_progress::{ReadProgress, ReadProgressForManga, ReadProgressSummary};
|
||||
pub use session::Session;
|
||||
|
||||
63
backend/src/domain/page_tag.rs
Normal file
63
backend/src/domain/page_tag.rs
Normal file
@@ -0,0 +1,63 @@
|
||||
use chrono::{DateTime, Utc};
|
||||
use serde::{Deserialize, Serialize};
|
||||
use sqlx::FromRow;
|
||||
use uuid::Uuid;
|
||||
|
||||
#[derive(Debug, Clone, Deserialize)]
|
||||
pub struct NewPageTag {
|
||||
pub tag: String,
|
||||
}
|
||||
|
||||
/// Returned by `GET /v1/me/page-tags`. Joins through chapters and
|
||||
/// mangas so the library Page-tags tab can render the breadcrumb
|
||||
/// without follow-up requests.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct TaggedPageItem {
|
||||
pub tag: String,
|
||||
pub page_id: Uuid,
|
||||
pub chapter_id: Uuid,
|
||||
pub manga_id: Uuid,
|
||||
pub page_number: i32,
|
||||
pub chapter_number: i32,
|
||||
pub chapter_title: Option<String>,
|
||||
pub manga_title: String,
|
||||
pub storage_key: String,
|
||||
pub tagged_at: DateTime<Utc>,
|
||||
}
|
||||
|
||||
/// Distinct-tags histogram row. Used both for autocomplete in the
|
||||
/// "Add tag" sheet and for the chip cloud in the library Page-tags
|
||||
/// tab.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct PageTagSummary {
|
||||
pub tag: String,
|
||||
pub count: i64,
|
||||
}
|
||||
|
||||
/// One chapter (with breadcrumb) ranked by how many of its pages the
|
||||
/// caller has tagged with a given tag. Returned by the `/search`
|
||||
/// page's Chapters tab via `GET /v1/me/page-tags/chapters`.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct TaggedChapterAggregate {
|
||||
pub chapter_id: Uuid,
|
||||
pub manga_id: Uuid,
|
||||
pub manga_title: String,
|
||||
pub chapter_number: i32,
|
||||
pub chapter_title: Option<String>,
|
||||
pub match_count: i64,
|
||||
/// Up to 3 storage keys of matching pages in this chapter,
|
||||
/// page-number ascending. Powers the thumbnail strip in the row.
|
||||
pub sample_storage_keys: Vec<String>,
|
||||
}
|
||||
|
||||
/// One manga ranked by how many of its pages (across all chapters)
|
||||
/// the caller has tagged with a given tag. Returned by the `/search`
|
||||
/// page's Mangas tab.
|
||||
#[derive(Debug, Clone, Serialize, FromRow)]
|
||||
pub struct TaggedMangaAggregate {
|
||||
pub manga_id: Uuid,
|
||||
pub manga_title: String,
|
||||
pub manga_cover_image_path: Option<String>,
|
||||
pub match_count: i64,
|
||||
pub sample_storage_keys: Vec<String>,
|
||||
}
|
||||
Reference in New Issue
Block a user