fix: daily auto refresh fix (#755)

* daily auto refresh fix

* Update www/app/lib/AuthProvider.tsx

Co-authored-by: pr-agent-monadical[bot] <198624643+pr-agent-monadical[bot]@users.noreply.github.com>

* Update www/app/[roomName]/components/DailyRoom.tsx

Co-authored-by: pr-agent-monadical[bot] <198624643+pr-agent-monadical[bot]@users.noreply.github.com>

* fix bot lint

---------

Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com>
Co-authored-by: pr-agent-monadical[bot] <198624643+pr-agent-monadical[bot]@users.noreply.github.com>
This commit is contained in:
Igor Monadical
2025-11-27 18:31:03 -05:00
committed by GitHub
parent a2bb6a27d6
commit fe47c46489
3 changed files with 29 additions and 13 deletions

View File

@@ -11,6 +11,7 @@ import {
recordingTypeRequiresConsent, recordingTypeRequiresConsent,
} from "../../lib/consent"; } from "../../lib/consent";
import { useRoomJoinMeeting } from "../../lib/apiHooks"; import { useRoomJoinMeeting } from "../../lib/apiHooks";
import { assertExists } from "../../lib/utils";
type Meeting = components["schemas"]["Meeting"]; type Meeting = components["schemas"]["Meeting"];
@@ -22,16 +23,16 @@ export default function DailyRoom({ meeting }: DailyRoomProps) {
const router = useRouter(); const router = useRouter();
const params = useParams(); const params = useParams();
const auth = useAuth(); const auth = useAuth();
const status = auth.status; const authStatus = auth.status;
const authLastUserId = auth.lastUserId;
const containerRef = useRef<HTMLDivElement>(null); const containerRef = useRef<HTMLDivElement>(null);
const joinMutation = useRoomJoinMeeting(); const joinMutation = useRoomJoinMeeting();
const [joinedMeeting, setJoinedMeeting] = useState<Meeting | null>(null); const [joinedMeeting, setJoinedMeeting] = useState<Meeting | null>(null);
const roomName = params?.roomName as string; const roomName = params?.roomName as string;
// Always call /join to get a fresh token with user_id
useEffect(() => { useEffect(() => {
if (status === "loading" || !meeting?.id || !roomName) return; if (authLastUserId === null || !meeting?.id || !roomName) return;
const join = async () => { const join = async () => {
try { try {
@@ -50,18 +51,16 @@ export default function DailyRoom({ meeting }: DailyRoomProps) {
}; };
join(); join();
}, [meeting?.id, roomName, status]); }, [meeting?.id, roomName, authLastUserId]);
const roomUrl = joinedMeeting?.host_room_url || joinedMeeting?.room_url; const roomUrl = joinedMeeting?.host_room_url || joinedMeeting?.room_url;
const isLoading =
status === "loading" || joinMutation.isPending || !joinedMeeting;
const handleLeave = useCallback(() => { const handleLeave = useCallback(() => {
router.push("/browse"); router.push("/browse");
}, [router]); }, [router]);
useEffect(() => { useEffect(() => {
if (isLoading || !roomUrl || !containerRef.current) return; if (!authLastUserId || !roomUrl || !containerRef.current) return;
let frame: DailyCall | null = null; let frame: DailyCall | null = null;
let destroyed = false; let destroyed = false;
@@ -90,9 +89,14 @@ export default function DailyRoom({ meeting }: DailyRoomProps) {
frame.on("left-meeting", handleLeave); frame.on("left-meeting", handleLeave);
// TODO this method must not ignore no-recording option
// TODO this method is here to make dev environments work in some cases (not examined which)
frame.on("joined-meeting", async () => { frame.on("joined-meeting", async () => {
try { try {
await frame.startRecording({ type: "raw-tracks" }); assertExists(
frame,
"frame object got lost somewhere after frame.on was called",
).startRecording({ type: "raw-tracks" });
} catch (error) { } catch (error) {
console.error("Failed to start recording:", error); console.error("Failed to start recording:", error);
} }
@@ -104,7 +108,9 @@ export default function DailyRoom({ meeting }: DailyRoomProps) {
} }
}; };
createAndJoin(); createAndJoin().catch((error) => {
console.error("Failed to create and join meeting:", error);
});
return () => { return () => {
destroyed = true; destroyed = true;
@@ -114,9 +120,9 @@ export default function DailyRoom({ meeting }: DailyRoomProps) {
}); });
} }
}; };
}, [roomUrl, isLoading, handleLeave]); }, [roomUrl, authLastUserId, handleLeave]);
if (isLoading) { if (!authLastUserId) {
return ( return (
<Center width="100vw" height="100vh"> <Center width="100vw" height="100vh">
<Spinner size="xl" /> <Spinner size="xl" />

View File

@@ -1,6 +1,6 @@
"use client"; "use client";
import { createContext, useContext } from "react"; import { createContext, useContext, useRef } from "react";
import { useSession as useNextAuthSession } from "next-auth/react"; import { useSession as useNextAuthSession } from "next-auth/react";
import { signOut, signIn } from "next-auth/react"; import { signOut, signIn } from "next-auth/react";
import { configureApiAuth } from "./apiClient"; import { configureApiAuth } from "./apiClient";
@@ -25,6 +25,8 @@ type AuthContextType = (
update: () => Promise<Session | null>; update: () => Promise<Session | null>;
signIn: typeof signIn; signIn: typeof signIn;
signOut: typeof signOut; signOut: typeof signOut;
// TODO probably rename isLoading to isReloading and make THIS field "isLoading"
lastUserId: CustomSession["user"]["id"] | null;
}; };
const AuthContext = createContext<AuthContextType | undefined>(undefined); const AuthContext = createContext<AuthContextType | undefined>(undefined);
@@ -41,10 +43,13 @@ const noopAuthContext: AuthContextType = {
signOut: async () => { signOut: async () => {
throw new Error("signOut not supposed to be called"); throw new Error("signOut not supposed to be called");
}, },
lastUserId: null,
}; };
export function AuthProvider({ children }: { children: React.ReactNode }) { export function AuthProvider({ children }: { children: React.ReactNode }) {
const { data: session, status, update } = useNextAuthSession(); const { data: session, status, update } = useNextAuthSession();
// referential comparison done in component, must be primitive /or cached
const lastUserId = useRef<CustomSession["user"]["id"] | null>(null);
const contextValue: AuthContextType = isAuthEnabled const contextValue: AuthContextType = isAuthEnabled
? { ? {
@@ -78,6 +83,9 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
status: "unauthenticated" as const, status: "unauthenticated" as const,
}; };
} else if (customSession?.accessToken) { } else if (customSession?.accessToken) {
// updates anyways with updated properties below
// warning! execution order conscience, must be ran before reading lastUserId.current below
lastUserId.current = customSession.user.id;
return { return {
status, status,
accessToken: customSession.accessToken, accessToken: customSession.accessToken,
@@ -103,6 +111,8 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
update, update,
signIn, signIn,
signOut, signOut,
// for optimistic cases when we assume "loading" doesn't immediately invalidate the user
lastUserId: lastUserId.current,
} }
: noopAuthContext; : noopAuthContext;

View File

@@ -148,7 +148,7 @@ export const authOptions = (): AuthOptions =>
}, },
async session({ session, token }) { async session({ session, token }) {
const extendedToken = token as JWTWithAccessToken; const extendedToken = token as JWTWithAccessToken;
console.log("extendedToken", extendedToken);
const userId = await getUserId(extendedToken.accessToken); const userId = await getUserId(extendedToken.accessToken);
return { return {