diff --git a/components/ContentThumb.tsx b/components/ContentThumb.tsx
index 7064616..79568e8 100644
--- a/components/ContentThumb.tsx
+++ b/components/ContentThumb.tsx
@@ -1,22 +1,43 @@
-import { Link, styled } from 'components';
+import { Link, Dialog, styled, IconButton, useTheme } from 'components';
import { THUMB_HEIGHT, THUMB_WIDTH } from 'util/constants';
import { constructNextImageURL, contentUrl } from 'util/url-factory';
import { defineMessages, useIntl } from 'i18n';
import { Content } from 'data/graphql-generated';
+import { MouseEvent, useEffect, useState } from 'react';
+import { ArrowBackIosNew, ArrowForwardIos, Close } from '@mui/icons-material';
+import { Backdrop } from '@mui/material';
+import Header from './content/Header';
+import Image from 'next/image';
const ThumbLink = styled(Link)`
- max-width: ${THUMB_WIDTH}px;
- min-height: ${THUMB_HEIGHT}px;
+ width: ${THUMB_WIDTH}px;
+ height: ${THUMB_HEIGHT}px;
display: flex;
align-items: center;
justify-content: center;
`;
+const DialogImage = styled('img')`
+ max-height: 80vh;
+ max-width: 100%;
+
+ &:not(:last-child) {
+ margin-bottom: ${(props) => props.theme.spacing(1)};
+ }
+`;
+
+const DialogContent = styled('div')`
+ header {
+ align-self: center;
+ }
+`;
+
const StyledThumb = styled('img')`
display: inline-block;
- max-width: ${THUMB_WIDTH}px;
- max-height: ${THUMB_HEIGHT}px;
+ width: ${THUMB_WIDTH}px;
+ height: ${THUMB_HEIGHT}px;
+ object-fit: cover;
`;
const messages = defineMessages({
@@ -27,24 +48,56 @@ export default function Thumb({
className,
item,
currentContent: currentContentProp,
+ isOpen,
+ onOpen,
+ onClose,
+ handlePrev,
+ handleNext,
}: {
className?: string;
item: Content;
currentContent?: Content;
+ isOpen: boolean;
+ onOpen: () => void;
+ onClose: () => void;
+ handlePrev: () => void;
+ handleNext: () => void;
}) {
const intl = useIntl();
+ const [isDialogOpen, setIsDialogOpen] = useState(false);
+ const theme = useTheme();
+
+ useEffect(() => setIsDialogOpen(isOpen), [isOpen]);
const currentContent = currentContentProp || { forceRefresh: false };
const thumbAltText = intl.formatMessage(messages.thumbnail);
- const thumb = (
+ const thumb =
// TODO(mime): is loading lazy necessary here for next.js? i forget
-
- );
+ item.thumb.startsWith('/resource') ? (
+
+ ) : (
+
+ );
// We're using the fancy new "loading" attribute for images to lazy load thumbs. Woo!
// return !isEditing && item.externalLink ? (
@@ -62,14 +115,112 @@ export default function Thumb({
//
// );
+ const handleClick = (evt: MouseEvent) => {
+ evt.preventDefault();
+
+ setIsDialogOpen(true);
+ onOpen();
+ };
+ const handleClose = () => {
+ setIsDialogOpen(false);
+ onClose();
+ };
+
+ const isPhotosSectionAndHasPhotos = item.section === 'photos' && !!item.prefetchImages?.length;
+
return (
-
- {thumb}
-
+ <>
+
+ {thumb}
+
+
+ {isPhotosSectionAndHasPhotos && (
+
+ )}
+ >
);
}
diff --git a/components/Help.tsx b/components/Help.tsx
index 38a9bbc..58eef90 100644
--- a/components/Help.tsx
+++ b/components/Help.tsx
@@ -1,14 +1,4 @@
-import {
- Checkbox,
- IconButton,
- List,
- ListItem,
- ListItemText,
- Menu,
- MenuItem,
- SwipeableDrawer,
- Typography,
-} from '@mui/material';
+import { Checkbox, IconButton, List, ListItem, ListItemText, Menu, MenuItem, Drawer, Typography } from '@mui/material';
import { F, defineMessages, useIntl } from 'i18n';
import { MouseEvent, useContext, useEffect, useState } from 'react';
import { styled, useTheme } from '@mui/material/styles';
@@ -148,12 +138,7 @@ export default function Help() {
- setIsExperimentsOpen(false)}
- onOpen={() => setIsExperimentsOpen(true)}
- >
+ setIsExperimentsOpen(false)}>
Experiments
@@ -170,7 +155,7 @@ export default function Help() {
))}
-
+
);
}
diff --git a/components/ItemWrapper.tsx b/components/ItemWrapper.tsx
index 6087c17..61a815c 100644
--- a/components/ItemWrapper.tsx
+++ b/components/ItemWrapper.tsx
@@ -80,7 +80,6 @@ const StyledItemWrapper = styled('div', { label: 'ItemWrapper' })`
& img {
max-width: calc(100% + 16px);
margin: ${(props) => props.theme.spacing(0, -1)};
- height: auto;
}
& img:not([alt='thumbnail']) {
diff --git a/components/content/SiteMap.tsx b/components/content/SiteMap.tsx
index ae82c1b..9bc2b34 100644
--- a/components/content/SiteMap.tsx
+++ b/components/content/SiteMap.tsx
@@ -15,7 +15,7 @@ import baseTheme from 'styles';
import constants from 'util/constants';
import { transientOptions } from 'util/css';
import { useRouter } from 'next/router';
-import { SwipeableDrawer, useMediaQuery } from '@mui/material';
+import { Drawer, useMediaQuery } from '@mui/material';
export const SITE_MAP_WIDTH = 175;
@@ -35,7 +35,7 @@ const Nav = styled('nav', transientOptions)`
3px 3px ${(props) => props.theme.palette.primary.light};
`;
-const SwipeableDrawerStyled = styled(SwipeableDrawer)`
+const DrawerStyled = styled(Drawer)`
height: calc(100vh - ${(props) => props.theme.spacing(1)} * 2);
width: ${SITE_MAP_WIDTH}px;
overflow: auto;
@@ -266,7 +266,7 @@ export default function SiteMap({ content, username }: { content?: Content; user
}
const items = generateItems(siteMap);
- const DrawerComponent = isTablet ? SwipeableDrawerStyled : Nav;
+ const DrawerComponent = isTablet ? DrawerStyled : Nav;
return (
<>
@@ -293,7 +293,6 @@ export default function SiteMap({ content, username }: { content?: Content; user
anchor="left"
open={isDrawerOpen}
onClose={() => setIsDrawerOpen(false)}
- onOpen={() => setIsDrawerOpen(true)}
PaperProps={{ sx: { width: '100%' } }}
>
diff --git a/components/content/templates/Album.tsx b/components/content/templates/Album.tsx
index 4a4957b..3c3e1e9 100644
--- a/components/content/templates/Album.tsx
+++ b/components/content/templates/Album.tsx
@@ -1,12 +1,10 @@
import { Alert, Snackbar, styled } from 'components';
-import { THUMB_WIDTH } from 'util/constants';
import { gql, useQuery } from '@apollo/client';
import { Content } from 'data/graphql-generated';
-import ContentLink from 'components/ContentLink';
import ContentThumb from 'components/ContentThumb';
import { F } from 'i18n';
-import { useState } from 'react';
+import { useEffect, useState } from 'react';
const StyledAlbum = styled('ul')`
list-style: none;
@@ -32,14 +30,14 @@ const Item = styled('li')`
}
`;
-const LinkWrapper = styled('span')`
- & a {
- display: block;
- width: ${THUMB_WIDTH}px;
- max-width: ${THUMB_WIDTH}px;
- min-height: 1.1em;
- }
-`;
+// const LinkWrapper = styled('span')`
+// & a {
+// display: block;
+// width: ${THUMB_WIDTH}px;
+// max-width: ${THUMB_WIDTH}px;
+// min-height: 1.1em;
+// }
+// `;
// const DeleteButton = styled(IconButton)`
// position: absolute;
@@ -63,6 +61,7 @@ const FETCH_COLLECTION = gql`
thumb
title
username
+ prefetchImages
}
}
`;
@@ -77,6 +76,32 @@ export default function Album({ content }: { content: Content }) {
const { username, section, album, name } = content;
const [errorMsg] = useState('');
const [isToastOpen, setIsToastOpen] = useState(false);
+ const [currentIndexOpen, setCurrentIndexOpen] = useState(-1);
+
+ useEffect(() => {
+ window.addEventListener('keyup', handleKeyUp);
+ return () => window.removeEventListener('keyup', handleKeyUp);
+ });
+
+ const handleNext = () => setCurrentIndexOpen(Math.min(collection.length - 1, currentIndexOpen + 1));
+ const handlePrev = () => setCurrentIndexOpen(Math.max(0, currentIndexOpen - 1));
+
+ const handleKeyUp = (evt: KeyboardEvent) => {
+ if (currentIndexOpen === -1) {
+ return;
+ }
+
+ switch (evt.key) {
+ case 'ArrowLeft':
+ handlePrev();
+ break;
+ case 'ArrowRight':
+ handleNext();
+ break;
+ default:
+ break;
+ }
+ };
const { loading, data } = useQuery(FETCH_COLLECTION, {
variables: {
@@ -132,17 +157,25 @@ export default function Album({ content }: { content: Content }) {
)}
- {collection.map((item: Content) => (
+ {collection.map((item: Content, index: number) => (
-
{/* {isEditing ? (
handleClick(item)}>
x
) : null} */}
-
- {item.title && (
+ setCurrentIndexOpen(index)}
+ handlePrev={handlePrev}
+ handleNext={handleNext}
+ onClose={() => setCurrentIndexOpen(-1)}
+ />
+ {/* {item.title && (
- {/* {!isEditing && item.externalLink ? (
+ {!isEditing && item.externalLink ? (
{item.title}
- ) : null} */}
+ ) : null}
{item.title}
- )}
+ )} */}
))}
+
{errorMsg}
diff --git a/components/index.tsx b/components/index.tsx
index 3dd33f3..17c251c 100644
--- a/components/index.tsx
+++ b/components/index.tsx
@@ -10,6 +10,7 @@ export {
ButtonGroup,
Chip,
Container,
+ Dialog,
FormControl,
FormGroup,
Grid,
diff --git a/data/resolvers/content.ts b/data/resolvers/content.ts
index 5f61ed0..f37c28c 100644
--- a/data/resolvers/content.ts
+++ b/data/resolvers/content.ts
@@ -142,7 +142,7 @@ const Content = {
{ username, name }: QueryFetchContentNeighborsArgs,
{ currentUsername, prisma }: Context
) {
- const ATTRIBUTES_NAVIGATION_WITH_VIEW = Object.assign({ view: true }, ATTRIBUTES_NAVIGATION);
+ const ATTRIBUTES_NAVIGATION_WITH_VIEW = Object.assign({ content: true, view: true }, ATTRIBUTES_NAVIGATION);
const content = (await prisma.content.findUnique({
where: { username_name: { username: username || '', name: name || '' } },
})) as ContentType | null;
@@ -225,7 +225,10 @@ const Content = {
}
// For links section, we grab the anchor url in the view.
- const select = section === 'links' ? Object.assign({ view: true }, ATTRIBUTES_NAVIGATION) : ATTRIBUTES_NAVIGATION;
+ const select =
+ section === 'links' || section === 'photos'
+ ? Object.assign({ content: true, view: true }, ATTRIBUTES_NAVIGATION)
+ : ATTRIBUTES_NAVIGATION;
let collection: ContentType[] = [];
// Check first to see if this is an album and grab items within it.
@@ -293,7 +296,7 @@ const Content = {
});
}
- return decorateArrayWithRefreshFlag(collection);
+ return decorateArrayWithPrefetchImages(decorateArrayWithRefreshFlag(collection));
},
async fetchCollectionPaginated(
@@ -560,9 +563,19 @@ function decorateWithRefreshFlag(item: ContentType) {
return item;
}
+function decorateArrayWithPrefetchImages(list: ContentType[]) {
+ for (const item of list) {
+ decoratePrefetchImages(item);
+ }
+
+ return list;
+}
+
function decoratePrefetchImages(item: ContentType) {
if (item) {
- item.prefetchImages = (item.view.match(/src=['"][^'"]+['"]/g) || []).map((i) => i.slice(5, -1));
+ item.prefetchImages = (item.view.match(/src=['"][^'"]+['"]/g) || [])
+ .map((i) => i.slice(5, -1))
+ .concat((item.content.match(/https[^)]*/g) || []).map((i) => i.slice(5, -1)));
}
return item;
diff --git a/next.config.js b/next.config.js
index 4a4cc92..b1495f0 100644
--- a/next.config.js
+++ b/next.config.js
@@ -21,7 +21,9 @@ const nextConfig = {
reactStrictMode: true,
images: {
- domains: ['i.creativecommons.org', 's3.amazonaws.com', process.env.S3_AWS_S3_BUCKET_NAME].filter((i) => !!i),
+ domains: ['i.creativecommons.org', 's3.amazonaws.com', process.env.S3_AWS_S3_BUCKET_NAME, 'i.ytimg.com'].filter(
+ (i) => !!i
+ ),
minimumCacheTTL: 60,
},
diff --git a/pages/_app.tsx b/pages/_app.tsx
index afea19e..8975778 100644
--- a/pages/_app.tsx
+++ b/pages/_app.tsx
@@ -134,7 +134,7 @@ function MyApp({ Component, emotionCache = clientSideEmotionCache, pageProps }:
>
-
+
diff --git a/util/constants.ts b/util/constants.ts
index cf648ff..f430847 100644
--- a/util/constants.ts
+++ b/util/constants.ts
@@ -46,4 +46,4 @@ export const WEB_SUB_HUB = 'https://pubsubhubbub.appspot.com/';
export const MAX_FILE_SIZE = 1024 * 1024 * 10; // up to 10 MB
export const THUMB_WIDTH = 154;
-export const THUMB_HEIGHT = 115;
+export const THUMB_HEIGHT = 154;
diff --git a/util/url-factory.ts b/util/url-factory.ts
index f950299..9aa93eb 100644
--- a/util/url-factory.ts
+++ b/util/url-factory.ts
@@ -100,7 +100,8 @@ export function ensureAbsoluteUrl(basisAbsoluteUrl: string, urlOrPath: string) {
export function constructNextImageURL(src: string, size = 3840) {
if (
!process.env.IS_STORYBOOK &&
- (src.startsWith(`https://${process.env.S3_AWS_S3_BUCKET_NAME}`) || src.startsWith(`https://s3.amazonaws.com`))
+ (src.startsWith(`https://${process.env.NEXT_PUBLIC_S3_AWS_S3_BUCKET_NAME}`) ||
+ src.startsWith(`https://s3.amazonaws.com`))
) {
return `/_next/image?url=${encodeURIComponent(src)}&w=${size}&q=75`;
}