@streamplace/components 0.7.19 → 0.7.25
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/dist/components/chat/chat-box.js +5 -0
- package/dist/components/chat/chat-message.js +5 -4
- package/dist/components/chat/chat.js +14 -4
- package/dist/components/chat/mod-view.js +19 -1
- package/dist/components/mobile-player/fullscreen.js +2 -0
- package/dist/components/mobile-player/ui/autoplay-button.js +68 -0
- package/dist/components/mobile-player/ui/index.js +1 -0
- package/dist/components/mobile-player/video.js +11 -1
- package/dist/components/mobile-player/webrtc-diagnostics.js +67 -13
- package/dist/lib/system-messages.js +1 -0
- package/dist/livestream-store/chat.js +25 -1
- package/dist/livestream-store/stream-key.js +1 -0
- package/dist/livestream-store/websocket-consumer.js +4 -1
- package/dist/player-store/player-provider.js +2 -1
- package/dist/player-store/player-store.js +2 -0
- package/dist/streamplace-store/stream.js +2 -0
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +4 -4
- package/src/components/chat/chat-box.tsx +3 -0
- package/src/components/chat/chat-message.tsx +5 -4
- package/src/components/chat/chat.tsx +20 -4
- package/src/components/chat/mod-view.tsx +39 -5
- package/src/components/mobile-player/fullscreen.tsx +2 -0
- package/src/components/mobile-player/ui/autoplay-button.tsx +86 -0
- package/src/components/mobile-player/ui/index.ts +1 -0
- package/src/components/mobile-player/video.tsx +11 -1
- package/src/components/mobile-player/webrtc-diagnostics.tsx +73 -15
- package/src/lib/system-messages.ts +1 -0
- package/src/livestream-store/chat.tsx +24 -0
- package/src/livestream-store/stream-key.tsx +1 -0
- package/src/livestream-store/websocket-consumer.tsx +4 -1
- package/src/player-store/player-provider.tsx +2 -1
- package/src/player-store/player-state.tsx +6 -0
- package/src/player-store/player-store.tsx +4 -0
- package/src/streamplace-store/stream.tsx +2 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -10,6 +10,7 @@ import { usePDSAgent } from "../../streamplace-store/xrpc";
|
|
|
10
10
|
|
|
11
11
|
import { Linking } from "react-native";
|
|
12
12
|
import { ChatMessageViewHydrated } from "streamplace";
|
|
13
|
+
import { useDeleteChatMessage } from "../../livestream-store";
|
|
13
14
|
import { useStreamplaceStore } from "../../streamplace-store";
|
|
14
15
|
import {
|
|
15
16
|
atoms,
|
|
@@ -172,11 +173,16 @@ export const ModView = forwardRef<ModViewRef, ModViewProps>(() => {
|
|
|
172
173
|
>
|
|
173
174
|
<Text color="primary">View user on {BSKY_FRONTEND_DOMAIN}</Text>
|
|
174
175
|
</DropdownMenuItem>
|
|
175
|
-
|
|
176
|
-
message={message}
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
176
|
+
{message.author.did === agent?.did && (
|
|
177
|
+
<DeleteButton message={message} />
|
|
178
|
+
)}
|
|
179
|
+
{message.author.did !== agent?.did && (
|
|
180
|
+
<ReportButton
|
|
181
|
+
message={message}
|
|
182
|
+
setReportModalOpen={setReportModalOpen}
|
|
183
|
+
setReportSubject={setReportSubject}
|
|
184
|
+
/>
|
|
185
|
+
)}
|
|
180
186
|
</DropdownMenuGroup>
|
|
181
187
|
</>
|
|
182
188
|
)}
|
|
@@ -185,6 +191,34 @@ export const ModView = forwardRef<ModViewRef, ModViewProps>(() => {
|
|
|
185
191
|
);
|
|
186
192
|
});
|
|
187
193
|
|
|
194
|
+
export function DeleteButton({
|
|
195
|
+
message,
|
|
196
|
+
}: {
|
|
197
|
+
message: ChatMessageViewHydrated;
|
|
198
|
+
}) {
|
|
199
|
+
const deleteChatMessage = useDeleteChatMessage();
|
|
200
|
+
const [confirming, setConfirming] = useState(false);
|
|
201
|
+
const { onOpenChange } = useRootContext();
|
|
202
|
+
return (
|
|
203
|
+
<DropdownMenuItem
|
|
204
|
+
onPress={() => {
|
|
205
|
+
if (!message) return;
|
|
206
|
+
if (!confirming) {
|
|
207
|
+
setConfirming(true);
|
|
208
|
+
return;
|
|
209
|
+
}
|
|
210
|
+
deleteChatMessage(message.uri).then(() => {
|
|
211
|
+
onOpenChange?.(false);
|
|
212
|
+
});
|
|
213
|
+
}}
|
|
214
|
+
>
|
|
215
|
+
<Text color="destructive">
|
|
216
|
+
{confirming ? "Are you sure?" : "Delete message"}
|
|
217
|
+
</Text>
|
|
218
|
+
</DropdownMenuItem>
|
|
219
|
+
);
|
|
220
|
+
}
|
|
221
|
+
|
|
188
222
|
export function ReportButton({
|
|
189
223
|
message,
|
|
190
224
|
setReportModalOpen,
|
|
@@ -16,12 +16,14 @@ export function Fullscreen(props: {
|
|
|
16
16
|
const fullscreen = usePlayerStore((x) => x.fullscreen, playerId);
|
|
17
17
|
const setFullscreen = usePlayerStore((x) => x.setFullscreen, playerId);
|
|
18
18
|
const setSrc = usePlayerStore((x) => x.setSrc);
|
|
19
|
+
const setAutoplayFailed = usePlayerStore((x) => x.setAutoplayFailed);
|
|
19
20
|
|
|
20
21
|
const divRef = useRef<RNView>(null);
|
|
21
22
|
const videoRef = useRef<HTMLVideoElement | null>(null);
|
|
22
23
|
|
|
23
24
|
useEffect(() => {
|
|
24
25
|
setSrc(props.src);
|
|
26
|
+
setAutoplayFailed(false);
|
|
25
27
|
}, [props.src]);
|
|
26
28
|
|
|
27
29
|
useEffect(() => {
|
|
@@ -0,0 +1,86 @@
|
|
|
1
|
+
import { Play } from "lucide-react-native";
|
|
2
|
+
import { Pressable } from "react-native";
|
|
3
|
+
import { View, layout, usePlayerStore } from "../../..";
|
|
4
|
+
import { h, p, w } from "../../../ui";
|
|
5
|
+
|
|
6
|
+
export function AutoplayButton() {
|
|
7
|
+
const autoplayFailed = usePlayerStore((x) => x.autoplayFailed);
|
|
8
|
+
const setAutoplayFailed = usePlayerStore((x) => x.setAutoplayFailed);
|
|
9
|
+
const setMuted = usePlayerStore((x) => x.setMuted);
|
|
10
|
+
const setMuteWasForced = usePlayerStore((x) => x.setMuteWasForced);
|
|
11
|
+
const setUserInteraction = usePlayerStore((x) => x.setUserInteraction);
|
|
12
|
+
const videoRef = usePlayerStore((x) => x.videoRef);
|
|
13
|
+
|
|
14
|
+
const handlePlayButtonPress = () => {
|
|
15
|
+
if (videoRef && typeof videoRef === "object" && videoRef.current) {
|
|
16
|
+
videoRef.current
|
|
17
|
+
.play()
|
|
18
|
+
.then(() => {
|
|
19
|
+
setAutoplayFailed(false);
|
|
20
|
+
setUserInteraction();
|
|
21
|
+
})
|
|
22
|
+
.catch((err) => {
|
|
23
|
+
console.error("Manual play failed", err);
|
|
24
|
+
if (err.name === "NotAllowedError") {
|
|
25
|
+
setMuted(true);
|
|
26
|
+
videoRef.current!.muted = true;
|
|
27
|
+
videoRef
|
|
28
|
+
.current!.play()
|
|
29
|
+
.then(() => {
|
|
30
|
+
setAutoplayFailed(false);
|
|
31
|
+
setMuteWasForced(true);
|
|
32
|
+
setUserInteraction();
|
|
33
|
+
})
|
|
34
|
+
.catch((err) => {
|
|
35
|
+
console.error("Manual muted play also failed", err);
|
|
36
|
+
});
|
|
37
|
+
}
|
|
38
|
+
});
|
|
39
|
+
}
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
if (!autoplayFailed) return null;
|
|
43
|
+
|
|
44
|
+
return (
|
|
45
|
+
<View
|
|
46
|
+
style={[
|
|
47
|
+
layout.position.absolute,
|
|
48
|
+
layout.flex.center,
|
|
49
|
+
h.percent[100],
|
|
50
|
+
w.percent[100],
|
|
51
|
+
]}
|
|
52
|
+
>
|
|
53
|
+
<Pressable
|
|
54
|
+
onPress={handlePlayButtonPress}
|
|
55
|
+
style={[
|
|
56
|
+
{
|
|
57
|
+
flexDirection: "column",
|
|
58
|
+
alignItems: "center",
|
|
59
|
+
justifyContent: "center",
|
|
60
|
+
gap: 8,
|
|
61
|
+
},
|
|
62
|
+
]}
|
|
63
|
+
>
|
|
64
|
+
<View
|
|
65
|
+
style={[
|
|
66
|
+
p[4],
|
|
67
|
+
{
|
|
68
|
+
backgroundColor: "rgba(200,200,255, 0.1)",
|
|
69
|
+
borderRadius: 999,
|
|
70
|
+
borderWidth: 2,
|
|
71
|
+
borderColor: "rgba(200,200,255, 0.45)",
|
|
72
|
+
boxShadow: "0 0px 4px rgba(0, 0, 0, 1)",
|
|
73
|
+
shadowColor: "rgba(0, 0, 0, 1)",
|
|
74
|
+
},
|
|
75
|
+
]}
|
|
76
|
+
>
|
|
77
|
+
<Play
|
|
78
|
+
size="48"
|
|
79
|
+
color="rgba(120,120,120,0.3)"
|
|
80
|
+
fill="rgba(200,200,255,1)"
|
|
81
|
+
/>
|
|
82
|
+
</View>
|
|
83
|
+
</Pressable>
|
|
84
|
+
</View>
|
|
85
|
+
);
|
|
86
|
+
}
|
|
@@ -154,6 +154,7 @@ const VideoElement = forwardRef<
|
|
|
154
154
|
playerEvent(url, now.toISOString(), evType, {});
|
|
155
155
|
};
|
|
156
156
|
const [firstAttempt, setFirstAttempt] = useState(true);
|
|
157
|
+
const setAutoplayFailed = usePlayerStore((x) => x.setAutoplayFailed);
|
|
157
158
|
|
|
158
159
|
const localVideoRef = props.videoRef ?? useRef<HTMLVideoElement | null>(null);
|
|
159
160
|
|
|
@@ -206,13 +207,22 @@ const VideoElement = forwardRef<
|
|
|
206
207
|
})
|
|
207
208
|
.catch((err) => {
|
|
208
209
|
console.error("Muted play also failed", err);
|
|
210
|
+
setAutoplayFailed(true);
|
|
209
211
|
});
|
|
210
212
|
}
|
|
213
|
+
} else {
|
|
214
|
+
// For other errors (not NotAllowedError), also show play button
|
|
215
|
+
setAutoplayFailed(true);
|
|
211
216
|
}
|
|
212
217
|
});
|
|
213
218
|
}
|
|
214
219
|
};
|
|
215
220
|
|
|
221
|
+
const handlePlaying = (e) => {
|
|
222
|
+
setAutoplayFailed(false);
|
|
223
|
+
event("playing")(e);
|
|
224
|
+
};
|
|
225
|
+
|
|
216
226
|
useEffect(() => {
|
|
217
227
|
return () => {
|
|
218
228
|
setStatus(PlayerStatus.START);
|
|
@@ -275,7 +285,7 @@ const VideoElement = forwardRef<
|
|
|
275
285
|
onLoadStart={event("loadstart")}
|
|
276
286
|
onPause={event("pause")}
|
|
277
287
|
onPlay={event("play")}
|
|
278
|
-
onPlaying={
|
|
288
|
+
onPlaying={handlePlaying}
|
|
279
289
|
onRateChange={event("ratechange")}
|
|
280
290
|
onSeeked={event("seeked")}
|
|
281
291
|
onSeeking={event("seeking")}
|
|
@@ -7,6 +7,7 @@ export interface WebRTCDiagnostics {
|
|
|
7
7
|
rtcSessionDescription: boolean;
|
|
8
8
|
getUserMedia: boolean;
|
|
9
9
|
getDisplayMedia: boolean;
|
|
10
|
+
isHwH264Supported: boolean;
|
|
10
11
|
errors: string[];
|
|
11
12
|
warnings: string[];
|
|
12
13
|
}
|
|
@@ -19,6 +20,7 @@ export function useWebRTCDiagnostics(): WebRTCDiagnostics {
|
|
|
19
20
|
rtcSessionDescription: false,
|
|
20
21
|
getUserMedia: false,
|
|
21
22
|
getDisplayMedia: false,
|
|
23
|
+
isHwH264Supported: false,
|
|
22
24
|
errors: [],
|
|
23
25
|
warnings: [],
|
|
24
26
|
});
|
|
@@ -27,6 +29,23 @@ export function useWebRTCDiagnostics(): WebRTCDiagnostics {
|
|
|
27
29
|
const errors: string[] = [];
|
|
28
30
|
const warnings: string[] = [];
|
|
29
31
|
|
|
32
|
+
const checkH264Support = async (): Promise<boolean> => {
|
|
33
|
+
try {
|
|
34
|
+
const pc = new RTCPeerConnection();
|
|
35
|
+
const offer = await pc.createOffer();
|
|
36
|
+
pc.close();
|
|
37
|
+
|
|
38
|
+
if (offer.sdp) {
|
|
39
|
+
const h264Match = offer.sdp.search(/rtpmap:([0-9]+) H264/g);
|
|
40
|
+
return h264Match !== -1;
|
|
41
|
+
}
|
|
42
|
+
return false;
|
|
43
|
+
} catch (error) {
|
|
44
|
+
console.warn("Failed to check H.264 support:", error);
|
|
45
|
+
return false;
|
|
46
|
+
}
|
|
47
|
+
};
|
|
48
|
+
|
|
30
49
|
// Check if we're in a browser environment
|
|
31
50
|
if (typeof window === "undefined") {
|
|
32
51
|
errors.push("Running in non-browser environment");
|
|
@@ -37,6 +56,7 @@ export function useWebRTCDiagnostics(): WebRTCDiagnostics {
|
|
|
37
56
|
rtcSessionDescription: false,
|
|
38
57
|
getUserMedia: false,
|
|
39
58
|
getDisplayMedia: false,
|
|
59
|
+
isHwH264Supported: false,
|
|
40
60
|
errors,
|
|
41
61
|
warnings,
|
|
42
62
|
});
|
|
@@ -105,22 +125,45 @@ export function useWebRTCDiagnostics(): WebRTCDiagnostics {
|
|
|
105
125
|
|
|
106
126
|
const browserSupport = rtcPeerConnection && rtcSessionDescription;
|
|
107
127
|
|
|
108
|
-
|
|
109
|
-
|
|
110
|
-
|
|
111
|
-
|
|
112
|
-
|
|
113
|
-
|
|
114
|
-
|
|
115
|
-
|
|
116
|
-
|
|
117
|
-
|
|
128
|
+
// Check H.264 support asynchronously
|
|
129
|
+
if (rtcPeerConnection) {
|
|
130
|
+
checkH264Support().then((isHwH264Supported) => {
|
|
131
|
+
if (!isHwH264Supported) {
|
|
132
|
+
warnings.push(
|
|
133
|
+
"H.264 hardware acceleration is not supported\n In Firefox, try enabling 'media.webrtc.hw.h264.enabled' in about:config",
|
|
134
|
+
);
|
|
135
|
+
}
|
|
136
|
+
setDiagnostics({
|
|
137
|
+
done: true,
|
|
138
|
+
browserSupport,
|
|
139
|
+
rtcPeerConnection,
|
|
140
|
+
rtcSessionDescription,
|
|
141
|
+
getUserMedia,
|
|
142
|
+
getDisplayMedia,
|
|
143
|
+
isHwH264Supported,
|
|
144
|
+
errors,
|
|
145
|
+
warnings,
|
|
146
|
+
});
|
|
147
|
+
});
|
|
148
|
+
} else {
|
|
149
|
+
setDiagnostics({
|
|
150
|
+
done: true,
|
|
151
|
+
browserSupport,
|
|
152
|
+
rtcPeerConnection,
|
|
153
|
+
rtcSessionDescription,
|
|
154
|
+
getUserMedia,
|
|
155
|
+
getDisplayMedia,
|
|
156
|
+
isHwH264Supported: false,
|
|
157
|
+
errors,
|
|
158
|
+
warnings,
|
|
159
|
+
});
|
|
160
|
+
}
|
|
118
161
|
}, []);
|
|
119
162
|
|
|
120
163
|
return diagnostics;
|
|
121
164
|
}
|
|
122
165
|
|
|
123
|
-
export function logWebRTCDiagnostics() {
|
|
166
|
+
export async function logWebRTCDiagnostics() {
|
|
124
167
|
console.group("WebRTC Diagnostics");
|
|
125
168
|
|
|
126
169
|
// Log browser support
|
|
@@ -133,17 +176,32 @@ export function logWebRTCDiagnostics() {
|
|
|
133
176
|
console.log("User Agent:", navigator.userAgent);
|
|
134
177
|
console.log("Protocol:", location.protocol);
|
|
135
178
|
console.log("Host:", location.hostname);
|
|
136
|
-
|
|
137
|
-
// Test basic WebRTC functionality
|
|
179
|
+
console.groupEnd();
|
|
138
180
|
if (window.RTCPeerConnection) {
|
|
139
181
|
try {
|
|
140
182
|
const pc = new RTCPeerConnection();
|
|
141
|
-
|
|
183
|
+
// Check H.264 support
|
|
184
|
+
try {
|
|
185
|
+
const offer = await pc.createOffer({ offerToReceiveVideo: true });
|
|
186
|
+
const isHwH264Supported = offer.sdp
|
|
187
|
+
? offer.sdp.search(/rtpmap:([0-9]+) H264/g) !== -1
|
|
188
|
+
: false;
|
|
189
|
+
console.group("WebRTC Peer Connection Test");
|
|
190
|
+
console.log("RTCPeerConnection creation: ✓ Success");
|
|
191
|
+
console.log(
|
|
192
|
+
"H.264 support:",
|
|
193
|
+
isHwH264Supported ? "✓ Supported" : "✗ Not supported",
|
|
194
|
+
);
|
|
195
|
+
} catch (error) {
|
|
196
|
+
console.group("WebRTC Peer Connection Test");
|
|
197
|
+
console.error("H.264 check failed:", error);
|
|
198
|
+
}
|
|
199
|
+
|
|
142
200
|
pc.close();
|
|
143
201
|
} catch (error) {
|
|
202
|
+
console.group("WebRTC Peer Connection Test");
|
|
144
203
|
console.error("RTCPeerConnection creation: ✗ Failed", error);
|
|
145
204
|
}
|
|
146
205
|
}
|
|
147
|
-
|
|
148
206
|
console.groupEnd();
|
|
149
207
|
}
|
|
@@ -76,6 +76,7 @@ export const useCreateChatMessage = () => {
|
|
|
76
76
|
await rt.detectFacets(pdsAgent);
|
|
77
77
|
|
|
78
78
|
const record: PlaceStreamChatMessage.Record = {
|
|
79
|
+
$type: "place.stream.chat.message",
|
|
79
80
|
text: msg.text,
|
|
80
81
|
createdAt: new Date().toISOString(),
|
|
81
82
|
streamer: streamerProfile.did,
|
|
@@ -119,6 +120,28 @@ export const useCreateChatMessage = () => {
|
|
|
119
120
|
};
|
|
120
121
|
};
|
|
121
122
|
|
|
123
|
+
export const useDeleteChatMessage = () => {
|
|
124
|
+
const pdsAgent = usePDSAgent();
|
|
125
|
+
if (!pdsAgent) {
|
|
126
|
+
throw new Error("No PDS agent found");
|
|
127
|
+
}
|
|
128
|
+
const userDID = useDID();
|
|
129
|
+
if (!userDID) {
|
|
130
|
+
throw new Error("No user DID found");
|
|
131
|
+
}
|
|
132
|
+
return async (uri: string) => {
|
|
133
|
+
const rkey = uri.split("/").pop();
|
|
134
|
+
if (!rkey) {
|
|
135
|
+
throw new Error("No rkey found");
|
|
136
|
+
}
|
|
137
|
+
return await pdsAgent.com.atproto.repo.deleteRecord({
|
|
138
|
+
repo: userDID,
|
|
139
|
+
collection: "place.stream.chat.message",
|
|
140
|
+
rkey: rkey,
|
|
141
|
+
});
|
|
142
|
+
};
|
|
143
|
+
};
|
|
144
|
+
|
|
122
145
|
const buildSortedChatList = (
|
|
123
146
|
chatIndex: { [key: string]: ChatMessageViewHydrated },
|
|
124
147
|
existingChatList: ChatMessageViewHydrated[],
|
|
@@ -273,6 +296,7 @@ export const reduceChatIncremental = (
|
|
|
273
296
|
processedMessage = {
|
|
274
297
|
...message,
|
|
275
298
|
replyTo: {
|
|
299
|
+
$type: "place.stream.chat.defs#messageView",
|
|
276
300
|
cid: parentMsg.cid,
|
|
277
301
|
uri: parentMsg.uri,
|
|
278
302
|
author: parentMsg.author,
|
|
@@ -20,7 +20,7 @@ export const handleWebSocketMessages = (
|
|
|
20
20
|
state: LivestreamState,
|
|
21
21
|
messages: any[],
|
|
22
22
|
): LivestreamState => {
|
|
23
|
-
for (
|
|
23
|
+
for (let message of messages) {
|
|
24
24
|
if (PlaceStreamLivestream.isLivestreamView(message)) {
|
|
25
25
|
const newLivestream = message as LivestreamViewHydrated;
|
|
26
26
|
const oldLivestream = state.livestream;
|
|
@@ -41,11 +41,13 @@ export const handleWebSocketMessages = (
|
|
|
41
41
|
livestream: newLivestream,
|
|
42
42
|
};
|
|
43
43
|
} else if (PlaceStreamLivestream.isViewerCount(message)) {
|
|
44
|
+
message = message as PlaceStreamLivestream.ViewerCount;
|
|
44
45
|
state = {
|
|
45
46
|
...state,
|
|
46
47
|
viewers: message.count,
|
|
47
48
|
};
|
|
48
49
|
} else if (PlaceStreamChatDefs.isMessageView(message)) {
|
|
50
|
+
message = message as PlaceStreamChatDefs.MessageView;
|
|
49
51
|
// Explicitly map MessageView to MessageViewHydrated
|
|
50
52
|
const hydrated: ChatMessageViewHydrated = {
|
|
51
53
|
uri: message.uri,
|
|
@@ -74,6 +76,7 @@ export const handleWebSocketMessages = (
|
|
|
74
76
|
const block = message as PlaceStreamDefs.BlockView;
|
|
75
77
|
state = reduceChat(state, [], [block], []);
|
|
76
78
|
} else if (PlaceStreamDefs.isRenditions(message)) {
|
|
79
|
+
message = message as PlaceStreamDefs.Renditions;
|
|
77
80
|
state = {
|
|
78
81
|
...state,
|
|
79
82
|
renditions: message.renditions,
|
|
@@ -1,3 +1,4 @@
|
|
|
1
|
+
import { randomUUID } from "crypto";
|
|
1
2
|
import React, { useCallback, useMemo, useState } from "react";
|
|
2
3
|
import { StoreApi } from "zustand";
|
|
3
4
|
import { PlayerContext } from "./context";
|
|
@@ -33,7 +34,7 @@ export const PlayerProvider: React.FC<PlayerProviderProps> = ({
|
|
|
33
34
|
);
|
|
34
35
|
|
|
35
36
|
const createPlayer = useCallback((id?: string) => {
|
|
36
|
-
const playerId = id ||
|
|
37
|
+
const playerId = id || randomUUID();
|
|
37
38
|
const playerStore = makePlayerStore(playerId);
|
|
38
39
|
|
|
39
40
|
setPlayers((prev) => ({
|
|
@@ -142,6 +142,12 @@ export interface PlayerState {
|
|
|
142
142
|
/** Function to set the muteWasForced flag */
|
|
143
143
|
setMuteWasForced: (muteWasForced: boolean) => void;
|
|
144
144
|
|
|
145
|
+
/** Flag indicating if autoplay failed and needs user interaction */
|
|
146
|
+
autoplayFailed: boolean;
|
|
147
|
+
|
|
148
|
+
/** Function to set the autoplayFailed flag */
|
|
149
|
+
setAutoplayFailed: (autoplayFailed: boolean) => void;
|
|
150
|
+
|
|
145
151
|
/** Flag indicating if the player is embedded in another context */
|
|
146
152
|
embedded: boolean;
|
|
147
153
|
|
|
@@ -99,6 +99,10 @@ export const makePlayerStore = (id?: string): StoreApi<PlayerState> => {
|
|
|
99
99
|
setMuteWasForced: (muteWasForced: boolean) =>
|
|
100
100
|
set(() => ({ muteWasForced })),
|
|
101
101
|
|
|
102
|
+
autoplayFailed: false,
|
|
103
|
+
setAutoplayFailed: (autoplayFailed: boolean) =>
|
|
104
|
+
set(() => ({ autoplayFailed })),
|
|
105
|
+
|
|
102
106
|
embedded: false,
|
|
103
107
|
setEmbedded: (embedded: boolean) => set(() => ({ embedded })),
|
|
104
108
|
|
|
@@ -248,6 +248,7 @@ export function useCreateStreamRecord() {
|
|
|
248
248
|
}
|
|
249
249
|
|
|
250
250
|
const record: PlaceStreamLivestream.Record = {
|
|
251
|
+
$type: "place.stream.livestream",
|
|
251
252
|
title: title,
|
|
252
253
|
url: finalUrl,
|
|
253
254
|
createdAt: new Date().toISOString(),
|
|
@@ -313,6 +314,7 @@ export function useUpdateStreamRecord(customUrl: string | null = null) {
|
|
|
313
314
|
}
|
|
314
315
|
|
|
315
316
|
const record: PlaceStreamLivestream.Record = {
|
|
317
|
+
$type: "place.stream.livestream",
|
|
316
318
|
title: title,
|
|
317
319
|
url: finalUrl,
|
|
318
320
|
createdAt: new Date().toISOString(),
|