| 1 | export const PRESENTATION_GENERATION_FEEDBACK_SOURCE = |
| 2 | "presentation_generation"; |
| 3 | |
| 4 | export const GLOBAL_FEEDBACK_SOURCE = "global"; |
| 5 | |
| 6 | export type FeedbackMetadataValue = string | number | boolean | null; |
| 7 | |
| 8 | export type FeedbackMetadataMap = Record<string, FeedbackMetadataValue>; |
| 9 | |
| 10 | export type ParsedFeedbackMetadata = Partial<FeedbackMetadataMap> & { |
| 11 | source?: string; |
| 12 | reaction?: string; |
| 13 | presentationId?: string; |
| 14 | presentationTitle?: string; |
| 15 | hasDetailedFeedback?: boolean; |
| 16 | }; |
| 17 | |
| 18 | export function parseFeedbackMetadata( |
| 19 | metadata: string, |
| 20 | ): ParsedFeedbackMetadata { |
| 21 | try { |
| 22 | const parsed = JSON.parse(metadata); |
| 23 | |
| 24 | if (!isFeedbackMetadataRecord(parsed)) { |
| 25 | return {}; |
| 26 | } |
| 27 | |
| 28 | return parsed; |
| 29 | } catch { |
| 30 | return {}; |
| 31 | } |
| 32 | } |
| 33 | |
| 34 | export function getFeedbackSource( |
| 35 | metadata: ParsedFeedbackMetadata, |
| 36 | ): |
| 37 | | typeof GLOBAL_FEEDBACK_SOURCE |
| 38 | | typeof PRESENTATION_GENERATION_FEEDBACK_SOURCE { |
| 39 | return metadata.source === PRESENTATION_GENERATION_FEEDBACK_SOURCE |
| 40 | ? PRESENTATION_GENERATION_FEEDBACK_SOURCE |
| 41 | : GLOBAL_FEEDBACK_SOURCE; |
| 42 | } |
| 43 | |
| 44 | function isFeedbackMetadataRecord( |
| 45 | value: unknown, |
| 46 | ): value is ParsedFeedbackMetadata { |
| 47 | if (typeof value !== "object" || value === null || Array.isArray(value)) { |
| 48 | return false; |
| 49 | } |
| 50 | |
| 51 | return Object.values(value).every(isFeedbackMetadataValue); |
| 52 | } |
| 53 | |
| 54 | function isFeedbackMetadataValue( |
| 55 | value: unknown, |
| 56 | ): value is FeedbackMetadataValue { |
| 57 | return ( |
| 58 | value === null || |
| 59 | typeof value === "string" || |
| 60 | typeof value === "number" || |
| 61 | typeof value === "boolean" |
| 62 | ); |
| 63 | } |
| 64 |