mirror of
https://github.com/Monadical-SAS/reflector.git
synced 2026-03-21 22:56:47 +00:00
* fix: live flow real-time updates during processing Three gaps caused transcript pages to require manual refresh after live recording/processing: 1. UserEventsProvider only invalidated list queries on TRANSCRIPT_STATUS, not individual transcript queries. Now parses data.id from the event and calls invalidateTranscript for the specific transcript. 2. useWebSockets had no reconnection logic — a dropped WS silently killed all real-time updates. Added exponential backoff reconnection (1s-30s, max 10 retries) with intentional close detection. 3. No polling fallback — WS was single point of failure. Added conditional refetchInterval to useTranscriptGet that polls every 5s when transcript status is processing/uploaded/recording. * feat: type-safe WebSocket events via OpenAPI stub Define Pydantic models with Literal discriminators for all WS events (9 transcript-level, 5 user-level). Expose via stub GET endpoints so pnpm openapi generates TS discriminated unions with exhaustive switch narrowing on the frontend. - New server/reflector/ws_events.py with TranscriptWsEvent and UserWsEvent - Tighten backend emit signatures with TranscriptEventName literal - Frontend uses generated types, removes Zod schema and manual casts - Fix pre-existing bugs: waveform mapping, FINAL_LONG_SUMMARY field name - STATUS value now typed as TranscriptStatus literal end-to-end - TOPIC handler simplified to query invalidation only (avoids shape mismatch) * fix: restore TOPIC WS handler with immediate state update The setTopics call provides instant topic rendering during live transcription. Query invalidation still follows for full data sync. * fix: align TOPIC WS event data with GetTranscriptTopic shape Convert TranscriptTopic → GetTranscriptTopic in pipeline before emitting, so WS sends segments instead of words. Removes the `as unknown as Topic` cast on the frontend. * fix: use NonEmptyString and TranscriptStatus in user WS event models --------- Co-authored-by: Igor Loskutov <igor.loskutoff@gmail.com>
865 lines
21 KiB
TypeScript
865 lines
21 KiB
TypeScript
"use client";
|
|
|
|
import { $api } from "./apiClient";
|
|
import { useError } from "../(errors)/errorContext";
|
|
import { QueryClient, useQueryClient } from "@tanstack/react-query";
|
|
import type { components } from "../reflector-api";
|
|
import { useAuth } from "./AuthProvider";
|
|
import { MeetingId } from "./types";
|
|
import { NonEmptyString } from "./utils";
|
|
import type { TranscriptStatus } from "./transcript";
|
|
|
|
/*
|
|
* XXX error types returned from the hooks are not always correct; declared types are ValidationError but real type could be string or any other
|
|
* this is either a limitation or incorrect usage of Python json schema generator
|
|
* or, limitation or incorrect usage of .d type generator from json schema
|
|
* */
|
|
|
|
export const useAuthReady = () => {
|
|
const auth = useAuth();
|
|
|
|
return {
|
|
isAuthenticated: auth.status === "authenticated",
|
|
isLoading: auth.status === "loading",
|
|
};
|
|
};
|
|
|
|
export function useRoomsList(page: number = 1) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/rooms",
|
|
{
|
|
params: {
|
|
query: { page },
|
|
},
|
|
},
|
|
{
|
|
enabled: isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
type SourceKind = components["schemas"]["SourceKind"];
|
|
|
|
export const TRANSCRIPT_SEARCH_URL = "/v1/transcripts/search" as const;
|
|
|
|
export const invalidateTranscriptLists = (queryClient: QueryClient) =>
|
|
queryClient.invalidateQueries({
|
|
queryKey: ["get", TRANSCRIPT_SEARCH_URL],
|
|
});
|
|
|
|
export function useTranscriptsSearch(
|
|
q: string = "",
|
|
options: {
|
|
limit?: number;
|
|
offset?: number;
|
|
room_id?: string;
|
|
source_kind?: SourceKind;
|
|
} = {},
|
|
) {
|
|
return $api.useQuery(
|
|
"get",
|
|
TRANSCRIPT_SEARCH_URL,
|
|
{
|
|
params: {
|
|
query: {
|
|
q,
|
|
limit: options.limit,
|
|
offset: options.offset,
|
|
room_id: options.room_id,
|
|
source_kind: options.source_kind,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
enabled: true,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptDelete() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation("delete", "/v1/transcripts/{transcript_id}", {
|
|
onSuccess: () => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: ["get", TRANSCRIPT_SEARCH_URL],
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error deleting the transcript");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useTranscriptProcess() {
|
|
const { setError } = useError();
|
|
|
|
return $api.useMutation("post", "/v1/transcripts/{transcript_id}/process", {
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error processing the transcript");
|
|
},
|
|
});
|
|
}
|
|
|
|
const ACTIVE_TRANSCRIPT_STATUSES = new Set<TranscriptStatus>([
|
|
"processing",
|
|
"uploaded",
|
|
"recording",
|
|
]);
|
|
|
|
export function useTranscriptGet(transcriptId: NonEmptyString | null) {
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}",
|
|
{
|
|
params: {
|
|
path: {
|
|
transcript_id: transcriptId!,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
enabled: !!transcriptId,
|
|
refetchInterval: (query) => {
|
|
const status = query.state.data?.status;
|
|
return status && ACTIVE_TRANSCRIPT_STATUSES.has(status) ? 5000 : false;
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export const invalidateTranscript = (
|
|
queryClient: QueryClient,
|
|
transcriptId: NonEmptyString,
|
|
) =>
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions("get", "/v1/transcripts/{transcript_id}", {
|
|
params: { path: { transcript_id: transcriptId } },
|
|
}).queryKey,
|
|
});
|
|
|
|
export function useRoomGet(roomId: string | null) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/rooms/{room_id}",
|
|
{
|
|
params: {
|
|
path: { room_id: roomId! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!roomId && isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useRoomTestWebhook() {
|
|
const { setError } = useError();
|
|
|
|
return $api.useMutation("post", "/v1/rooms/{room_id}/webhook/test", {
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error testing the webhook");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRoomCreate() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation("post", "/v1/rooms", {
|
|
onSuccess: () => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions("get", "/v1/rooms").queryKey,
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error creating the room");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRoomUpdate() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation("patch", "/v1/rooms/{room_id}", {
|
|
onSuccess: async (room) => {
|
|
await Promise.all([
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions("get", "/v1/rooms").queryKey,
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions("get", "/v1/rooms/{room_id}", {
|
|
params: {
|
|
path: {
|
|
room_id: room.id,
|
|
},
|
|
},
|
|
}).queryKey,
|
|
}),
|
|
]);
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error updating the room");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRoomDelete() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation("delete", "/v1/rooms/{room_id}", {
|
|
onSuccess: () => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions("get", "/v1/rooms").queryKey,
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error deleting the room");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useZulipStreams() {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/zulip/streams",
|
|
{},
|
|
{
|
|
enabled: isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useZulipTopics(streamId: number | null) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
const enabled = !!streamId && isAuthenticated;
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/zulip/streams/{stream_id}/topics",
|
|
{
|
|
params: {
|
|
path: {
|
|
stream_id: enabled ? streamId : 0,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
enabled,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptUpdate() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation("patch", "/v1/transcripts/{transcript_id}", {
|
|
onSuccess: (data, variables) => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions("get", "/v1/transcripts/{transcript_id}", {
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
}).queryKey,
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error updating the transcript");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useTranscriptPostToZulip() {
|
|
const { setError } = useError();
|
|
|
|
// @ts-ignore - Zulip endpoint not in OpenAPI spec
|
|
return $api.useMutation("post", "/v1/transcripts/{transcript_id}/zulip", {
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error posting to Zulip");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useTranscriptUploadAudio() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation(
|
|
"post",
|
|
"/v1/transcripts/{transcript_id}/record/upload",
|
|
{
|
|
onSuccess: (data, variables) => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}",
|
|
{
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
},
|
|
).queryKey,
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error uploading the audio file");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptWaveform(transcriptId: NonEmptyString | null) {
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/audio/waveform",
|
|
{
|
|
params: {
|
|
path: { transcript_id: transcriptId! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!transcriptId,
|
|
},
|
|
);
|
|
}
|
|
|
|
export const invalidateTranscriptWaveform = (
|
|
queryClient: QueryClient,
|
|
transcriptId: NonEmptyString,
|
|
) =>
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/audio/waveform",
|
|
{
|
|
params: { path: { transcript_id: transcriptId } },
|
|
},
|
|
).queryKey,
|
|
});
|
|
|
|
export function useTranscriptMP3(transcriptId: NonEmptyString | null) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/audio/mp3",
|
|
{
|
|
params: {
|
|
path: { transcript_id: transcriptId! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!transcriptId && isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptTopics(transcriptId: NonEmptyString | null) {
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/topics",
|
|
{
|
|
params: {
|
|
path: { transcript_id: transcriptId! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!transcriptId,
|
|
},
|
|
);
|
|
}
|
|
|
|
export const invalidateTranscriptTopics = (
|
|
queryClient: QueryClient,
|
|
transcriptId: NonEmptyString,
|
|
) =>
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/topics",
|
|
{
|
|
params: { path: { transcript_id: transcriptId } },
|
|
},
|
|
).queryKey,
|
|
});
|
|
|
|
export function useTranscriptTopicsWithWords(
|
|
transcriptId: NonEmptyString | null,
|
|
) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/topics/with-words",
|
|
{
|
|
params: {
|
|
path: { transcript_id: transcriptId! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!transcriptId && isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptTopicsWithWordsPerSpeaker(
|
|
transcriptId: NonEmptyString | null,
|
|
topicId: string | null,
|
|
) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/topics/{topic_id}/words-per-speaker",
|
|
{
|
|
params: {
|
|
path: {
|
|
transcript_id: transcriptId!,
|
|
topic_id: topicId!,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
enabled: !!transcriptId && !!topicId && isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptParticipants(transcriptId: NonEmptyString | null) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/participants",
|
|
{
|
|
params: {
|
|
path: { transcript_id: transcriptId! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!transcriptId && isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptParticipantUpdate() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation(
|
|
"patch",
|
|
"/v1/transcripts/{transcript_id}/participants/{participant_id}",
|
|
{
|
|
onSuccess: (data, variables) => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/participants",
|
|
{
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
},
|
|
).queryKey,
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error updating the participant");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptParticipantCreate() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation(
|
|
"post",
|
|
"/v1/transcripts/{transcript_id}/participants",
|
|
{
|
|
onSuccess: (data, variables) => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/participants",
|
|
{
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
},
|
|
).queryKey,
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error creating the participant");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptParticipantDelete() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation(
|
|
"delete",
|
|
"/v1/transcripts/{transcript_id}/participants/{participant_id}",
|
|
{
|
|
onSuccess: (data, variables) => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/participants",
|
|
{
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
},
|
|
).queryKey,
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error deleting the participant");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptSpeakerAssign() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation(
|
|
"patch",
|
|
"/v1/transcripts/{transcript_id}/speaker/assign",
|
|
{
|
|
onSuccess: (data, variables) => {
|
|
return Promise.all([
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}",
|
|
{
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
},
|
|
).queryKey,
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/participants",
|
|
{
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
},
|
|
).queryKey,
|
|
}),
|
|
]);
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error assigning the speaker");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptSpeakerMerge() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation(
|
|
"patch",
|
|
"/v1/transcripts/{transcript_id}/speaker/merge",
|
|
{
|
|
onSuccess: (data, variables) => {
|
|
return Promise.all([
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}",
|
|
{
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
},
|
|
).queryKey,
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/transcripts/{transcript_id}/participants",
|
|
{
|
|
params: {
|
|
path: { transcript_id: variables.params.path.transcript_id },
|
|
},
|
|
},
|
|
).queryKey,
|
|
}),
|
|
]);
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error merging speakers");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useMeetingStartRecording() {
|
|
const { setError } = useError();
|
|
|
|
return $api.useMutation(
|
|
"post",
|
|
"/v1/meetings/{meeting_id}/recordings/start",
|
|
{
|
|
onError: (error) => {
|
|
setError(error as Error, "Failed to start recording");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useMeetingAudioConsent() {
|
|
const { setError } = useError();
|
|
|
|
return $api.useMutation("post", "/v1/meetings/{meeting_id}/consent", {
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error recording consent");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useMeetingDeactivate() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation("patch", `/v1/meetings/{meeting_id}/deactivate`, {
|
|
onError: (error) => {
|
|
setError(error as Error, "Failed to end meeting");
|
|
},
|
|
onSuccess: () => {
|
|
return queryClient.invalidateQueries({
|
|
predicate: (query) => {
|
|
const key = query.queryKey;
|
|
return key.some(
|
|
(k) =>
|
|
typeof k === "string" &&
|
|
!!MEETING_LIST_PATH_PARTIALS.find((e) => k.includes(e)),
|
|
);
|
|
},
|
|
});
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useTranscriptWebRTC() {
|
|
const { setError } = useError();
|
|
|
|
return $api.useMutation(
|
|
"post",
|
|
"/v1/transcripts/{transcript_id}/record/webrtc",
|
|
{
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error with WebRTC connection");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useTranscriptCreate() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation("post", "/v1/transcripts", {
|
|
onSuccess: () => {
|
|
return queryClient.invalidateQueries({
|
|
queryKey: ["get", TRANSCRIPT_SEARCH_URL],
|
|
});
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error creating the transcript");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRoomsCreateMeeting() {
|
|
const { setError } = useError();
|
|
const queryClient = useQueryClient();
|
|
|
|
return $api.useMutation("post", "/v1/rooms/{room_name}/meeting", {
|
|
onSuccess: async (data, variables) => {
|
|
const roomName = variables.params.path.room_name;
|
|
await Promise.all([
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions("get", "/v1/rooms").queryKey,
|
|
}),
|
|
queryClient.invalidateQueries({
|
|
queryKey: $api.queryOptions(
|
|
"get",
|
|
"/v1/rooms/{room_name}/meetings/active" satisfies `/v1/rooms/{room_name}/${typeof MEETINGS_ACTIVE_PATH_PARTIAL}`,
|
|
{
|
|
params: {
|
|
path: { room_name: roomName },
|
|
},
|
|
},
|
|
).queryKey,
|
|
}),
|
|
]);
|
|
},
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error creating the meeting");
|
|
},
|
|
});
|
|
}
|
|
|
|
// Calendar integration hooks
|
|
export function useRoomGetByName(roomName: string | null) {
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/rooms/name/{room_name}",
|
|
{
|
|
params: {
|
|
path: { room_name: roomName! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!roomName,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useRoomUpcomingMeetings(roomName: string | null) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/rooms/{room_name}/meetings/upcoming" satisfies `/v1/rooms/{room_name}/${typeof MEETINGS_UPCOMING_PATH_PARTIAL}`,
|
|
{
|
|
params: {
|
|
path: { room_name: roomName! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!roomName && isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
const MEETINGS_PATH_PARTIAL = "meetings" as const;
|
|
const MEETINGS_ACTIVE_PATH_PARTIAL = `${MEETINGS_PATH_PARTIAL}/active` as const;
|
|
const MEETINGS_UPCOMING_PATH_PARTIAL =
|
|
`${MEETINGS_PATH_PARTIAL}/upcoming` as const;
|
|
const MEETING_LIST_PATH_PARTIALS = [
|
|
MEETINGS_ACTIVE_PATH_PARTIAL,
|
|
MEETINGS_UPCOMING_PATH_PARTIAL,
|
|
];
|
|
|
|
export function useRoomActiveMeetings(roomName: string | null) {
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/rooms/{room_name}/meetings/active" satisfies `/v1/rooms/{room_name}/${typeof MEETINGS_ACTIVE_PATH_PARTIAL}`,
|
|
{
|
|
params: {
|
|
path: { room_name: roomName! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!roomName,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useRoomGetMeeting(
|
|
roomName: string | null,
|
|
meetingId: MeetingId | null,
|
|
) {
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/rooms/{room_name}/meetings/{meeting_id}",
|
|
{
|
|
params: {
|
|
path: {
|
|
room_name: roomName!,
|
|
meeting_id: meetingId!,
|
|
},
|
|
},
|
|
},
|
|
{
|
|
enabled: !!roomName && !!meetingId,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useRoomJoinMeeting() {
|
|
const { setError } = useError();
|
|
|
|
return $api.useMutation(
|
|
"post",
|
|
"/v1/rooms/{room_name}/meetings/{meeting_id}/join",
|
|
{
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error joining the meeting");
|
|
},
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useRoomIcsSync() {
|
|
const { setError } = useError();
|
|
|
|
return $api.useMutation("post", "/v1/rooms/{room_name}/ics/sync", {
|
|
onError: (error) => {
|
|
setError(error as Error, "There was an error syncing the calendar");
|
|
},
|
|
});
|
|
}
|
|
|
|
export function useRoomIcsStatus(roomName: string | null) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/rooms/{room_name}/ics/status",
|
|
{
|
|
params: {
|
|
path: { room_name: roomName! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!roomName && isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
|
|
export function useRoomCalendarEvents(roomName: string | null) {
|
|
const { isAuthenticated } = useAuthReady();
|
|
|
|
return $api.useQuery(
|
|
"get",
|
|
"/v1/rooms/{room_name}/meetings",
|
|
{
|
|
params: {
|
|
path: { room_name: roomName! },
|
|
},
|
|
},
|
|
{
|
|
enabled: !!roomName && isAuthenticated,
|
|
},
|
|
);
|
|
}
|
|
// End of Calendar integration hooks
|