Skip to content

Commit

Permalink
Merge pull request #55 from danskernesdigitalebibliotek/DDFBRA-200-an…
Browse files Browse the repository at this point in the history
…onym-og-indlogget-bruger-kan-prove-e-bog

feat: add reader page to show publizon reader and enhance work page layout with smartLink
  • Loading branch information
ThomasGross authored Nov 27, 2024
2 parents f959835 + de83dc9 commit 7db9ba9
Show file tree
Hide file tree
Showing 13 changed files with 157 additions and 78 deletions.
18 changes: 11 additions & 7 deletions __tests__/url.test.ts
Original file line number Diff line number Diff line change
@@ -1,22 +1,26 @@
import { expect, test } from "vitest"

import goConfig from "@/lib/config/config"

Check failure on line 3 in __tests__/url.test.ts

View workflow job for this annotation

GitHub Actions / build

Cannot find module '@/lib/config/config' or its corresponding type declarations.

import { resolveUrl } from "../lib/helpers/helper.routes"

test("That resolveUrl can return a work url", async () => {
const workUrl = resolveUrl({ type: "work", routeParams: { wid: 123 } })
expect(workUrl).toBe("/work/123")
const workUrl = resolveUrl({ routeParams: { work: "work", wid: 123 } })
const appUrl = goConfig("app.url")
expect(workUrl).toBe(`${appUrl}/work/123`)
})

test("That resolveUrl can return a work url with a manifestation type", async () => {
const workUrl = resolveUrl({
type: "work",
routeParams: { wid: 123 },
routeParams: { work: "work", wid: 123 },
queryParams: { audio: "true" },
})
expect(workUrl).toBe("/work/123?audio=true")
const appUrl = goConfig("app.url")
expect(workUrl).toBe(`${appUrl}/work/123?audio=true`)
})

test("That resolveUrl can return a search url", async () => {
const workUrl = resolveUrl({ type: "search", queryParams: { q: "test" } })
expect(workUrl).toBe("/search?q=test")
const workUrl = resolveUrl({ routeParams: { search: "search" }, queryParams: { q: "test" } })
const appUrl = goConfig("app.url")
expect(workUrl).toBe(`${appUrl}/search?q=test`)
})
20 changes: 20 additions & 0 deletions app/work/[id]/read/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,20 @@
"use client"

import React from "react"

import Reader from "@/components/shared/publizonReader/PublizonReader"

function Page({ searchParams: { id } }: { searchParams: { id: string } }) {
const handleBack = () => {
window.history.back()
}

return (
<div className="absolute inset-0 h-screen w-screen">
<div className="absolute h-full w-full bg-reader-grey"></div>
<Reader onBackCallback={() => handleBack()} type="demo" identifier={id} />
</div>
)
}

export default Page
22 changes: 20 additions & 2 deletions components/pages/workPageLayout/WorkPageLayout.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,17 +3,35 @@
import { useQuery } from "@tanstack/react-query"
import React from "react"

import { Button } from "@/components/shared/button/Button"
import SmartLink from "@/components/shared/smartLink/SmartLink"
import { useGetMaterialQuery } from "@/lib/graphql/generated/fbi/graphql"
import { resolveUrl } from "@/lib/helpers/helper.routes"

function WorkPageLayout({ wid }: { wid: string }) {
const data = useQuery({
const { data } = useQuery({
queryKey: useGetMaterialQuery.getKey({ wid }),
queryFn: useGetMaterialQuery.fetcher({ wid }),
})

// TODO: Handle potential error states
const manifestations = data?.work?.manifestations.all
const identifier = manifestations?.[0].identifiers?.[0].value || ""

const url = resolveUrl({
routeParams: { work: "work", ":wid": wid, read: "read" },
queryParams: { id: identifier },
})

return (
<div>
<pre>{JSON.stringify({ data }, null, 2)}</pre>
{identifier && (
<Button ariaLabel="Prøv ebog" asChild>
<SmartLink linkType="external" href={url}>
Prøv ebog
</SmartLink>
</Button>
)}
</div>
)
}
Expand Down
59 changes: 25 additions & 34 deletions components/shared/publizonReader/PublizonReader.tsx
Original file line number Diff line number Diff line change
@@ -1,81 +1,72 @@
import React, { CSSProperties, useEffect } from "react"
"use client"

import { appendAsset, readerAssets } from "./helper"
import React, { useEffect } from "react"

// type ReaderType = { identifier?: string; orderId?: string };
import { appendAsset, readerAssets, removeAsset } from "./helper"

// Define mutually exclusive types for identifier and orderId
type ReaderType =
| {
type: "demo"
identifier: string
orderId?: never
onBackCallback: () => void
}
| {
type: "rent"
identifier?: never
orderId: string
onBackCallback: () => void
}

const Reader = ({ identifier, orderId }: ReaderType) => {
const Reader = ({ type, onBackCallback, identifier, orderId }: ReaderType) => {
useEffect(() => {
readerAssets.forEach(appendAsset)
}, [identifier, orderId])

const readerStyles: CSSProperties = {
position: "absolute",
top: "0", // Padding from the top
left: "0", // Padding from the left
right: "0", // Padding from the right
bottom: "0", // Padding from the bottom
padding: "20px", // Padding for the reader
width: "100%",
maxWidth: "unset",
zIndex: 1000,
// border: "1px dotted black", // Should be removed in production
margin: "0",
}

const handleBack = () => {
window.history.back()
}
// Attach the onReaderBackCallback function to the window object to be able to enable callback methods calls through the close button
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line @typescript-eslint/no-shadow
window.onReaderBackCallback = () => {
onBackCallback()
}

const handleKeyDown = (event: React.KeyboardEvent<HTMLDivElement>) => {
if (event.key === "Escape") {
handleBack()
return () => {
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
delete window.onReaderBackCallback
readerAssets.forEach(removeAsset)
}
}
}, [])

if (orderId) {
if (type === "rent") {
return (
<div>
<p>orderId: {orderId}</p>
<div
style={readerStyles}
id="pubhub-reader"
// eslint-disable-next-line react/no-unknown-property
order-id={orderId}
role="button"
tabIndex={0}
onClick={handleBack}
onKeyDown={handleKeyDown}
close-href="javascript:window.onReaderBackCallback()"
aria-label="Go back"
/>
</div>
)
}

if (identifier) {
if (type === "demo") {
return (
<div
style={readerStyles}
id="pubhub-reader"
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
// @ts-ignore
// eslint-disable-next-line react/no-unknown-property
identifier={identifier}
close-href="javascript:window.onReaderBackCallback()"
role="button"
tabIndex={0}
onClick={handleBack}
onKeyDown={handleKeyDown}
aria-label="Go back"
/>
)
Expand Down
18 changes: 15 additions & 3 deletions components/shared/publizonReader/helper.ts
Original file line number Diff line number Diff line change
@@ -1,9 +1,9 @@
type AssetType = {
type TAssetType = {
src: string
type: "script" | "link"
}

export const readerAssets: AssetType[] = [
export const readerAssets: TAssetType[] = [
{
src: "https://reader.pubhub.dk/2.2.0/js/chunk-vendors.js",
type: "script",
Expand All @@ -22,7 +22,7 @@ export const readerAssets: AssetType[] = [
},
]

export const appendAsset = ({ src, type }: AssetType) => {
export const appendAsset = ({ src, type }: TAssetType) => {
if (type === "script") {
const scriptElement = document.createElement("script")
scriptElement.src = src
Expand All @@ -39,3 +39,15 @@ export const appendAsset = ({ src, type }: AssetType) => {
document.head.appendChild(linkElement)
}
}

export const removeAsset = ({ src, type }: TAssetType) => {
if (type === "script") {
const scriptElement = document.querySelector(`script[src="${src}"]`)
scriptElement?.remove()
}

if (type === "link") {
const linkElement = document.querySelector(`link[href="${src}"]`)
linkElement?.remove()
}
}
36 changes: 36 additions & 0 deletions components/shared/smartLink/SmartLink.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,36 @@
import Link from "next/link"
import React from "react"

function SmartLink({
href,
target = "_self",
linkType = "internal",
children,
className,
}: {
href: string
target?: string
linkType?: "internal" | "external"
children: React.ReactNode
className?: string
}) {
// Internal link
if (linkType === "internal") {
return (
<Link className={className} href={href} target={target}>
{children}
</Link>
)
}

// External link
if (linkType === "external") {
return (
<a className={className} href={href} target={target}>
{children}
</a>
)
}
}

export default SmartLink
2 changes: 1 addition & 1 deletion components/shared/workCard/WorkCard.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -58,7 +58,7 @@ const WorkCard = ({ work }: WorkCardProps) => {
return (
<Link
className="block space-y-3 lg:space-y-5"
href={resolveUrl({ type: "work", routeParams: { wid: work.workId } })}>
href={resolveUrl({ routeParams: { work: "work", wid: work.workId } })}>
<div>
<div
key={work.workId}
Expand Down
52 changes: 23 additions & 29 deletions lib/helpers/helper.routes.ts
Original file line number Diff line number Diff line change
@@ -1,60 +1,54 @@
import goConfig from "../config/config"

Check failure on line 1 in lib/helpers/helper.routes.ts

View workflow job for this annotation

GitHub Actions / build

Cannot find module '../config/config' or its corresponding type declarations.

type RouteParams = { [key: string]: string | number }
type QueryParams = { [key: string]: string | number }

export function buildRoute({
route,
params,
query,
}: {
route: string
params?: RouteParams
query?: QueryParams
}): string {
let routeParams = ""
if (params) {
route = Object.keys(params).reduce((acc, key) => {
routeParams = Object.keys(params).reduce((acc, key) => {
const value = encodeURIComponent(params[key])
return acc.replace(`:${key}`, value)
}, route)
return `${acc}/${value}`
}, routeParams)
}

const queryParams = new URLSearchParams()
const url = new URL(routeParams, goConfig("app.url"))
if (query) {
Object.keys(query).forEach(key => {
queryParams.append(key, query[key].toString())
url.searchParams.append(key, query[key].toString())
})
}

if (queryParams.toString()) {
return `${route}?${queryParams}`
}

return route
return url.toString()
}

type ResolveUrlOptions =
| {
type: "work"
routeParams?: { wid: number | string }
routeParams?: { work: "work"; wid: number | string }
queryParams?: QueryParams
}
| {
type: "search"
routeParams?: undefined
routeParams?: { work: "work"; ":wid": number | string; read: "read" }
queryParams?: QueryParams
}
| {
routeParams?: { search: "search" }
queryParams?: QueryParams
}

export const resolveUrl = ({ type, routeParams, queryParams }: ResolveUrlOptions) => {
switch (type as ResolveUrlOptions["type"]) {
case "work":
if (!routeParams?.wid) return ""
return buildRoute({
route: "/work/:wid",
params: { wid: routeParams.wid },
query: queryParams,
})
case "search":
return buildRoute({ route: "/search", query: queryParams })
default:
return ""
export const resolveUrl = ({ routeParams, queryParams }: ResolveUrlOptions) => {
if (!routeParams) {
throw new Error("routeParams is required")
}

return buildRoute({
params: routeParams,
query: queryParams,
})
}
3 changes: 3 additions & 0 deletions lib/shadcn/utils.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,3 @@
import { cn as classNames } from "../helpers/helper.cn"

export const cn = classNames
1 change: 0 additions & 1 deletion package.json
Original file line number Diff line number Diff line change
Expand Up @@ -27,7 +27,6 @@
"dependencies": {
"@next/env": "^14.2.15",
"@radix-ui/react-accordion": "^1.2.1",
"@radix-ui/react-dialog": "^1.1.2",
"@radix-ui/react-icons": "^1.3.0",
"@radix-ui/react-slot": "^1.1.0",
"@redux-devtools/extension": "^3.3.0",
Expand Down
1 change: 1 addition & 0 deletions styles/globals.css
Original file line number Diff line number Diff line change
Expand Up @@ -72,6 +72,7 @@
--blue-overlay: hsla(238, 54%, 89%, 0.09);

--border: hsla(48, 4.4%, 77.8%, 1);
--reader-grey: hsla(0, 0%, 94.1%, 1);

--shadow-1: hsla(0, 0%, 0%, 0.5);
--shadow-2: hsla(0, 0%, 0%, 0.15);
Expand Down
1 change: 1 addition & 0 deletions tailwind.config.ts
Original file line number Diff line number Diff line change
Expand Up @@ -192,6 +192,7 @@ export const extendedTheme = {
},
foreground: "var(--foreground)",
border: "var(--border)",
"reader-grey": "var(--reader-grey)",
content: {
pink: "var(--content-pink)",
blue: "var(--content-blue)",
Expand Down
Loading

0 comments on commit 7db9ba9

Please sign in to comment.