Skip to content
Open
Show file tree
Hide file tree
Changes from 1 commit
Commits
File filter

Filter by extension

Filter by extension

Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
4 changes: 4 additions & 0 deletions next.config.js
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,10 @@ const nextConfig = {
's3.amazonaws.com',
// Facebook OAuth user avatars
'platform-lookaside.fbsbx.com',
// Gravatar default avatars
'www.gravatar.com',
// Sanity CMS hosted assets
'cdn.sanity.io',
],
},
webpack: (webpackConfig) => {
Expand Down
13 changes: 11 additions & 2 deletions src/components/Sanity/Blocks/index.tsx
Original file line number Diff line number Diff line change
@@ -1,4 +1,5 @@
import classNames from 'classnames';
import Image from 'next/image';

import styles from './Blocks.module.scss';

Expand Down Expand Up @@ -33,13 +34,21 @@ const PageBlocks: React.FC<Props> = ({ page }) => {
const { _type: childElementType, _key: childElementKey, text } = childElement;
if (childElementType === 'inlineImage') {
const { asset, _key: imageKey } = childElement;
const width = asset?.metadata?.dimensions?.width ?? 800;
const height = asset?.metadata?.dimensions?.height ?? 600;
elementBlocks.push(
<div
key={`${bodyElementKey}-${childElementKey}-${imageKey}`}
className={styles.imageContainer}
>
{/* eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text */}
<img className={styles.image} src={getImageUrl(asset)} />
<Image
className={styles.image}
src={getImageUrl(asset)}
alt=""
width={width}
height={height}
sizes="(max-width: 768px) 100vw, 800px"
/>
Comment on lines 33 to 55

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Skip inline Sanity <Image> when no URL is available

Here getImageUrl(asset) is passed directly to next/image. When Sanity is not configured, getImageUrl returns ''; unlike the previous <img> tag, next/image will throw because the src is empty. Any page containing inline images will fail to render in setups without Sanity credentials. A simple conditional render or fallback placeholder avoids the crash.

Useful? React with 👍 / 👎.

</div>,
);
return;
Expand Down
11 changes: 9 additions & 2 deletions src/components/Sanity/Page/index.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
import React from 'react';

import classNames from 'classnames';
import Image from 'next/image';
import useTranslation from 'next-translate/useTranslation';

import PageBlocks from '../Blocks';
Expand Down Expand Up @@ -57,8 +58,14 @@ const Page: React.FC<Props> = ({ page, isIndividualPage = false }) => {
{page.summary}
{page.mainPhoto && (
<div className={styles.imageContainer}>
{/* eslint-disable-next-line @next/next/no-img-element, jsx-a11y/alt-text */}
<img className={styles.image} src={getImageUrl(page.mainPhoto)} />
<Image
className={styles.image}
src={getImageUrl(page.mainPhoto)}
alt=""
width={page.mainPhoto?.metadata?.dimensions?.width ?? 800}
height={page.mainPhoto?.metadata?.dimensions?.height ?? 600}
sizes="(max-width: 768px) 100vw, 800px"
Comment on lines 59 to 68

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

P1 Badge Guard Page hero <Image> when Sanity assets are disabled

getImageUrl(page.mainPhoto) returns an empty string when NEXT_PUBLIC_SANITY_PROJECT_ID is not set. <img> tags tolerated that and simply rendered nothing, but next/image will throw at runtime when the src is empty or points to an unconfigured host, causing the whole page to crash in local/dev environments where Sanity is off. Consider skipping the <Image> when getImageUrl returns a falsy value or providing a placeholder.

Useful? React with 👍 / 👎.

/>
</div>
)}
</>
Expand Down
21 changes: 10 additions & 11 deletions src/pages/_app.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -4,6 +4,7 @@ import { DirectionProvider } from '@radix-ui/react-direction';
import { TooltipProvider } from '@radix-ui/react-tooltip';
import Head from 'next/head';
import { useRouter } from 'next/router';
import Script from 'next/script';

import AppContent from '@/components/AppContent/AppContent';
import FontPreLoader from '@/components/Fonts/FontPreLoader';
Expand Down Expand Up @@ -32,6 +33,12 @@ function MyApp({ Component, pageProps }: { Component: any; pageProps: any }) {
const { locale } = router;
const resolvedLocale = locale ?? 'en';
const languageDirection = getDir(resolvedLocale);
const buildInfo = {
date: process.env.NEXT_PUBLIC_BUILD_DATE || new Date().toISOString(),
hash: process.env.NEXT_PUBLIC_COMMIT_HASH || 'development',
version: process.env.NEXT_PUBLIC_APP_VERSION || '',
env: process.env.NEXT_PUBLIC_APP_ENV,
};

useEffect(() => {
document.documentElement.dir = languageDirection;
Expand All @@ -50,18 +57,10 @@ function MyApp({ Component, pageProps }: { Component: any; pageProps: any }) {
<link rel="apple-touch-icon" sizes="192x192" href="/images/logo/[email protected]" />
<link rel="manifest" href="/manifest.json" />
<link rel="preconnect" href={API_HOST} />
<script
// eslint-disable-next-line react/no-danger
dangerouslySetInnerHTML={{
__html: `window.__BUILD_INFO__ = {
date: "${process.env.NEXT_PUBLIC_BUILD_DATE || new Date().toISOString()}",
hash: "${process.env.NEXT_PUBLIC_COMMIT_HASH || 'development'}",
version: "${process.env.NEXT_PUBLIC_APP_VERSION || ''}",
env: "${process.env.NEXT_PUBLIC_APP_ENV}"
}`,
}}
/>
</Head>
<Script id="build-info" strategy="beforeInteractive">
{`window.__BUILD_INFO__ = ${JSON.stringify(buildInfo)};`}
</Script>
<FontPreLoader locale={resolvedLocale} />
<DirectionProvider dir={languageDirection}>
<TooltipProvider>
Expand Down
14 changes: 10 additions & 4 deletions src/pages/profile.module.scss
Original file line number Diff line number Diff line change
@@ -1,8 +1,8 @@
@use "src/styles/utility";
@use 'src/styles/utility';

.container {
margin-block-start: var(--spacing-medium);
min-height: 80vh;
min-block-size: 80vh;
}

.profileContainer {
Expand All @@ -13,10 +13,16 @@

.profilePicture {
--profile-picture-size: calc(5 * var(--spacing-medium));
width: var(--profile-picture-size);
height: var(--profile-picture-size);
inline-size: var(--profile-picture-size);
block-size: var(--profile-picture-size);
background-color: var(--shade-4);
border-radius: var(--border-radius-circle);
position: relative;
overflow: hidden;
}

.profilePictureImage {
object-fit: cover;
}

.profileInfoContainer {
Expand Down
11 changes: 9 additions & 2 deletions src/pages/profile.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,7 @@
/* eslint-disable max-lines */
import classNames from 'classnames';
import { NextPage, GetStaticProps } from 'next';
import Image from 'next/image';
import useTranslation from 'next-translate/useTranslation';

import layoutStyle from './index.module.scss';
Expand Down Expand Up @@ -74,8 +75,14 @@ const ProfilePage: NextPage<Props> = () => {
<div className={classNames(layoutStyle.flowItem)}>
<div className={styles.profileContainer}>
<div className={styles.profilePicture}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img className={styles.profilePicture} alt="avatar" src={photoUrl} />
<Image
src={photoUrl}
alt="avatar"
fill
sizes="(max-width: 768px) 120px, 160px"
className={styles.profilePictureImage}
priority
/>
</div>
{isLoading ? (
profileSkeletonInfoSkeleton
Expand Down
17 changes: 11 additions & 6 deletions src/pages/reciters/[reciterId]/[chapterId].tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { useSelector } from '@xstate/react';
import classNames from 'classnames';
import clipboardCopy from 'clipboard-copy';
import { GetStaticPaths, GetStaticProps } from 'next';
import Image from 'next/image';
import { useRouter } from 'next/router';
import useTranslation from 'next-translate/useTranslation';

Expand Down Expand Up @@ -114,12 +115,16 @@ const RecitationPage = ({ selectedReciter, selectedChapter }: ShareRecitationPag
/>
<div className={classNames(layoutStyle.flow)}>
<div className={classNames(layoutStyle.flowItem, styles.container)}>
{/* eslint-disable-next-line @next/next/no-img-element */}
<img
className={styles.reciterImage}
alt={selectedReciter.translatedName.name}
src={makeCDNUrl(selectedReciter.profilePicture)}
/>
<div className={styles.reciterImage}>
<Image
src={makeCDNUrl(selectedReciter.profilePicture)}
alt={selectedReciter.translatedName.name}
fill
sizes="(max-width: 768px) 80vw, 320px"
className={styles.reciterImageContent}
priority
/>
</div>
<div>
<div className={styles.chapterName}>
{/* eslint-disable-next-line i18next/no-literal-string */}
Expand Down
17 changes: 11 additions & 6 deletions src/pages/reciters/[reciterId]/chapterId.module.scss
Original file line number Diff line number Diff line change
@@ -1,4 +1,4 @@
@use "src/styles/breakpoints";
@use 'src/styles/breakpoints';

.container {
display: flex;
Expand All @@ -8,20 +8,25 @@
}

.reciterImage {
width: 80%;
max-width: calc(20 * var(--spacing-medium));
border-radius: var(--border-radius-circle);
inline-size: 80%;
max-inline-size: calc(20 * var(--spacing-medium));
margin-block-end: var(--spacing-medium);
margin-inline: auto;
aspect-ratio: 1/1;
border-radius: var(--border-radius-circle);
position: relative;
overflow: hidden;
}

.reciterImageContent {
object-fit: cover;
}

.actionsContainer {
display: flex;
flex-direction: column;
width: 80%;
max-width: calc(30 * var(--spacing-medium));
inline-size: 80%;
max-inline-size: calc(30 * var(--spacing-medium));
}

.playButton {
Expand Down