From 9143f3548266396233d5d3a4e207ac543f457506 Mon Sep 17 00:00:00 2001 From: Arthur Le Meur Date: Tue, 22 Sep 2026 12:07:39 +0200 Subject: [PATCH] update from dialog to pages for profile screens --- .../customization/Customization.tsx | 87 +++++ .../components/layout/LibraryHeader.tsx | 112 +----- src/renderer/library/customization/route.ts | 20 + src/renderer/library/routing.ts | 6 + .../screen/faq-en.html | 369 +++++++++++++++--- .../screen/faq-fr.html | 366 ++++++++++++++--- 6 files changed, 752 insertions(+), 208 deletions(-) create mode 100644 src/renderer/library/components/customization/Customization.tsx create mode 100644 src/renderer/library/customization/route.ts diff --git a/src/renderer/library/components/customization/Customization.tsx b/src/renderer/library/components/customization/Customization.tsx new file mode 100644 index 0000000000..435a276857 --- /dev/null +++ b/src/renderer/library/components/customization/Customization.tsx @@ -0,0 +1,87 @@ +// ==LICENSE-BEGIN== +// Copyright 2017 European Digital Reading Lab. All rights reserved. +// Licensed to the Readium Foundation under one or more contributor license agreements. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file exposed on Github (readium) in the project repository. +// ==LICENSE-END== + +import * as React from "react"; +import DOMPurify from "dompurify"; +import { useParams } from "react-router-dom"; + +import LibraryLayout from "../layout/LibraryLayout"; +import PublicationAddButton from "../catalog/PublicationAddButton"; +import { useTranslator } from "readium-desktop/renderer/common/hooks/useTranslator"; +import { useSelector } from "readium-desktop/renderer/common/hooks/useSelector"; +import { ILibraryRootState } from "readium-desktop/common/redux/states/renderer/libraryRootState"; +import { convertMultiLangStringToString } from "readium-desktop/common/language-string"; +import { decodeCustomizationRouteParam } from "../../customization/route"; +import { encodeURIComponent_RFC3986 } from "@r2-utils-js/_utils/http/UrlUtils"; +import { URL_PROTOCOL_THORIUMHTTPS, URL_HOST_COMMON, URL_PATH_PREFIX_CUSTOMPROFILEZIP } from "readium-desktop/common/streamerProtocol"; + +const secondaryHeader = ; + +const CustomizationPage = () => { + const [__] = useTranslator(); + const { hrefEncoded } = useParams<{ hrefEncoded: string }>(); + + const customizationManifest = useSelector((state: ILibraryRootState) => state.customization.manifest); + const locale = useSelector((state: ILibraryRootState) => state.i18n.locale); + const customizationId = customizationManifest?.identifier; + const customizationBaseUrl = customizationId ? `${URL_PROTOCOL_THORIUMHTTPS}://${URL_HOST_COMMON}/${URL_PATH_PREFIX_CUSTOMPROFILEZIP}/${encodeURIComponent_RFC3986(Buffer.from(customizationId).toString("base64"))}/` : ""; + + const href = hrefEncoded ? decodeCustomizationRouteParam(hrefEncoded) : undefined; + const screenLink = customizationManifest?.links?.find((ln) => ln.rel === "screen" && ln.href === href); + const title = convertMultiLangStringToString(screenLink?.title, locale) || __("catalog.customization.fallback.screen"); + + const [dangerousInnerHTML_CustomProfileScreenSanitized, setDangerousInnerHtml] = React.useState(undefined); + + React.useEffect(() => { + let cancelled = false; + setDangerousInnerHtml(undefined); + + if (href && customizationBaseUrl) { + // URL is thoriumhttps:// "custom-profile-zip" protocol handler, so no use of isURL(url) and /^https?:\/\//.test(url) checks here + const url = customizationBaseUrl + encodeURIComponent_RFC3986(Buffer.from(href).toString("base64")); + + fetch(url) + .then((response) => { + if (response.ok) { + return response.text(); + } + return Promise.reject(response.statusText); + }) + .then((rawHtmlContent) => { + if (cancelled || !rawHtmlContent) { + return; + } + + const htmlSanitized = DOMPurify.sanitize(rawHtmlContent, { FORBID_TAGS: [/*"style"*/], FORBID_ATTR: [/*"style"*/] /* TODO: handle external https links */ }); + // NOTE that xxx is fine, caught by webContents.on("will-navigate", ...) with event.preventDefault() and shell.openExternal(...) on normalized/escaped URL and filtered on HTTP(S):// + // NOTE that the attribute target="_blank" (etc) is automatically removed by DOMPurify but would be caught by webContents.setWindowOpenHandler(...) with { action: "deny" }, although no shell.openExternal(...) in this case + setDangerousInnerHtml(htmlSanitized); + }) + .catch((e) => { + console.error("Error fetching data:", e); + }); + } + + return () => { + cancelled = true; + }; + }, [href, customizationBaseUrl]); + + return ( + + { + dangerousInnerHTML_CustomProfileScreenSanitized ? +
: <> + } + + ); +}; + +export default CustomizationPage; diff --git a/src/renderer/library/components/layout/LibraryHeader.tsx b/src/renderer/library/components/layout/LibraryHeader.tsx index 9d617a0666..05aca60312 100644 --- a/src/renderer/library/components/layout/LibraryHeader.tsx +++ b/src/renderer/library/components/layout/LibraryHeader.tsx @@ -9,11 +9,9 @@ import { ipcRenderer } from "electron"; import * as stylesHeader from "readium-desktop/renderer/assets/styles/header.scss"; import * as stylesButtons from "readium-desktop/renderer/assets/styles/components/buttons.scss"; -import * as stylesModals from "readium-desktop/renderer/assets/styles/components/modals.scss"; import { useDispatch } from "readium-desktop/renderer/common/hooks/useDispatch"; import { screenReaderActions, toastActions } from "readium-desktop/common/redux/actions"; import { ToastType } from "readium-desktop/common/models/toast"; -import * as Dialog from "@radix-ui/react-dialog"; import { Link } from "react-router-dom"; import classNames from "classnames"; @@ -30,13 +28,11 @@ import SVG from "readium-desktop/renderer/common/components/SVG"; import { Settings } from "../settings/Settings"; import { _APP_NAME } from "readium-desktop/preprocessor-directives"; import { buildOpdsBrowserRoute } from "../../opds/route"; +import { buildCustomizationRoute } from "../../customization/route"; import { encodeURIComponent_RFC3986 } from "@r2-utils-js/_utils/http/UrlUtils"; import { URL_PROTOCOL_THORIUMHTTPS, URL_HOST_COMMON, URL_PATH_PREFIX_CUSTOMPROFILEZIP } from "readium-desktop/common/streamerProtocol"; -import * as VisuallyHidden from "@radix-ui/react-visually-hidden"; -import DOMPurify from "dompurify"; import { useSelector } from "readium-desktop/renderer/common/hooks/useSelector"; import { useTranslator } from "readium-desktop/renderer/common/hooks/useTranslator"; -import { IStringMap } from "@r2-shared-js/models/metadata-multilang"; import { convertMultiLangStringToString } from "readium-desktop/common/language-string"; // import { WizardModal } from "../Wizard"; @@ -171,11 +167,6 @@ const Header = () => { } return a; }, [customizationManifest, locale]); - const screenZipObj = React.useMemo(() => screenZipLinks?.map(({ href, title }) => href && customizationBaseUrl ? {url: customizationBaseUrl + encodeURIComponent_RFC3986(Buffer.from(href).toString("base64")), title } : undefined), [screenZipLinks, customizationBaseUrl]); - - const [screenHtmlArray, setScreenHtmlArray] = React.useState>([]); - const [cancel, setCancel] = React.useState(false); - const screenReaderActivate = useSelector((state: ILibraryRootState) => state.screenReader.activate); @@ -215,51 +206,6 @@ const Header = () => { }; }, [isAccessibilitySupportEnabled]); - React.useEffect(() => { - if (customizationId && screenZipObj?.length) { - - setScreenHtmlArray([]); - setCancel(false); - - for (const screenHref of screenZipObj) { - - // URL is thoriumhttps:// "custom-profile-zip" protocol handler, so no use of isURL(url) and /^https?:\/\//.test(url) checks here - fetch(screenHref.url) - .then((response) => { - if (response.ok) { - return response.text(); - } - return Promise.reject(response.statusText); - }) - .then((rawHtmlContent) => { - // console.log("RAW HTML", rawHtmlContent); - - if (cancel) { - return ; - } - - if (!rawHtmlContent) { - return ; - } - - const htmlSanitized = DOMPurify.sanitize(rawHtmlContent, { FORBID_TAGS: [/*"style"*/], FORBID_ATTR: [/*"style"*/] /* TODO: handle external https links */ }); - // console.log(rawHtmlContent, htmlSanitized); - // NOTE that xxx is fine, caught by webContents.on("will-navigate", ...) with event.preventDefault() and shell.openExternal(...) on normalized/escaped URL and filtered on HTTP(S):// - // NOTE that the attribute target="_blank" (etc) is automatically removed by DOMPurify but would be caught by webContents.setWindowOpenHandler(...) with { action: "deny" }, although no shell.openExternal(...) in this case - setScreenHtmlArray((screenHtmlArray) => [...screenHtmlArray, { dangerousInnerHTML_CustomProfileScreenSanitized: htmlSanitized, title: screenHref.title }]); - }) - .catch((e) => { - console.error("Error fetching data:", e); - }); - } - } else { - setScreenHtmlArray([]); - setCancel(true); - } - - }, [screenZipObj, customizationId, setScreenHtmlArray, cancel, setCancel]); - - const customizationCatalogs = customizationManifest?.links?.filter(({ rel }) => rel === "catalog"); if (customizationCatalogs?.length) { for (const catalog of customizationCatalogs) { @@ -285,6 +231,25 @@ const Header = () => { }); } } + + if (screenZipLinks?.length) { + for (const screenLink of screenZipLinks) { + if (!screenLink.href) { + continue; + } + const route = buildCustomizationRoute(screenLink.href); + const label = convertMultiLangStringToString(screenLink.title, locale) || __("catalog.customization.fallback.screen"); + headerNav.push({ + route, + label, + matchRoutes: [route], + searchEnable: false, + styles: [], + svg: InfoIcon, + }); + } + } + const displayScreenReaderInvite = !screenReaderActivate && isAccessibilitySupportEnabled; return (<> @@ -347,43 +312,6 @@ const Header = () => { }, ) } - { - screenHtmlArray.length ? screenHtmlArray.map(({dangerousInnerHTML_CustomProfileScreenSanitized, title: titleStringOrObject}, index) => { - - const title = convertMultiLangStringToString(titleStringOrObject, locale); - - return <> -
  • - - - - - -
    - - { - // FALSE this to test sourcemaps: - true && - - {title || __("catalog.customization.fallback.screen")} - - } - - { - dangerousInnerHTML_CustomProfileScreenSanitized ? -
    : <> - } - - - - -
  • - ; - }) : <> - }
  • diff --git a/src/renderer/library/customization/route.ts b/src/renderer/library/customization/route.ts new file mode 100644 index 0000000000..ade6883f4a --- /dev/null +++ b/src/renderer/library/customization/route.ts @@ -0,0 +1,20 @@ +// ==LICENSE-BEGIN== +// Copyright 2017 European Digital Reading Lab. All rights reserved. +// Licensed to the Readium Foundation under one or more contributor license agreements. +// Use of this source code is governed by a BSD-style license +// that can be found in the LICENSE file exposed on Github (readium) in the project repository. +// ==LICENSE-END== + +import { encodeB64, decodeB64 } from "../../common/logics/base64"; + +/** + * formating customization screen route to react-router route + * @param href href of the customization "screen" link (manifest.json) + */ +export function buildCustomizationRoute(href: string) { + return `/customization/${encodeB64(href)}`; +} + +export function decodeCustomizationRouteParam(hrefEncoded: string) { + return decodeB64(hrefEncoded); +} diff --git a/src/renderer/library/routing.ts b/src/renderer/library/routing.ts index 746b89de1d..6ab7b29c82 100644 --- a/src/renderer/library/routing.ts +++ b/src/renderer/library/routing.ts @@ -13,6 +13,7 @@ import Catalog from "./components/catalog/Catalog"; import Browser from "./components/opds/Browser"; import Opds from "./components/opds/Opds"; import AllPublicationPage from "./components/searchResult/AllPublicationPage"; +import CustomizationPage from "./components/customization/Customization"; // import TagSearchResult from "./components/searchResult/TagSearchResult"; // import TextSearchResult from "./components/searchResult/TextSearchResult"; @@ -103,6 +104,11 @@ const _routes = { // exact: true, component: Catalog, } as Route, + "/customization": { + path: "/customization/:hrefEncoded", + // exact: true, + component: CustomizationPage, + } as Route, "/": { path: "/", // exact: false, diff --git a/test/customization/profile/thorium_university_press/screen/faq-en.html b/test/customization/profile/thorium_university_press/screen/faq-en.html index aae9ed8e51..a3767ae55d 100644 --- a/test/customization/profile/thorium_university_press/screen/faq-en.html +++ b/test/customization/profile/thorium_university_press/screen/faq-en.html @@ -1,65 +1,318 @@ + - - - FAQ — Thorium University Library + + +FAQ — Thorium University Library + + + + + -

    FAQ — Thorium University Library

    -

    A simple list of frequently asked questions. No JavaScript, no specific styling.

    - -
      -
    • -

      What are the library's opening hours?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam non urna vitae augue bibendum.

      -
    • - -
    • -

      How can I borrow a book?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vel sapien nec justo cursus.

      -
    • - -
    • -

      What is the loan duration?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus euismod, justo at volutpat.

      -
    • - -
    • -

      How can I renew a loan?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed urna at sapien gravida.

      -
    • - -
    • -

      Are there late fees for overdue books?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis.

      -
    • - -
    • -

      How can I access online resources?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tristique leo sed massa luctus.

      -
    • - -
    • -

      Can I reserve a study room?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi a arcu sit amet lacus.

      -
    • - -
    • -

      What are the rules for using computer stations?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod, nibh et tincidunt.

      -
    • - -
    • -

      How can I contact the library staff?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sit amet mi nec risus.

      -
    • - -
    • -

      Where can I find research documents and theses?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar, lectus non convallis.

      -
    • -
    + +
    +
    +

    Frequently asked questions file

    +

    FAQ — Thorium University Library

    +

    Answers to the questions most often asked at the front desk and circulation counter.

    +
    +
    + +
    +
    + Classification 020 — Library Science + 10 cards +
    + +
    + + 020.1 + What are the opening hours? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam non urna vitae augue bibendum.

    +
    + +
    + + 020.2 + How do I borrow a book? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vel sapien nec justo cursus.

    +
    + +
    + + 020.3 + What is the loan period? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus euismod, justo at volutpat.

    +
    + +
    + + 020.4 + How do I renew a loan? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed urna at sapien gravida.

    +
    + +
    + + 020.5 + Are there late fees? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis.

    +
    + +
    + + 020.6 + How do I access online resources? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tristique leo sed massa luctus.

    +
    + +
    + + 020.7 + Can I book a study room? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi a arcu sit amet lacus.

    +
    + +
    + + 020.8 + What are the rules for using computer stations? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod, nibh et tincidunt.

    +
    + +
    + + 020.9 + How do I contact library staff? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sit amet mi nec risus.

    +
    + +
    + + 020.10 + Where can I find research papers and theses? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar, lectus non convallis.

    +
    +
    + +
    Thorium University Library
    - + \ No newline at end of file diff --git a/test/customization/profile/thorium_university_press/screen/faq-fr.html b/test/customization/profile/thorium_university_press/screen/faq-fr.html index 81a1a63601..5c38b80705 100644 --- a/test/customization/profile/thorium_university_press/screen/faq-fr.html +++ b/test/customization/profile/thorium_university_press/screen/faq-fr.html @@ -2,67 +2,317 @@ - - - FAQ — Bibliothèque Thorium University + + +FAQ — Bibliothèque Thorium University + + + + -

    FAQ — Bibliothèque Thorium University

    -

    Liste simple de questions fréquentes. Pas de JavaScript, pas de styles spécifiques.

    - -
      -
    • -

      Quels sont les horaires d'ouverture ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam non urna vitae augue bibendum.

      -
    • - -
    • -

      Comment emprunter un livre ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vel sapien nec justo cursus.

      -
    • - -
    • -

      Quelle est la durée du prêt ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus euismod, justo at volutpat.

      -
    • - -
    • -

      Comment renouveler un prêt ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed urna at sapien gravida.

      -
    • - -
    • -

      Y a-t-il des pénalités en cas de retard ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis.

      -
    • - -
    • -

      Comment accéder aux ressources en ligne ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tristique leo sed massa luctus.

      -
    • - -
    • -

      Puis-je réserver une salle d'étude ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi a arcu sit amet lacus.

      -
    • - -
    • -

      Quelles sont les règles d'utilisation des postes informatiques ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod, nibh et tincidunt.

      -
    • - -
    • -

      Comment contacter le personnel de la bibliothèque ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sit amet mi nec risus.

      -
    • - -
    • -

      Où trouver les documents de recherche et thèses ?

      -

      Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar, lectus non convallis.

      -
    • -
    - +
    +
    +

    Fichier des questions courantes

    +

    FAQ — Bibliothèque Thorium University

    +

    Les réponses aux questions les plus posées à l'accueil et au comptoir de prêt.

    +
    +
    + +
    +
    + Classification 020 — Bibliothéconomie + 10 fiches +
    + +
    + + 020.1 + Quels sont les horaires d'ouverture ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nullam non urna vitae augue bibendum.

    +
    + +
    + + 020.2 + Comment emprunter un livre ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Integer vel sapien nec justo cursus.

    +
    + +
    + + 020.3 + Quelle est la durée du prêt ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus euismod, justo at volutpat.

    +
    +
    + + 020.4 + Comment renouveler un prêt ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Cras sed urna at sapien gravida.

    +
    + +
    + + 020.5 + Y a-t-il des pénalités en cas de retard ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Vestibulum ante ipsum primis.

    +
    + +
    + + 020.6 + Comment accéder aux ressources en ligne ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Sed tristique leo sed massa luctus.

    +
    + +
    + + 020.7 + Puis-je réserver une salle d'étude ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Morbi a arcu sit amet lacus.

    +
    + +
    + + 020.8 + Quelles sont les règles d'utilisation des postes informatiques ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Aenean euismod, nibh et tincidunt.

    +
    + +
    + + 020.9 + Comment contacter le personnel de la bibliothèque ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Fusce sit amet mi nec risus.

    +
    + +
    + + 020.10 + Où trouver les documents de recherche et thèses ? + ▸ + +

    Lorem ipsum dolor sit amet, consectetur adipiscing elit. Nunc pulvinar, lectus non convallis.

    +
    +
    + + + + \ No newline at end of file