diff --git a/internal/router/reader.go b/internal/router/reader.go index e067b8e..0e0d89d 100644 --- a/internal/router/reader.go +++ b/internal/router/reader.go @@ -1,18 +1,15 @@ package router import ( - "bookhoard/internal/database" "bookhoard/internal/handlers" "bookhoard/internal/services" "bookhoard/internal/sync" "bookhoard/internal/utils" "bookhoard/templates" "bytes" - "errors" "net/http" "github.com/google/uuid" - "github.com/jackc/pgx/v5" "github.com/jackc/pgx/v5/pgtype" "github.com/labstack/echo/v5" ) @@ -69,21 +66,10 @@ func registerReaderRoutes(cfg *Config) { if !visible { return renderErrorPage(c, "Access denied", "access_denied") } - // Get reading progress - var progress database.ReadingProgress - progress, err = cfg.Queries.GetReadingProgress(c.Request().Context(), database.GetReadingProgressParams{ - MediaItemID: pgtype.UUID{Bytes: parsedUUID, Valid: true}, - UserID: uuidToPGType(userUUID), - }) - if err != nil && !errors.Is(err, pgx.ErrNoRows) { - progress = database.ReadingProgress{} - } - // Get bookmarks - bookmarks, _ := cfg.Queries.GetMediaBookmarks(c.Request().Context(), database.GetMediaBookmarksParams{ - MediaItemID: pgtype.UUID{Bytes: parsedUUID, Valid: true}, - UserID: uuidToPGType(userUUID), - }) - // Convert to template types + // Convert to template types. Reading state is deliberately NOT + // fetched or embedded: the reader pulls position, bookmarks, and + // annotations from the APIs at open time so the page can never + // carry (nor write back) a stale snapshot. mediaUUID, _ := uuid.FromBytes(mediaItem.ID.Bytes[0:16]) libUUID, _ := uuid.FromBytes(mediaItem.LibraryID.Bytes[0:16]) metadata := templates.ReaderMetadata{ @@ -104,58 +90,9 @@ func registerReaderRoutes(cfg *Config) { TotalCharacters: mediaItem.TotalCharacters.Int64, EstimatedPages: sync.EstimatedPages(mediaItem.TotalCharacters.Int64), } - // Progress conversion (inline) - progressUUID, _ := uuid.FromBytes(progress.ID.Bytes[0:16]) - progressMediaUUID, _ := uuid.FromBytes(progress.MediaItemID.Bytes[0:16]) - progressUserUUID, _ := uuid.FromBytes(progress.UserID.Bytes[0:16]) - templateProgress := templates.ReadingProgress{ - ID: progressUUID.String(), - MediaItemID: progressMediaUUID.String(), - UserID: progressUserUUID.String(), - CurrentPage: int(progress.CurrentPage.Int32), - TotalPages: int(progress.TotalPages.Int32), - Percentage: progress.Percentage.Float64 * 100, - EpubCfi: textToString(progress.Epubcfi), - LastReadAt: progress.LastReadAt.Time, - Chapter: int(progress.Chapter.Int32), - ChapterProgress: progress.ChapterProgress.Float64 * 100, - FormatGroup: mediaItem.FormatGroup, - } - // Bookmarks conversion (inline, with loop) - templateBookmarks := make([]templates.Bookmark, len(bookmarks)) - for i, b := range bookmarks { - bookmarkUUID, _ := uuid.FromBytes(b.ID.Bytes[0:16]) - bookmarkMediaUUID, _ := uuid.FromBytes(b.MediaItemID.Bytes[0:16]) - bookmarkUserUUID, _ := uuid.FromBytes(b.UserID.Bytes[0:16]) - - var pageNumber *int - if b.PageNumber.Valid { - val := int(b.PageNumber.Int32) - pageNumber = &val - } - - var chapterNumber *int - if b.ChapterNumber.Valid { - val := int(b.ChapterNumber.Int32) - chapterNumber = &val - } - - templateBookmarks[i] = templates.Bookmark{ - ID: bookmarkUUID.String(), - MediaItemID: bookmarkMediaUUID.String(), - UserID: bookmarkUserUUID.String(), - PageNumber: pageNumber, - ChapterNumber: chapterNumber, - CfiPosition: textToString(b.CfiPosition), - Title: b.Title, - Position: textToString(b.Position), - Notes: textToString(b.Notes), - CreatedAt: b.CreatedAt.Time, - } - } - // 8. Render template + // Render template var buf bytes.Buffer - err = templates.Reader(user, metadata, templateProgress, templateBookmarks).Render(c.Request().Context(), &buf) + err = templates.Reader(user, metadata).Render(c.Request().Context(), &buf) if err != nil { return renderErrorPage(c, "Error rendering reader", "render_error") } diff --git a/templates/reader.templ b/templates/reader.templ index 39c46ca..e866618 100644 --- a/templates/reader.templ +++ b/templates/reader.templ @@ -5,7 +5,11 @@ import ( "fmt" ) -func readerInitExpr(metadata ReaderMetadata, progress ReadingProgress, bookmarks []Bookmark) string { +// The init config carries only immutable book metadata. Reading state +// (position, bookmarks, annotations) is never embedded: the reader fetches +// it from the APIs at open time, so the page can never carry — nor write +// back — a stale snapshot of it. +func readerInitExpr(metadata ReaderMetadata) string { config := map[string]interface{}{ "mediaItemId": metadata.MediaItemID, "fileUrl": metadata.FileURL, @@ -13,42 +17,11 @@ func readerInitExpr(metadata ReaderMetadata, progress ReadingProgress, bookmarks "readingDirection": metadata.ReadingDirection, "mangaType": metadata.MangaType, } - if progress.Percentage > 0 { - config["savedPercentage"] = progress.Percentage / 100 - } - if progress.EpubCfi != "" { - config["savedCfi"] = progress.EpubCfi - } - // Fixed-layout & comic formats: the page index is the canonical, exact - // locator (pages are fixed images). Pass it so the reader restores by page. - if (metadata.FormatGroup == "fixed_layout" || metadata.FormatGroup == "comic_archive") && progress.CurrentPage > 0 { - config["savedPage"] = progress.CurrentPage - if progress.TotalPages > 0 { - config["savedTotalPages"] = progress.TotalPages - } - } - if len(bookmarks) > 0 { - items := make([]map[string]interface{}, 0, len(bookmarks)) - for _, b := range bookmarks { - var page any - if b.PageNumber != nil { - page = *b.PageNumber - } - items = append(items, map[string]interface{}{ - "id": b.ID, - "title": b.Title, - "positionLabel": b.Position, - "cfi": b.CfiPosition, - "page": page, - }) - } - config["bookmarks"] = items - } jsonBytes, _ := json.Marshal(config) return fmt.Sprintf("initReader(%s)", string(jsonBytes)) } -templ Reader(user User, metadata ReaderMetadata, progress ReadingProgress, bookmarks []Bookmark) { +templ Reader(user User, metadata ReaderMetadata) {
@@ -64,7 +37,7 @@ templ Reader(user User, metadata ReaderMetadata, progress ReadingProgress, bookmNo pages
No pages