From f6594448e6803145176f6bb3b935d9fb48ef9425 Mon Sep 17 00:00:00 2001 From: Igor Loskutov Date: Fri, 6 Feb 2026 14:02:08 -0500 Subject: [PATCH] regenerate api --- www/app/[roomName]/components/DailyRoom.tsx | 3 +- www/app/lib/apiHooks.ts | 43 ++-- www/app/reflector-api.d.ts | 222 ++++++++++++++++++++ 3 files changed, 254 insertions(+), 14 deletions(-) diff --git a/www/app/[roomName]/components/DailyRoom.tsx b/www/app/[roomName]/components/DailyRoom.tsx index 6e48ea22..57493624 100644 --- a/www/app/[roomName]/components/DailyRoom.tsx +++ b/www/app/[roomName]/components/DailyRoom.tsx @@ -27,6 +27,7 @@ import { useMeetingStartRecording, useMeetingJoining, useMeetingJoined, + buildMeetingLeaveUrl, } from "../../lib/apiHooks"; import { omit } from "remeda"; import { @@ -251,7 +252,7 @@ export default function DailyRoom({ meeting, room }: DailyRoomProps) { const handleBeforeUnload = () => { // sendBeacon guarantees delivery even if tab closes mid-request - const url = `/v1/rooms/${roomName}/meetings/${meeting.id}/leave`; + const url = buildMeetingLeaveUrl(roomName, meeting.id); navigator.sendBeacon(url, JSON.stringify({})); }; diff --git a/www/app/lib/apiHooks.ts b/www/app/lib/apiHooks.ts index f94fc5a6..06c82517 100644 --- a/www/app/lib/apiHooks.ts +++ b/www/app/lib/apiHooks.ts @@ -1,9 +1,10 @@ "use client"; -import { $api } from "./apiClient"; +import { createFinalURL, createQuerySerializer } from "openapi-fetch"; +import { $api, API_URL } from "./apiClient"; import { useError } from "../(errors)/errorContext"; import { QueryClient, useQueryClient } from "@tanstack/react-query"; -import type { components } from "../reflector-api"; +import type { components, paths } from "../reflector-api"; import { useAuth } from "./AuthProvider"; import { MeetingId } from "./types"; import { NonEmptyString } from "./utils"; @@ -14,6 +15,10 @@ import { NonEmptyString } from "./utils"; * or, limitation or incorrect usage of .d type generator from json schema * */ +/* +if you experience lack of expected endpoints on $api, try to regenerate + */ + export const useAuthReady = () => { const auth = useAuth(); @@ -807,35 +812,47 @@ export function useRoomJoinMeeting() { ); } -// Presence race fix endpoints (not yet in OpenAPI spec) -// These signal join intent to prevent race conditions during WebRTC handshake -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function useMeetingJoining(): any { - return ($api as any).useMutation( +// Presence race fix endpoints - signal join intent to prevent race conditions during WebRTC handshake +export function useMeetingJoining() { + return $api.useMutation( "post", "/v1/rooms/{room_name}/meetings/{meeting_id}/joining", {}, ); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function useMeetingJoined(): any { - return ($api as any).useMutation( +export function useMeetingJoined() { + return $api.useMutation( "post", "/v1/rooms/{room_name}/meetings/{meeting_id}/joined", {}, ); } -// eslint-disable-next-line @typescript-eslint/no-explicit-any -export function useMeetingLeave(): any { - return ($api as any).useMutation( +export function useMeetingLeave() { + return $api.useMutation( "post", "/v1/rooms/{room_name}/meetings/{meeting_id}/leave", {}, ); } +/** + * Build absolute URL for /leave endpoint (for sendBeacon which can't use hooks). + */ +export function buildMeetingLeaveUrl( + roomName: string, + meetingId: string, +): string { + return createFinalURL("/v1/rooms/{room_name}/meetings/{meeting_id}/leave", { + baseUrl: API_URL, + params: { + path: { room_name: roomName, meeting_id: meetingId }, + }, + querySerializer: createQuerySerializer(), + }); +} + export function useRoomIcsSync() { const { setError } = useError(); diff --git a/www/app/reflector-api.d.ts b/www/app/reflector-api.d.ts index 12a7085c..58ea7eda 100644 --- a/www/app/reflector-api.d.ts +++ b/www/app/reflector-api.d.ts @@ -313,6 +313,78 @@ export interface paths { patch?: never; trace?: never; }; + "/v1/rooms/{room_name}/meetings/{meeting_id}/joining": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Meeting Joining + * @description Signal intent to join meeting. Called before WebRTC handshake starts. + * + * This creates a pending join record that prevents the meeting from being + * deactivated while the WebRTC handshake is in progress. The record expires + * automatically after 30 seconds if the connection is not established. + */ + post: operations["v1_meeting_joining"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/rooms/{room_name}/meetings/{meeting_id}/joined": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Meeting Joined + * @description Signal that WebRTC connection is established. + * + * This clears the pending join record, confirming the user has successfully + * connected to the meeting. Safe to call even if meeting was deactivated + * during the handshake (idempotent cleanup). + */ + post: operations["v1_meeting_joined"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; + "/v1/rooms/{room_name}/meetings/{meeting_id}/leave": { + parameters: { + query?: never; + header?: never; + path?: never; + cookie?: never; + }; + get?: never; + put?: never; + /** + * Meeting Leave + * @description Trigger presence recheck when user leaves meeting. + * + * Called on tab close/navigation via sendBeacon(). Immediately queues presence + * poll to detect dirty disconnects faster than 30s periodic poll. + * Daily.co webhooks handle clean disconnects, but tab close/crash need this. + */ + post: operations["v1_meeting_leave"]; + delete?: never; + options?: never; + head?: never; + patch?: never; + trace?: never; + }; "/v1/transcripts": { parameters: { query?: never; @@ -1497,6 +1569,52 @@ export interface components { /** Reason */ reason?: string | null; }; + /** + * JoinedRequest + * @description Request body for /joined endpoint (after WebRTC connection established). + */ + JoinedRequest: { + /** + * Connection Id + * @description A non-empty string + */ + connection_id: string; + }; + /** JoinedResponse */ + JoinedResponse: { + /** + * Status + * @constant + */ + status: "ok"; + }; + /** + * JoiningRequest + * @description Request body for /joining endpoint (before WebRTC handshake). + */ + JoiningRequest: { + /** + * Connection Id + * @description A non-empty string + */ + connection_id: string; + }; + /** JoiningResponse */ + JoiningResponse: { + /** + * Status + * @constant + */ + status: "ok"; + }; + /** LeaveResponse */ + LeaveResponse: { + /** + * Status + * @constant + */ + status: "ok"; + }; /** Meeting */ Meeting: { /** Id */ @@ -2687,6 +2805,110 @@ export interface operations { }; }; }; + v1_meeting_joining: { + parameters: { + query?: never; + header?: never; + path: { + room_name: string; + meeting_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["JoiningRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["JoiningResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + v1_meeting_joined: { + parameters: { + query?: never; + header?: never; + path: { + room_name: string; + meeting_id: string; + }; + cookie?: never; + }; + requestBody: { + content: { + "application/json": components["schemas"]["JoinedRequest"]; + }; + }; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["JoinedResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; + v1_meeting_leave: { + parameters: { + query?: never; + header?: never; + path: { + room_name: string; + meeting_id: string; + }; + cookie?: never; + }; + requestBody?: never; + responses: { + /** @description Successful Response */ + 200: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["LeaveResponse"]; + }; + }; + /** @description Validation Error */ + 422: { + headers: { + [name: string]: unknown; + }; + content: { + "application/json": components["schemas"]["HTTPValidationError"]; + }; + }; + }; + }; v1_transcripts_list: { parameters: { query?: {