@streamplace/components 0.7.18 → 0.7.21

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.
Files changed (122) hide show
  1. package/dist/assets/emoji-data.json +19371 -0
  2. package/dist/components/chat/chat-box.js +319 -0
  3. package/dist/components/chat/chat-message.js +87 -0
  4. package/dist/components/chat/chat.js +150 -0
  5. package/dist/components/chat/emoji-suggestions.js +35 -0
  6. package/dist/components/chat/mention-suggestions.js +42 -0
  7. package/dist/components/chat/mod-view.js +112 -0
  8. package/dist/components/chat/system-message.js +19 -0
  9. package/dist/components/dashboard/chat-panel.js +38 -0
  10. package/dist/components/dashboard/header.js +80 -0
  11. package/dist/components/dashboard/index.js +14 -0
  12. package/dist/components/dashboard/information-widget.js +234 -0
  13. package/dist/components/dashboard/mod-actions.js +71 -0
  14. package/dist/components/dashboard/problems.js +74 -0
  15. package/dist/components/icons/bluesky-icon.js +9 -0
  16. package/dist/components/keep-awake.js +7 -0
  17. package/dist/components/keep-awake.native.js +16 -0
  18. package/dist/components/mobile-player/fullscreen.js +76 -0
  19. package/dist/components/mobile-player/fullscreen.native.js +141 -0
  20. package/dist/components/mobile-player/player.js +94 -0
  21. package/dist/components/mobile-player/props.js +2 -0
  22. package/dist/components/mobile-player/shared.js +54 -0
  23. package/dist/components/mobile-player/ui/autoplay-button.js +68 -0
  24. package/dist/components/mobile-player/ui/countdown.js +83 -0
  25. package/dist/components/mobile-player/ui/index.js +12 -0
  26. package/dist/components/mobile-player/ui/input.js +42 -0
  27. package/dist/components/mobile-player/ui/metrics.js +44 -0
  28. package/dist/components/mobile-player/ui/report-modal.js +90 -0
  29. package/dist/components/mobile-player/ui/streamer-context-menu.js +7 -0
  30. package/dist/components/mobile-player/ui/streamer-loading-overlay.js +104 -0
  31. package/dist/components/mobile-player/ui/viewer-context-menu.js +51 -0
  32. package/dist/components/mobile-player/ui/viewer-loading-overlay.js +49 -0
  33. package/dist/components/mobile-player/ui/viewers.js +23 -0
  34. package/dist/components/mobile-player/use-webrtc.js +243 -0
  35. package/dist/components/mobile-player/video-async.native.js +276 -0
  36. package/dist/components/mobile-player/video-retry.js +29 -0
  37. package/dist/components/mobile-player/video.js +475 -0
  38. package/dist/components/mobile-player/video.native.js +56 -0
  39. package/dist/components/mobile-player/webrtc-diagnostics.js +110 -0
  40. package/dist/components/mobile-player/webrtc-primitives.js +27 -0
  41. package/dist/components/mobile-player/webrtc-primitives.native.js +8 -0
  42. package/dist/components/share/sharesheet.js +91 -0
  43. package/dist/components/ui/button.js +223 -0
  44. package/dist/components/ui/dialog.js +206 -0
  45. package/dist/components/ui/dropdown.js +172 -0
  46. package/dist/components/ui/icons.js +25 -0
  47. package/dist/components/ui/index.js +34 -0
  48. package/dist/components/ui/info-box.js +31 -0
  49. package/dist/components/ui/info-row.js +23 -0
  50. package/dist/components/ui/input.js +205 -0
  51. package/dist/components/ui/loader.js +10 -0
  52. package/dist/components/ui/primitives/button.js +125 -0
  53. package/dist/components/ui/primitives/input.js +206 -0
  54. package/dist/components/ui/primitives/modal.js +206 -0
  55. package/dist/components/ui/primitives/text.js +292 -0
  56. package/dist/components/ui/resizeable.js +121 -0
  57. package/dist/components/ui/slider.js +5 -0
  58. package/dist/components/ui/text.js +177 -0
  59. package/dist/components/ui/textarea.js +19 -0
  60. package/dist/components/ui/toast.js +175 -0
  61. package/dist/components/ui/view.js +252 -0
  62. package/dist/hooks/index.js +14 -0
  63. package/dist/hooks/useAvatars.js +35 -0
  64. package/dist/hooks/useCameraToggle.js +12 -0
  65. package/dist/hooks/useKeyboard.js +36 -0
  66. package/dist/hooks/useKeyboardSlide.js +14 -0
  67. package/dist/hooks/useLivestreamInfo.js +69 -0
  68. package/dist/hooks/useOuterAndInnerDimensions.js +30 -0
  69. package/dist/hooks/usePlayerDimensions.js +22 -0
  70. package/dist/hooks/usePointerDevice.js +71 -0
  71. package/dist/hooks/useSegmentDimensions.js +17 -0
  72. package/dist/hooks/useSegmentTiming.js +65 -0
  73. package/dist/index.js +34 -0
  74. package/dist/lib/browser.js +35 -0
  75. package/dist/lib/facet.js +92 -0
  76. package/dist/lib/system-messages.js +101 -0
  77. package/dist/lib/theme/atoms.js +646 -0
  78. package/dist/lib/theme/atoms.types.js +6 -0
  79. package/dist/lib/theme/index.js +35 -0
  80. package/dist/lib/theme/theme.js +256 -0
  81. package/dist/lib/theme/tokens.js +659 -0
  82. package/dist/lib/utils.js +105 -0
  83. package/dist/livestream-provider/index.js +30 -0
  84. package/dist/livestream-provider/websocket.js +45 -0
  85. package/dist/livestream-store/chat.js +308 -0
  86. package/dist/livestream-store/context.js +5 -0
  87. package/dist/livestream-store/index.js +7 -0
  88. package/dist/livestream-store/livestream-state.js +2 -0
  89. package/dist/livestream-store/livestream-store.js +58 -0
  90. package/dist/livestream-store/problems.js +76 -0
  91. package/dist/livestream-store/stream-key.js +88 -0
  92. package/dist/livestream-store/websocket-consumer.js +94 -0
  93. package/dist/player-store/context.js +5 -0
  94. package/dist/player-store/index.js +9 -0
  95. package/dist/player-store/player-provider.js +58 -0
  96. package/dist/player-store/player-state.js +25 -0
  97. package/dist/player-store/player-store.js +201 -0
  98. package/dist/player-store/single-player-provider.js +121 -0
  99. package/dist/streamplace-provider/context.js +5 -0
  100. package/dist/streamplace-provider/index.js +20 -0
  101. package/dist/streamplace-provider/poller.js +49 -0
  102. package/dist/streamplace-provider/xrpc.js +0 -0
  103. package/dist/streamplace-store/block.js +65 -0
  104. package/dist/streamplace-store/index.js +6 -0
  105. package/dist/streamplace-store/stream.js +247 -0
  106. package/dist/streamplace-store/streamplace-store.js +47 -0
  107. package/dist/streamplace-store/user.js +52 -0
  108. package/dist/streamplace-store/xrpc.js +15 -0
  109. package/dist/ui/index.js +79 -0
  110. package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
  111. package/package.json +5 -4
  112. package/src/components/chat/chat-box.tsx +3 -0
  113. package/src/components/chat/mod-view.tsx +39 -5
  114. package/src/components/mobile-player/fullscreen.tsx +2 -0
  115. package/src/components/mobile-player/ui/autoplay-button.tsx +86 -0
  116. package/src/components/mobile-player/ui/index.ts +1 -0
  117. package/src/components/mobile-player/video.tsx +11 -1
  118. package/src/livestream-store/chat.tsx +22 -0
  119. package/src/player-store/player-provider.tsx +2 -1
  120. package/src/player-store/player-state.tsx +6 -0
  121. package/src/player-store/player-store.tsx +4 -0
  122. package/tsconfig.tsbuildinfo +1 -0
@@ -0,0 +1,49 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.default = Poller;
4
+ const jsx_runtime_1 = require("react/jsx-runtime");
5
+ const react_1 = require("react");
6
+ const streamplace_1 = require("streamplace");
7
+ const streamplace_store_1 = require("../streamplace-store");
8
+ const xrpc_1 = require("../streamplace-store/xrpc");
9
+ function Poller({ children }) {
10
+ const url = (0, streamplace_store_1.useStreamplaceStore)((state) => state.url);
11
+ const setLiveUsers = (0, streamplace_store_1.useStreamplaceStore)((state) => state.setLiveUsers);
12
+ const did = (0, streamplace_store_1.useDID)();
13
+ const pdsAgent = (0, xrpc_1.usePDSAgent)();
14
+ const getChatProfile = (0, streamplace_store_1.useGetChatProfile)();
15
+ const getBskyProfile = (0, streamplace_store_1.useGetBskyProfile)();
16
+ const liveUserRefresh = (0, streamplace_store_1.useStreamplaceStore)((state) => state.liveUsersRefresh);
17
+ (0, react_1.useEffect)(() => {
18
+ if (pdsAgent && did) {
19
+ getChatProfile();
20
+ getBskyProfile();
21
+ }
22
+ }, [pdsAgent, did]);
23
+ (0, react_1.useEffect)(() => {
24
+ const agent = new streamplace_1.StreamplaceAgent(url);
25
+ const go = async () => {
26
+ setLiveUsers({
27
+ liveUsersLoading: true,
28
+ });
29
+ try {
30
+ const res = await agent.place.stream.live.getLiveUsers();
31
+ setLiveUsers({
32
+ liveUsers: res.data.streams || [],
33
+ liveUsersLoading: false,
34
+ liveUsersError: null,
35
+ });
36
+ }
37
+ catch (e) {
38
+ setLiveUsers({
39
+ liveUsersLoading: false,
40
+ liveUsersError: e.message,
41
+ });
42
+ }
43
+ };
44
+ go();
45
+ const handle = setInterval(go, 3000);
46
+ return () => clearInterval(handle);
47
+ }, [url, liveUserRefresh]);
48
+ return (0, jsx_runtime_1.jsx)(jsx_runtime_1.Fragment, { children: children });
49
+ }
File without changes
@@ -0,0 +1,65 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useCreateBlockRecord = useCreateBlockRecord;
4
+ exports.useCreateHideChatRecord = useCreateHideChatRecord;
5
+ const react_1 = require("react");
6
+ const xrpc_1 = require("./xrpc");
7
+ function useCreateBlockRecord() {
8
+ let agent = (0, xrpc_1.usePDSAgent)();
9
+ const [isLoading, setIsLoading] = (0, react_1.useState)(false);
10
+ const createBlock = async (subjectDID) => {
11
+ if (!agent) {
12
+ throw new Error("No PDS agent found");
13
+ }
14
+ if (!agent.did) {
15
+ throw new Error("No user DID found, assuming not logged in");
16
+ }
17
+ setIsLoading(true);
18
+ try {
19
+ const record = {
20
+ $type: "app.bsky.graph.block",
21
+ subject: subjectDID,
22
+ createdAt: new Date().toISOString(),
23
+ };
24
+ const result = await agent.com.atproto.repo.createRecord({
25
+ repo: agent.did,
26
+ collection: "app.bsky.graph.block",
27
+ record,
28
+ });
29
+ return result;
30
+ }
31
+ finally {
32
+ setIsLoading(false);
33
+ }
34
+ };
35
+ return { createBlock, isLoading };
36
+ }
37
+ function useCreateHideChatRecord() {
38
+ let agent = (0, xrpc_1.usePDSAgent)();
39
+ const [isLoading, setIsLoading] = (0, react_1.useState)(false);
40
+ const createHideChat = async (chatMessageUri) => {
41
+ if (!agent) {
42
+ throw new Error("No PDS agent found");
43
+ }
44
+ if (!agent.did) {
45
+ throw new Error("No user DID found, assuming not logged in");
46
+ }
47
+ setIsLoading(true);
48
+ try {
49
+ const record = {
50
+ $type: "place.stream.chat.gate",
51
+ hiddenMessage: chatMessageUri,
52
+ };
53
+ const result = await agent.com.atproto.repo.createRecord({
54
+ repo: agent.did,
55
+ collection: "place.stream.chat.gate",
56
+ record,
57
+ });
58
+ return result;
59
+ }
60
+ finally {
61
+ setIsLoading(false);
62
+ }
63
+ };
64
+ return { createHideChat, isLoading };
65
+ }
@@ -0,0 +1,6 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ const tslib_1 = require("tslib");
4
+ tslib_1.__exportStar(require("./stream"), exports);
5
+ tslib_1.__exportStar(require("./streamplace-store"), exports);
6
+ tslib_1.__exportStar(require("./user"), exports);
@@ -0,0 +1,247 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useCreateStreamRecord = useCreateStreamRecord;
4
+ exports.useUpdateStreamRecord = useUpdateStreamRecord;
5
+ const tslib_1 = require("tslib");
6
+ const api_1 = require("@atproto/api");
7
+ const streamplace_store_1 = require("./streamplace-store");
8
+ const xrpc_1 = require("./xrpc");
9
+ const package_json_1 = tslib_1.__importDefault(require("../../package.json"));
10
+ const react_1 = require("react");
11
+ const react_native_1 = require("react-native");
12
+ const browser_1 = require("../lib/browser");
13
+ const useUploadThumbnail = () => {
14
+ const abortRef = (0, react_1.useRef)(null);
15
+ (0, react_1.useEffect)(() => {
16
+ return () => {
17
+ // On unmount, abort any ongoing upload
18
+ abortRef.current?.abort();
19
+ };
20
+ }, []);
21
+ const uploadThumbnail = async (pdsAgent, customThumbnail) => {
22
+ if (!customThumbnail)
23
+ return undefined;
24
+ abortRef.current = new AbortController();
25
+ const { signal } = abortRef.current;
26
+ const maxTries = 3;
27
+ let lastError = null;
28
+ for (let tries = 0; tries < maxTries; tries++) {
29
+ try {
30
+ const thumbnail = await pdsAgent.uploadBlob(customThumbnail, {
31
+ signal,
32
+ });
33
+ if (thumbnail.success &&
34
+ thumbnail.data.blob.size === customThumbnail.size) {
35
+ console.log("Successfully uploaded thumbnail");
36
+ return thumbnail.data.blob;
37
+ }
38
+ else {
39
+ console.warn(`Blob size mismatch (attempt ${tries + 1}): received ${thumbnail.data.blob.size}, expected ${customThumbnail.size}`);
40
+ }
41
+ }
42
+ catch (e) {
43
+ if (signal.aborted) {
44
+ console.warn("Upload aborted");
45
+ return undefined;
46
+ }
47
+ lastError = e;
48
+ console.warn(`Error uploading thumbnail (attempt ${tries + 1}): ${e}`);
49
+ }
50
+ }
51
+ throw new Error(`Could not successfully upload blob after ${maxTries} attempts. Last error: ${lastError}`);
52
+ };
53
+ return uploadThumbnail;
54
+ };
55
+ async function createNewPost(agent, record) {
56
+ try {
57
+ const post = await agent.post(record);
58
+ return { uri: post.uri, cid: post.cid };
59
+ }
60
+ catch (error) {
61
+ console.error("Error creating new post:", error);
62
+ throw error;
63
+ }
64
+ }
65
+ async function buildGoLivePost(text, url, profile, params, thumbnail, agent) {
66
+ const now = new Date();
67
+ const linkUrl = `${url.protocol}//${url.host}/${profile.handle}?${params.toString()}`;
68
+ const prefix = `🔴 LIVE `;
69
+ const textUrl = `${url.protocol}//${url.host}/${profile.handle}`;
70
+ const suffix = ` ${text}`;
71
+ const content = prefix + textUrl + suffix;
72
+ const rt = new api_1.RichText({ text: content });
73
+ await rt.detectFacets(agent);
74
+ const record = {
75
+ $type: "app.bsky.feed.post",
76
+ text: content,
77
+ "place.stream.livestream": {
78
+ url: linkUrl,
79
+ title: text,
80
+ },
81
+ facets: rt.facets,
82
+ createdAt: now.toISOString(),
83
+ };
84
+ record.embed = {
85
+ $type: "app.bsky.embed.external",
86
+ external: {
87
+ description: text,
88
+ thumb: thumbnail,
89
+ title: `@${profile.handle} is 🔴LIVE on ${url.host}!`,
90
+ uri: linkUrl,
91
+ },
92
+ };
93
+ return record;
94
+ }
95
+ function useCreateStreamRecord() {
96
+ let agent = (0, xrpc_1.usePDSAgent)();
97
+ let url = (0, streamplace_store_1.useUrl)();
98
+ const uploadThumbnail = useUploadThumbnail();
99
+ return async ({ title, customThumbnail, submitPost, customUrl, }) => {
100
+ if (!submitPost) {
101
+ submitPost = true;
102
+ }
103
+ if (!customUrl) {
104
+ customUrl = null;
105
+ }
106
+ if (!agent) {
107
+ throw new Error("No PDS agent found");
108
+ }
109
+ if (!agent.did) {
110
+ throw new Error("No user DID found, assuming not logged in");
111
+ }
112
+ // Use customUrl if provided, otherwise fall back to the store URL
113
+ const finalUrl = customUrl || url;
114
+ const u = new URL(finalUrl);
115
+ let thumbnail = undefined;
116
+ if (customThumbnail) {
117
+ try {
118
+ thumbnail = await uploadThumbnail(agent, customThumbnail);
119
+ }
120
+ catch (e) {
121
+ throw new Error(`Custom thumbnail upload failed ${e}`);
122
+ }
123
+ }
124
+ else {
125
+ // No custom thumbnail: fetch the server-side image and upload it
126
+ // try thrice lel
127
+ let tries = 0;
128
+ try {
129
+ for (; tries < 3; tries++) {
130
+ try {
131
+ console.log(`Fetching thumbnail from ${u.protocol}//${u.host}/api/playback/${agent.did}/stream.png`);
132
+ const thumbnailRes = await fetch(`${u.protocol}//${u.host}/api/playback/${agent.did}/stream.png`);
133
+ if (!thumbnailRes.ok) {
134
+ throw new Error(`Failed to fetch thumbnail: ${thumbnailRes.status})`);
135
+ }
136
+ const thumbnailBlob = await thumbnailRes.blob();
137
+ console.log(thumbnailBlob);
138
+ thumbnail = await uploadThumbnail(agent, thumbnailBlob);
139
+ }
140
+ catch (e) {
141
+ console.warn(`Failed to fetch thumbnail, retrying (${tries + 1}/3): ${e}`);
142
+ // Wait 1 second before retrying
143
+ await new Promise((resolve) => setTimeout(resolve, 2000));
144
+ if (tries === 2) {
145
+ throw new Error(`Failed to fetch thumbnail after 3 tries: ${e}`);
146
+ }
147
+ }
148
+ }
149
+ }
150
+ catch (e) {
151
+ throw new Error(`Thumbnail upload failed ${e}`);
152
+ }
153
+ }
154
+ let newPost = undefined;
155
+ const did = agent.did;
156
+ const profile = await agent.getProfile({ actor: did });
157
+ if (submitPost) {
158
+ if (!profile) {
159
+ throw new Error("No profile found for the user DID");
160
+ }
161
+ const params = new URLSearchParams({
162
+ did: did,
163
+ time: new Date().toISOString(),
164
+ });
165
+ let post = await buildGoLivePost(title, u, profile.data, params, thumbnail, agent);
166
+ newPost = await createNewPost(agent, post);
167
+ if (!newPost.uri || !newPost.cid) {
168
+ throw new Error("Cannot read properties of undefined (reading 'uri' or 'cid')");
169
+ }
170
+ }
171
+ let platform = react_native_1.Platform.OS;
172
+ let platVersion = react_native_1.Platform.Version
173
+ ? react_native_1.Platform.Version.toString()
174
+ : "";
175
+ // no Platform.Version on web, so use browser name instead
176
+ if (platform === "web" &&
177
+ typeof window !== "undefined" &&
178
+ window.navigator) {
179
+ platVersion = (0, browser_1.getBrowserName)(window.navigator.userAgent);
180
+ }
181
+ const record = {
182
+ title: title,
183
+ url: finalUrl,
184
+ createdAt: new Date().toISOString(),
185
+ // would match up with e.g. https://stream.place/iame.li
186
+ canonicalUrl: `${finalUrl}/${profile.data.handle}`,
187
+ // user agent style string
188
+ // e.g. `@streamplace/components/0.1.0 (ios, 32.0)`
189
+ agent: `@streamplace/components/${package_json_1.default.version} (${platform}, ${platVersion})`,
190
+ post: newPost,
191
+ thumb: thumbnail,
192
+ };
193
+ await agent.com.atproto.repo.createRecord({
194
+ repo: agent.did,
195
+ collection: "place.stream.livestream",
196
+ record,
197
+ });
198
+ return record;
199
+ };
200
+ }
201
+ function useUpdateStreamRecord(customUrl = null) {
202
+ let agent = (0, xrpc_1.usePDSAgent)();
203
+ let url = (0, streamplace_store_1.useUrl)();
204
+ const uploadThumbnail = useUploadThumbnail();
205
+ return async (title, livestream, customThumbnail) => {
206
+ if (!agent) {
207
+ throw new Error("No PDS agent found");
208
+ }
209
+ if (!agent.did) {
210
+ throw new Error("No user DID found, assuming not logged in");
211
+ }
212
+ if (!livestream) {
213
+ throw new Error("No latest record");
214
+ }
215
+ // Use customUrl if provided, otherwise fall back to the store URL
216
+ const finalUrl = customUrl || url;
217
+ let rkey = livestream.uri.split("/").pop();
218
+ let oldRecordValue = livestream.record;
219
+ if (!rkey) {
220
+ throw new Error("No rkey?");
221
+ }
222
+ let thumbnail = oldRecordValue.thumb;
223
+ // update thumbnail if a new one is provided
224
+ if (customThumbnail) {
225
+ try {
226
+ thumbnail = await uploadThumbnail(agent, customThumbnail);
227
+ }
228
+ catch (e) {
229
+ throw new Error(`Custom thumbnail upload failed ${e}`);
230
+ }
231
+ }
232
+ const record = {
233
+ title: title,
234
+ url: finalUrl,
235
+ createdAt: new Date().toISOString(),
236
+ post: oldRecordValue.post,
237
+ thumb: thumbnail,
238
+ };
239
+ await agent.com.atproto.repo.putRecord({
240
+ repo: agent.did,
241
+ collection: "place.stream.livestream",
242
+ rkey,
243
+ record,
244
+ });
245
+ return record;
246
+ };
247
+ }
@@ -0,0 +1,47 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useSetHandle = exports.useHandle = exports.useDID = exports.useUrl = exports.makeStreamplaceStore = void 0;
4
+ exports.getStreamplaceStoreFromContext = getStreamplaceStoreFromContext;
5
+ exports.useStreamplaceStore = useStreamplaceStore;
6
+ const react_1 = require("react");
7
+ const zustand_1 = require("zustand");
8
+ const context_1 = require("../streamplace-provider/context");
9
+ const makeStreamplaceStore = ({ url, }) => {
10
+ return (0, zustand_1.createStore)()((set) => ({
11
+ url,
12
+ liveUsers: null,
13
+ setLiveUsers: (opts) => {
14
+ set({
15
+ ...opts,
16
+ });
17
+ },
18
+ liveUsersRefresh: 0,
19
+ liveUsersLoading: true,
20
+ liveUsersError: null,
21
+ oauthSession: null,
22
+ handle: null,
23
+ chatProfile: null,
24
+ }));
25
+ };
26
+ exports.makeStreamplaceStore = makeStreamplaceStore;
27
+ function getStreamplaceStoreFromContext() {
28
+ const context = (0, react_1.useContext)(context_1.StreamplaceContext);
29
+ if (!context) {
30
+ throw new Error("useStreamplaceStore must be used within a StreamplaceProvider");
31
+ }
32
+ return context.store;
33
+ }
34
+ function useStreamplaceStore(selector) {
35
+ return (0, zustand_1.useStore)(getStreamplaceStoreFromContext(), selector);
36
+ }
37
+ const useUrl = () => useStreamplaceStore((x) => x.url);
38
+ exports.useUrl = useUrl;
39
+ const useDID = () => useStreamplaceStore((x) => x.oauthSession?.did);
40
+ exports.useDID = useDID;
41
+ const useHandle = () => useStreamplaceStore((x) => x.handle);
42
+ exports.useHandle = useHandle;
43
+ const useSetHandle = () => {
44
+ const store = getStreamplaceStoreFromContext();
45
+ return (handle) => store.setState({ handle });
46
+ };
47
+ exports.useSetHandle = useSetHandle;
@@ -0,0 +1,52 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.useGetChatProfile = useGetChatProfile;
4
+ exports.useGetBskyProfile = useGetBskyProfile;
5
+ exports.useChatProfile = useChatProfile;
6
+ const streamplace_1 = require("streamplace");
7
+ const streamplace_store_1 = require("./streamplace-store");
8
+ const xrpc_1 = require("./xrpc");
9
+ function useGetChatProfile() {
10
+ const did = (0, streamplace_store_1.useDID)();
11
+ const pdsAgent = (0, xrpc_1.usePDSAgent)();
12
+ const store = (0, streamplace_store_1.getStreamplaceStoreFromContext)();
13
+ return async () => {
14
+ if (!did || !pdsAgent) {
15
+ throw new Error("No DID or PDS agent");
16
+ }
17
+ const res = await pdsAgent.com.atproto.repo.getRecord({
18
+ repo: did,
19
+ collection: "place.stream.chat.profile",
20
+ rkey: "self",
21
+ });
22
+ if (!res.success) {
23
+ throw new Error("Failed to get chat profile record");
24
+ }
25
+ if (streamplace_1.PlaceStreamChatProfile.isRecord(res.data.value)) {
26
+ store.setState({ chatProfile: res.data.value });
27
+ }
28
+ else {
29
+ console.log("not a record", res.data.value);
30
+ }
31
+ };
32
+ }
33
+ function useGetBskyProfile() {
34
+ const did = (0, streamplace_store_1.useDID)();
35
+ const pdsAgent = (0, xrpc_1.usePDSAgent)();
36
+ const store = (0, streamplace_store_1.getStreamplaceStoreFromContext)();
37
+ return async () => {
38
+ if (!did || !pdsAgent) {
39
+ throw new Error("No DID or PDS agent");
40
+ }
41
+ const res = await pdsAgent.app.bsky.actor.getProfile({
42
+ actor: did,
43
+ });
44
+ if (!res.success) {
45
+ throw new Error("Failed to get chat profile record");
46
+ }
47
+ store.setState({ handle: res.data.handle });
48
+ };
49
+ }
50
+ function useChatProfile() {
51
+ return (0, streamplace_store_1.useStreamplaceStore)((x) => x.chatProfile);
52
+ }
@@ -0,0 +1,15 @@
1
+ "use strict";
2
+ Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.usePDSAgent = usePDSAgent;
4
+ const react_1 = require("react");
5
+ const streamplace_1 = require("streamplace");
6
+ const _1 = require(".");
7
+ function usePDSAgent() {
8
+ const oauthSession = (0, _1.useStreamplaceStore)((state) => state.oauthSession);
9
+ return (0, react_1.useMemo)(() => {
10
+ if (!oauthSession) {
11
+ return null;
12
+ }
13
+ return new streamplace_1.StreamplaceAgent(oauthSession);
14
+ }, [oauthSession]);
15
+ }
@@ -0,0 +1,79 @@
1
+ "use strict";
2
+ /**
3
+ * @streamplace/components/ui - Streamplace ZeroCSS
4
+ *
5
+ * Clean export path for ZeroCSS styling utilities, design tokens, and atomic styles.
6
+ * ZeroCSS provides a zero-config, atomic styling system optimized for React Native.
7
+ */
8
+ Object.defineProperty(exports, "__esModule", { value: true });
9
+ exports.atomsNS = exports.theme = exports.useTheme = exports.usePlatformTypography = exports.lightTheme = exports.darkTheme = exports.createThemedStyles = exports.createThemeStyles = exports.createThemeIcons = exports.createThemeColors = exports.ThemeProvider = exports.responsiveValue = exports.platformStyle = exports.mergeStyles = exports.debounce = exports.typography = exports.spacing = exports.shadows = exports.colors = exports.breakpoints = exports.borderRadius = exports.w = exports.top = exports.text = exports.right = exports.r = exports.py = exports.px = exports.pt = exports.pr = exports.position = exports.pl = exports.pb = exports.p = exports.my = exports.mx = exports.mt = exports.mr = exports.ml = exports.mb = exports.m = exports.left = exports.layout = exports.h = exports.gap = exports.flex = exports.bottom = exports.borders = exports.bg = exports.atoms = void 0;
10
+ exports.utils = exports.tokens = void 0;
11
+ const tslib_1 = require("tslib");
12
+ // Export the most commonly used ZeroCSS utilities
13
+ var atoms_1 = require("../lib/theme/atoms");
14
+ // Core atoms object
15
+ Object.defineProperty(exports, "atoms", { enumerable: true, get: function () { return atoms_1.atoms; } });
16
+ // Common shorthand utilities
17
+ Object.defineProperty(exports, "bg", { enumerable: true, get: function () { return atoms_1.bg; } });
18
+ // Border utilities
19
+ Object.defineProperty(exports, "borders", { enumerable: true, get: function () { return atoms_1.borders; } });
20
+ Object.defineProperty(exports, "bottom", { enumerable: true, get: function () { return atoms_1.bottom; } });
21
+ // Flex utilities
22
+ Object.defineProperty(exports, "flex", { enumerable: true, get: function () { return atoms_1.flex; } });
23
+ // Gap utilities (React Native 0.71+)
24
+ Object.defineProperty(exports, "gap", { enumerable: true, get: function () { return atoms_1.gap; } });
25
+ Object.defineProperty(exports, "h", { enumerable: true, get: function () { return atoms_1.h; } });
26
+ // Layout utilities
27
+ Object.defineProperty(exports, "layout", { enumerable: true, get: function () { return atoms_1.layout; } });
28
+ Object.defineProperty(exports, "left", { enumerable: true, get: function () { return atoms_1.left; } });
29
+ Object.defineProperty(exports, "m", { enumerable: true, get: function () { return atoms_1.m; } });
30
+ Object.defineProperty(exports, "mb", { enumerable: true, get: function () { return atoms_1.mb; } });
31
+ Object.defineProperty(exports, "ml", { enumerable: true, get: function () { return atoms_1.ml; } });
32
+ Object.defineProperty(exports, "mr", { enumerable: true, get: function () { return atoms_1.mr; } });
33
+ Object.defineProperty(exports, "mt", { enumerable: true, get: function () { return atoms_1.mt; } });
34
+ Object.defineProperty(exports, "mx", { enumerable: true, get: function () { return atoms_1.mx; } });
35
+ Object.defineProperty(exports, "my", { enumerable: true, get: function () { return atoms_1.my; } });
36
+ Object.defineProperty(exports, "p", { enumerable: true, get: function () { return atoms_1.p; } });
37
+ Object.defineProperty(exports, "pb", { enumerable: true, get: function () { return atoms_1.pb; } });
38
+ Object.defineProperty(exports, "pl", { enumerable: true, get: function () { return atoms_1.pl; } });
39
+ // Position utilities
40
+ Object.defineProperty(exports, "position", { enumerable: true, get: function () { return atoms_1.position; } });
41
+ Object.defineProperty(exports, "pr", { enumerable: true, get: function () { return atoms_1.pr; } });
42
+ Object.defineProperty(exports, "pt", { enumerable: true, get: function () { return atoms_1.pt; } });
43
+ Object.defineProperty(exports, "px", { enumerable: true, get: function () { return atoms_1.px; } });
44
+ Object.defineProperty(exports, "py", { enumerable: true, get: function () { return atoms_1.py; } });
45
+ Object.defineProperty(exports, "r", { enumerable: true, get: function () { return atoms_1.r; } });
46
+ Object.defineProperty(exports, "right", { enumerable: true, get: function () { return atoms_1.right; } });
47
+ Object.defineProperty(exports, "text", { enumerable: true, get: function () { return atoms_1.text; } });
48
+ Object.defineProperty(exports, "top", { enumerable: true, get: function () { return atoms_1.top; } });
49
+ Object.defineProperty(exports, "w", { enumerable: true, get: function () { return atoms_1.w; } });
50
+ // Export ZeroCSS design tokens
51
+ var tokens_1 = require("../lib/theme/tokens");
52
+ Object.defineProperty(exports, "borderRadius", { enumerable: true, get: function () { return tokens_1.borderRadius; } });
53
+ Object.defineProperty(exports, "breakpoints", { enumerable: true, get: function () { return tokens_1.breakpoints; } });
54
+ Object.defineProperty(exports, "colors", { enumerable: true, get: function () { return tokens_1.colors; } });
55
+ Object.defineProperty(exports, "shadows", { enumerable: true, get: function () { return tokens_1.shadows; } });
56
+ Object.defineProperty(exports, "spacing", { enumerable: true, get: function () { return tokens_1.spacing; } });
57
+ Object.defineProperty(exports, "typography", { enumerable: true, get: function () { return tokens_1.typography; } });
58
+ // Export ZeroCSS utility functions
59
+ var utils_1 = require("../lib/utils");
60
+ Object.defineProperty(exports, "debounce", { enumerable: true, get: function () { return utils_1.debounce; } });
61
+ Object.defineProperty(exports, "mergeStyles", { enumerable: true, get: function () { return utils_1.mergeStyles; } });
62
+ Object.defineProperty(exports, "platformStyle", { enumerable: true, get: function () { return utils_1.platformStyle; } });
63
+ Object.defineProperty(exports, "responsiveValue", { enumerable: true, get: function () { return utils_1.responsiveValue; } });
64
+ // Export ZeroCSS theme system
65
+ var theme_1 = require("../lib/theme/theme");
66
+ Object.defineProperty(exports, "ThemeProvider", { enumerable: true, get: function () { return theme_1.ThemeProvider; } });
67
+ Object.defineProperty(exports, "createThemeColors", { enumerable: true, get: function () { return theme_1.createThemeColors; } });
68
+ Object.defineProperty(exports, "createThemeIcons", { enumerable: true, get: function () { return theme_1.createThemeIcons; } });
69
+ Object.defineProperty(exports, "createThemeStyles", { enumerable: true, get: function () { return theme_1.createThemeStyles; } });
70
+ Object.defineProperty(exports, "createThemedStyles", { enumerable: true, get: function () { return theme_1.createThemedStyles; } });
71
+ Object.defineProperty(exports, "darkTheme", { enumerable: true, get: function () { return theme_1.darkTheme; } });
72
+ Object.defineProperty(exports, "lightTheme", { enumerable: true, get: function () { return theme_1.lightTheme; } });
73
+ Object.defineProperty(exports, "usePlatformTypography", { enumerable: true, get: function () { return theme_1.usePlatformTypography; } });
74
+ Object.defineProperty(exports, "useTheme", { enumerable: true, get: function () { return theme_1.useTheme; } });
75
+ // Namespace exports for power users
76
+ exports.theme = tslib_1.__importStar(require("../lib/theme"));
77
+ exports.atomsNS = tslib_1.__importStar(require("../lib/theme/atoms"));
78
+ exports.tokens = tslib_1.__importStar(require("../lib/theme/tokens"));
79
+ exports.utils = tslib_1.__importStar(require("../lib/utils"));
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@streamplace/components",
3
- "version": "0.7.18",
3
+ "version": "0.7.21",
4
4
  "description": "Streamplace React (Native) Components",
5
5
  "main": "dist/index.js",
6
6
  "types": "src/index.tsx",
@@ -40,7 +40,7 @@
40
40
  "react-native-svg": "^15.0.0",
41
41
  "react-native-webrtc": "git+https://github.com/streamplace/react-native-webrtc.git#6b8472a771ac47f89217d327058a8a4124a6ae56",
42
42
  "react-use-websocket": "^4.13.0",
43
- "streamplace": "0.7.2",
43
+ "streamplace": "0.7.21",
44
44
  "viem": "^2.21.44",
45
45
  "zustand": "^5.0.5"
46
46
  },
@@ -49,7 +49,8 @@
49
49
  },
50
50
  "scripts": {
51
51
  "build": "tsc",
52
- "start": "tsc --watch --preserveWatchOutput"
52
+ "start": "tsc --watch --preserveWatchOutput",
53
+ "prepare": "tsc"
53
54
  },
54
- "gitHead": "c0449c9e843e90abf365ff7f017a74560838d482"
55
+ "gitHead": "e930332a9465e0ffd9a78a01ea39b134cd78e49e"
55
56
  }
@@ -83,6 +83,9 @@ export function ChatBox({
83
83
  const authors = useMemo(() => {
84
84
  if (!chat) return null;
85
85
  return chat.reduce((acc, msg) => {
86
+ // our fake system user "did"
87
+ if (msg.author.did === "did:sys:system") return acc;
88
+ if (acc.has(msg.author.handle)) return acc;
86
89
  acc.set(msg.author.handle, msg.chatProfile);
87
90
  return acc;
88
91
  }, new Map<string, ChatMessageViewHydrated["chatProfile"]>());
@@ -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
- <ReportButton
176
- message={message}
177
- setReportModalOpen={setReportModalOpen}
178
- setReportSubject={setReportSubject}
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,