diff --git a/locales/ar/reader.json b/locales/ar/reader.json index 8daea408..f9652cec 100644 --- a/locales/ar/reader.json +++ b/locales/ar/reader.json @@ -51,7 +51,7 @@ "sources": "مصادر", "error": "حدث خطأ ما. حاول مرة أخرى أو اتصل بنا إذا استمرت المشكلة.", "pg-x": "صفحة {page, number, nogroup}", - "pg-x-vol": "P{vol, number, nogroup}/صفحة {page, number, nogroup}" + "pg-x-vol": "المجلد {vol, number, nogroup}، صفحة {page, number, nogroup}" }, "edition": "الإصدار", "select-edition": "اختر إصدارًا", diff --git a/locales/en/reader.json b/locales/en/reader.json index 3048e0f0..4dac2361 100644 --- a/locales/en/reader.json +++ b/locales/en/reader.json @@ -51,7 +51,7 @@ "sources": "Sources", "error": "Something went wrong. Try again or contact us if it persists.", "pg-x": "P{page, number, nogroup}", - "pg-x-vol": "P{vol, number, nogroup}/{page, number, nogroup}" + "pg-x-vol": "Vol {vol, number, nogroup}, Pg {page, number, nogroup}" }, "edition": "Edition", "select-edition": "Select an edition", diff --git a/src/app/[locale]/t/[bookId]/[pageNumber]/page.tsx b/src/app/[locale]/t/[bookId]/[pageNumber]/page.tsx index a2fe5957..fb3625f7 100644 --- a/src/app/[locale]/t/[bookId]/[pageNumber]/page.tsx +++ b/src/app/[locale]/t/[bookId]/[pageNumber]/page.tsx @@ -8,6 +8,7 @@ import ReaderNavigation from "../_components/reader-navigation"; import { getMetadata } from "@/lib/seo"; import { navigation } from "@/lib/urls"; import { permanentRedirect } from "@/navigation"; +import { BookDetailsProvider } from "../_contexts/book-details.context"; export const generateMetadata = async ({ params: { bookId, pageNumber }, @@ -112,27 +113,19 @@ async function SidebarContent({ } return ( - - } - > - - -
- -
-
+ + + } + > + + +
+ +
+
+
); } - export default SidebarContent; diff --git a/src/app/[locale]/t/[bookId]/_components/ai-tab/index.tsx b/src/app/[locale]/t/[bookId]/_components/ai-tab/index.tsx index 0f4e8e4f..903b8db9 100644 --- a/src/app/[locale]/t/[bookId]/_components/ai-tab/index.tsx +++ b/src/app/[locale]/t/[bookId]/_components/ai-tab/index.tsx @@ -8,7 +8,6 @@ import ChatMessage from "./ChatMessage"; import { useCallback } from "react"; import { useScrollAnchor } from "./useScrollAnchor"; import { useTranslations } from "next-intl"; -import type { TabProps } from "../sidebar/tabs"; import { usePageNavigation } from "../usePageNavigation"; import ChatForm from "./ChatForm"; import { InfoIcon, SquarePenIcon } from "lucide-react"; @@ -17,9 +16,11 @@ import { VersionAlert } from "../version-alert"; import SidebarContainer from "../sidebar/sidebar-container"; import { Badge } from "@/components/ui/badge"; import { OpenAILogo } from "@/components/Icons"; +import { useBookDetails } from "../../_contexts/book-details.context"; -export default function AITab({ bookResponse }: TabProps) { - const { getVirtuosoScrollProps } = usePageNavigation(bookResponse); +export default function AITab() { + const { bookResponse } = useBookDetails(); + const { getVirtuosoScrollProps } = usePageNavigation(); const t = useTranslations(); const { messagesRef, diff --git a/src/app/[locale]/t/[bookId]/_components/ai-tab/useChat.tsx b/src/app/[locale]/t/[bookId]/_components/ai-tab/useChat.tsx index d2603a5d..1bab1b02 100644 --- a/src/app/[locale]/t/[bookId]/_components/ai-tab/useChat.tsx +++ b/src/app/[locale]/t/[bookId]/_components/ai-tab/useChat.tsx @@ -1,4 +1,4 @@ -import { chatWithBook, parseSourceNode } from "@/server/services/chat"; +import { chatWithBook } from "@/server/services/chat"; import type { SemanticSearchBookNode } from "@/types/SemanticSearchBookNode"; import type { ChatResponse } from "@/types/chat"; import { useCallback } from "react"; @@ -48,7 +48,7 @@ const handleEventSource = async ( id, role: "ai", text: allContent, - sourceNodes: (sources ?? []).map(parseSourceNode), + sourceNodes: sources ?? [], }); } diff --git a/src/app/[locale]/t/[bookId]/_components/content-tab/index.tsx b/src/app/[locale]/t/[bookId]/_components/content-tab/index.tsx index ad0dc9c4..eb994e9d 100644 --- a/src/app/[locale]/t/[bookId]/_components/content-tab/index.tsx +++ b/src/app/[locale]/t/[bookId]/_components/content-tab/index.tsx @@ -7,10 +7,11 @@ import type { TabProps } from "../sidebar/tabs"; import { usePageNavigation } from "../usePageNavigation"; import { useSearchParams } from "next/navigation"; import PdfChaptersList from "./pdf-chapters-section"; +import { useBookDetails } from "../../_contexts/book-details.context"; -function ContentTab({ bookResponse, isSinglePage }: TabProps) { - const { pagesRange, getVirtuosoScrollProps } = - usePageNavigation(bookResponse); +function ContentTab({ isSinglePage }: TabProps) { + const { bookResponse } = useBookDetails(); + const { pagesRange, getVirtuosoScrollProps } = usePageNavigation(); const _view = (useSearchParams().get("view") ?? "default") as | "pdf" diff --git a/src/app/[locale]/t/[bookId]/_components/mobile-sidebar-provider.tsx b/src/app/[locale]/t/[bookId]/_components/mobile-sidebar-provider.tsx index 1698a028..5cbe79e2 100644 --- a/src/app/[locale]/t/[bookId]/_components/mobile-sidebar-provider.tsx +++ b/src/app/[locale]/t/[bookId]/_components/mobile-sidebar-provider.tsx @@ -22,7 +22,6 @@ export function MobileSidebarProvider({ tabId, bookSlug, versionId, - bookResponse, }: { icon: React.ReactNode; tabId: string; @@ -55,7 +54,6 @@ export function MobileSidebarProvider({ diff --git a/src/app/[locale]/t/[bookId]/_components/reader-content/index.tsx b/src/app/[locale]/t/[bookId]/_components/reader-content/index.tsx index 55e96458..56958e3f 100644 --- a/src/app/[locale]/t/[bookId]/_components/reader-content/index.tsx +++ b/src/app/[locale]/t/[bookId]/_components/reader-content/index.tsx @@ -5,36 +5,35 @@ import { Virtualizer } from "virtua"; import React, { forwardRef, memo, useMemo, useRef } from "react"; import { useReaderVirtuoso, useSetReaderScroller } from "../context"; import Footer from "@/app/_components/footer"; -import type { ApiBookResponse } from "@/types/api/book"; import ReaderPage from "./reader-page"; import { READER_OVERSCAN_SIZE, READER_PAGINATION_SIZE } from "@/lib/constants"; import Container from "@/components/ui/container"; import Paginator from "../../[pageNumber]/paginator"; import { HighlightPopover } from "@/components/ui/highlight-popover"; import ReaderHighlightPopover from "./highlight-popover"; +import { useBookDetails } from "../../_contexts/book-details.context"; export default function ReaderContent({ - response, isSinglePage, currentPage, }: { - response: ApiBookResponse; isSinglePage?: boolean; currentPage?: number; }) { + const { bookResponse } = useBookDetails(); const virtuosoRef = useReaderVirtuoso(); const setContainerEl = useSetReaderScroller(); const containerEl = useRef(null); const defaultPages = useMemo(() => { - if (response.content.source === "turath") { - return response.content.pages; - } else if (response.content.source === "openiti") { - return response.content.pages; + if (bookResponse.content.source === "turath") { + return bookResponse.content.pages; + } else if (bookResponse.content.source === "openiti") { + return bookResponse.content.pages; } else { return []; } - }, [response]); + }, [bookResponse]); return (
{isSinglePage && ( )} ))} > - {new Array(isSinglePage ? 1 : response.pagination.total) + {new Array(isSinglePage ? 1 : bookResponse.pagination.total) .fill(null) .map((_, index) => ( ))} diff --git a/src/app/[locale]/t/[bookId]/_components/reader-content/reader-page.tsx b/src/app/[locale]/t/[bookId]/_components/reader-content/reader-page.tsx index 98e56c4a..9959f048 100644 --- a/src/app/[locale]/t/[bookId]/_components/reader-content/reader-page.tsx +++ b/src/app/[locale]/t/[bookId]/_components/reader-content/reader-page.tsx @@ -7,7 +7,7 @@ import type { OpenitiContent } from "@/types/api/content/openiti"; import type { TurathContent } from "@/types/api/content/turath"; import { useQuery } from "@tanstack/react-query"; import { useTranslations } from "next-intl"; -import { useParams } from "next/navigation"; +import { useParams, useSearchParams } from "next/navigation"; import { type PropsWithChildren, useMemo } from "react"; type DefaultPages = NonNullable< @@ -92,6 +92,7 @@ const useFetchPage = ( defaultPages: DefaultPages, ) => { const params = useParams(); + const versionId = useSearchParams().get("versionId"); const slug = params.bookId as string; const pageNumber = params.pageNumber as string | undefined; @@ -112,16 +113,19 @@ const useFetchPage = ( const shouldUseDefaultPages = pageInfo.startIndex < defaultPages.length; const { data, isLoading, isError } = useQuery({ - queryKey: ["reader", pageInfo.startIndex], + queryKey: ["reader", slug, versionId, pageInfo.startIndex] as const, queryFn: async ({ queryKey }) => { - const startIndex = queryKey[1] as number; + const [, _bookSlug, _versionId, startIndex] = queryKey; - const response = await getBook(slug, { + const response = await getBook(_bookSlug, { startIndex: startIndex, size: perPage, + versionId: _versionId ?? undefined, }); - if (!response || "type" in response) return null; + if (!response || "type" in response) { + return null; + } return (response.content as any).pages as DefaultPages; }, diff --git a/src/app/[locale]/t/[bookId]/_components/reader-navigation/book-info-header.tsx b/src/app/[locale]/t/[bookId]/_components/reader-navigation/book-info-header.tsx index d505fa6b..90a54b23 100644 --- a/src/app/[locale]/t/[bookId]/_components/reader-navigation/book-info-header.tsx +++ b/src/app/[locale]/t/[bookId]/_components/reader-navigation/book-info-header.tsx @@ -10,7 +10,6 @@ import { Separator } from "@/components/ui/separator"; import { useDirection } from "@/lib/locale/utils"; import { navigation } from "@/lib/urls"; import { Link } from "@/navigation"; -import type { ApiBookResponse } from "@/types/api/book"; import { ChevronDownIcon } from "lucide-react"; import { useTranslations } from "next-intl"; import { Fragment } from "react"; @@ -18,12 +17,10 @@ import * as AccordionPrimitive from "@radix-ui/react-accordion"; import { cn } from "@/lib/utils"; import { useBookDetailsStore } from "../../_stores/book-details"; import Container from "@/components/ui/container"; +import { useBookDetails } from "../../_contexts/book-details.context"; -export default function BookInfoHeader({ - bookResponse, -}: { - bookResponse: ApiBookResponse; -}) { +export default function BookInfoHeader() { + const { bookResponse } = useBookDetails(); const dir = useDirection(); const t = useTranslations(); const { isOpen, setIsOpen } = useBookDetailsStore(); diff --git a/src/app/[locale]/t/[bookId]/_components/reader-navigation/index.tsx b/src/app/[locale]/t/[bookId]/_components/reader-navigation/index.tsx index f2d8bda8..215456dc 100644 --- a/src/app/[locale]/t/[bookId]/_components/reader-navigation/index.tsx +++ b/src/app/[locale]/t/[bookId]/_components/reader-navigation/index.tsx @@ -18,6 +18,7 @@ import { TabContent } from "../tab-content"; import { tabs } from "../sidebar/tabs"; import { useState } from "react"; import { Drawer, DrawerContent } from "@/components/ui/drawer"; +import { useBookDetails } from "../../_contexts/book-details.context"; const getPdfUrl = (bookResponse: ApiBookResponse) => { if (bookResponse.content.source === "pdf") { @@ -32,12 +33,11 @@ const getPdfUrl = (bookResponse: ApiBookResponse) => { }; export default function ReaderNavigation({ - bookResponse, isSinglePage, }: { - bookResponse: ApiBookResponse; isSinglePage?: boolean; }) { + const { bookResponse } = useBookDetails(); const showNavbar = useNavbarStore((s) => s.showNavbar); const bookSlug = bookResponse.book.slug; const t = useTranslations("reader"); @@ -126,7 +126,6 @@ export default function ReaderNavigation({ bookSlug={bookSlug} versionId={versionId} isSinglePage={isSinglePage} - bookResponse={bookResponse} />
)} @@ -136,7 +135,7 @@ export default function ReaderNavigation({ - + ); } diff --git a/src/app/[locale]/t/[bookId]/_components/search-tab/index.tsx b/src/app/[locale]/t/[bookId]/_components/search-tab/index.tsx index e9855d67..348294ba 100644 --- a/src/app/[locale]/t/[bookId]/_components/search-tab/index.tsx +++ b/src/app/[locale]/t/[bookId]/_components/search-tab/index.tsx @@ -16,29 +16,30 @@ import { Button } from "@/components/ui/button"; // SelectValue, // } from "@/components/ui/select"; import Spinner from "@/components/ui/spinner"; -import { useMutation } from "@tanstack/react-query"; -import type { SemanticSearchBookNode } from "@/types/SemanticSearchBookNode"; +import { useQuery } from "@tanstack/react-query"; import { searchBook } from "@/server/services/chat"; import SearchResult from "./SearchResult"; import { useTranslations } from "next-intl"; // import ComingSoonModal from "@/components/coming-soon-modal"; -import type { TabProps } from "../sidebar/tabs"; import { usePageNavigation } from "../usePageNavigation"; import { VersionAlert } from "../version-alert"; import { Badge } from "@/components/ui/badge"; -import { ChevronRightIcon } from "lucide-react"; +import { ChevronLeftIcon, ChevronRightIcon } from "lucide-react"; import { useSearchStore } from "../../_stores/search"; import { useState } from "react"; import { Switch } from "@/components/ui/switch"; import { Label } from "@/components/ui/label"; import { SparklesIcon } from "@heroicons/react/24/outline"; +import { useBookDetails } from "../../_contexts/book-details.context"; -export default function SearchTab({ bookResponse }: TabProps) { - const { getVirtuosoScrollProps } = usePageNavigation(bookResponse); +export default function SearchTab() { + const { bookResponse } = useBookDetails(); + const { getVirtuosoScrollProps } = usePageNavigation(); const t = useTranslations(); - const { value, setValue, results, setResults } = useSearchStore(); - const [type, setType] = useState<"semantic" | "keyword">("keyword"); + + const { value, setValue, page, setPage, type, setType } = useSearchStore(); + const [inputValue, setInputValue] = useState(value); const isVersionMismatch = type === "semantic" @@ -50,40 +51,36 @@ export default function SearchTab({ bookResponse }: TabProps) { bookContent.source === "external" || bookContent.source === "pdf"; const headings = !isExternal ? bookContent.headings : []; - const { mutateAsync, isPending, error } = useMutation< - SemanticSearchBookNode[], - Error, - string - >({ - mutationKey: ["search"], - mutationFn: async (q: string) => { - if (!q) return []; - return await searchBook(bookResponse.book.id, q, type); + const { + data: results, + isLoading, + error, + } = useQuery({ + queryKey: ["search", bookResponse.book.id, value, type, page] as const, + queryFn: async ({ queryKey }) => { + const [, _bookId, _q, _type, _page] = queryKey; + if (!_q) return null; + + return await searchBook(_bookId, _q, _type, _page); }, }); - const handleSearch = async (q: string) => { - if (!q) { - setResults(null); - return; - } - - const data = await mutateAsync(q); - setResults(data); - }; - const handleChange = (e: React.ChangeEvent) => { const newValue = e.target.value; - setValue(newValue); + setInputValue(newValue); }; const handleSubmit = (e: React.FormEvent) => { e.preventDefault(); - handleSearch(value); + setValue(inputValue, 1); + }; + + const toggleType = (checked: boolean) => { + setType(checked ? "semantic" : "keyword", 1); }; const renderResults = () => { - if (results === null) + if (!results) return ( // TODO: change fixed height
@@ -107,7 +104,7 @@ export default function SearchTab({ bookResponse }: TabProps) { ); } - if (results.length === 0) + if (results.total === 0 || results.results.length === 0) return (

{t("common.search-bar.no-results")}

@@ -115,15 +112,46 @@ export default function SearchTab({ bookResponse }: TabProps) { ); return ( -
- {results.map((r, idx) => ( - - ))} +
+
+ {results.results.map((r, idx) => ( + + ))} +
+ + +

+ {results.total} Results - Page {results.currentPage} /{" "} + {results.totalPages} +

+ +
+ + + +
+
); }; @@ -149,12 +177,12 @@ export default function SearchTab({ bookResponse }: TabProps) {
setType("semantic")} + onCheckedChange={toggleType} />
@@ -175,7 +203,7 @@ export default function SearchTab({ bookResponse }: TabProps) {
- {isPending ? ( + {isLoading ? ( ) : ( @@ -183,7 +211,7 @@ export default function SearchTab({ bookResponse }: TabProps) { diff --git a/src/app/[locale]/t/[bookId]/_components/sidebar/index.tsx b/src/app/[locale]/t/[bookId]/_components/sidebar/index.tsx index 5bed4b50..31e11998 100644 --- a/src/app/[locale]/t/[bookId]/_components/sidebar/index.tsx +++ b/src/app/[locale]/t/[bookId]/_components/sidebar/index.tsx @@ -37,7 +37,6 @@ const TabButton = ({ }; export default function ReaderSidebar({ - bookResponse, bookSlug, versionId, isSinglePage, @@ -70,7 +69,6 @@ export default function ReaderSidebar({ bookSlug={bookSlug} versionId={versionId} isSinglePage={isSinglePage} - bookResponse={bookResponse} /> ))} diff --git a/src/app/[locale]/t/[bookId]/_components/sidebar/tabs.ts b/src/app/[locale]/t/[bookId]/_components/sidebar/tabs.ts index a63d456a..83ade1fc 100644 --- a/src/app/[locale]/t/[bookId]/_components/sidebar/tabs.ts +++ b/src/app/[locale]/t/[bookId]/_components/sidebar/tabs.ts @@ -1,5 +1,3 @@ -import type { ApiBookResponse } from "@/types/api/book"; - import { MagnifyingGlassIcon, ListBulletIcon, @@ -7,7 +5,6 @@ import { } from "@heroicons/react/24/outline"; export type TabProps = { - bookResponse: ApiBookResponse; bookSlug: string; versionId?: string; isSinglePage?: boolean; diff --git a/src/app/[locale]/t/[bookId]/_components/tab-content.tsx b/src/app/[locale]/t/[bookId]/_components/tab-content.tsx index 97a4d80c..7079ef67 100644 --- a/src/app/[locale]/t/[bookId]/_components/tab-content.tsx +++ b/src/app/[locale]/t/[bookId]/_components/tab-content.tsx @@ -3,6 +3,7 @@ import type { TabProps } from "./sidebar/tabs"; import { ComingSoonAlert } from "./coming-soon-alert"; import { tabIdToComponent } from "./sidebar/tabs-content"; +import { useBookDetails } from "../_contexts/book-details.context"; export const TabContent = ({ tabId, @@ -10,11 +11,22 @@ export const TabContent = ({ }: TabProps & { tabId: keyof typeof tabIdToComponent; }) => { - const isSupported = props.bookResponse.book.aiSupported; + const { bookResponse } = useBookDetails(); + const aiSupported = bookResponse.book.aiSupported; + const keywordSupported = bookResponse.book.keywordSupported; + const source = bookResponse.content.source; + /** + * 1. If the tab is ai and the book is not ai supported + * 2. If the tab is search and the book is not keyword supported + * 3. If the book is not openiti or turath and the tab is ai or search + */ if ( - (props.bookResponse.content.source === "external" || !isSupported) && - (tabId === "ai" || tabId === "search") + (tabId === "ai" && !aiSupported) || + (tabId === "search" && !keywordSupported) || + (source !== "openiti" && + source !== "turath" && + (tabId === "ai" || tabId === "search")) ) { return ; } diff --git a/src/app/[locale]/t/[bookId]/_components/usePageNavigation.tsx b/src/app/[locale]/t/[bookId]/_components/usePageNavigation.tsx index 03f2ad92..57865a1a 100644 --- a/src/app/[locale]/t/[bookId]/_components/usePageNavigation.tsx +++ b/src/app/[locale]/t/[bookId]/_components/usePageNavigation.tsx @@ -1,6 +1,7 @@ -import type { TabProps } from "./sidebar/tabs"; +import { useBookDetails } from "../_contexts/book-details.context"; -export const usePageNavigation = (bookResponse: TabProps["bookResponse"]) => { +export const usePageNavigation = () => { + const { bookResponse } = useBookDetails(); const source = bookResponse.content.source; if (source === "external" || source === "pdf") { diff --git a/src/app/[locale]/t/[bookId]/_contexts/book-details.context.tsx b/src/app/[locale]/t/[bookId]/_contexts/book-details.context.tsx new file mode 100644 index 00000000..38ec76c4 --- /dev/null +++ b/src/app/[locale]/t/[bookId]/_contexts/book-details.context.tsx @@ -0,0 +1,32 @@ +"use client"; + +import type { ApiBookResponse } from "@/types/api/book"; +import { createContext, useContext } from "react"; + +interface BookDetailsContextValue { + bookResponse: ApiBookResponse; +} + +const BookDetailsContext = createContext(null); + +export function BookDetailsProvider({ + children, + bookResponse, +}: { + children: React.ReactNode; + bookResponse: ApiBookResponse; +}) { + return ( + + {children} + + ); +} + +export function useBookDetails() { + const context = useContext(BookDetailsContext); + if (!context) { + throw new Error("useBookDetails must be used within a BookDetailsProvider"); + } + return context; +} diff --git a/src/app/[locale]/t/[bookId]/_stores/search.ts b/src/app/[locale]/t/[bookId]/_stores/search.ts index f5b609cc..2eeadf66 100644 --- a/src/app/[locale]/t/[bookId]/_stores/search.ts +++ b/src/app/[locale]/t/[bookId]/_stores/search.ts @@ -1,18 +1,23 @@ -import type { SemanticSearchBookNode } from "@/types/SemanticSearchBookNode"; import { create } from "zustand"; interface SearchStore { value: string; - setValue: (value: string) => void; - results: SemanticSearchBookNode[] | null; - setResults: (results: SemanticSearchBookNode[] | null) => void; + page: number; + type: "semantic" | "keyword"; + + setValue: (value: string, page: number) => void; + setType: (type: "semantic" | "keyword", page: number) => void; + setPage: (page: number) => void; + reset: () => void; } export const useSearchStore = create((set) => ({ value: "", - setValue: (value: string) => set({ value }), - results: null, - setResults: (results: SemanticSearchBookNode[] | null) => set({ results }), - reset: () => set({ value: "", results: null }), + page: 1, + type: "keyword", + setValue: (value: string, page: number) => set({ value, page }), + setType: (type: "semantic" | "keyword", page: number) => set({ type, page }), + setPage: (page: number) => set({ page }), + reset: () => set({ value: "", page: 1, type: "keyword" }), })); diff --git a/src/app/[locale]/t/[bookId]/page.tsx b/src/app/[locale]/t/[bookId]/page.tsx index d4b1d198..e334bdd6 100644 --- a/src/app/[locale]/t/[bookId]/page.tsx +++ b/src/app/[locale]/t/[bookId]/page.tsx @@ -13,6 +13,7 @@ import ReaderNavigation from "./_components/reader-navigation"; import { getMetadata } from "@/lib/seo"; import { navigation } from "@/lib/urls"; import { permanentRedirect } from "@/navigation"; +import { BookDetailsProvider } from "./_contexts/book-details.context"; const PdfView = dynamic(() => import("./_components/pdf-view"), { ssr: false, @@ -137,24 +138,20 @@ export default async function SidebarContent({ } else { readerContent = (
- +
); } return ( - - } - > - - - {readerContent} - + + } + > + + + {readerContent} + + ); } diff --git a/src/components/book-search-result/info-dialog.tsx b/src/components/book-search-result/info-dialog.tsx index 8978a200..6aab0559 100644 --- a/src/components/book-search-result/info-dialog.tsx +++ b/src/components/book-search-result/info-dialog.tsx @@ -48,10 +48,10 @@ export default function InfoDialog({ const shouldFetch = open; const { data: author, isFetching } = useQuery({ - queryKey: ["author", document.authorId] as const, + queryKey: ["author", document.authorId, pathLocale] as const, queryFn: ({ queryKey }) => { - const [, authorId] = queryKey; - return findAuthorBySlug(authorId, pathLocale); + const [, authorId, locale] = queryKey; + return findAuthorBySlug(authorId, locale); }, enabled: shouldFetch, }); diff --git a/src/components/ui/source-modal/index.tsx b/src/components/ui/source-modal/index.tsx index 5d89ae97..83179197 100644 --- a/src/components/ui/source-modal/index.tsx +++ b/src/components/ui/source-modal/index.tsx @@ -24,6 +24,7 @@ import { useParams, useSearchParams } from "next/navigation"; import { ScrollArea } from "../scroll-area"; import { useBookShareUrl } from "@/lib/share"; import Spinner from "../spinner"; +import { useBookDetails } from "@/app/[locale]/t/[bookId]/_contexts/book-details.context"; export default function SourceModal({ source, @@ -32,6 +33,7 @@ export default function SourceModal({ source: SemanticSearchBookNode; getVirtuosoScrollProps: UsePageNavigationReturnType["getVirtuosoScrollProps"]; }) { + const { bookResponse } = useBookDetails(); const [isOpen, setIsOpen] = useState(false); const slug = useParams().bookId as string; const versionId = useSearchParams().get("versionId"); @@ -40,15 +42,20 @@ export default function SourceModal({ const t = useTranslations(); const virtuosoRef = useReaderVirtuoso(); - const chapter = source.metadata.chapters[0]; + const chapterIndex = source.metadata.chapters[0]; + const chapter = + chapterIndex !== undefined && "headings" in bookResponse.content + ? bookResponse.content.headings?.[chapterIndex]?.title + : undefined; + const page = source.metadata.pages[0]!; const { isPending, data } = useQuery({ - queryKey: ["page", page.page, page.volume, versionId] as const, + queryKey: ["page", slug, page.page, page.volume, versionId] as const, queryFn: async ({ queryKey }) => { - const [, pg, vol, version] = queryKey; + const [, _slug, pg, vol, version] = queryKey; - const result = await getBookPageIndex(slug, { + const result = await getBookPageIndex(_slug, { page: pg, volume: vol, versionId: version ?? undefined, diff --git a/src/components/ui/source-modal/source-modal.stories.tsx b/src/components/ui/source-modal/source-modal.stories.tsx index 65868aaf..ea71080b 100644 --- a/src/components/ui/source-modal/source-modal.stories.tsx +++ b/src/components/ui/source-modal/source-modal.stories.tsx @@ -21,7 +21,7 @@ export const Basic: Story = { { - const results = (await baseRequest( + const results = await baseRequest( "GET", - `/search?q=${query}&bookId=${bookId}&type=${type}`, - )) as SemanticSearchBookNode[]; - - return results.map(parseSourceNode); -}; - -export const parseSourceNode = (sourceNode: SemanticSearchBookNode) => { - const chapters = sourceNode.metadata.chapters; - const pages = sourceNode.metadata.pages; + `/search?q=${query}&bookId=${bookId}&type=${type}&page=${page}`, + ); - return { - ...sourceNode, - metadata: { - chapters, - pages, - }, - }; + return results; }; diff --git a/src/types/SemanticSearchBookNode.ts b/src/types/SemanticSearchBookNode.ts index 4b7c37b6..f3df5a7e 100644 --- a/src/types/SemanticSearchBookNode.ts +++ b/src/types/SemanticSearchBookNode.ts @@ -1,6 +1,6 @@ export interface SemanticSearchBookNode { metadata: { - chapters: string[]; + chapters: number[]; pages: { volume: string; page: number;