diff --git a/CHANGELOG.md b/CHANGELOG.md
index 67adf529..39b601f5 100644
--- a/CHANGELOG.md
+++ b/CHANGELOG.md
@@ -8,6 +8,7 @@
- SFR-2033: Verify the external NYPL header links of DRB App
- Update PR template with new Jira link
- Add error page for /read links with invalid source
+- Implement "Read Online" for UP items
## [0.18.1]
diff --git a/package-lock.json b/package-lock.json
index ab6011f7..5c2fa50f 100644
--- a/package-lock.json
+++ b/package-lock.json
@@ -11,7 +11,7 @@
"@chakra-ui/react": "2.5.4",
"@newrelic/next": "^0.7.0",
"@nypl/design-system-react-components": "3.1.1",
- "@nypl/web-reader": "4.3.4",
+ "@nypl/web-reader": "4.3.5",
"@types/node": "^16.11.6",
"css-loader": "^7.1.1",
"dotenv": "^16.0.3",
@@ -22,7 +22,7 @@
"next": "^13.5.6",
"next-transpile-modules": "^7.0.0",
"react": "^18.2.0",
- "react-cookie": "^4.1.1",
+ "react-cookie": "7.1.4",
"redux": "4.0.1",
"sass": "^1.62.1",
"typescript": "^4.1.3"
@@ -2332,11 +2332,11 @@
}
},
"node_modules/@grpc/grpc-js": {
- "version": "1.10.6",
- "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.6.tgz",
- "integrity": "sha512-xP58G7wDQ4TCmN/cMUHh00DS7SRDv/+lC+xFLrTkMIN8h55X5NhZMLYbvy7dSELP15qlI6hPhNCRWVMtZMwqLA==",
+ "version": "1.10.10",
+ "resolved": "https://registry.npmjs.org/@grpc/grpc-js/-/grpc-js-1.10.10.tgz",
+ "integrity": "sha512-HPa/K5NX6ahMoeBv15njAc/sfF4/jmiXLar9UlC2UfHFKZzsCVLc3wbe7+7qua7w9VPh2/L6EBxyAV7/E8Wftg==",
"dependencies": {
- "@grpc/proto-loader": "^0.7.10",
+ "@grpc/proto-loader": "^0.7.13",
"@js-sdsl/ordered-map": "^4.4.2"
},
"engines": {
@@ -2344,13 +2344,13 @@
}
},
"node_modules/@grpc/proto-loader": {
- "version": "0.7.12",
- "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.12.tgz",
- "integrity": "sha512-DCVwMxqYzpUCiDMl7hQ384FqP4T3DbNpXU8pt681l3UWCip1WUiD5JrkImUwCB9a7f2cq4CUTmi5r/xIMRPY1Q==",
+ "version": "0.7.13",
+ "resolved": "https://registry.npmjs.org/@grpc/proto-loader/-/proto-loader-0.7.13.tgz",
+ "integrity": "sha512-AiXO/bfe9bmxBjxxtYxFAXGZvMaN5s8kO+jBHAJCON8rJoB5YS/D6X7ZNc6XQkuHNmyl4CYaMI1fJ/Gn27RGGw==",
"dependencies": {
"lodash.camelcase": "^4.3.0",
"long": "^5.0.0",
- "protobufjs": "^7.2.4",
+ "protobufjs": "^7.2.5",
"yargs": "^17.7.2"
},
"bin": {
@@ -5894,9 +5894,9 @@
"integrity": "sha512-nsO+KSNgo1SbJqJEYRE9ERzo7YtYbou/OqjSQKxV7jcKox7+usiUVZOAC+XnDOABXggQTno0Y1CpVnuWEc1boQ=="
},
"node_modules/@nypl/web-reader": {
- "version": "4.3.4",
- "resolved": "https://registry.npmjs.org/@nypl/web-reader/-/web-reader-4.3.4.tgz",
- "integrity": "sha512-nC0yL3rsGkipG7Qyv+Uv5oGkwV0Bqo/LY2im3OfRzBNn0ytltynWWtrz6PJRbOt3n4YYWsxKrqk3dj7XpEZrDA==",
+ "version": "4.3.5",
+ "resolved": "https://registry.npmjs.org/@nypl/web-reader/-/web-reader-4.3.5.tgz",
+ "integrity": "sha512-OKgqk6wWE76wVc+Gnl0jfNu2hOnAQeYLM+PQjkdus1IVtv7D3V4IjAzwUcB7B2IT2ubqJhPEcGHUqk23uUfr3w==",
"dependencies": {
"@chakra-ui/react": "2.8.1",
"comlink": "4.4.1",
@@ -7723,9 +7723,9 @@
}
},
"node_modules/@types/cookie": {
- "version": "0.3.3",
- "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.3.3.tgz",
- "integrity": "sha512-LKVP3cgXBT9RYj+t+9FDKwS5tdI+rPBXaNSkma7hvqy35lc7mAokC2zsqWJH0LaqIt3B962nuYI77hsJoT1gow=="
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/@types/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-4Kh9a6B2bQciAhf7FSuMRRkUWecJgJu9nPnx3yzpsfXX/c50REIqpHY4C82bXP90qrLtXtkDxTZosYO3UpOwlA=="
},
"node_modules/@types/eslint": {
"version": "8.56.10",
@@ -10212,11 +10212,11 @@
}
},
"node_modules/braces": {
- "version": "3.0.2",
- "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.2.tgz",
- "integrity": "sha512-b8um+L1RzM3WDSzvhm6gIz1yfTbBt6YTlcEKAvsmqCZZFw46z626lVj9j1yEPW33H5H+lBQpZMP1k8l+78Ha0A==",
+ "version": "3.0.3",
+ "resolved": "https://registry.npmjs.org/braces/-/braces-3.0.3.tgz",
+ "integrity": "sha512-yQbXgO/OSZVD2IsiLlro+7Hf6Q18EJrKSEsdoMzKePKXct3gvD8oLcOQdIzGupr5Fj+EDe8gO/lxc1BzfMpxvA==",
"dependencies": {
- "fill-range": "^7.0.1"
+ "fill-range": "^7.1.1"
},
"engines": {
"node": ">=8"
@@ -10861,9 +10861,9 @@
"integrity": "sha512-Kvp459HrV2FEJ1CAsi1Ku+MY3kasH19TFykTz2xWmMeq6bk2NU3XXvfJ+Q61m0xktWwt+1HSYf3JZsTms3aRJg=="
},
"node_modules/cookie": {
- "version": "0.4.2",
- "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.4.2.tgz",
- "integrity": "sha512-aSWTXFzaKWkvHO1Ny/s+ePFpvKsPnjc551iI41v3ny/ow6tBG5Vd+FuqGNhh1LxOmVzOlGUriIlOaokOvhaStA==",
+ "version": "0.6.0",
+ "resolved": "https://registry.npmjs.org/cookie/-/cookie-0.6.0.tgz",
+ "integrity": "sha512-U71cyTamuh1CRNCfpGY6to28lxvNwPG4Guz/EVjgf3Jmzv0vlDp1atT9eS5dDjMYHucpHbWns6Lwf3BKz6svdw==",
"engines": {
"node": ">= 0.6"
}
@@ -13041,9 +13041,9 @@
}
},
"node_modules/fill-range": {
- "version": "7.0.1",
- "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.0.1.tgz",
- "integrity": "sha512-qOo9F+dMUmC2Lcb4BbVvnKJxTPjCm+RRpe4gDuGrzkL7mEVl/djYSu2OdQ2Pa302N4oqkSg9ir6jaLWJ2USVpQ==",
+ "version": "7.1.1",
+ "resolved": "https://registry.npmjs.org/fill-range/-/fill-range-7.1.1.tgz",
+ "integrity": "sha512-YsGpe3WHLK8ZYi4tWDg2Jy3ebRz2rXowDxnld4bkQB00cc/1Zw9AWnC0i9ztDJitivtQvaI9KaLyKrc+hBW0yg==",
"dependencies": {
"to-regex-range": "^5.0.1"
},
@@ -20655,13 +20655,13 @@
}
},
"node_modules/react-cookie": {
- "version": "4.1.1",
- "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-4.1.1.tgz",
- "integrity": "sha512-ffn7Y7G4bXiFbnE+dKhHhbP+b8I34mH9jqnm8Llhj89zF4nPxPutxHT1suUqMeCEhLDBI7InYwf1tpaSoK5w8A==",
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/react-cookie/-/react-cookie-7.1.4.tgz",
+ "integrity": "sha512-wDxxa/HYaSXSMlyWJvJ5uZTzIVtQTPf1gMksFgwAz/2/W3lCtY8r4OChCXMPE7wax0PAdMY97UkNJedGv7KnDw==",
"dependencies": {
- "@types/hoist-non-react-statics": "^3.0.1",
- "hoist-non-react-statics": "^3.0.0",
- "universal-cookie": "^4.0.0"
+ "@types/hoist-non-react-statics": "^3.3.5",
+ "hoist-non-react-statics": "^3.3.2",
+ "universal-cookie": "^7.0.0"
},
"peerDependencies": {
"react": ">= 16.3.0"
@@ -23532,12 +23532,12 @@
}
},
"node_modules/universal-cookie": {
- "version": "4.0.4",
- "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-4.0.4.tgz",
- "integrity": "sha512-lbRVHoOMtItjWbM7TwDLdl8wug7izB0tq3/YVKhT/ahB4VDvWMyvnADfnJI8y6fSvsjh51Ix7lTGC6Tn4rMPhw==",
+ "version": "7.1.4",
+ "resolved": "https://registry.npmjs.org/universal-cookie/-/universal-cookie-7.1.4.tgz",
+ "integrity": "sha512-Q+DVJsdykStWRMtXr2Pdj3EF98qZHUH/fXv/gwFz/unyToy1Ek1w5GsWt53Pf38tT8Gbcy5QNsj61Xe9TggP4g==",
"dependencies": {
- "@types/cookie": "^0.3.3",
- "cookie": "^0.4.0"
+ "@types/cookie": "^0.6.0",
+ "cookie": "^0.6.0"
}
},
"node_modules/universalify": {
@@ -24256,9 +24256,9 @@
}
},
"node_modules/ws": {
- "version": "8.16.0",
- "resolved": "https://registry.npmjs.org/ws/-/ws-8.16.0.tgz",
- "integrity": "sha512-HS0c//TP7Ina87TfiPUz1rQzMhHrl/SG2guqRcTOIUYD2q8uhUdNHZYJUaQ8aTGPzCh+c6oawMKW35nFl1dxyQ==",
+ "version": "8.18.0",
+ "resolved": "https://registry.npmjs.org/ws/-/ws-8.18.0.tgz",
+ "integrity": "sha512-8VbfWfHLbbwu3+N6OKsOMpBdT4kXPDDB9cJk2bJ6mh9ucxdlnNvH1e+roYkKmN9Nxw2yjz7VzeO9oOz2zJ04Pw==",
"engines": {
"node": ">=10.0.0"
},
diff --git a/package.json b/package.json
index 2e449daf..32d15646 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
"@chakra-ui/react": "2.5.4",
"@newrelic/next": "^0.7.0",
"@nypl/design-system-react-components": "3.1.1",
- "@nypl/web-reader": "4.3.4",
+ "@nypl/web-reader": "4.3.5",
"@types/node": "^16.11.6",
"css-loader": "^7.1.1",
"dotenv": "^16.0.3",
@@ -28,7 +28,7 @@
"next": "^13.5.6",
"next-transpile-modules": "^7.0.0",
"react": "^18.2.0",
- "react-cookie": "^4.1.1",
+ "react-cookie": "7.1.4",
"redux": "4.0.1",
"sass": "^1.62.1",
"typescript": "^4.1.3"
diff --git a/src/components/EditionCard/EditionCard.test.tsx b/src/components/EditionCard/EditionCard.test.tsx
index b4c436f4..f1e5f6a1 100644
--- a/src/components/EditionCard/EditionCard.test.tsx
+++ b/src/components/EditionCard/EditionCard.test.tsx
@@ -8,6 +8,7 @@ import {
upEdition,
} from "~/src/__tests__/fixtures/EditionCardFixture";
import { NYPL_SESSION_ID } from "~/src/constants/auth";
+import { Cookies, CookiesProvider } from "react-cookie";
jest.mock("next/router", () => require("next-router-mock"));
@@ -124,8 +125,13 @@ describe("Edition with EDD", () => {
test("Shows EDD Request button when user is logged in", () => {
// Set cookie before rendering the component
- document.cookie = `${NYPL_SESSION_ID}="randomvalue"`;
- render();
+ const cookies = new Cookies();
+ cookies.set(NYPL_SESSION_ID, "randomvalue");
+ render(
+
+
+
+ );
expect(
screen.getByRole("link", { name: "Request scan for title" })
@@ -151,7 +157,6 @@ describe("Edition with EDD", () => {
describe("Edition with UP", () => {
test("Shows Login button when user is not logged in", () => {
- document.cookie = `${NYPL_SESSION_ID}=""`;
render();
expect(
screen.getByRole("link", { name: "title Log in to read online" })
@@ -171,8 +176,13 @@ describe("Edition with UP", () => {
});
test("Shows Read Online and Download buttons when user is logged in", () => {
// Set cookie before rendering the component
- document.cookie = `${NYPL_SESSION_ID}="randomvalue"`;
- render();
+ const cookies = new Cookies();
+ cookies.set(NYPL_SESSION_ID, "randomvalue");
+ render(
+
+
+
+ );
expect(
screen.getByRole("link", { name: "title Read Online" })
diff --git a/src/components/EditionCard/ReadOnlineLink.tsx b/src/components/EditionCard/ReadOnlineLink.tsx
index 95e84f95..f9c3bcf5 100644
--- a/src/components/EditionCard/ReadOnlineLink.tsx
+++ b/src/components/EditionCard/ReadOnlineLink.tsx
@@ -9,13 +9,18 @@ const ReadOnlineLink: React.FC<{
readOnlineLink: ItemLink;
isLoggedIn: boolean;
title: string;
+ loginCookie?: any;
}> = ({ readOnlineLink, isLoggedIn, title }) => {
let linkText = "Read Online";
let linkUrl: any = {
pathname: `/read/${readOnlineLink.link_id}`,
};
- if (readOnlineLink.flags.nypl_login && !isLoggedIn) {
+ if (
+ (readOnlineLink.flags.nypl_login ||
+ readOnlineLink.flags.fulfill_limited_access) &&
+ !isLoggedIn
+ ) {
linkText = "Log in to read online";
linkUrl = LOGIN_LINK_BASE + encodeURIComponent(window.location.href);
}
diff --git a/src/components/InstanceCard/InstanceCard.test.tsx b/src/components/InstanceCard/InstanceCard.test.tsx
index 06b7ed83..28d8296f 100644
--- a/src/components/InstanceCard/InstanceCard.test.tsx
+++ b/src/components/InstanceCard/InstanceCard.test.tsx
@@ -13,6 +13,7 @@ import {
eddInstance,
upInstance,
} from "~/src/__tests__/fixtures/InstanceCardFixture";
+import { Cookies, CookiesProvider } from "react-cookie";
jest.mock("next/router", () => require("next-router-mock"));
@@ -114,9 +115,15 @@ describe("Instance with EDD", () => {
test("Shows EDD Request button when user is logged in", () => {
// Set cookie before rendering the component
- document.cookie = `${NYPL_SESSION_ID}="randomvalue"`;
+ const cookies = new Cookies();
+ cookies.set(NYPL_SESSION_ID, "randomvalue");
render(
-
+
+
+
);
expect(
@@ -143,7 +150,6 @@ describe("Instance with EDD", () => {
describe("Instance with UP", () => {
test("Shows Login button when user is not logged in", () => {
- document.cookie = `${NYPL_SESSION_ID}=""`;
render();
expect(
screen.getByRole("link", { name: "title Log in to read online" })
@@ -163,8 +169,13 @@ describe("Instance with UP", () => {
});
test("Shows Read Online and Download buttons when user is logged in", () => {
// Set cookie before rendering the component
- document.cookie = `${NYPL_SESSION_ID}="randomvalue"`;
- render();
+ const cookies = new Cookies();
+ cookies.set(NYPL_SESSION_ID, "randomvalue");
+ render(
+
+
+
+ );
expect(
screen.getByRole("link", { name: "title Read Online" })
diff --git a/src/components/InstanceCard/InstanceCard.tsx b/src/components/InstanceCard/InstanceCard.tsx
index 44f5df04..2a7a52ba 100644
--- a/src/components/InstanceCard/InstanceCard.tsx
+++ b/src/components/InstanceCard/InstanceCard.tsx
@@ -43,6 +43,7 @@ export const InstanceCard: React.FC<{
border="1px"
borderColor="ui.border.default"
padding="s"
+ paddingLeft={{ base: "l", md: null }}
paddingBottom="l"
paddingRight="l"
>
@@ -61,13 +62,22 @@ export const InstanceCard: React.FC<{
isAlignedRightActions
id={`card-${instance.instance_id}`}
paddingTop="m"
+ flexFlow={{ md: "column nowrap", lg: "row" }}
+ justifyContent={{ md: "center", lg: "left" }}
+ sx={{
+ ".card-right": {
+ maxWidth: { base: "100%", lg: "200px" },
+ marginStart: { base: "0", lg: "m" },
+ marginTop: { base: "xs", lg: 0 },
+ },
+ }}
>
-
+
{edition.publication_date
? edition.publication_date
@@ -92,6 +102,9 @@ export const InstanceCard: React.FC<{
display="flex"
flexDir="column"
whiteSpace="nowrap"
+ sx={{
+ width: { base: "100%", lg: "200px" },
+ }}
gap="xs"
>
diff --git a/src/components/ReaderLayout/ReaderLayout.tsx b/src/components/ReaderLayout/ReaderLayout.tsx
index 65699184..95944943 100644
--- a/src/components/ReaderLayout/ReaderLayout.tsx
+++ b/src/components/ReaderLayout/ReaderLayout.tsx
@@ -10,6 +10,15 @@ import { MAX_TITLE_LENGTH } from "~/src/constants/editioncard";
import dynamic from "next/dynamic";
import { MediaTypes } from "~/src/constants/mediaTypes";
import ReaderLogoSvg from "../Svgs/ReaderLogoSvg";
+import Link from "../Link/Link";
+import { addTocToManifest } from "@nypl/web-reader";
+import Loading from "../Loading/Loading";
+import { trackCtaClick } from "~/src/lib/adobe/Analytics";
+import { NYPL_SESSION_ID } from "~/src/constants/auth";
+import { useCookies } from "react-cookie";
+import { useRouter } from "next/router";
+import NotFound404 from "~/src/pages/404";
+
const WebReader = dynamic(() => import("@nypl/web-reader"), { ssr: false });
// This is how we can import a css file as a url. It's complex, but necessary
// eslint-disable-next-line @typescript-eslint/ban-ts-comment
@@ -22,12 +31,6 @@ import readiumDefault from "!file-loader!extract-loader!css-loader!@nypl/web-rea
// @ts-ignore
import readiumAfter from "!file-loader!extract-loader!css-loader!@nypl/web-reader/dist/injectable-html-styles/ReadiumCSS-after.css";
-import Link from "../Link/Link";
-import { addTocToManifest } from "@nypl/web-reader";
-import Loading from "../Loading/Loading";
-import { trackCtaClick } from "~/src/lib/adobe/Analytics";
-import NotFound404 from "~/src/pages/404";
-
const origin =
typeof window !== "undefined" && window.location?.origin
? window.location.origin
@@ -68,6 +71,11 @@ const ReaderLayout: React.FC<{
const isEmbed = MediaTypes.embed.includes(link.media_type);
const isRead = MediaTypes.read.includes(link.media_type);
+ const isLimitedAccess = link.flags.fulfill_limited_access;
+
+ const [cookies] = useCookies([NYPL_SESSION_ID]);
+ const nyplIdentityCookie = cookies[NYPL_SESSION_ID];
+ const router = useRouter();
const pdfWorkerSrc = `${origin}/pdf-worker/pdf.worker.min.js`;
@@ -114,7 +122,8 @@ const ReaderLayout: React.FC<{
if (
manifest &&
manifest.readingOrder &&
- manifest.readingOrder.length === 1
+ manifest.readingOrder.length === 1 &&
+ !isLimitedAccess
) {
const modifiedManifest = await addTocToManifest(
manifest,
@@ -135,7 +144,7 @@ const ReaderLayout: React.FC<{
document.getElementById("nypl-header").style.display = "none";
document.getElementById("nypl-footer").style.display = "none";
}
- }, [isRead, pdfWorkerSrc, proxyUrl, url]);
+ }, [isLimitedAccess, isRead, pdfWorkerSrc, proxyUrl, url]);
const BackButton = () => {
return (
@@ -185,6 +194,11 @@ const ReaderLayout: React.FC<{
pdfWorkerSrc={pdfWorkerSrc}
headerLeft={}
injectablesFixed={injectables}
+ getContent={
+ isLimitedAccess
+ ? EditionCardUtils.createGetContent(nyplIdentityCookie, router)
+ : undefined
+ }
/>
)}
>
diff --git a/src/types/DataModel.ts b/src/types/DataModel.ts
index 3b434cee..a8e6de32 100644
--- a/src/types/DataModel.ts
+++ b/src/types/DataModel.ts
@@ -65,6 +65,7 @@ export type LinkFlags = {
edd?: boolean;
embed?: boolean;
nypl_login?: boolean;
+ fulfill_limited_access?: boolean;
};
export type ItemLink = {
diff --git a/src/util/EditionCardUtils.tsx b/src/util/EditionCardUtils.tsx
index e59b2334..3849fc38 100644
--- a/src/util/EditionCardUtils.tsx
+++ b/src/util/EditionCardUtils.tsx
@@ -1,4 +1,3 @@
-import React from "react";
import {
Agent,
Instance,
@@ -16,6 +15,8 @@ import {
PLACEHOLDER_COVER_LINK,
} from "../constants/editioncard";
import { MediaTypes } from "../constants/mediaTypes";
+import { NextRouter } from "next/router";
+import { LOGIN_LINK_BASE } from "../constants/links";
// EditionCard holds all the methods needed to build an Edition Card
export default class EditionCardUtils {
@@ -220,4 +221,41 @@ export default class EditionCardUtils {
: undefined;
return universityPress !== undefined;
}
+
+ static createGetContent = (nyplIdentityCookie: any, router: NextRouter) => {
+ const fetchWithAuth = async (fulfillUrl: string, proxyUrl?: string) => {
+ const url = new URL(fulfillUrl);
+ const res = await fetch(url.toString(), {
+ method: "GET",
+ headers: {
+ Authorization: `Bearer ${nyplIdentityCookie.access_token}`,
+ },
+ });
+
+ if (res.ok) {
+ // Generate the resource URL using the proxy
+ const resourceUrl = res.url;
+ const proxiedUrl: string = proxyUrl
+ ? `${proxyUrl}${encodeURIComponent(resourceUrl)}`
+ : resourceUrl;
+ const response = await fetch(proxiedUrl, { mode: "cors" });
+ const resourceAsByteArray = new Uint8Array(
+ await response.arrayBuffer()
+ );
+
+ if (!response.ok) {
+ throw new Error("Response not Ok for URL: " + url);
+ }
+ return resourceAsByteArray;
+ } else {
+ // redirect to the NYPL login page if access token is invalid
+ if (res.status === 401) {
+ router.push(
+ LOGIN_LINK_BASE + encodeURIComponent(window.location.href)
+ );
+ }
+ }
+ };
+ return fetchWithAuth;
+ };
}