slop review

This commit is contained in:
Igor Loskutov
2025-06-17 19:42:32 -04:00
parent 0c91f5dd59
commit 782171d7be
9 changed files with 176 additions and 765 deletions

View File

@@ -12,6 +12,7 @@ import { DomainContext } from "../domainContext";
import { useRecordingConsent } from "../recordingConsentContext";
import useSessionAccessToken from "../lib/useSessionAccessToken";
import useSessionUser from "../lib/useSessionUser";
import useApi from "../lib/useApi";
export type RoomDetails = {
params: {
@@ -31,6 +32,7 @@ export default function Room(details: RoomDetails) {
const { api_url } = useContext(DomainContext);
const { accessToken } = useSessionAccessToken();
const { id: userId } = useSessionUser();
const api = useApi();
const roomUrl = meeting?.response?.host_room_url
@@ -43,38 +45,25 @@ export default function Room(details: RoomDetails) {
router.push("/browse");
}, [router]);
// TODO hook
const handleConsent = useCallback(async (meetingId: string, given: boolean) => {
if (!api) return;
setShowConsentDialog(false);
setConsentLoading(true);
try {
const requestBody = {
consent_given: given
};
// TODO generated API
const response = await fetch(`${api_url}/v1/meetings/${meetingId}/consent`, {
method: 'POST',
headers: {
'Content-Type': 'application/json',
...(accessToken && { 'Authorization': `Bearer ${accessToken}` })
},
body: JSON.stringify(requestBody),
await api.v1MeetingAudioConsent({
meetingId,
requestBody: { consent_given: given }
});
if (response.ok) {
touch(meetingId);
} else {
console.error('Failed to submit consent');
}
touch(meetingId);
} catch (error) {
console.error('Error submitting consent:', error);
} finally {
setConsentLoading(false);
}
}, [api_url, accessToken, touch]);
}, [api, touch]);
useEffect(() => {

View File

@@ -548,6 +548,18 @@ export const $Meeting = {
title: "Meeting",
} as const;
export const $MeetingConsentRequest = {
properties: {
consent_given: {
type: "boolean",
title: "Consent Given",
},
},
type: "object",
required: ["consent_given"],
title: "MeetingConsentRequest",
} as const;
export const $Page_GetTranscript_ = {
properties: {
items: {
@@ -1166,6 +1178,35 @@ export const $ValidationError = {
title: "ValidationError",
} as const;
export const $WherebyWebhookEvent = {
properties: {
apiVersion: {
type: "string",
title: "Apiversion",
},
id: {
type: "string",
title: "Id",
},
createdAt: {
type: "string",
format: "date-time",
title: "Createdat",
},
type: {
type: "string",
title: "Type",
},
data: {
type: "object",
title: "Data",
},
},
type: "object",
required: ["apiVersion", "id", "createdAt", "type", "data"],
title: "WherebyWebhookEvent",
} as const;
export const $Word = {
properties: {
text: {

View File

@@ -4,6 +4,8 @@ import type { CancelablePromise } from "./core/CancelablePromise";
import type { BaseHttpRequest } from "./core/BaseHttpRequest";
import type {
MetricsResponse,
V1MeetingAudioConsentData,
V1MeetingAudioConsentResponse,
V1RoomsListData,
V1RoomsListResponse,
V1RoomsCreateData,
@@ -64,6 +66,8 @@ import type {
V1ZulipGetStreamsResponse,
V1ZulipGetTopicsData,
V1ZulipGetTopicsResponse,
V1WherebyWebhookData,
V1WherebyWebhookResponse,
} from "./types.gen";
export class DefaultService {
@@ -82,6 +86,31 @@ export class DefaultService {
});
}
/**
* Meeting Audio Consent
* @param data The data for the request.
* @param data.meetingId
* @param data.requestBody
* @returns unknown Successful Response
* @throws ApiError
*/
public v1MeetingAudioConsent(
data: V1MeetingAudioConsentData,
): CancelablePromise<V1MeetingAudioConsentResponse> {
return this.httpRequest.request({
method: "POST",
url: "/v1/meetings/{meeting_id}/consent",
path: {
meeting_id: data.meetingId,
},
body: data.requestBody,
mediaType: "application/json",
errors: {
422: "Validation Error",
},
});
}
/**
* Rooms List
* @param data The data for the request.
@@ -807,4 +836,25 @@ export class DefaultService {
},
});
}
/**
* Whereby Webhook
* @param data The data for the request.
* @param data.requestBody
* @returns unknown Successful Response
* @throws ApiError
*/
public v1WherebyWebhook(
data: V1WherebyWebhookData,
): CancelablePromise<V1WherebyWebhookResponse> {
return this.httpRequest.request({
method: "POST",
url: "/v1/whereby",
body: data.requestBody,
mediaType: "application/json",
errors: {
422: "Validation Error",
},
});
}
}

View File

@@ -109,6 +109,10 @@ export type Meeting = {
end_date: string;
};
export type MeetingConsentRequest = {
consent_given: boolean;
};
export type Page_GetTranscript_ = {
items: Array<GetTranscript>;
total: number;
@@ -229,6 +233,16 @@ export type ValidationError = {
type: string;
};
export type WherebyWebhookEvent = {
apiVersion: string;
id: string;
createdAt: string;
type: string;
data: {
[key: string]: unknown;
};
};
export type Word = {
text: string;
start: number;
@@ -238,6 +252,13 @@ export type Word = {
export type MetricsResponse = unknown;
export type V1MeetingAudioConsentData = {
meetingId: string;
requestBody: MeetingConsentRequest;
};
export type V1MeetingAudioConsentResponse = unknown;
export type V1RoomsListData = {
/**
* Page number
@@ -454,6 +475,12 @@ export type V1ZulipGetTopicsData = {
export type V1ZulipGetTopicsResponse = Array<Topic>;
export type V1WherebyWebhookData = {
requestBody: WherebyWebhookEvent;
};
export type V1WherebyWebhookResponse = unknown;
export type $OpenApiTs = {
"/metrics": {
get: {
@@ -465,6 +492,21 @@ export type $OpenApiTs = {
};
};
};
"/v1/meetings/{meeting_id}/consent": {
post: {
req: V1MeetingAudioConsentData;
res: {
/**
* Successful Response
*/
200: unknown;
/**
* Validation Error
*/
422: HTTPValidationError;
};
};
};
"/v1/rooms": {
get: {
req: V1RoomsListData;
@@ -902,4 +944,19 @@ export type $OpenApiTs = {
};
};
};
"/v1/whereby": {
post: {
req: V1WherebyWebhookData;
res: {
/**
* Successful Response
*/
200: unknown;
/**
* Validation Error
*/
422: HTTPValidationError;
};
};
};
};

View File

@@ -29,17 +29,20 @@ interface RecordingConsentProviderProps {
children: React.ReactNode;
}
const LOCAL_STORAGE_KEY = "recording_consent_meetings";
export const RecordingConsentProvider: React.FC<RecordingConsentProviderProps> = ({ children }) => {
const [state, setState] = useState<ConsentContextState>({ ready: false });
const safeWriteToStorage = (meetingIds: string[]): void => {
try {
localStorage.setItem("recording_consent_meetings", JSON.stringify(meetingIds));
localStorage.setItem(LOCAL_STORAGE_KEY, JSON.stringify(meetingIds));
} catch (error) {
console.error("Failed to save consent data to localStorage:", error);
}
};
// writes to local storage and to the state of context both
const touch = (meetingId: string): void => {
if (!state.ready) {
@@ -47,13 +50,14 @@ export const RecordingConsentProvider: React.FC<RecordingConsentProviderProps> =
return;
}
// Update context state (always works)
const newSet = new Set([...state.consentAnsweredForMeetings, meetingId]);
// has success regardless local storage write success: we don't handle that
// and don't want to crash anything with just consent functionality
const newSet = state.consentAnsweredForMeetings.has(meetingId) ?
state.consentAnsweredForMeetings :
new Set([...state.consentAnsweredForMeetings, meetingId]);
// note: preserves the set insertion order
const array = Array.from(newSet).slice(-5); // Keep latest 5
safeWriteToStorage(array);
// Update state regardless of storage success
setState({ ready: true, consentAnsweredForMeetings: newSet });
};
@@ -62,10 +66,10 @@ export const RecordingConsentProvider: React.FC<RecordingConsentProviderProps> =
return state.consentAnsweredForMeetings.has(meetingId);
};
// Initialize from localStorage on mount (client-side only)
// initialize on mount
useEffect(() => {
try {
const stored = localStorage.getItem("recording_consent_meetings");
const stored = localStorage.getItem(LOCAL_STORAGE_KEY);
if (!stored) {
setState({ ready: true, consentAnsweredForMeetings: new Set() });
return;
@@ -77,10 +81,12 @@ export const RecordingConsentProvider: React.FC<RecordingConsentProviderProps> =
setState({ ready: true, consentAnsweredForMeetings: new Set() });
return;
}
const consentAnsweredForMeetings = new Set(parsed.filter(id => typeof id === 'string'));
// pre-historic way of parsing!
const consentAnsweredForMeetings = new Set(parsed.filter(id => !!id && typeof id === 'string'));
setState({ ready: true, consentAnsweredForMeetings });
} catch (error) {
// we don't want to fail the page here; the component is not essential.
console.error("Failed to parse consent data from localStorage:", error);
setState({ ready: true, consentAnsweredForMeetings: new Set() });
}