@syra.fm/sdk 0.13.1 → 0.14.1
This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
- package/README.md +3 -0
- package/lib/commonjs/client.js +24 -0
- package/lib/commonjs/client.js.map +1 -1
- package/lib/commonjs/index.js +6 -0
- package/lib/commonjs/index.js.map +1 -1
- package/lib/commonjs/live/components/CreateRoomSheet.js +73 -15
- package/lib/commonjs/live/components/CreateRoomSheet.js.map +1 -1
- package/lib/commonjs/live/components/InsightsPanel.js +1 -1
- package/lib/commonjs/live/components/LiveRoomSheet.js +1 -1
- package/lib/commonjs/live/components/MiniRoomBar.js +1 -1
- package/lib/commonjs/live/components/PanelHeader.js +1 -1
- package/lib/commonjs/live/components/PanelHeader.js.map +1 -1
- package/lib/commonjs/live/components/PodcastStreamPicker.js +1 -1
- package/lib/commonjs/live/components/RecordingCard.js +1 -1
- package/lib/commonjs/live/components/RecordingsPanel.js +1 -1
- package/lib/commonjs/live/components/RoomCard.js +1 -1
- package/lib/commonjs/live/components/StreamConfigModal.js +1 -1
- package/lib/commonjs/live/components/StreamConfigPanel.js +1 -1
- package/lib/commonjs/live/icons/MaterialCommunityIcons.js +43 -0
- package/lib/commonjs/live/icons/MaterialCommunityIcons.js.map +1 -0
- package/lib/commonjs/schema.js +10 -1
- package/lib/commonjs/schema.js.map +1 -1
- package/lib/module/client.js +25 -1
- package/lib/module/client.js.map +1 -1
- package/lib/module/index.js +1 -1
- package/lib/module/index.js.map +1 -1
- package/lib/module/live/components/CreateRoomSheet.js +74 -15
- package/lib/module/live/components/CreateRoomSheet.js.map +1 -1
- package/lib/module/live/components/InsightsPanel.js +1 -1
- package/lib/module/live/components/InsightsPanel.js.map +1 -1
- package/lib/module/live/components/LiveRoomSheet.js +1 -1
- package/lib/module/live/components/LiveRoomSheet.js.map +1 -1
- package/lib/module/live/components/MiniRoomBar.js +1 -1
- package/lib/module/live/components/MiniRoomBar.js.map +1 -1
- package/lib/module/live/components/PanelHeader.js +1 -1
- package/lib/module/live/components/PanelHeader.js.map +1 -1
- package/lib/module/live/components/PodcastStreamPicker.js +1 -1
- package/lib/module/live/components/PodcastStreamPicker.js.map +1 -1
- package/lib/module/live/components/RecordingCard.js +1 -1
- package/lib/module/live/components/RecordingCard.js.map +1 -1
- package/lib/module/live/components/RecordingsPanel.js +1 -1
- package/lib/module/live/components/RecordingsPanel.js.map +1 -1
- package/lib/module/live/components/RoomCard.js +1 -1
- package/lib/module/live/components/RoomCard.js.map +1 -1
- package/lib/module/live/components/StreamConfigModal.js +1 -1
- package/lib/module/live/components/StreamConfigModal.js.map +1 -1
- package/lib/module/live/components/StreamConfigPanel.js +1 -1
- package/lib/module/live/components/StreamConfigPanel.js.map +1 -1
- package/lib/module/live/icons/MaterialCommunityIcons.js +36 -0
- package/lib/module/live/icons/MaterialCommunityIcons.js.map +1 -0
- package/lib/module/schema.js +9 -0
- package/lib/module/schema.js.map +1 -1
- package/lib/typescript/commonjs/client.d.ts +18 -1
- package/lib/typescript/commonjs/client.d.ts.map +1 -1
- package/lib/typescript/commonjs/index.d.ts +2 -2
- package/lib/typescript/commonjs/index.d.ts.map +1 -1
- package/lib/typescript/commonjs/live/components/CreateRoomSheet.d.ts +10 -0
- package/lib/typescript/commonjs/live/components/CreateRoomSheet.d.ts.map +1 -1
- package/lib/typescript/commonjs/live/icons/MaterialCommunityIcons.d.ts +26 -0
- package/lib/typescript/commonjs/live/icons/MaterialCommunityIcons.d.ts.map +1 -0
- package/lib/typescript/commonjs/live/validation.d.ts +4 -4
- package/lib/typescript/commonjs/live/validation.d.ts.map +1 -1
- package/lib/typescript/commonjs/schema.d.ts +61 -0
- package/lib/typescript/commonjs/schema.d.ts.map +1 -1
- package/lib/typescript/module/client.d.ts +18 -1
- package/lib/typescript/module/client.d.ts.map +1 -1
- package/lib/typescript/module/index.d.ts +2 -2
- package/lib/typescript/module/index.d.ts.map +1 -1
- package/lib/typescript/module/live/components/CreateRoomSheet.d.ts +10 -0
- package/lib/typescript/module/live/components/CreateRoomSheet.d.ts.map +1 -1
- package/lib/typescript/module/live/icons/MaterialCommunityIcons.d.ts +26 -0
- package/lib/typescript/module/live/icons/MaterialCommunityIcons.d.ts.map +1 -0
- package/lib/typescript/module/live/validation.d.ts +4 -4
- package/lib/typescript/module/live/validation.d.ts.map +1 -1
- package/lib/typescript/module/schema.d.ts +61 -0
- package/lib/typescript/module/schema.d.ts.map +1 -1
- package/package.json +5 -4
- package/src/client.ts +47 -0
- package/src/index.ts +2 -0
- package/src/live/components/CreateRoomSheet.tsx +68 -15
- package/src/live/components/InsightsPanel.tsx +1 -1
- package/src/live/components/LiveRoomSheet.tsx +1 -1
- package/src/live/components/MiniRoomBar.tsx +1 -1
- package/src/live/components/PanelHeader.tsx +1 -1
- package/src/live/components/PodcastStreamPicker.tsx +1 -1
- package/src/live/components/RecordingCard.tsx +1 -1
- package/src/live/components/RecordingsPanel.tsx +1 -1
- package/src/live/components/RoomCard.tsx +1 -1
- package/src/live/components/StreamConfigModal.tsx +1 -1
- package/src/live/components/StreamConfigPanel.tsx +1 -1
- package/src/live/icons/MaterialCommunityIcons.tsx +33 -0
- package/src/schema.ts +11 -0
|
@@ -7,12 +7,25 @@ import {
|
|
|
7
7
|
TextInput,
|
|
8
8
|
ScrollView,
|
|
9
9
|
FlatList,
|
|
10
|
+
AccessibilityInfo,
|
|
10
11
|
} from 'react-native';
|
|
11
|
-
import MaterialCommunityIcons from '
|
|
12
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
12
13
|
|
|
13
14
|
import { useLiveConfig } from '../context/LiveConfigContext';
|
|
14
15
|
import { useLiveRoom } from '../context/LiveRoomContext';
|
|
15
16
|
import type { Room, House } from '../types';
|
|
17
|
+
import { LIVE_COLOR, LIVE_TINT_COLOR } from '../colors';
|
|
18
|
+
|
|
19
|
+
/**
|
|
20
|
+
* Failure copy the sheet shows INLINE. It used to go to the host `toast`, but
|
|
21
|
+
* the sheet is usually presented in a native modal window, and a toast renders
|
|
22
|
+
* in the main window underneath it: a failed create (a 401 from an expired
|
|
23
|
+
* session, a network drop) looked like a button that did nothing.
|
|
24
|
+
*/
|
|
25
|
+
export const CREATE_ROOM_ERRORS = {
|
|
26
|
+
createFailed: "Couldn't create the room. Check your connection and that you're signed in, then try again.",
|
|
27
|
+
scheduleMissing: 'Enter a scheduled start time to schedule the room.',
|
|
28
|
+
} as const;
|
|
16
29
|
|
|
17
30
|
const TOPICS = [
|
|
18
31
|
'Technology',
|
|
@@ -82,6 +95,7 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
82
95
|
const [selectedHouse, setSelectedHouse] = useState<House | null>(null);
|
|
83
96
|
const [recordingEnabled, setRecordingEnabled] = useState(true);
|
|
84
97
|
const [loading, setLoading] = useState(false);
|
|
98
|
+
const [error, setError] = useState<string | null>(null);
|
|
85
99
|
|
|
86
100
|
const isValid = title.trim().length > 0;
|
|
87
101
|
const isBroadcast = roomType === 'broadcast';
|
|
@@ -101,9 +115,17 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
101
115
|
recordingEnabled,
|
|
102
116
|
});
|
|
103
117
|
|
|
118
|
+
// Keep the sheet open and say what went wrong where the user is looking, so
|
|
119
|
+
// they can fix it and retry. Screen readers get it announced too.
|
|
120
|
+
const showError = (message: string) => {
|
|
121
|
+
setError(message);
|
|
122
|
+
AccessibilityInfo.announceForAccessibility?.(message);
|
|
123
|
+
};
|
|
124
|
+
|
|
104
125
|
const handleCreateAndStart = async () => {
|
|
105
126
|
if (!isValid || loading) return;
|
|
106
127
|
|
|
128
|
+
setError(null);
|
|
107
129
|
setLoading(true);
|
|
108
130
|
try {
|
|
109
131
|
const room = await roomsService.createRoom(buildCreatePayload());
|
|
@@ -118,11 +140,11 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
118
140
|
}
|
|
119
141
|
onRoomCreated?.(room);
|
|
120
142
|
} else {
|
|
121
|
-
|
|
143
|
+
showError(CREATE_ROOM_ERRORS.createFailed);
|
|
122
144
|
}
|
|
123
|
-
} catch (
|
|
124
|
-
console.error('Error creating room:',
|
|
125
|
-
|
|
145
|
+
} catch (err) {
|
|
146
|
+
console.error('Error creating room:', err);
|
|
147
|
+
showError(CREATE_ROOM_ERRORS.createFailed);
|
|
126
148
|
} finally {
|
|
127
149
|
setLoading(false);
|
|
128
150
|
}
|
|
@@ -132,10 +154,11 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
132
154
|
if (!isValid || loading) return;
|
|
133
155
|
|
|
134
156
|
if (!scheduledStart.trim()) {
|
|
135
|
-
|
|
157
|
+
showError(CREATE_ROOM_ERRORS.scheduleMissing);
|
|
136
158
|
return;
|
|
137
159
|
}
|
|
138
160
|
|
|
161
|
+
setError(null);
|
|
139
162
|
setLoading(true);
|
|
140
163
|
try {
|
|
141
164
|
const room = await roomsService.createRoom({
|
|
@@ -147,11 +170,11 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
147
170
|
onClose();
|
|
148
171
|
onRoomCreated?.(room);
|
|
149
172
|
} else {
|
|
150
|
-
|
|
173
|
+
showError(CREATE_ROOM_ERRORS.createFailed);
|
|
151
174
|
}
|
|
152
|
-
} catch (
|
|
153
|
-
console.error('Error creating room:',
|
|
154
|
-
|
|
175
|
+
} catch (err) {
|
|
176
|
+
console.error('Error creating room:', err);
|
|
177
|
+
showError(CREATE_ROOM_ERRORS.createFailed);
|
|
155
178
|
} finally {
|
|
156
179
|
setLoading(false);
|
|
157
180
|
}
|
|
@@ -160,6 +183,7 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
160
183
|
const handleCreateForEmbed = async () => {
|
|
161
184
|
if (!isValid || loading) return;
|
|
162
185
|
|
|
186
|
+
setError(null);
|
|
163
187
|
setLoading(true);
|
|
164
188
|
try {
|
|
165
189
|
const room = await roomsService.createRoom(buildCreatePayload());
|
|
@@ -168,11 +192,11 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
168
192
|
onClose();
|
|
169
193
|
onRoomCreated?.(room);
|
|
170
194
|
} else {
|
|
171
|
-
|
|
195
|
+
showError(CREATE_ROOM_ERRORS.createFailed);
|
|
172
196
|
}
|
|
173
|
-
} catch (
|
|
174
|
-
console.error('Error creating room:',
|
|
175
|
-
|
|
197
|
+
} catch (err) {
|
|
198
|
+
console.error('Error creating room:', err);
|
|
199
|
+
showError(CREATE_ROOM_ERRORS.createFailed);
|
|
176
200
|
} finally {
|
|
177
201
|
setLoading(false);
|
|
178
202
|
}
|
|
@@ -260,7 +284,12 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
260
284
|
return (
|
|
261
285
|
<View style={[styles.container, { backgroundColor: theme.colors.background }]}>
|
|
262
286
|
<View style={[styles.header, { borderBottomColor: theme.colors.border }]}>
|
|
263
|
-
<TouchableOpacity
|
|
287
|
+
<TouchableOpacity
|
|
288
|
+
onPress={onClose}
|
|
289
|
+
style={styles.headerCloseBtn}
|
|
290
|
+
accessibilityRole="button"
|
|
291
|
+
accessibilityLabel="Close"
|
|
292
|
+
>
|
|
264
293
|
<MaterialCommunityIcons name="close" size={20} color={theme.colors.text} />
|
|
265
294
|
</TouchableOpacity>
|
|
266
295
|
<Text style={[styles.headerTitle, { color: theme.colors.text }]}>
|
|
@@ -269,6 +298,18 @@ export const CreateRoomSheet = forwardRef<CreateRoomSheetRef, CreateRoomSheetPro
|
|
|
269
298
|
<View style={{ width: 28 }} />
|
|
270
299
|
</View>
|
|
271
300
|
|
|
301
|
+
{error && (
|
|
302
|
+
<View
|
|
303
|
+
testID="create-room-error"
|
|
304
|
+
accessibilityRole="alert"
|
|
305
|
+
accessibilityLiveRegion="assertive"
|
|
306
|
+
style={[styles.errorBanner, { backgroundColor: LIVE_TINT_COLOR, borderColor: LIVE_COLOR }]}
|
|
307
|
+
>
|
|
308
|
+
<MaterialCommunityIcons name="alert-circle-outline" size={18} color={LIVE_COLOR} />
|
|
309
|
+
<Text style={[styles.errorText, { color: theme.colors.text }]}>{error}</Text>
|
|
310
|
+
</View>
|
|
311
|
+
)}
|
|
312
|
+
|
|
272
313
|
<Scroll
|
|
273
314
|
style={{ flex: 1 }}
|
|
274
315
|
contentContainerStyle={[styles.scrollContent, hideFooter && { paddingBottom: 72 }]}
|
|
@@ -532,6 +573,18 @@ const styles = StyleSheet.create({
|
|
|
532
573
|
},
|
|
533
574
|
headerCloseBtn: { width: 28, height: 28, alignItems: 'center', justifyContent: 'center' },
|
|
534
575
|
headerTitle: { fontSize: 16, fontWeight: '600' },
|
|
576
|
+
errorBanner: {
|
|
577
|
+
flexDirection: 'row',
|
|
578
|
+
alignItems: 'flex-start',
|
|
579
|
+
gap: 8,
|
|
580
|
+
marginHorizontal: 16,
|
|
581
|
+
marginTop: 12,
|
|
582
|
+
paddingHorizontal: 12,
|
|
583
|
+
paddingVertical: 10,
|
|
584
|
+
borderRadius: 12,
|
|
585
|
+
borderWidth: 1,
|
|
586
|
+
},
|
|
587
|
+
errorText: { flex: 1, fontSize: 13, lineHeight: 18 },
|
|
535
588
|
scrollContent: { paddingVertical: 16, paddingBottom: 12 },
|
|
536
589
|
sectionPadded: { paddingHorizontal: 16 },
|
|
537
590
|
inputSection: { marginBottom: 16 },
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, ScrollView } from 'react-native';
|
|
3
|
-
import MaterialCommunityIcons from '
|
|
3
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
4
4
|
|
|
5
5
|
import { PanelHeader } from './PanelHeader';
|
|
6
6
|
import type { Room, RoomParticipant, LiveTheme } from '../types';
|
|
@@ -8,7 +8,7 @@ import {
|
|
|
8
8
|
ScrollView,
|
|
9
9
|
type ViewStyle,
|
|
10
10
|
} from 'react-native';
|
|
11
|
-
import MaterialCommunityIcons from '
|
|
11
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
12
12
|
import { useAuth } from '@oxy.so/services';
|
|
13
13
|
|
|
14
14
|
import { useLiveConfig } from '../context/LiveConfigContext';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useEffect } from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
|
3
|
-
import MaterialCommunityIcons from '
|
|
3
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
4
4
|
import Animated, {
|
|
5
5
|
useSharedValue,
|
|
6
6
|
useAnimatedStyle,
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, TouchableOpacity } from 'react-native';
|
|
3
|
-
import MaterialCommunityIcons from '
|
|
3
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
4
4
|
|
|
5
5
|
import type { LiveTheme } from '../types';
|
|
6
6
|
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
Linking,
|
|
11
11
|
} from 'react-native';
|
|
12
12
|
import type { ViewStyle } from 'react-native';
|
|
13
|
-
import MaterialCommunityIcons from '
|
|
13
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
14
14
|
|
|
15
15
|
import { useLiveConfig, type PinnedPodcast } from '../context/LiveConfigContext';
|
|
16
16
|
import type { LiveTheme } from '../types';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, TouchableOpacity, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
-
import MaterialCommunityIcons from '
|
|
3
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
4
4
|
import { useAuth } from '@oxy.so/services';
|
|
5
5
|
|
|
6
6
|
import { useLiveConfig } from '../context/LiveConfigContext';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useState, useEffect } from 'react';
|
|
2
2
|
import { View, Text, StyleSheet, ScrollView, TouchableOpacity, ActivityIndicator } from 'react-native';
|
|
3
|
-
import MaterialCommunityIcons from '
|
|
3
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
4
4
|
|
|
5
5
|
import { PanelHeader } from './PanelHeader';
|
|
6
6
|
import { useLiveConfig } from '../context/LiveConfigContext';
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import React, { useMemo } from 'react';
|
|
2
2
|
import { View, Text, TouchableOpacity, StyleProp, ViewStyle } from 'react-native';
|
|
3
|
-
import MaterialCommunityIcons from '
|
|
3
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
4
4
|
import { useAuth } from '@oxy.so/services';
|
|
5
5
|
|
|
6
6
|
import { useLiveConfig } from '../context/LiveConfigContext';
|
|
@@ -12,7 +12,7 @@ import {
|
|
|
12
12
|
ScrollView,
|
|
13
13
|
KeyboardAvoidingView,
|
|
14
14
|
} from 'react-native';
|
|
15
|
-
import MaterialCommunityIcons from '
|
|
15
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
16
16
|
import { useAuth } from '@oxy.so/services';
|
|
17
17
|
import * as ImagePicker from 'expo-image-picker';
|
|
18
18
|
|
|
@@ -10,7 +10,7 @@ import {
|
|
|
10
10
|
Platform,
|
|
11
11
|
ScrollView,
|
|
12
12
|
} from 'react-native';
|
|
13
|
-
import MaterialCommunityIcons from '
|
|
13
|
+
import MaterialCommunityIcons from '../icons/MaterialCommunityIcons';
|
|
14
14
|
import * as ImagePicker from 'expo-image-picker';
|
|
15
15
|
|
|
16
16
|
import { useLiveConfig } from '../context/LiveConfigContext';
|
|
@@ -0,0 +1,33 @@
|
|
|
1
|
+
import React, { type ComponentProps } from 'react';
|
|
2
|
+
// The SUBPATH, never the `@expo/vector-icons` barrel: the barrel re-exports every
|
|
3
|
+
// icon family, so one glyph through it drags every family's font into a web
|
|
4
|
+
// bundle.
|
|
5
|
+
import BaseMaterialCommunityIcons from '@expo/vector-icons/MaterialCommunityIcons';
|
|
6
|
+
|
|
7
|
+
export type MaterialCommunityIconsProps = ComponentProps<typeof BaseMaterialCommunityIcons>;
|
|
8
|
+
|
|
9
|
+
/**
|
|
10
|
+
* The live-rooms UI's one entry to the MaterialCommunityIcons glyph font. SDK
|
|
11
|
+
* components import this, never `@expo/vector-icons/MaterialCommunityIcons`
|
|
12
|
+
* directly — `src/live/icons/glyphImports.test.ts` fails the suite if one does.
|
|
13
|
+
*
|
|
14
|
+
* A vector-icons glyph is a `Text` node whose content is a private-use code
|
|
15
|
+
* point. A screen reader reads it like any other text, so the Create Room sheet
|
|
16
|
+
* announced its options as "\U000f036c, Talk, Open conversation" and its button
|
|
17
|
+
* as "\U000f040a, Start Now": on Android RN builds an unlabelled pressable's
|
|
18
|
+
* description from its children's text, code point included.
|
|
19
|
+
*
|
|
20
|
+
* Every glyph in the SDK is decorative — the control around it carries the
|
|
21
|
+
* words, or an `accessibilityLabel` — so the glyph is hidden from assistive
|
|
22
|
+
* technology here, once, rather than at each call site where the next icon
|
|
23
|
+
* would forget it. `aria-hidden` is `importantForAccessibility=
|
|
24
|
+
* "no-hide-descendants"` on Android, `accessibilityElementsHidden` on iOS and
|
|
25
|
+
* `aria-hidden` on web. It is applied AFTER the caller's props so a call site
|
|
26
|
+
* cannot switch it back on: a glyph that needs a name belongs inside a control
|
|
27
|
+
* that has one.
|
|
28
|
+
*/
|
|
29
|
+
export function MaterialCommunityIcons(props: MaterialCommunityIconsProps) {
|
|
30
|
+
return <BaseMaterialCommunityIcons {...props} aria-hidden />;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
export default MaterialCommunityIcons;
|
package/src/schema.ts
CHANGED
|
@@ -82,6 +82,17 @@ export const podcastSummarySchema = z.object({
|
|
|
82
82
|
});
|
|
83
83
|
export type PodcastSummary = z.infer<typeof podcastSummarySchema>;
|
|
84
84
|
|
|
85
|
+
/**
|
|
86
|
+
* One show the caller is subscribed to, from `GET /api/podcasts/subscriptions`.
|
|
87
|
+
* `lastEpisodeAt` is the newest episode's publish time — what a client compares
|
|
88
|
+
* against its own last visit to badge a show with new episodes.
|
|
89
|
+
*/
|
|
90
|
+
export const podcastSubscriptionSchema = z.object({
|
|
91
|
+
podcast: podcastSummarySchema,
|
|
92
|
+
lastEpisodeAt: z.string().optional(),
|
|
93
|
+
});
|
|
94
|
+
export type PodcastSubscription = z.infer<typeof podcastSubscriptionSchema>;
|
|
95
|
+
|
|
85
96
|
/**
|
|
86
97
|
* The summary view of a podcast EPISODE returned by the public podcast endpoints
|
|
87
98
|
* (`GET /api/podcasts/:id/episodes`, `GET /api/episodes/:id`) — just enough to
|