Skip to content
Merged
Show file tree
Hide file tree
Changes from all commits
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
2 changes: 1 addition & 1 deletion cypress/e2e/home.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('Home page', () => {
it('should render page header', () => {
cy.get('[data-testid=websiteTile]')
.should('exist')
.contains('Mobility Database');
.contains('MobilityDatabase');
});

it('should render home page title', () => {
Expand Down
2 changes: 1 addition & 1 deletion cypress/e2e/signin.cy.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,7 +6,7 @@ describe('Sign In page', () => {
it('should render page header', () => {
cy.get('[data-testid=websiteTile]')
.should('exist')
.contains('Mobility Database');
.contains('MobilityDatabase');
});

it('should render signin', () => {
Expand Down
1 change: 1 addition & 0 deletions package.json
Original file line number Diff line number Diff line change
Expand Up @@ -51,6 +51,7 @@
"redux-persist": "^6.0.0",
"redux-saga": "^1.2.3",
"server-only": "^0.0.1",
"swr": "^2.4.0",
"yup": "^1.3.2"
},
"scripts": {
Expand Down
5 changes: 4 additions & 1 deletion src/app/App.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -41,7 +41,7 @@ function App({ locale }: AppProps): React.ReactElement {
const initialPath = buildPathFromNextRouter(pathname, searchParams, locale);

useEffect(() => {
app.auth().onAuthStateChanged((user) => {
const unsubscribe = app.auth().onAuthStateChanged((user) => {
if (user != null) {
setIsAppReady(true);
} else {
Expand All @@ -50,6 +50,9 @@ function App({ locale }: AppProps): React.ReactElement {
}
});
dispatch(anonymousLogin());
return () => {
unsubscribe();
};
}, [dispatch]);

return (
Expand Down
15 changes: 6 additions & 9 deletions src/app/[locale]/about/components/AboutPage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ import { Container, Typography, Button } from '@mui/material';
import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import { type ReactElement } from 'react';
import { getTranslations } from 'next-intl/server';
import Link from 'next/link';

export default async function AboutPage(): Promise<ReactElement> {
const t = await getTranslations('about');
Expand Down Expand Up @@ -72,15 +73,11 @@ export default async function AboutPage(): Promise<ReactElement> {
<li>{t('benefits.mirrored')}</li>
<li>{t('benefits.boundingBoxes')}</li>
<li>
<Button
variant='text'
className='line-start inline'
href='/contribute'
rel='noreferrer'
target='_blank'
>
{t('benefits.addFeeds')}
</Button>
<Link href='/contribute' rel='noreferrer' target='_blank'>
<Button variant='text' className='line-start inline'>
{t('benefits.addFeeds')}
</Button>
</Link>
</li>
<li>{t('benefits.openSource')}</li>
</ul>
Expand Down
9 changes: 6 additions & 3 deletions src/app/[locale]/components/HomePage.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import OpenInNewIcon from '@mui/icons-material/OpenInNew';
import SearchBox from './SearchBox';
import { getTranslations } from 'next-intl/server';
import '../../styles/TextShimmer.css';
import Link from 'next/link';

interface ActionBoxProps {
IconComponent: React.ElementType;
Expand All @@ -35,9 +36,11 @@ const ActionBox = ({
}}
>
<IconComponent sx={{ width: '100%', height: iconHeight }} />
<Button variant='contained' href={buttonHref} sx={{ m: 2, px: 2 }}>
{buttonText}
</Button>
<Link href={buttonHref}>
<Button variant='contained' sx={{ m: 2, px: 2 }}>
{buttonText}
</Button>
</Link>
</Box>
);

Expand Down
26 changes: 26 additions & 0 deletions src/app/[locale]/contact-us/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type ReactElement } from 'react';
import { setRequestLocale } from 'next-intl/server';
import { type Locale, routing } from '../../../i18n/routing';
import ContactUs from '../../screens/ContactUs';

export const dynamic = 'force-static';

export function generateStaticParams(): Array<{
locale: Locale;
}> {
return routing.locales.map((locale) => ({ locale }));
}

interface PageProps {
params: Promise<{ locale: string }>;
}

export default async function ContactUsPage({
params,
}: PageProps): Promise<ReactElement> {
const { locale } = await params;

setRequestLocale(locale);

return <ContactUs />;
}
26 changes: 26 additions & 0 deletions src/app/[locale]/contribute-faq/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type ReactElement } from 'react';
import { setRequestLocale } from 'next-intl/server';
import { type Locale, routing } from '../../../i18n/routing';
import FeedSubmissionFAQ from '../../screens/FeedSubmissionFAQ';

export const dynamic = 'force-static';

export function generateStaticParams(): Array<{
locale: Locale;
}> {
return routing.locales.map((locale) => ({ locale }));
}

interface PageProps {
params: Promise<{ locale: string }>;
}

export default async function ContributeFAQPage({
params,
}: PageProps): Promise<ReactElement> {
const { locale } = await params;

setRequestLocale(locale);

return <FeedSubmissionFAQ />;
}
26 changes: 26 additions & 0 deletions src/app/[locale]/faq/page.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import { type ReactElement } from 'react';
import { setRequestLocale } from 'next-intl/server';
import { type Locale, routing } from '../../../i18n/routing';
import FAQ from '../../screens/FAQ';

export const dynamic = 'force-static';

export function generateStaticParams(): Array<{
locale: Locale;
}> {
return routing.locales.map((locale) => ({ locale }));
}

interface PageProps {
params: Promise<{ locale: string }>;
}

export default async function FAQPage({
params,
}: PageProps): Promise<ReactElement> {
const { locale } = await params;

setRequestLocale(locale);

return <FAQ />;
}
13 changes: 0 additions & 13 deletions src/app/[locale]/feeds/[feedDataType]/[feedId]/authed/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,7 +1,6 @@
import { type ReactNode } from 'react';
import { notFound } from 'next/navigation';
import { headers } from 'next/headers';
import { fetchCompleteFeedData } from '../lib/feed-data';
import { AUTHED_PROXY_HEADER } from '../../../../../utils/proxy-helpers';

/**
Expand All @@ -12,7 +11,6 @@ export const dynamic = 'force-dynamic';

interface Props {
children: ReactNode;
params: Promise<{ feedDataType: string; feedId: string }>;
}

/**
Expand All @@ -28,23 +26,12 @@ interface Props {
*/
export default async function AuthedFeedLayout({
children,
params,
}: Props): Promise<React.ReactElement> {
// Block direct access - only allow requests that came through the proxy
const headersList = await headers();
if (headersList.get(AUTHED_PROXY_HEADER) !== '1') {
notFound();
}

const { feedId, feedDataType } = await params;

// Fetch complete feed data (cached per-user)
// This will be reused by child pages without additional API calls
const feedData = await fetchCompleteFeedData(feedDataType, feedId);

if (feedData == null) {
notFound();
}

return <>{children}</>;
}
110 changes: 110 additions & 0 deletions src/app/[locale]/feeds/[feedDataType]/[feedId]/authed/loading.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,110 @@
import { Box, Container, Skeleton } from '@mui/material';

/**
* Loading page streams content as it becomes available, providing a faster time-to-interactive and better user experience.
* NextJs automatically automatically detects user agents to choose between blocking and streaming behavior.
* Since streaming is server-rendered, it does not impact SEO
* ref: https://nextjs.org/docs/app/api-reference/file-conventions/loading
*/

export default function Loading(): React.ReactElement {
return (
<Container
component='main'
maxWidth='xl'
sx={{ my: 4, bgcolor: 'background.paper', py: 2, borderRadius: 2, px: 1 }}
>
<Box>
{/* Breadcrumb skeleton */}
<Skeleton
animation='wave'
variant='text'
sx={{ fontSize: '1rem', width: '200px', mb: 2 }}
/>

{/* Feed title skeleton */}
<Skeleton
animation='wave'
variant='text'
sx={{ fontSize: '3rem', width: { xs: '100%', sm: '500px' }, mb: 1 }}
/>

{/* Provider info skeleton */}
<Skeleton
animation='wave'
variant='text'
sx={{ fontSize: '1.2rem', width: '300px', mb: 1 }}
/>

{/* Status chip skeleton */}
<Skeleton
animation='wave'
variant='rounded'
height={30}
width={100}
sx={{ mb: 3 }}
/>

{/* Divider line */}
<Box
sx={{
background: 'rgba(0,0,0,0.2)',
height: '1px',
width: '100%',
mb: 3,
mt: 2,
}}
/>

{/* Action buttons skeleton */}
<Box sx={{ display: 'flex', gap: 2, mb: 4 }}>
<Skeleton
animation='wave'
variant='rectangular'
width={162}
height={40}
/>
<Skeleton
animation='wave'
variant='rectangular'
width={162}
height={40}
/>
</Box>

{/* Main content area skeleton */}
<Box
sx={{
mt: 2,
display: 'flex',
justifyContent: 'space-between',
gap: 2,
flexWrap: { xs: 'wrap', sm: 'nowrap' },
}}
>
{/* Left panel skeleton */}
<Skeleton
animation='wave'
variant='rectangular'
sx={{
width: { xs: '100%', sm: '50%' },
height: '630px',
borderRadius: 1,
}}
/>

{/* Right panel skeleton */}
<Skeleton
animation='wave'
variant='rectangular'
sx={{
width: { xs: '100%', sm: '50%' },
height: '630px',
borderRadius: 1,
}}
/>
</Box>
</Box>
</Container>
);
}
Original file line number Diff line number Diff line change
@@ -1,5 +1,6 @@
import FullMapView from '../../../../../../screens/Feed/components/FullMapView';
import { type ReactElement } from 'react';
import { notFound } from 'next/navigation';
import { fetchCompleteFeedData } from '../../lib/feed-data';

interface Props {
Expand Down Expand Up @@ -31,7 +32,7 @@ export default async function AuthedFullMapViewPage({
const feedData = await fetchCompleteFeedData(feedDataType, feedId);

if (feedData == null) {
return <div>Feed not found</div>;
notFound();
}

return <FullMapView feedData={feedData} />;
Expand Down
15 changes: 0 additions & 15 deletions src/app/[locale]/feeds/[feedDataType]/[feedId]/static/layout.tsx
Original file line number Diff line number Diff line change
@@ -1,6 +1,4 @@
import { type ReactNode } from 'react';
import { notFound } from 'next/navigation';
import { fetchGuestFeedData } from '../lib/guest-feed-data';

/**
* ISR caching: revalidate cached HTML every 14 days.
Expand Down Expand Up @@ -31,18 +29,5 @@ export default async function StaticFeedLayout({
children,
params,
}: Props): Promise<React.ReactElement> {
const { feedId, feedDataType } = await params;

let feedData;
try {
feedData = await fetchGuestFeedData(feedDataType, feedId);
} catch {
notFound();
}

if (feedData == null) {
notFound();
}

return <>{children}</>;
}
Loading