Next.js 16, React 19.2, Identities page, Invitations identities step, PNPM Catalogs (#381)

* Upgraded to Next.js 16
* Refactored code to comply with React 19.2 ESLint rules
* Refactored some useEffect usages with the new useEffectEvent
* Added Identities page and added second step to set up an identity after accepting an invitation
* Updated all dependencies
* Introduced PNPM catalogs for some frequently updated dependencies
* Bugs fixing and improvements
This commit is contained in:
Giancarlo Buomprisco
2025-10-22 11:47:47 +09:00
committed by GitHub
parent ea0c1dde80
commit 2c0d0bf7a1
98 changed files with 4812 additions and 4394 deletions

View File

@@ -73,7 +73,6 @@ class AuthCallbackService {
// remove the query params from the url
searchParams.delete('token_hash');
searchParams.delete('type');
searchParams.delete('next');
// if we have a next path, we redirect to that path

View File

@@ -1,8 +1,6 @@
'use client';
import { useEffect } from 'react';
import { usePathname } from 'next/navigation';
import { useEffect, useEffectEvent } from 'react';
import type { AuthChangeEvent, Session } from '@supabase/supabase-js';
@@ -12,7 +10,13 @@ import { useSupabase } from './use-supabase';
* @name PRIVATE_PATH_PREFIXES
* @description A list of private path prefixes
*/
const PRIVATE_PATH_PREFIXES = ['/home', '/admin', '/join', '/update-password'];
const PRIVATE_PATH_PREFIXES = [
'/home',
'/admin',
'/join',
'/identities',
'/update-password',
];
/**
* @name AUTH_PATHS
@@ -28,19 +32,23 @@ const AUTH_PATHS = ['/auth'];
*/
export function useAuthChangeListener({
privatePathPrefixes = PRIVATE_PATH_PREFIXES,
appHomePath,
onEvent,
}: {
appHomePath: string;
privatePathPrefixes?: string[];
onEvent?: (event: AuthChangeEvent, user: Session | null) => void;
}) {
const client = useSupabase();
const pathName = usePathname();
useEffect(() => {
const setupAuthListener = useEffectEvent(() => {
// don't run on the server
if (typeof window === 'undefined') {
return;
}
// keep this running for the whole session unless the component was unmounted
const listener = client.auth.onAuthStateChange((event, user) => {
return client.auth.onAuthStateChange((event, user) => {
const pathName = window.location.pathname;
if (onEvent) {
onEvent(event, user);
}
@@ -68,10 +76,16 @@ export function useAuthChangeListener({
window.location.reload();
}
});
});
useEffect(() => {
const listener = setupAuthListener();
// destroy listener on un-mounts
return () => listener.data.subscription.unsubscribe();
}, [client.auth, pathName, appHomePath, privatePathPrefixes, onEvent]);
return () => {
listener?.data.subscription.unsubscribe();
};
}, []);
}
/**

View File

@@ -1,4 +1,4 @@
import type { SupabaseClient } from '@supabase/supabase-js';
import type { AMREntry, SupabaseClient } from '@supabase/supabase-js';
import { checkRequiresMultiFactorAuthentication } from './check-requires-mfa';
import { JWTUserData } from './types';
@@ -24,6 +24,7 @@ type UserClaims = {
aal: `aal1` | `aal2`;
session_id: string;
is_anonymous: boolean;
amr: AMREntry[];
};
/**
@@ -97,6 +98,7 @@ export async function requireUser(
app_metadata: user.app_metadata,
user_metadata: user.user_metadata,
id: user.sub,
amr: user.amr,
},
};
}

View File

@@ -1,3 +1,5 @@
import type { AMREntry } from '@supabase/supabase-js';
/**
* @name JWTUserData
* @description The user data mapped from the JWT claims.
@@ -10,4 +12,5 @@ export type JWTUserData = {
app_metadata: Record<string, unknown>;
user_metadata: Record<string, unknown>;
id: string;
amr: AMREntry[];
};