Skip to content

Commit

Permalink
🐛 Fix Read again not starting from start
Browse files Browse the repository at this point in the history
Fix for #206

🚧 Go to first page when clicking Read again

Completed at does not get reset yet

Run codegen
  • Loading branch information
aaronleopold committed Dec 3, 2023
1 parent 313d6a3 commit 4b4ef57
Show file tree
Hide file tree
Showing 4 changed files with 80 additions and 18 deletions.
17 changes: 16 additions & 1 deletion packages/api/src/media.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,10 @@
import type { Media, PatchMediaThumbnail, ReadProgress } from '@stump/types'
import type {
Media,
MediaIsComplete,
PatchMediaThumbnail,
PutMediaCompletionStatus,
ReadProgress,
} from '@stump/types'

import { API } from './axios'
import { ApiResult, CursorQueryParams, PageableApiResult } from './types'
Expand Down Expand Up @@ -71,6 +77,13 @@ export function patchMediaThumbnail(id: string, params: PatchMediaThumbnail) {
return API.patch(`/media/${id}/thumbnail`, params)
}

export function putMediaCompletion(
id: string,
payload: PutMediaCompletionStatus,
): Promise<ApiResult<MediaIsComplete>> {
return API.put(`/media/${id}/complete`, payload)
}

export const mediaApi = {
getInProgressMedia,
getMedia,
Expand All @@ -82,6 +95,7 @@ export const mediaApi = {
getPaginatedMedia,
getRecentlyAddedMedia,
patchMediaThumbnail,
putMediaCompletion,
updateMediaProgress,
}

Expand All @@ -96,5 +110,6 @@ export const mediaQueryKeys: Record<keyof typeof mediaApi, string> = {
getPaginatedMedia: 'media.getPaginated',
getRecentlyAddedMedia: 'media.getRecentlyAdded',
patchMediaThumbnail: 'media.patchThumbnail',
putMediaCompletion: 'media.putCompletion',
updateMediaProgress: 'media.updateProgress',
}
61 changes: 45 additions & 16 deletions packages/interface/src/scenes/book/BookReaderLink.tsx
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import { ButtonOrLink } from '@stump/components'
import { Media } from '@stump/types'
import { useMemo } from 'react'

import paths from '../../paths'
import { EBOOK_EXTENSION } from '../../utils/patterns'
Expand All @@ -8,38 +9,66 @@ type Props = {
book?: Media
}
export default function BookReaderLink({ book }: Props) {
if (!book) {
return null
}
const currentPage = book?.current_page ?? -1

/**
* A boolean used to control the redering of the 'Read again' prompt. A book
* is considered to be read again if:
*
* - It has been completed AND the current page is the last page
* - It has been completed AND is an epub AND there is no current epubcfi
*/
const isReadAgain = useMemo(() => {
if (!book) return false

const currentPage = book.current_page || -1
const { is_completed, current_page, pages, current_epubcfi, extension } = book

const getTitle = () => {
if (book.is_completed || currentPage === book.pages) {
const isEpub = extension.match(EBOOK_EXTENSION)
const epubCompleted = isEpub && !current_epubcfi && is_completed
const otherCompleted = !isEpub && current_page === pages && is_completed

return epubCompleted || otherCompleted
}, [book])

const epubcfi = book?.current_epubcfi
const title = useMemo(() => {
if (isReadAgain) {
return 'Read again'
} else if (currentPage > -1 || !!book.current_epubcfi) {
} else if (currentPage > -1 || !!epubcfi) {
return 'Continue reading'
} else {
return 'Start reading'
}
}
}, [isReadAgain, currentPage, epubcfi])

const getHref = () => {
if (book.current_epubcfi || book.extension?.match(EBOOK_EXTENSION)) {
return paths.bookReader(book.id, {
epubcfi: book.current_epubcfi,
/**
* The URL to use for the read link. If the book is an epub, the epubcfi is used
* to open the book at the correct location. Otherwise, the page number is used.
*
* If the book is completed, the read link will omit the epubcfi or page number
*/
const readUrl = useMemo(() => {
if (!book) return undefined

const { current_epubcfi, extension, id, current_page } = book

if (current_epubcfi || extension.match(EBOOK_EXTENSION)) {
return paths.bookReader(id, {
epubcfi: isReadAgain ? undefined : current_epubcfi,
isEpub: true,
})
} else {
return paths.bookReader(book?.id || '', { page: book?.current_page || 1 })
return paths.bookReader(id, { page: isReadAgain ? undefined : current_page || 1 })
}
}
}, [book, isReadAgain])

const title = getTitle()
if (!book) {
return null
}

return (
<div className="flex w-full md:w-auto">
<ButtonOrLink variant="primary" href={getHref()} title={title} className="w-full md:w-auto">
<ButtonOrLink variant="primary" href={readUrl} title={title} className="w-full md:w-auto">
{title}
</ButtonOrLink>
</div>
Expand Down
14 changes: 13 additions & 1 deletion packages/interface/src/scenes/book/BookReaderScene.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -39,8 +39,20 @@ export default function BookReaderScene() {
}
}, [])

/**
* An effect to update the read progress whenever the page changes in the URL
*/
useEffect(() => {
const parsedPage = parseInt(page || '', 10)
if (!parsedPage || isNaN(parsedPage) || !media) return

const maxPage = media.pages
if (parsedPage <= 0 || parsedPage > maxPage) return

updateReadProgress(parsedPage)
}, [page, updateReadProgress, media])

function handleChangePage(newPage: number) {
updateReadProgress(newPage)
navigate(paths.bookReader(id!, { isAnimated, page: newPage }))
}

Expand Down
6 changes: 6 additions & 0 deletions packages/types/generated.ts
Original file line number Diff line number Diff line change
Expand Up @@ -172,8 +172,14 @@ export type UpdateLibrary = { id: string; name: string; path: string; descriptio

export type CleanLibraryResponse = { deleted_media_count: number; deleted_series_count: number; is_empty: boolean }

export type PutMediaCompletionStatus = { is_complete: boolean }

export type MediaIsComplete = { is_completed: boolean; completed_at: string | null }

export type MediaMetadataOverview = { genres: string[]; writers: string[]; pencillers: string[]; inkers: string[]; colorists: string[]; letterers: string[]; editors: string[]; publishers: string[]; characters: string[]; teams: string[] }

export type SeriesIsComplete = { is_complete: boolean; completed_at: string | null }

export type UpdateSchedulerConfig = { interval_secs: number | null; excluded_library_ids: string[] | null }

export type GetBookClubsParams = { all?: boolean }
Expand Down

0 comments on commit 4b4ef57

Please sign in to comment.