Merge branch 'main' into mathieu/calendar-integration-rebased2

This commit is contained in:
2025-09-10 18:52:41 -06:00
31 changed files with 170 additions and 218 deletions

View File

@@ -88,8 +88,13 @@ export function AuthProvider({ children }: { children: React.ReactNode }) {
};
// not useEffect, we need it ASAP
// apparently, still no guarantee this code runs before mutations are fired
configureApiAuth(
contextValue.status === "authenticated" ? contextValue.accessToken : null,
contextValue.status === "authenticated"
? contextValue.accessToken
: contextValue.status === "loading"
? undefined
: null,
);
return (

View File

@@ -4,18 +4,16 @@ import "@whereby.com/browser-sdk/embed";
import { Box, Button, HStack, Text, Link } from "@chakra-ui/react";
import { toaster } from "../components/ui/toaster";
interface WherebyEmbedProps {
interface WherebyWebinarEmbedProps {
roomUrl: string;
onLeave?: () => void;
isWebinar?: boolean;
}
// used for both webinars and meetings
// used for webinars only
export default function WherebyWebinarEmbed({
roomUrl,
onLeave,
isWebinar = false,
}: WherebyEmbedProps) {
}: WherebyWebinarEmbedProps) {
const wherebyRef = useRef<HTMLElement>(null);
// TODO extract common toast logic / styles to be used by consent toast on normal rooms
@@ -28,8 +26,7 @@ export default function WherebyWebinarEmbed({
<Box p={4} bg="white" borderRadius="md" boxShadow="md">
<HStack justifyContent="space-between" alignItems="center">
<Text>
This {isWebinar ? "webinar" : "meeting"} is being recorded. By
continuing, you agree to our{" "}
This webinar is being recorded. By continuing, you agree to our{" "}
<Link
href="https://monadical.com/privacy"
color="blue.600"

View File

@@ -2,12 +2,6 @@
import createClient from "openapi-fetch";
import type { paths } from "../reflector-api";
import {
queryOptions,
useMutation,
useQuery,
useSuspenseQuery,
} from "@tanstack/react-query";
import createFetchClient from "openapi-react-query";
import { assertExistsAndNonEmptyString } from "./utils";
import { isBuildPhase } from "./next";
@@ -16,18 +10,31 @@ const API_URL = !isBuildPhase
? assertExistsAndNonEmptyString(process.env.NEXT_PUBLIC_API_URL)
: "http://localhost";
// Create the base openapi-fetch client with a default URL
// The actual URL will be set via middleware in AuthProvider
export const client = createClient<paths>({
baseUrl: API_URL,
});
export const $api = createFetchClient<paths>(client);
let currentAuthToken: string | null | undefined = null;
const waitForAuthTokenDefinitivePresenceOrAbscence = async () => {
let tries = 0;
let time = 0;
const STEP = 100;
while (currentAuthToken === undefined) {
await new Promise((resolve) => setTimeout(resolve, STEP));
time += STEP;
tries++;
// most likely first try is more than enough, if it's more there's already something weird happens
if (tries > 10) {
// even when there's no auth assumed at all, we probably should explicitly call configureApiAuth(null)
throw new Error(
`Could not get auth token definitive presence/absence in ${time}ms. not calling configureApiAuth?`,
);
}
}
};
client.use({
onRequest({ request }) {
async onRequest({ request }) {
await waitForAuthTokenDefinitivePresenceOrAbscence();
if (currentAuthToken) {
request.headers.set("Authorization", `Bearer ${currentAuthToken}`);
}
@@ -44,7 +51,13 @@ client.use({
},
});
export const $api = createFetchClient<paths>(client);
let currentAuthToken: string | null | undefined = undefined;
// the function contract: lightweight, idempotent
export const configureApiAuth = (token: string | null | undefined) => {
// watch only for the initial loading; "reloading" state assumes token presence/absence
if (token === undefined && currentAuthToken !== undefined) return;
currentAuthToken = token;
};

View File

@@ -96,8 +96,6 @@ export function useTranscriptProcess() {
}
export function useTranscriptGet(transcriptId: string | null) {
const { isAuthenticated } = useAuthReady();
return $api.useQuery(
"get",
"/v1/transcripts/{transcript_id}",
@@ -109,7 +107,7 @@ export function useTranscriptGet(transcriptId: string | null) {
},
},
{
enabled: !!transcriptId && isAuthenticated,
enabled: !!transcriptId,
},
);
}
@@ -292,18 +290,16 @@ export function useTranscriptUploadAudio() {
}
export function useTranscriptWaveform(transcriptId: string | null) {
const { isAuthenticated } = useAuthReady();
return $api.useQuery(
"get",
"/v1/transcripts/{transcript_id}/audio/waveform",
{
params: {
path: { transcript_id: transcriptId || "" },
path: { transcript_id: transcriptId! },
},
},
{
enabled: !!transcriptId && isAuthenticated,
enabled: !!transcriptId,
},
);
}
@@ -316,7 +312,7 @@ export function useTranscriptMP3(transcriptId: string | null) {
"/v1/transcripts/{transcript_id}/audio/mp3",
{
params: {
path: { transcript_id: transcriptId || "" },
path: { transcript_id: transcriptId! },
},
},
{
@@ -326,8 +322,6 @@ export function useTranscriptMP3(transcriptId: string | null) {
}
export function useTranscriptTopics(transcriptId: string | null) {
const { isAuthenticated } = useAuthReady();
return $api.useQuery(
"get",
"/v1/transcripts/{transcript_id}/topics",
@@ -337,7 +331,7 @@ export function useTranscriptTopics(transcriptId: string | null) {
},
},
{
enabled: !!transcriptId && isAuthenticated,
enabled: !!transcriptId,
},
);
}

View File

@@ -0,0 +1,5 @@
import { components } from "../reflector-api";
type ApiTranscriptStatus = components["schemas"]["GetTranscript"]["status"];
export type TranscriptStatus = ApiTranscriptStatus;