@streamplace/components 0.7.21 → 0.7.26
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 +0 -6
- package/dist/components/chat/chat-message.js +9 -4
- package/dist/components/chat/chat.js +14 -4
- package/dist/components/mobile-player/ui/autoplay-button.js +1 -1
- package/dist/components/mobile-player/video-async.native.js +4 -4
- package/dist/components/mobile-player/video.js +3 -3
- package/dist/components/mobile-player/webrtc-diagnostics.js +67 -13
- package/dist/index.js +4 -1
- package/dist/lib/system-messages.js +1 -0
- package/dist/livestream-store/chat.js +11 -0
- package/dist/livestream-store/stream-key.js +1 -0
- package/dist/livestream-store/websocket-consumer.js +4 -1
- package/dist/player-store/player-store.js +0 -4
- package/dist/storage/index.js +5 -0
- package/dist/storage/lock.js +40 -0
- package/dist/storage/storage.js +14 -0
- package/dist/storage/storage.native.js +44 -0
- package/dist/storage/storage.shared.js +2 -0
- package/dist/streamplace-provider/index.js +1 -0
- package/dist/streamplace-store/stream.js +2 -0
- package/dist/streamplace-store/streamplace-store.js +75 -2
- package/dist/streamplace-store/xrpc.js +10 -1
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +5 -4
- package/src/components/chat/chat-box.tsx +0 -11
- package/src/components/chat/chat-message.tsx +8 -4
- package/src/components/chat/chat.tsx +20 -4
- package/src/components/mobile-player/ui/autoplay-button.tsx +2 -2
- package/src/components/mobile-player/video-async.native.tsx +6 -4
- package/src/components/mobile-player/video.tsx +6 -3
- package/src/components/mobile-player/webrtc-diagnostics.tsx +73 -15
- package/src/index.tsx +4 -0
- package/src/lib/system-messages.ts +1 -0
- package/src/livestream-store/chat.tsx +15 -0
- package/src/livestream-store/stream-key.tsx +1 -0
- package/src/livestream-store/websocket-consumer.tsx +4 -1
- package/src/player-store/player-state.tsx +0 -12
- package/src/player-store/player-store.tsx +0 -8
- package/src/storage/index.tsx +3 -0
- package/src/storage/lock.tsx +38 -0
- package/src/storage/storage.native.tsx +42 -0
- package/src/storage/storage.shared.tsx +5 -0
- package/src/storage/storage.tsx +15 -0
- package/src/streamplace-provider/index.tsx +2 -1
- package/src/streamplace-store/stream.tsx +2 -0
- package/src/streamplace-store/streamplace-store.tsx +92 -2
- package/src/streamplace-store/xrpc.tsx +9 -2
- package/tsconfig.json +2 -1
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -2,6 +2,7 @@ import { SessionManager } from "@atproto/api/dist/session-manager";
|
|
|
2
2
|
import { useContext } from "react";
|
|
3
3
|
import { PlaceStreamChatProfile, PlaceStreamLivestream } from "streamplace";
|
|
4
4
|
import { createStore, StoreApi, useStore } from "zustand";
|
|
5
|
+
import storage from "../storage";
|
|
5
6
|
import { StreamplaceContext } from "../streamplace-provider/context";
|
|
6
7
|
|
|
7
8
|
// there are three categories of XRPC that we need to handle:
|
|
@@ -28,9 +29,15 @@ export interface StreamplaceState {
|
|
|
28
29
|
liveUsersRefresh: number;
|
|
29
30
|
liveUsersLoading: boolean;
|
|
30
31
|
liveUsersError: string | null;
|
|
31
|
-
oauthSession: SessionManager | null;
|
|
32
|
+
oauthSession: SessionManager | null | undefined;
|
|
32
33
|
handle: string | null;
|
|
33
34
|
chatProfile: PlaceStreamChatProfile.Record | null;
|
|
35
|
+
|
|
36
|
+
// Volume state
|
|
37
|
+
volume: number;
|
|
38
|
+
muted: boolean;
|
|
39
|
+
setVolume: (volume: number) => void;
|
|
40
|
+
setMuted: (muted: boolean) => void;
|
|
34
41
|
}
|
|
35
42
|
|
|
36
43
|
export type StreamplaceStore = StoreApi<StreamplaceState>;
|
|
@@ -40,7 +47,10 @@ export const makeStreamplaceStore = ({
|
|
|
40
47
|
}: {
|
|
41
48
|
url: string;
|
|
42
49
|
}): StoreApi<StreamplaceState> => {
|
|
43
|
-
|
|
50
|
+
const VOLUME_STORAGE_KEY = "globalVolume";
|
|
51
|
+
const MUTED_STORAGE_KEY = "globalMuted";
|
|
52
|
+
|
|
53
|
+
const store = createStore<StreamplaceState>()((set) => ({
|
|
44
54
|
url,
|
|
45
55
|
liveUsers: null,
|
|
46
56
|
setLiveUsers: (opts: {
|
|
@@ -59,7 +69,73 @@ export const makeStreamplaceStore = ({
|
|
|
59
69
|
oauthSession: null,
|
|
60
70
|
handle: null,
|
|
61
71
|
chatProfile: null,
|
|
72
|
+
|
|
73
|
+
// Volume state - start with defaults
|
|
74
|
+
volume: 1.0,
|
|
75
|
+
muted: false,
|
|
76
|
+
|
|
77
|
+
setVolume: (volume: number) => {
|
|
78
|
+
// Ensure the value is finite and within bounds
|
|
79
|
+
if (!Number.isFinite(volume)) {
|
|
80
|
+
console.warn("Invalid volume value:", volume, "- using 1.0");
|
|
81
|
+
volume = 1.0;
|
|
82
|
+
}
|
|
83
|
+
const clampedVolume = Math.max(0, Math.min(1, volume));
|
|
84
|
+
|
|
85
|
+
set({ volume: clampedVolume });
|
|
86
|
+
|
|
87
|
+
// Auto-unmute if volume > 0
|
|
88
|
+
if (clampedVolume > 0) {
|
|
89
|
+
set({ muted: false });
|
|
90
|
+
storage.setItem(MUTED_STORAGE_KEY, "false").catch(console.error);
|
|
91
|
+
}
|
|
92
|
+
|
|
93
|
+
storage
|
|
94
|
+
.setItem(VOLUME_STORAGE_KEY, clampedVolume.toString())
|
|
95
|
+
.catch(console.error);
|
|
96
|
+
},
|
|
97
|
+
|
|
98
|
+
setMuted: (muted: boolean) => {
|
|
99
|
+
set({ muted });
|
|
100
|
+
storage.setItem(MUTED_STORAGE_KEY, muted.toString()).catch(console.error);
|
|
101
|
+
},
|
|
62
102
|
}));
|
|
103
|
+
|
|
104
|
+
// Load initial volume state from storage asynchronously
|
|
105
|
+
(async () => {
|
|
106
|
+
try {
|
|
107
|
+
const storedVolume = await storage.getItem(VOLUME_STORAGE_KEY);
|
|
108
|
+
const storedMuted = await storage.getItem(MUTED_STORAGE_KEY);
|
|
109
|
+
|
|
110
|
+
let initialVolume = 1.0;
|
|
111
|
+
let initialMuted = false;
|
|
112
|
+
|
|
113
|
+
if (storedVolume) {
|
|
114
|
+
const parsedVolume = parseFloat(storedVolume);
|
|
115
|
+
if (
|
|
116
|
+
Number.isFinite(parsedVolume) &&
|
|
117
|
+
parsedVolume >= 0 &&
|
|
118
|
+
parsedVolume <= 1
|
|
119
|
+
) {
|
|
120
|
+
initialVolume = parsedVolume;
|
|
121
|
+
}
|
|
122
|
+
}
|
|
123
|
+
|
|
124
|
+
if (storedMuted) {
|
|
125
|
+
initialMuted = storedMuted === "true";
|
|
126
|
+
}
|
|
127
|
+
|
|
128
|
+
// Update the store with loaded values
|
|
129
|
+
store.setState({
|
|
130
|
+
volume: initialVolume,
|
|
131
|
+
muted: initialMuted,
|
|
132
|
+
});
|
|
133
|
+
} catch (e) {
|
|
134
|
+
console.warn("Failed to load volume settings from storage:", e);
|
|
135
|
+
}
|
|
136
|
+
})();
|
|
137
|
+
|
|
138
|
+
return store;
|
|
63
139
|
};
|
|
64
140
|
|
|
65
141
|
export function getStreamplaceStoreFromContext(): StreamplaceStore {
|
|
@@ -87,3 +163,17 @@ export const useSetHandle = (): ((handle: string) => void) => {
|
|
|
87
163
|
const store = getStreamplaceStoreFromContext();
|
|
88
164
|
return (handle: string) => store.setState({ handle });
|
|
89
165
|
};
|
|
166
|
+
|
|
167
|
+
// Volume convenience hooks
|
|
168
|
+
export const useVolume = () => useStreamplaceStore((x) => x.volume);
|
|
169
|
+
export const useMuted = () => useStreamplaceStore((x) => x.muted);
|
|
170
|
+
export const useSetVolume = () => useStreamplaceStore((x) => x.setVolume);
|
|
171
|
+
export const useSetMuted = () => useStreamplaceStore((x) => x.setMuted);
|
|
172
|
+
|
|
173
|
+
// Composite hook for effective volume (0 if muted) - used by video components
|
|
174
|
+
export const useEffectiveVolume = () =>
|
|
175
|
+
useStreamplaceStore((state) => {
|
|
176
|
+
const effectiveVolume = state.muted ? 0 : state.volume;
|
|
177
|
+
// Ensure we always return a finite number for HTMLMediaElement.volume
|
|
178
|
+
return Number.isFinite(effectiveVolume) ? effectiveVolume : 1.0;
|
|
179
|
+
});
|
|
@@ -4,10 +4,17 @@ import { useStreamplaceStore } from ".";
|
|
|
4
4
|
|
|
5
5
|
export function usePDSAgent(): StreamplaceAgent | null {
|
|
6
6
|
const oauthSession = useStreamplaceStore((state) => state.oauthSession);
|
|
7
|
-
|
|
7
|
+
// oauthsession is
|
|
8
|
+
// - undefined when loading
|
|
9
|
+
// - null when logged out, and
|
|
10
|
+
// - SessionManager when logged in
|
|
8
11
|
return useMemo(() => {
|
|
9
12
|
if (!oauthSession) {
|
|
10
|
-
return null;
|
|
13
|
+
if (oauthSession === undefined) return null;
|
|
14
|
+
// TODO: change once we allow unauthed requests + profile indexing
|
|
15
|
+
// it's bluesky's AppView b/c otherwise we'd have goosewithpipe.jpg
|
|
16
|
+
// showing up everywhere
|
|
17
|
+
return new StreamplaceAgent("https://public.api.bsky.app"); // nodeUrl);
|
|
11
18
|
}
|
|
12
19
|
|
|
13
20
|
return new StreamplaceAgent(oauthSession);
|