@@ -81,30 +81,28 @@ export default function BookCard({
)
}
- function getProgress() {
- if (isCoverOnly || !media.current_page) {
+ const getProgress = useCallback(() => {
+ const { active_reading_session, finished_reading_sessions } = media
+
+ if (isCoverOnly || (!active_reading_session && !finished_reading_sessions)) {
return null
- }
+ } else if (active_reading_session) {
+ const { epubcfi, percentage_completed, page } = active_reading_session
- if (media.current_epubcfi) {
- const firstWithPercent = media.read_progresses?.find((rp) => !!rp.percentage_completed)
- if (firstWithPercent) {
- return Math.round(firstWithPercent.percentage_completed! * 100)
- }
- } else {
- const page = media.current_page
- const pages = media.pages
+ if (epubcfi && percentage_completed) {
+ return Math.round(percentage_completed * 100)
+ } else if (page) {
+ const pages = media.pages
- const percent = Math.round((page / pages) * 100)
- if (percent > 100) {
- return 100
+ const percent = Math.round((page / pages) * 100)
+ return Math.min(percent, 100)
}
-
- return percent
+ } else if (finished_reading_sessions?.length) {
+ return 100
}
return null
- }
+ }, [isCoverOnly, media])
const href = useMemo(() => {
if (onSelect) {
diff --git a/packages/browser/src/components/filters/FilterProvider.tsx b/packages/browser/src/components/filters/FilterProvider.tsx
index c8395d8a9..1c042b5f9 100644
--- a/packages/browser/src/components/filters/FilterProvider.tsx
+++ b/packages/browser/src/components/filters/FilterProvider.tsx
@@ -4,6 +4,8 @@ import { useSearchParams } from 'react-router-dom'
import { FilterContext } from './context'
+// TODO: clean up this file!
+
type Props = {
children: React.ReactNode
}
diff --git a/packages/browser/src/components/navigation/mobile/LayoutModeButtons.tsx b/packages/browser/src/components/navigation/mobile/LayoutModeButtons.tsx
deleted file mode 100644
index 0dbd23f36..000000000
--- a/packages/browser/src/components/navigation/mobile/LayoutModeButtons.tsx
+++ /dev/null
@@ -1,28 +0,0 @@
-import { IconButton } from '@stump/components'
-import { Grid2X2, Rows } from 'lucide-react'
-
-import { useLayoutMode } from '@/hooks'
-
-export default function LayoutModeButtons() {
- const { layoutMode, setLayoutMode } = useLayoutMode()
-
- const viewAsGrid = layoutMode === 'GRID'
-
- return (
-
- setLayoutMode('GRID')}
- variant={viewAsGrid ? 'subtle-dark' : 'subtle'}
- >
-
-
-
- setLayoutMode('LIST')}
- variant={viewAsGrid ? 'subtle' : 'subtle-dark'}
- >
-
-
-
- )
-}
diff --git a/packages/browser/src/components/readers/epub/EpubJsReader.tsx b/packages/browser/src/components/readers/epub/EpubJsReader.tsx
index c72ec7441..ab2948242 100644
--- a/packages/browser/src/components/readers/epub/EpubJsReader.tsx
+++ b/packages/browser/src/components/readers/epub/EpubJsReader.tsx
@@ -219,7 +219,7 @@ export default function EpubJsReader({ id, initialCfi }: EpubJsReaderProps) {
applyEpubPreferences(rendition_, epubPreferences)
setRendition(rendition_)
- const targetCfi = epub?.media_entity.read_progresses?.at(0)?.epubcfi ?? initialCfi
+ const targetCfi = epub?.media_entity.active_reading_session?.epubcfi ?? initialCfi
if (targetCfi) {
rendition_.display(targetCfi)
} else if (defaultLoc) {
@@ -604,7 +604,7 @@ export default function EpubJsReader({ id, initialCfi }: EpubJsReaderProps) {
},
toc: epub.toc,
},
- progress: epub.media_entity.read_progresses?.[0]?.percentage_completed || null,
+ progress: epub.media_entity.active_reading_session?.percentage_completed || null,
}}
controls={{
getCfiPreviewText,
diff --git a/packages/browser/src/scenes/book/BookOverviewScene.tsx b/packages/browser/src/scenes/book/BookOverviewScene.tsx
index 6844572ba..b0b956fd4 100644
--- a/packages/browser/src/scenes/book/BookOverviewScene.tsx
+++ b/packages/browser/src/scenes/book/BookOverviewScene.tsx
@@ -1,6 +1,7 @@
import { useMediaByIdQuery } from '@stump/client'
import { Badge, ButtonOrLink, Heading, Spacer, Text } from '@stump/components'
import dayjs from 'dayjs'
+import sortBy from 'lodash.sortby'
import { Suspense, useEffect, useMemo } from 'react'
import { Helmet } from 'react-helmet'
import { useParams } from 'react-router'
@@ -76,7 +77,11 @@ export default function BookOverviewScene() {
)
}
- const completedAt = media.read_progresses?.find((p) => !!p.completed_at)?.completed_at
+
+ // TODO(historical-read-session): double check default order
+ const completedAt = sortBy(media.finished_reading_sessions, ({ completed_at }) =>
+ dayjs(completed_at).toDate(),
+ ).at(-1)?.completed_at
const genres = media.metadata?.genre?.filter((g) => !!g) ?? []
const links = media.metadata?.links?.filter((l) => !!l) ?? []
diff --git a/packages/browser/src/scenes/settings/server/email/devices/DevicesTable.tsx b/packages/browser/src/scenes/settings/server/email/devices/DevicesTable.tsx
index 8ab261fab..57f831cc9 100644
--- a/packages/browser/src/scenes/settings/server/email/devices/DevicesTable.tsx
+++ b/packages/browser/src/scenes/settings/server/email/devices/DevicesTable.tsx
@@ -2,7 +2,7 @@ import { useEmailDevicesQuery } from '@stump/client'
import { Badge, Card, Heading, Text } from '@stump/components'
import { useLocaleContext } from '@stump/i18n'
import { RegisteredEmailDevice } from '@stump/types'
-import { createColumnHelper } from '@tanstack/react-table'
+import { ColumnDef, createColumnHelper } from '@tanstack/react-table'
import { CircleSlash2 } from 'lucide-react'
import React, { useMemo, useState } from 'react'
@@ -47,7 +47,7 @@ const baseColumns = [
),
id: 'status',
}),
-]
+] as ColumnDef