Skip to content

Commit c3a8041

Browse files
committed
Upgraded the whole project to use the latest version of NextJS
1 parent 272902b commit c3a8041

22 files changed

+4108
-4542
lines changed

next-env.d.ts

+1-1
Original file line numberDiff line numberDiff line change
@@ -2,4 +2,4 @@
22
/// <reference types="next/image-types/global" />
33

44
// NOTE: This file should not be edited
5-
// see https://nextjs.org/docs/basic-features/typescript for more information.
5+
// see https://nextjs.org/docs/app/api-reference/config/typescript for more information.

next.config.js

+2-13
Original file line numberDiff line numberDiff line change
@@ -10,28 +10,17 @@ module.exports = withSentryConfig({
1010

1111
return config;
1212
},
13-
},
14-
15-
// Injected content via Sentry wizard below
16-
{
13+
}, {
1714
// For all available options, see:
18-
// https://github.com/getsentry/sentry-webpack-plugin#options
15+
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
1916

20-
// Suppresses source map uploading logs during build
2117
silent: true,
2218
org: process.env.NEXT_PUBLIC_SENTRY_ORG,
2319
project: process.env.NEXT_PUBLIC_SENTRY_PROJECT,
24-
},
25-
{
26-
// For all available options, see:
27-
// https://docs.sentry.io/platforms/javascript/guides/nextjs/manual-setup/
2820

2921
// Upload a larger set of source maps for prettier stack traces (increases build time)
3022
widenClientFileUpload: true,
3123

32-
// Transpiles SDK to be compatible with IE11 (increases bundle size)
33-
transpileClientSDK: true,
34-
3524
// Routes browser requests to Sentry through a Next.js rewrite to circumvent ad-blockers (increases server load)
3625
tunnelRoute: '/monitoring',
3726

package.json

+21-20
Original file line numberDiff line numberDiff line change
@@ -12,31 +12,32 @@
1212
"emulate": "(yarn --cwd functions build -- --watch) | (firebase emulators:start --import=./test_data --export-on-exit)"
1313
},
1414
"dependencies": {
15-
"@emotion/react": "^11.11.1",
16-
"@emotion/styled": "^11.11.0",
17-
"@hookform/resolvers": "^3.3.2",
18-
"@mui/icons-material": "^5.14.18",
19-
"@mui/material": "^5.14.18",
20-
"@sentry/nextjs": "^7.81.0",
15+
"@emotion/react": "^11.14.0",
16+
"@emotion/styled": "^11.14.0",
17+
"@hookform/resolvers": "^3.10.0",
18+
"@mui/icons-material": "^6.4.0",
19+
"@mui/material": "^6.4.0",
20+
"@mui/material-nextjs": "^6.3.1",
21+
"@sentry/nextjs": "^8.50.0",
2122
"@svgr/webpack": "^8.1.0",
22-
"firebase": "^10.6.0",
23-
"firebase-admin": "^11.11.0",
23+
"firebase": "^11.2.0",
24+
"firebase-admin": "^13.0.2",
2425
"firebaseui": "^6.1.0",
2526
"models": "file:functions/models",
26-
"next": "^14.0.3",
27-
"next-firebase-auth-edge": "^0.10.2",
28-
"react": "^18",
29-
"react-dom": "^18",
30-
"react-hook-form": "^7.48.2",
27+
"next": "^15.1.5",
28+
"next-firebase-auth-edge": "^1.8.2",
29+
"react": "^19.0.0",
30+
"react-dom": "^19.0.0",
31+
"react-hook-form": "^7.54.2",
3132
"use-async-effect": "^2.2.7",
32-
"zod": "^3.22.4"
33+
"zod": "^3.24.1"
3334
},
3435
"devDependencies": {
35-
"@types/node": "^20.9.3",
36-
"@types/react": "^18.2.38",
37-
"@types/react-dom": "^18.2.16",
38-
"eslint": "^8.54.0",
39-
"eslint-config-next": "^14.0.3",
40-
"typescript": "^5.3.2"
36+
"@types/node": "^22.10.7",
37+
"@types/react": "^19.0.7",
38+
"@types/react-dom": "^19.0.3",
39+
"eslint": "^9.18.0",
40+
"eslint-config-next": "^15.1.5",
41+
"typescript": "^5.7.3"
4142
}
4243
}

sentry.client.config.ts

+19-4
Original file line numberDiff line numberDiff line change
@@ -7,17 +7,32 @@ import * as Sentry from "@sentry/nextjs";
77
Sentry.init({
88
dsn: process.env.NEXT_PUBLIC_SENTRY_DSN,
99
debug: false,
10+
enabled: process.env.NODE_ENV !== 'development',
11+
12+
ignoreErrors: [
13+
'ResizeObserver loop limit exceeded', // Chrome ResizeObserver
14+
'ResizeObserver loop completed with undelivered notifications', // Firefox ResizeObserver
15+
'ResizeObserver loop limit exceeded', // Safari ResizeObserver
16+
'auth/too-many-requests', // Firebase rate limiting
17+
'Failed to get document because the client is offline', // Firebase offline
18+
'FirebaseError: Missing or insufficient permissions.', // Firebase permissions
19+
'INTERNAL ASSERTION FAILED: Pending promise was never set', // Firebase/auth assertion
20+
'TypeError: Failed to fetch', // Firebase installations
21+
'Loading CSS chunk', // Next.js chunk loading
22+
'TypeError: Load failed', // Next.js chunk loading
23+
"Unexpected token '<'", // HTML parsing errors
24+
"expected expression, got '<'", // HTML parsing errors
25+
'Minified React error #', // React errors
26+
],
1027

1128
tracesSampleRate: 0.01,
1229
replaysOnErrorSampleRate: 1.0,
1330
replaysSessionSampleRate: 0.005,
1431

1532
integrations: [
16-
new Sentry.BrowserTracing(),
17-
new Sentry.Replay({
18-
// Additional Replay configuration goes in here, for example:
33+
Sentry.browserTracingIntegration(),
34+
Sentry.replayIntegration({
1935
maskAllText: false,
20-
blockAllMedia: true,
2136
}),
2237
],
2338
});

sentry.edge.config.ts

-12
This file was deleted.

sentry.server.config.ts

-11
This file was deleted.

src/app/layout.tsx

+5-10
Original file line numberDiff line numberDiff line change
@@ -2,9 +2,7 @@ import {ReactNode} from "react";
22
import {Metadata} from "next";
33
import {cookies} from 'next/headers';
44
import Script from "next/script";
5-
import CssBaseline from "@mui/material/CssBaseline";
65

7-
import EmotionRootStyleRegistry from '@/theme/EmotionRootStyleRegistry';
86
import ThemeProvider from "@/theme/ThemeProvider";
97
import ErrorBoundary from "@/components/ErrorBoundary";
108
import AppLayout from "@/components/home/AppLayout";
@@ -37,8 +35,8 @@ export async function generateMetadata(): Promise<Metadata> {
3735
export default async function RootLayout({children}: {
3836
children: ReactNode,
3937
}) {
40-
const defaultUser = await getUser(cookies());
41-
console.log('layout default user:', defaultUser?.id);
38+
const defaultUser = await getUser(await cookies());
39+
console.log(`layout default user: ${defaultUser?.id} with ${defaultUser?.signInProvider} provider`);
4240

4341
return <>
4442
<html lang="en">
@@ -53,18 +51,15 @@ export default async function RootLayout({children}: {
5351
</head>
5452

5553
<body>
56-
<EmotionRootStyleRegistry>
5754
<ThemeProvider>
5855
<ErrorBoundary>
5956
<AuthProvider defaultUser={defaultUser}>
60-
<CssBaseline/>
61-
<AppLayout>
62-
{children}
63-
</AppLayout>
57+
<AppLayout>
58+
{children}
59+
</AppLayout>
6460
</AuthProvider>
6561
</ErrorBoundary>
6662
</ThemeProvider>
67-
</EmotionRootStyleRegistry>
6863
</body>
6964
</html>
7065
</>

src/app/users/[userId]/page.tsx

+6-3
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,13 @@
11
import UserPage from '@/components/user/UserPage';
22
import {getUser} from "@/server/users";
33

4-
export default async function Page({params: {userId}}: {
5-
params: { userId: string}
6-
}) {
4+
const Page = async ({params}: {
5+
params: Promise<{ userId: string }>,
6+
}) => {
7+
const {userId} = await params;
78
const user = await getUser(userId);
89
console.log('Server user:', user);
910
return <UserPage initialUser={user} />
1011
}
12+
13+
export default Page;

src/auth/AuthContext.tsx

+1-1
Original file line numberDiff line numberDiff line change
@@ -1,5 +1,5 @@
11
import {createContext} from "react";
2-
import {ParsedToken} from "@firebase/auth";
2+
import type {ParsedToken} from "@firebase/auth";
33

44
export type AuthUser = {
55
id: string | null;

src/auth/AuthProvider.tsx

+21-18
Original file line numberDiff line numberDiff line change
@@ -1,8 +1,8 @@
11
'use client';
22

3-
import {ReactNode, useCallback, useEffect, useState} from "react";
3+
import {ReactNode, useCallback, useState} from "react";
44
import {AuthContext, AuthUser} from "./AuthContext";
5-
import {EmailAuthProvider, getAuth, onIdTokenChanged, User as FirebaseUser} from "firebase/auth";
5+
import type {User as FirebaseUser} from "firebase/auth";
66
import {updateUserInfo} from "@/services/users";
77
import {getAnalytics, setUserId} from "firebase/analytics";
88
import {useRouter} from "next/navigation";
@@ -14,21 +14,25 @@ function AuthProvider({defaultUser, children}: { defaultUser: AuthUser | null, c
1414
const [user, setUser] = useState<AuthUser | null>(defaultUser);
1515

1616
const handleIdTokenChanged = useCallback(async (firebaseUser: FirebaseUser | null) => {
17+
const providerData = firebaseUser?.providerData && firebaseUser.providerData[0];
1718
console.log('token changed:', firebaseUser);
19+
console.log('providerData:', providerData);
1820
if (firebaseUser && firebaseUser.uid === user?.id && firebaseUser.emailVerified === user?.emailVerified)
19-
return;
21+
return setUser(user => {
22+
if (!user) return null;
23+
// Set sign in provider as `next-firebase-auth-edge` doesn't load it from cookies
24+
return {...user, signInProvider: providerData?.providerId || null};
25+
});
2026

2127
// No user => log out the current one or do nothing if there is no current user
2228
if (!firebaseUser) {
23-
return setUser(currentUser => {
24-
if (!currentUser)
25-
return null;
26-
console.log('logging out...');
27-
fetch('/api/logout', {method: 'GET'})
28-
.then(() => console.log('logged out'))
29-
.then(() => router.refresh());
29+
console.log('No firebase user... Logging out...');
30+
if (!user)
3031
return null;
31-
});
32+
console.log('logging out...');
33+
setUser(null);
34+
await fetch('/api/logout', {method: 'GET'});
35+
return router.refresh();
3236
}
3337

3438
console.log(`Setting the user... ${user?.id} -> ${firebaseUser.uid}`);
@@ -38,14 +42,13 @@ function AuthProvider({defaultUser, children}: { defaultUser: AuthUser | null, c
3842
console.log(`login: ${login.status} ${login.statusText} ${await login.text()}`);
3943
if (login.status !== 200) {
4044
console.error('Failed to log in... Logging out instead');
45+
const {getAuth} = await import('firebase/auth');
4146
await getAuth().signOut();
4247
await fetch('/api/logout', {method: 'GET'});
4348
return router.refresh();
4449
}
4550

4651
// Update the current user
47-
const providerData = firebaseUser.providerData && firebaseUser.providerData[0];
48-
console.log('providerData:', providerData);
4952
setUser({
5053
id: firebaseUser.uid,
5154
displayName: firebaseUser.displayName || providerData?.displayName || firebaseUser.email || null,
@@ -59,13 +62,14 @@ function AuthProvider({defaultUser, children}: { defaultUser: AuthUser | null, c
5962

6063
console.log('Updating the page...');
6164
router.refresh();
62-
}, [router, user?.emailVerified, user?.id]);
65+
}, [router, user?.emailVerified, user?.id, setUser]);
6366

6467

65-
useEffect(() => {
68+
useAsyncEffect(async () => {
69+
const {getAuth, onIdTokenChanged} = await import('firebase/auth');
6670
const auth = getAuth();
6771
return onIdTokenChanged(auth, handleIdTokenChanged);
68-
}, [handleIdTokenChanged]);
72+
}, unsubscribe => unsubscribe && unsubscribe(), [handleIdTokenChanged]);
6973

7074

7175
useAsyncEffect(async () => {
@@ -76,8 +80,7 @@ function AuthProvider({defaultUser, children}: { defaultUser: AuthUser | null, c
7680
}, [user?.id, user?.displayName, user?.photoURL]);
7781

7882
const isVerified = Boolean(
79-
user && user.signInProvider &&
80-
(user.emailVerified || (user.signInProvider !== EmailAuthProvider.PROVIDER_ID && user.signInProvider !== 'custom'))
83+
user && user.signInProvider && (user.emailVerified || user.signInProvider !== 'password')
8184
);
8285
// console.log('isVerified:', isVerified, user?.emailVerified, user?.signInProvider);
8386

src/components/user/FirebaseAuth.tsx src/auth/FirebaseAuth.tsx

+20-15
Original file line numberDiff line numberDiff line change
@@ -1,28 +1,26 @@
11
import {useEffect, useRef, useState} from 'react';
22
import CircularProgress from "@mui/material/CircularProgress";
33
import useAsyncEffect from "use-async-effect";
4-
import {EmailAuthProvider, onAuthStateChanged, sendEmailVerification} from 'firebase/auth';
4+
import {EmailAuthProvider, getAuth, onAuthStateChanged, sendEmailVerification} from 'firebase/auth';
55
import 'firebaseui/dist/firebaseui.css';
6-
import {auth} from "firebaseui";
76
import Typography from "@mui/material/Typography";
87
import Box from "@mui/material/Box";
98
import Divider from "@mui/material/Divider";
9+
import {app} from "@/firebase";
10+
import type {auth} from "firebaseui";
1011

11-
interface Props {
12+
13+
const FirebaseAuth = ({uiConfig, className, uiCallback}: {
1214
// The Firebase UI Web UI Config object.
1315
// See: https://github.com/firebase/firebaseui-web#configuration
14-
uiConfig: auth.Config;
16+
uiConfig: auth.Config,
1517
// Callback that will be passed the FirebaseUi instance before it is
1618
// started. This allows access to certain configuration options such as
1719
// disableAutoSignIn().
18-
uiCallback?(ui: auth.AuthUI): void;
20+
uiCallback?(ui: auth.AuthUI): void,
1921
// The Firebase App auth instance to use.
20-
firebaseAuth: any; // As firebaseui-web
21-
className?: string;
22-
}
23-
24-
25-
const FirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) => {
22+
className?: string,
23+
}) => {
2624
const [firebaseui, setFirebaseui] = useState<typeof import('firebaseui') | null>(null);
2725
const [userSignedIn, setUserSignedIn] = useState(false);
2826
const [isVerified, setIsVerified] = useState(true);
@@ -39,6 +37,7 @@ const FirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) =>
3937
useEffect(() => {
4038
if (firebaseui === null )
4139
return;
40+
const firebaseAuth = getAuth(app);
4241

4342
// Get or Create a firebaseUI instance.
4443
const firebaseUiWidget = firebaseui.auth.AuthUI.getInstance() || new firebaseui.auth.AuthUI(firebaseAuth);
@@ -49,11 +48,17 @@ const FirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) =>
4948
const unregisterAuthObserver = onAuthStateChanged(firebaseAuth, user => {
5049
if (!user && userSignedIn)
5150
firebaseUiWidget.reset();
52-
if( user && user.providerData?.[0]?.providerId === EmailAuthProvider.PROVIDER_ID && !user.emailVerified ) {
51+
52+
const signInProvider = user?.providerData?.[0]?.providerId;
53+
const isVerified = Boolean(
54+
user && signInProvider && (user.emailVerified || signInProvider !== EmailAuthProvider.PROVIDER_ID)
55+
);
56+
57+
if( user && !isVerified ) {
5358
sendEmailVerification(user).then(() => console.log('Email verification sent!'));
5459
firebaseUiWidget.reset();
5560
}
56-
setIsVerified(!!user?.emailVerified);
61+
setIsVerified(isVerified);
5762
setUserSignedIn(!!user);
5863
});
5964

@@ -64,8 +69,8 @@ const FirebaseAuth = ({uiConfig, firebaseAuth, className, uiCallback}: Props) =>
6469
// Render the firebaseUi Widget.
6570
// @ts-ignore
6671
firebaseUiWidget.start(elementRef.current, {...uiConfig, callbacks: {uiShown() {
67-
setLoading(false);
68-
}}});
72+
setLoading(false);
73+
}}});
6974

7075
return () => {
7176
unregisterAuthObserver();

0 commit comments

Comments
 (0)