@streamplace/components 0.9.0 → 0.9.4
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.d.ts.map +1 -1
- package/dist/components/chat/chat-box.js +90 -34
- package/dist/components/chat/chat-box.js.map +1 -1
- package/dist/components/chat/chat-message.d.ts +4 -0
- package/dist/components/chat/chat-message.d.ts.map +1 -1
- package/dist/components/chat/chat-message.js +3 -2
- package/dist/components/chat/chat-message.js.map +1 -1
- package/dist/components/chat/chat.d.ts.map +1 -1
- package/dist/components/chat/chat.js +56 -3
- package/dist/components/chat/chat.js.map +1 -1
- package/dist/components/chat/emoji-suggestions.d.ts.map +1 -1
- package/dist/components/chat/emoji-suggestions.js +11 -11
- package/dist/components/chat/emoji-suggestions.js.map +1 -1
- package/dist/components/chat/mention-suggestions.d.ts.map +1 -1
- package/dist/components/chat/mention-suggestions.js +20 -19
- package/dist/components/chat/mention-suggestions.js.map +1 -1
- package/dist/components/chat/mod-view.d.ts.map +1 -1
- package/dist/components/chat/mod-view.js +1 -9
- package/dist/components/chat/mod-view.js.map +1 -1
- package/dist/components/chat/system-message.d.ts +5 -1
- package/dist/components/chat/system-message.d.ts.map +1 -1
- package/dist/components/chat/system-message.js +4 -4
- package/dist/components/chat/system-message.js.map +1 -1
- package/dist/components/mobile-player/shared.d.ts +1 -1
- package/dist/components/mobile-player/shared.d.ts.map +1 -1
- package/dist/components/mobile-player/shared.js +11 -10
- package/dist/components/mobile-player/shared.js.map +1 -1
- package/dist/components/mobile-player/ui/report-modal.d.ts.map +1 -1
- package/dist/components/mobile-player/ui/report-modal.js +1 -1
- package/dist/components/mobile-player/ui/report-modal.js.map +1 -1
- package/dist/components/mobile-player/ui/viewer-context-menu.d.ts +1 -1
- package/dist/components/mobile-player/ui/viewer-context-menu.d.ts.map +1 -1
- package/dist/components/mobile-player/ui/viewer-context-menu.js +60 -43
- package/dist/components/mobile-player/ui/viewer-context-menu.js.map +1 -1
- package/dist/components/mobile-player/ui/viewer-loading-overlay.d.ts.map +1 -1
- package/dist/components/mobile-player/ui/viewer-loading-overlay.js +0 -1
- package/dist/components/mobile-player/ui/viewer-loading-overlay.js.map +1 -1
- package/dist/components/stream-notification/index.d.ts +3 -0
- package/dist/components/stream-notification/index.d.ts.map +1 -0
- package/dist/components/stream-notification/index.js +9 -0
- package/dist/components/stream-notification/index.js.map +1 -0
- package/dist/components/stream-notification/stream-notification-manager.d.ts +36 -0
- package/dist/components/stream-notification/stream-notification-manager.d.ts.map +1 -0
- package/dist/components/stream-notification/stream-notification-manager.js +96 -0
- package/dist/components/stream-notification/stream-notification-manager.js.map +1 -0
- package/dist/components/stream-notification/stream-notification.d.ts +5 -0
- package/dist/components/stream-notification/stream-notification.d.ts.map +1 -0
- package/dist/components/stream-notification/stream-notification.js +146 -0
- package/dist/components/stream-notification/stream-notification.js.map +1 -0
- package/dist/components/stream-notification/teleport-notification.d.ts +8 -0
- package/dist/components/stream-notification/teleport-notification.d.ts.map +1 -0
- package/dist/components/stream-notification/teleport-notification.js +116 -0
- package/dist/components/stream-notification/teleport-notification.js.map +1 -0
- package/dist/components/ui/button.d.ts +1 -1
- package/dist/components/ui/button.d.ts.map +1 -1
- package/dist/components/ui/button.js +7 -0
- package/dist/components/ui/button.js.map +1 -1
- package/dist/components/ui/dialog.d.ts +2 -2
- package/dist/components/ui/dropdown.d.ts +4 -0
- package/dist/components/ui/dropdown.d.ts.map +1 -1
- package/dist/components/ui/dropdown.js +41 -15
- package/dist/components/ui/dropdown.js.map +1 -1
- package/dist/components/ui/index.d.ts +1 -0
- package/dist/components/ui/index.d.ts.map +1 -1
- package/dist/components/ui/index.js +1 -0
- package/dist/components/ui/index.js.map +1 -1
- package/dist/components/ui/portal.d.ts +2 -0
- package/dist/components/ui/portal.d.ts.map +1 -0
- package/dist/components/ui/portal.js +5 -0
- package/dist/components/ui/portal.js.map +1 -0
- package/dist/components/ui/portal.web.d.ts +11 -0
- package/dist/components/ui/portal.web.d.ts.map +1 -0
- package/dist/components/ui/portal.web.js +22 -0
- package/dist/components/ui/portal.web.js.map +1 -0
- package/dist/components/ui/resizeable.d.ts +2 -1
- package/dist/components/ui/resizeable.d.ts.map +1 -1
- package/dist/components/ui/resizeable.js +68 -26
- package/dist/components/ui/resizeable.js.map +1 -1
- package/dist/components/ui/text.d.ts +1 -1
- package/dist/components/ui/view.d.ts +3 -3
- package/dist/index.d.ts +2 -0
- package/dist/index.d.ts.map +1 -1
- package/dist/index.js +2 -0
- package/dist/index.js.map +1 -1
- package/dist/lib/slash-commands/teleport.d.ts +4 -0
- package/dist/lib/slash-commands/teleport.d.ts.map +1 -0
- package/dist/lib/slash-commands/teleport.js +110 -0
- package/dist/lib/slash-commands/teleport.js.map +1 -0
- package/dist/lib/slash-commands.d.ts +16 -0
- package/dist/lib/slash-commands.d.ts.map +1 -0
- package/dist/lib/slash-commands.js +46 -0
- package/dist/lib/slash-commands.js.map +1 -0
- package/dist/lib/stream-notifications.d.ts +13 -0
- package/dist/lib/stream-notifications.d.ts.map +1 -0
- package/dist/lib/stream-notifications.js +46 -0
- package/dist/lib/stream-notifications.js.map +1 -0
- package/dist/lib/system-messages.d.ts +4 -8
- package/dist/lib/system-messages.d.ts.map +1 -1
- package/dist/lib/system-messages.js +38 -2
- package/dist/lib/system-messages.js.map +1 -1
- package/dist/lib/theme/atoms.d.ts +193 -193
- package/dist/livestream-provider/index.d.ts +7 -2
- package/dist/livestream-provider/index.d.ts.map +1 -1
- package/dist/livestream-provider/index.js +72 -4
- package/dist/livestream-provider/index.js.map +1 -1
- package/dist/livestream-store/livestream-state.d.ts +4 -1
- package/dist/livestream-store/livestream-state.d.ts.map +1 -1
- package/dist/livestream-store/livestream-store.d.ts.map +1 -1
- package/dist/livestream-store/livestream-store.js +3 -0
- package/dist/livestream-store/livestream-store.js.map +1 -1
- package/dist/livestream-store/websocket-consumer.d.ts.map +1 -1
- package/dist/livestream-store/websocket-consumer.js +30 -43
- package/dist/livestream-store/websocket-consumer.js.map +1 -1
- package/dist/streamplace-store/index.d.ts +1 -0
- package/dist/streamplace-store/index.d.ts.map +1 -1
- package/dist/streamplace-store/index.js +1 -0
- package/dist/streamplace-store/index.js.map +1 -1
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +4 -2
- package/src/components/chat/chat-box.tsx +126 -53
- package/src/components/chat/chat-message.tsx +1 -1
- package/src/components/chat/chat.tsx +79 -5
- package/src/components/chat/emoji-suggestions.tsx +27 -25
- package/src/components/chat/mention-suggestions.tsx +36 -33
- package/src/components/chat/mod-view.tsx +2 -13
- package/src/components/chat/system-message.tsx +14 -5
- package/src/components/mobile-player/shared.tsx +2 -1
- package/src/components/mobile-player/ui/report-modal.tsx +2 -0
- package/src/components/mobile-player/ui/viewer-context-menu.tsx +192 -166
- package/src/components/mobile-player/ui/viewer-loading-overlay.tsx +0 -1
- package/src/components/stream-notification/index.ts +5 -0
- package/src/components/stream-notification/stream-notification-manager.ts +140 -0
- package/src/components/stream-notification/stream-notification.tsx +227 -0
- package/src/components/stream-notification/teleport-notification.tsx +187 -0
- package/src/components/ui/button.tsx +7 -0
- package/src/components/ui/dropdown.tsx +96 -26
- package/src/components/ui/index.ts +1 -0
- package/src/components/ui/portal.tsx +1 -0
- package/src/components/ui/portal.web.tsx +37 -0
- package/src/components/ui/resizeable.tsx +89 -35
- package/src/index.tsx +3 -0
- package/src/lib/slash-commands/teleport.ts +136 -0
- package/src/lib/slash-commands.ts +65 -0
- package/src/lib/stream-notifications.ts +51 -0
- package/src/lib/system-messages.ts +52 -2
- package/src/livestream-provider/index.tsx +106 -3
- package/src/livestream-store/livestream-state.tsx +4 -0
- package/src/livestream-store/livestream-store.tsx +3 -0
- package/src/livestream-store/websocket-consumer.tsx +35 -54
- package/src/streamplace-store/index.tsx +1 -0
|
@@ -1,33 +1,36 @@
|
|
|
1
1
|
import Picker from "@emoji-mart/react";
|
|
2
2
|
import { AtSignIcon, ExternalLink, X } from "lucide-react-native";
|
|
3
|
+
import { env } from "process";
|
|
3
4
|
import { useEffect, useMemo, useRef, useState } from "react";
|
|
4
5
|
import { Platform, Pressable, TextInput } from "react-native";
|
|
5
6
|
import { ChatMessageViewHydrated } from "streamplace";
|
|
7
|
+
import { Button, Loader, Text, useTheme, View } from "../../";
|
|
8
|
+
import { handleSlashCommand } from "../../lib/slash-commands";
|
|
9
|
+
import { registerTeleportCommand } from "../../lib/slash-commands/teleport";
|
|
10
|
+
import { StreamNotifications } from "../../lib/stream-notifications";
|
|
11
|
+
import { SystemMessages } from "../../lib/system-messages";
|
|
6
12
|
import {
|
|
7
|
-
|
|
8
|
-
Loader,
|
|
9
|
-
Text,
|
|
10
|
-
useChat,
|
|
11
|
-
useCreateChatMessage,
|
|
12
|
-
useLivestream,
|
|
13
|
-
useReplyToMessage,
|
|
14
|
-
useSetReplyToMessage,
|
|
15
|
-
useTheme,
|
|
16
|
-
View,
|
|
17
|
-
} from "../../";
|
|
18
|
-
import {
|
|
19
|
-
bg,
|
|
13
|
+
borders,
|
|
20
14
|
flex,
|
|
21
15
|
gap,
|
|
22
16
|
h,
|
|
23
17
|
layout,
|
|
24
18
|
mb,
|
|
25
|
-
mr,
|
|
26
19
|
pl,
|
|
27
20
|
pr,
|
|
28
21
|
py,
|
|
22
|
+
r,
|
|
29
23
|
w,
|
|
30
24
|
} from "../../lib/theme/atoms";
|
|
25
|
+
import {
|
|
26
|
+
useChat,
|
|
27
|
+
useCreateChatMessage,
|
|
28
|
+
useLivestream,
|
|
29
|
+
useLivestreamStore,
|
|
30
|
+
useReplyToMessage,
|
|
31
|
+
useSetReplyToMessage,
|
|
32
|
+
} from "../../livestream-store";
|
|
33
|
+
import { useDID, usePDSAgent } from "../../streamplace-store";
|
|
31
34
|
import { Textarea } from "../ui/textarea";
|
|
32
35
|
import { RenderChatMessage } from "./chat-message";
|
|
33
36
|
import { EmojiData, EmojiSuggestions } from "./emoji-suggestions";
|
|
@@ -65,7 +68,7 @@ export function ChatBox({
|
|
|
65
68
|
|
|
66
69
|
let linfo = useLivestream();
|
|
67
70
|
|
|
68
|
-
const { theme } = useTheme();
|
|
71
|
+
const { theme, zero: zt } = useTheme();
|
|
69
72
|
|
|
70
73
|
const chat = useChat();
|
|
71
74
|
const createChatMessage = useCreateChatMessage();
|
|
@@ -73,6 +76,18 @@ export function ChatBox({
|
|
|
73
76
|
const setReplyToMessage = useSetReplyToMessage();
|
|
74
77
|
const textAreaRef = useRef<TextInput>(null);
|
|
75
78
|
|
|
79
|
+
const pdsAgent = usePDSAgent();
|
|
80
|
+
const userDID = useDID();
|
|
81
|
+
const setActiveTeleportUri = useLivestreamStore(
|
|
82
|
+
(state) => state.setActiveTeleportUri,
|
|
83
|
+
);
|
|
84
|
+
|
|
85
|
+
useEffect(() => {
|
|
86
|
+
if (pdsAgent && userDID) {
|
|
87
|
+
registerTeleportCommand(pdsAgent, userDID, setActiveTeleportUri);
|
|
88
|
+
}
|
|
89
|
+
}, [pdsAgent, userDID, setActiveTeleportUri]);
|
|
90
|
+
|
|
76
91
|
const authors = useMemo(() => {
|
|
77
92
|
if (!chat) return null;
|
|
78
93
|
return chat.reduce((acc, msg) => {
|
|
@@ -84,6 +99,12 @@ export function ChatBox({
|
|
|
84
99
|
}, new Map<string, ChatMessageViewHydrated["chatProfile"]>());
|
|
85
100
|
}, [chat]);
|
|
86
101
|
|
|
102
|
+
useEffect(() => {
|
|
103
|
+
if (pdsAgent && linfo?.author?.did && pdsAgent.did === linfo.author.did) {
|
|
104
|
+
registerTeleportCommand(pdsAgent, pdsAgent.did, setActiveTeleportUri);
|
|
105
|
+
}
|
|
106
|
+
}, [pdsAgent, linfo?.author?.did, setActiveTeleportUri]);
|
|
107
|
+
|
|
87
108
|
const handleMentionSelect = (handle: string) => {
|
|
88
109
|
const beforeAt = message.slice(0, message.lastIndexOf("@"));
|
|
89
110
|
setMessage(`${beforeAt}@${handle} `);
|
|
@@ -117,7 +138,7 @@ export function ChatBox({
|
|
|
117
138
|
const colonIndex = text.lastIndexOf(":");
|
|
118
139
|
if (colonIndex !== -1) {
|
|
119
140
|
const searchText = text.slice(colonIndex + 1).toLowerCase();
|
|
120
|
-
if (searchText.length
|
|
141
|
+
if (searchText.length >= 3) {
|
|
121
142
|
if (!emojiData) return;
|
|
122
143
|
const aliasMatches = Object.entries(emojiData.aliases)
|
|
123
144
|
.map(([alias, emojiId]) => {
|
|
@@ -232,20 +253,44 @@ export function ChatBox({
|
|
|
232
253
|
}
|
|
233
254
|
};
|
|
234
255
|
|
|
235
|
-
const submit = () => {
|
|
256
|
+
const submit = async () => {
|
|
236
257
|
if (!message.trim()) return;
|
|
258
|
+
|
|
259
|
+
const messageText = message;
|
|
237
260
|
setMessage("");
|
|
238
261
|
setReplyToMessage(null);
|
|
239
262
|
|
|
263
|
+
if (messageText.startsWith("/")) {
|
|
264
|
+
const result = await handleSlashCommand(messageText);
|
|
265
|
+
if (result.handled) {
|
|
266
|
+
if (result.error) {
|
|
267
|
+
console.error("Slash command error:", result.error);
|
|
268
|
+
SystemMessages.commandError(result.error);
|
|
269
|
+
}
|
|
270
|
+
return;
|
|
271
|
+
}
|
|
272
|
+
}
|
|
240
273
|
setSubmitting(true);
|
|
241
|
-
createChatMessage({
|
|
242
|
-
text: message,
|
|
243
|
-
reply: replyTo || undefined,
|
|
244
|
-
});
|
|
245
|
-
setSubmitting(false);
|
|
246
274
|
|
|
247
|
-
|
|
248
|
-
|
|
275
|
+
try {
|
|
276
|
+
const result = await handleSlashCommand(messageText);
|
|
277
|
+
|
|
278
|
+
if (result.handled) {
|
|
279
|
+
if (result.error) {
|
|
280
|
+
console.error("Slash command error:", result.error);
|
|
281
|
+
}
|
|
282
|
+
} else {
|
|
283
|
+
createChatMessage({
|
|
284
|
+
text: messageText,
|
|
285
|
+
reply: replyTo || undefined,
|
|
286
|
+
});
|
|
287
|
+
}
|
|
288
|
+
} catch (err) {
|
|
289
|
+
console.error("Error submitting message:", err);
|
|
290
|
+
} finally {
|
|
291
|
+
setSubmitting(false);
|
|
292
|
+
}
|
|
293
|
+
|
|
249
294
|
if (textAreaRef.current && !textAreaRef.current.isFocused()) {
|
|
250
295
|
textAreaRef.current.focus();
|
|
251
296
|
requestAnimationFrame(() => {
|
|
@@ -268,38 +313,50 @@ export function ChatBox({
|
|
|
268
313
|
layout.flex.alignCenter,
|
|
269
314
|
layout.flex.spaceBetween,
|
|
270
315
|
pl[2],
|
|
271
|
-
pr[
|
|
272
|
-
mr[6],
|
|
316
|
+
pr[1],
|
|
273
317
|
mb[2],
|
|
274
318
|
py[1],
|
|
275
|
-
|
|
276
|
-
|
|
319
|
+
r["2xl"],
|
|
320
|
+
zt.bg.card,
|
|
277
321
|
]}
|
|
278
322
|
>
|
|
279
|
-
<
|
|
280
|
-
|
|
281
|
-
|
|
282
|
-
|
|
283
|
-
|
|
284
|
-
|
|
285
|
-
|
|
286
|
-
|
|
287
|
-
|
|
288
|
-
|
|
289
|
-
|
|
290
|
-
|
|
291
|
-
|
|
292
|
-
|
|
293
|
-
|
|
294
|
-
|
|
295
|
-
|
|
296
|
-
|
|
297
|
-
|
|
323
|
+
<View style={{ flex: 1, minWidth: 0, marginRight: 8 }}>
|
|
324
|
+
<RenderChatMessage
|
|
325
|
+
item={replyTo}
|
|
326
|
+
showReply={false}
|
|
327
|
+
userCache={authors || new Map()}
|
|
328
|
+
/>
|
|
329
|
+
</View>
|
|
330
|
+
<Pressable
|
|
331
|
+
onPress={() => setReplyToMessage(null)}
|
|
332
|
+
style={[
|
|
333
|
+
layout.flex.row,
|
|
334
|
+
layout.flex.alignCenter,
|
|
335
|
+
layout.flex.justifyCenter,
|
|
336
|
+
h[8],
|
|
337
|
+
w[8],
|
|
338
|
+
zt.bg.muted,
|
|
339
|
+
zt.border.border,
|
|
340
|
+
borders.width.thin,
|
|
341
|
+
{ borderRadius: 999 },
|
|
342
|
+
]}
|
|
343
|
+
>
|
|
344
|
+
<X size={24} style={[zt.text.primaryForeground]} />
|
|
298
345
|
</Pressable>
|
|
299
346
|
</View>
|
|
300
347
|
)}
|
|
301
348
|
{showEmojiSelector && (
|
|
302
|
-
|
|
349
|
+
<View
|
|
350
|
+
style={{
|
|
351
|
+
position: "absolute",
|
|
352
|
+
top: 0,
|
|
353
|
+
left: 0,
|
|
354
|
+
right: 0,
|
|
355
|
+
bottom: 0,
|
|
356
|
+
zIndex: 200,
|
|
357
|
+
}}
|
|
358
|
+
pointerEvents="box-none"
|
|
359
|
+
>
|
|
303
360
|
{/* Overlay to catch outside clicks */}
|
|
304
361
|
<Pressable
|
|
305
362
|
style={{
|
|
@@ -308,7 +365,6 @@ export function ChatBox({
|
|
|
308
365
|
left: 0,
|
|
309
366
|
right: 0,
|
|
310
367
|
bottom: 0,
|
|
311
|
-
zIndex: 200,
|
|
312
368
|
}}
|
|
313
369
|
onPress={() => setShowEmojiSelector(false)}
|
|
314
370
|
/>
|
|
@@ -319,13 +375,14 @@ export function ChatBox({
|
|
|
319
375
|
left: 0,
|
|
320
376
|
zIndex: 2001,
|
|
321
377
|
}}
|
|
378
|
+
pointerEvents="auto"
|
|
322
379
|
>
|
|
323
380
|
<Picker
|
|
324
381
|
data={emojiData}
|
|
325
382
|
onEmojiSelect={(e) => setMessage(message + e.native)}
|
|
326
383
|
/>
|
|
327
384
|
</View>
|
|
328
|
-
|
|
385
|
+
</View>
|
|
329
386
|
)}
|
|
330
387
|
<View style={[layout.flex.row, layout.flex.alignCenter, gap.all[2]]}>
|
|
331
388
|
<Textarea
|
|
@@ -440,19 +497,35 @@ export function ChatBox({
|
|
|
440
497
|
{ justifyContent: "flex-end" },
|
|
441
498
|
]}
|
|
442
499
|
>
|
|
500
|
+
{env.NODE_ENV === "development" && (
|
|
501
|
+
<Button
|
|
502
|
+
variant="secondary"
|
|
503
|
+
style={{ borderRadius: 16 }}
|
|
504
|
+
width="min"
|
|
505
|
+
onPress={() => {
|
|
506
|
+
StreamNotifications.teleport({
|
|
507
|
+
targetHandle: "test.bsky.social",
|
|
508
|
+
targetDID: "did:plc:test",
|
|
509
|
+
countdown: 30,
|
|
510
|
+
canCancel: true,
|
|
511
|
+
onDismiss: (reason) =>
|
|
512
|
+
console.log("teleport dismissed:", reason),
|
|
513
|
+
});
|
|
514
|
+
}}
|
|
515
|
+
>
|
|
516
|
+
Test Notification
|
|
517
|
+
</Button>
|
|
518
|
+
)}
|
|
443
519
|
<Button
|
|
444
520
|
variant="secondary"
|
|
445
521
|
style={{ borderRadius: 16, maxWidth: 44, aspectRatio: 1 }}
|
|
446
522
|
aria-label="Insert Mention"
|
|
447
523
|
onPress={() => {
|
|
448
|
-
// if the last character is not @, add it
|
|
449
524
|
!message.endsWith("@") && setMessage(message + "@");
|
|
450
|
-
// get all the text after the last @
|
|
451
525
|
const atIndex = message.lastIndexOf("@");
|
|
452
526
|
const searchText = message.slice(atIndex + 1).toLowerCase();
|
|
453
527
|
updateSuggestions(searchText);
|
|
454
528
|
setShowSuggestions(true);
|
|
455
|
-
// focus the textarea
|
|
456
529
|
textAreaRef.current?.focus();
|
|
457
530
|
}}
|
|
458
531
|
>
|
|
@@ -1,4 +1,4 @@
|
|
|
1
|
-
import { Ellipsis, Reply } from "lucide-react-native";
|
|
1
|
+
import { ChevronDown, Ellipsis, Reply } from "lucide-react-native";
|
|
2
2
|
import { ComponentProps, memo, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { Keyboard, Platform, Pressable } from "react-native";
|
|
4
4
|
import { FlatList } from "react-native-gesture-handler";
|
|
@@ -8,17 +8,22 @@ import Swipeable, {
|
|
|
8
8
|
import Reanimated, {
|
|
9
9
|
SharedValue,
|
|
10
10
|
useAnimatedStyle,
|
|
11
|
+
useSharedValue,
|
|
12
|
+
withTiming,
|
|
11
13
|
} from "react-native-reanimated";
|
|
12
14
|
import { ChatMessageViewHydrated } from "streamplace";
|
|
13
15
|
import {
|
|
16
|
+
getSystemMessageType,
|
|
14
17
|
SystemMessage,
|
|
18
|
+
SystemMessageType,
|
|
15
19
|
Text,
|
|
16
20
|
useChat,
|
|
17
21
|
usePlayerStore,
|
|
18
22
|
useSetReplyToMessage,
|
|
23
|
+
useTheme,
|
|
19
24
|
View,
|
|
20
25
|
} from "../../";
|
|
21
|
-
import { bg, flex, px, py } from "../../lib/theme/atoms";
|
|
26
|
+
import { bg, flex, layout, mr, px, py } from "../../lib/theme/atoms";
|
|
22
27
|
import { RenderChatMessage } from "./chat-message";
|
|
23
28
|
import { ModView } from "./mod-view";
|
|
24
29
|
|
|
@@ -168,8 +173,10 @@ const ChatLine = memo(({ item }: { item: ChatMessageViewHydrated }) => {
|
|
|
168
173
|
if (item.author.did === "did:sys:system") {
|
|
169
174
|
return (
|
|
170
175
|
<SystemMessage
|
|
176
|
+
variant={getSystemMessageType(item) || SystemMessageType.notification}
|
|
171
177
|
timestamp={new Date(item.record.createdAt)}
|
|
172
178
|
title={item.record.text}
|
|
179
|
+
facets={item.record.facets}
|
|
173
180
|
/>
|
|
174
181
|
);
|
|
175
182
|
}
|
|
@@ -243,8 +250,30 @@ export function Chat({
|
|
|
243
250
|
shownMessages?: number;
|
|
244
251
|
style?: ComponentProps<typeof View>["style"];
|
|
245
252
|
}) {
|
|
253
|
+
const { theme } = useTheme();
|
|
246
254
|
const chat = useChat();
|
|
247
255
|
const [isScrolledUp, setIsScrolledUp] = useState(false);
|
|
256
|
+
const flatListRef = useRef<FlatList>(null);
|
|
257
|
+
|
|
258
|
+
// Animation for scroll-to-bottom button
|
|
259
|
+
const buttonOpacity = useSharedValue(0);
|
|
260
|
+
const buttonTranslateY = useSharedValue(20);
|
|
261
|
+
|
|
262
|
+
useEffect(() => {
|
|
263
|
+
buttonOpacity.value = withTiming(isScrolledUp ? 1 : 0, { duration: 200 });
|
|
264
|
+
buttonTranslateY.value = withTiming(isScrolledUp ? 0 : 20, {
|
|
265
|
+
duration: 200,
|
|
266
|
+
});
|
|
267
|
+
}, [isScrolledUp]);
|
|
268
|
+
|
|
269
|
+
const buttonAnimatedStyle = useAnimatedStyle(() => ({
|
|
270
|
+
opacity: buttonOpacity.value,
|
|
271
|
+
transform: [{ translateY: buttonTranslateY.value }],
|
|
272
|
+
}));
|
|
273
|
+
|
|
274
|
+
const scrollToBottom = () => {
|
|
275
|
+
flatListRef.current?.scrollToOffset({ offset: 0, animated: true });
|
|
276
|
+
};
|
|
248
277
|
|
|
249
278
|
const handleScroll = (event: any) => {
|
|
250
279
|
const { contentOffset } = event.nativeEvent;
|
|
@@ -270,11 +299,18 @@ export function Chat({
|
|
|
270
299
|
|
|
271
300
|
return (
|
|
272
301
|
<View
|
|
273
|
-
style={[
|
|
274
|
-
|
|
275
|
-
|
|
302
|
+
style={[
|
|
303
|
+
flex.shrink[1],
|
|
304
|
+
{
|
|
305
|
+
minWidth: 0,
|
|
306
|
+
maxWidth: "100%",
|
|
307
|
+
position: "relative",
|
|
308
|
+
overflow: "visible",
|
|
309
|
+
},
|
|
310
|
+
].concat(propsStyle || [])}
|
|
276
311
|
>
|
|
277
312
|
<FlatList
|
|
313
|
+
ref={flatListRef}
|
|
278
314
|
style={[
|
|
279
315
|
flex.grow[1],
|
|
280
316
|
flex.shrink[1],
|
|
@@ -292,6 +328,44 @@ export function Chat({
|
|
|
292
328
|
scrollEventThrottle={16}
|
|
293
329
|
nestedScrollEnabled={true}
|
|
294
330
|
/>
|
|
331
|
+
<Reanimated.View
|
|
332
|
+
style={[
|
|
333
|
+
{
|
|
334
|
+
position: "absolute",
|
|
335
|
+
bottom: 16,
|
|
336
|
+
left: 0,
|
|
337
|
+
right: 0,
|
|
338
|
+
alignItems: "center",
|
|
339
|
+
pointerEvents: isScrolledUp ? "box-none" : "none",
|
|
340
|
+
},
|
|
341
|
+
buttonAnimatedStyle,
|
|
342
|
+
]}
|
|
343
|
+
>
|
|
344
|
+
<Pressable
|
|
345
|
+
onPress={scrollToBottom}
|
|
346
|
+
style={[
|
|
347
|
+
{
|
|
348
|
+
pointerEvents: "auto",
|
|
349
|
+
backgroundColor: theme.colors.primary,
|
|
350
|
+
opacity: 0.9,
|
|
351
|
+
borderRadius: 20,
|
|
352
|
+
shadowColor: "#000",
|
|
353
|
+
shadowOffset: { width: 0, height: 2 },
|
|
354
|
+
shadowOpacity: 0.25,
|
|
355
|
+
shadowRadius: 4,
|
|
356
|
+
elevation: 5,
|
|
357
|
+
},
|
|
358
|
+
layout.flex.row,
|
|
359
|
+
layout.flex.center,
|
|
360
|
+
px[2],
|
|
361
|
+
py[1],
|
|
362
|
+
{ gap: 6 },
|
|
363
|
+
]}
|
|
364
|
+
>
|
|
365
|
+
<ChevronDown size={24} style={{ marginTop: 2 }} color="white" />
|
|
366
|
+
<Text style={[mr[1]]}>Scroll to bottom</Text>
|
|
367
|
+
</Pressable>
|
|
368
|
+
</Reanimated.View>
|
|
295
369
|
<ModView />
|
|
296
370
|
</View>
|
|
297
371
|
);
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import { Pressable } from "react-native";
|
|
2
|
+
import { ScrollView } from "react-native-gesture-handler";
|
|
2
3
|
import { Code, Text, View } from "../..";
|
|
3
4
|
import { bg, layout, left, right, zIndex } from "../../lib/theme/atoms";
|
|
4
5
|
|
|
@@ -61,34 +62,35 @@ export function EmojiSuggestions({
|
|
|
61
62
|
borderRadius: 8,
|
|
62
63
|
boxShadow: "0px 4px 6px rgba(0, 0, 0, 0.1)",
|
|
63
64
|
maxHeight: 200,
|
|
64
|
-
overflow: "auto",
|
|
65
65
|
},
|
|
66
66
|
]}
|
|
67
67
|
>
|
|
68
|
-
|
|
69
|
-
|
|
70
|
-
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
{
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
|
|
82
|
-
|
|
83
|
-
|
|
84
|
-
|
|
85
|
-
{
|
|
86
|
-
|
|
87
|
-
|
|
88
|
-
<
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
68
|
+
<ScrollView>
|
|
69
|
+
{emojis.map((emoji, index) => (
|
|
70
|
+
<Pressable
|
|
71
|
+
key={emoji.id}
|
|
72
|
+
onPress={() => onSelect(emoji)}
|
|
73
|
+
style={[
|
|
74
|
+
{
|
|
75
|
+
padding: 8,
|
|
76
|
+
flexDirection: "row",
|
|
77
|
+
alignItems: "center",
|
|
78
|
+
backgroundColor:
|
|
79
|
+
index === highlightedIndex
|
|
80
|
+
? "rgba(255, 255, 255, 0.1)"
|
|
81
|
+
: "transparent",
|
|
82
|
+
},
|
|
83
|
+
]}
|
|
84
|
+
>
|
|
85
|
+
<Text style={{ fontSize: 16, marginRight: 8 }}>
|
|
86
|
+
{emoji.skins[0]?.native}
|
|
87
|
+
</Text>
|
|
88
|
+
<Text style={{ color: "white", fontSize: 14 }}>
|
|
89
|
+
<Code style={[bg.gray[950]]}>:{emoji.id}:</Code> {emoji.name}
|
|
90
|
+
</Text>
|
|
91
|
+
</Pressable>
|
|
92
|
+
))}
|
|
93
|
+
</ScrollView>
|
|
92
94
|
</View>
|
|
93
95
|
);
|
|
94
96
|
}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
|
-
import { Pressable } from "react-native";
|
|
1
|
+
import { Pressable, ScrollView } from "react-native";
|
|
2
2
|
import { ChatMessageViewHydrated } from "streamplace";
|
|
3
3
|
import { Text, View } from "../..";
|
|
4
|
-
import { bg, layout, left, right
|
|
4
|
+
import { bg, layout, left, right } from "../../lib/theme/atoms";
|
|
5
5
|
|
|
6
6
|
interface MentionSuggestionsProps {
|
|
7
7
|
authors: Map<string, ChatMessageViewHydrated["chatProfile"]>;
|
|
@@ -27,45 +27,48 @@ export function MentionSuggestions({
|
|
|
27
27
|
|
|
28
28
|
left[0],
|
|
29
29
|
right[0],
|
|
30
|
-
zIndex[50],
|
|
31
30
|
{
|
|
32
31
|
bottom: "100%",
|
|
33
32
|
borderRadius: 8,
|
|
34
33
|
boxShadow: "0px 4px 6px rgba(0, 0, 0, 0.1)",
|
|
34
|
+
maxHeight: 200,
|
|
35
|
+
zIndex: 999999,
|
|
35
36
|
},
|
|
36
37
|
]}
|
|
37
38
|
>
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
{
|
|
46
|
-
|
|
47
|
-
|
|
48
|
-
|
|
49
|
-
|
|
50
|
-
|
|
51
|
-
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
|
|
56
|
-
<Text
|
|
57
|
-
style={{
|
|
58
|
-
color: profile?.color
|
|
59
|
-
? `rgb(${profile.color.red}, ${profile.color.green}, ${profile.color.blue})`
|
|
60
|
-
: "black",
|
|
61
|
-
fontWeight: "bold",
|
|
62
|
-
}}
|
|
39
|
+
<ScrollView>
|
|
40
|
+
{authorHandles.map((handle, index) => {
|
|
41
|
+
let profile = authors.get(handle);
|
|
42
|
+
return (
|
|
43
|
+
<Pressable
|
|
44
|
+
key={handle}
|
|
45
|
+
onPress={() => onSelect(handle)}
|
|
46
|
+
style={[
|
|
47
|
+
{
|
|
48
|
+
padding: 8,
|
|
49
|
+
flexDirection: "row",
|
|
50
|
+
alignItems: "center",
|
|
51
|
+
backgroundColor:
|
|
52
|
+
index === highlightedIndex
|
|
53
|
+
? "rgba(0, 0, 0, 0.1)"
|
|
54
|
+
: "rgba(0, 0, 0, 0.5)",
|
|
55
|
+
},
|
|
56
|
+
]}
|
|
63
57
|
>
|
|
64
|
-
|
|
65
|
-
|
|
66
|
-
|
|
67
|
-
|
|
68
|
-
|
|
58
|
+
<Text
|
|
59
|
+
style={{
|
|
60
|
+
color: profile?.color
|
|
61
|
+
? `rgb(${profile.color.red}, ${profile.color.green}, ${profile.color.blue})`
|
|
62
|
+
: "black",
|
|
63
|
+
fontWeight: "bold",
|
|
64
|
+
}}
|
|
65
|
+
>
|
|
66
|
+
@{handle}
|
|
67
|
+
</Text>
|
|
68
|
+
</Pressable>
|
|
69
|
+
);
|
|
70
|
+
})}
|
|
71
|
+
</ScrollView>
|
|
69
72
|
</View>
|
|
70
73
|
);
|
|
71
74
|
}
|
|
@@ -95,17 +95,6 @@ export const ModView = forwardRef<ModViewRef, ModViewProps>(() => {
|
|
|
95
95
|
}
|
|
96
96
|
}, [message]);
|
|
97
97
|
|
|
98
|
-
// Early return AFTER all hooks have been called
|
|
99
|
-
if (!agent?.did) {
|
|
100
|
-
return <></>;
|
|
101
|
-
}
|
|
102
|
-
|
|
103
|
-
// Can show moderation actions if user can hide, ban, or manage livestream
|
|
104
|
-
const canModerate =
|
|
105
|
-
modPermissions.canHide ||
|
|
106
|
-
modPermissions.canBan ||
|
|
107
|
-
modPermissions.canManageLivestream;
|
|
108
|
-
|
|
109
98
|
// Check if any moderation actions are actually available for this message
|
|
110
99
|
// This must match the individual action checks inside the DropdownMenuGroup
|
|
111
100
|
const hasAvailableActions = !!(
|
|
@@ -328,14 +317,14 @@ function ModViewContent({
|
|
|
328
317
|
>
|
|
329
318
|
<Text color="primary">View user on {BSKY_FRONTEND_DOMAIN}</Text>
|
|
330
319
|
</DropdownMenuItem>
|
|
331
|
-
{message.author.did === agent
|
|
320
|
+
{agent?.did && message.author.did === agent.did && (
|
|
332
321
|
<DeleteButton
|
|
333
322
|
message={message}
|
|
334
323
|
deleteChatMessage={deleteChatMessage}
|
|
335
324
|
onOpenChange={onOpenChange}
|
|
336
325
|
/>
|
|
337
326
|
)}
|
|
338
|
-
{message.author.did !== agent
|
|
327
|
+
{(!agent?.did || message.author.did !== agent.did) && (
|
|
339
328
|
<ReportButton
|
|
340
329
|
message={message}
|
|
341
330
|
setReportModalOpen={setReportModalOpen}
|
|
@@ -1,14 +1,23 @@
|
|
|
1
1
|
import { View } from "react-native";
|
|
2
|
-
import {
|
|
3
|
-
import {
|
|
2
|
+
import { Main } from "streamplace/src/lexicons/types/place/stream/richtext/facet";
|
|
3
|
+
import { SystemMessageType } from "../../lib/system-messages";
|
|
4
|
+
import { colors, flex, gap, layout, ml, pb, pl, px, w } from "../../ui";
|
|
4
5
|
import { Code, Text } from "../ui/text";
|
|
6
|
+
import { RichTextMessage } from "./chat-message";
|
|
5
7
|
|
|
6
8
|
interface SystemMessageProps {
|
|
9
|
+
variant: SystemMessageType;
|
|
7
10
|
title: string;
|
|
8
11
|
timestamp: Date;
|
|
12
|
+
facets?: Main[];
|
|
9
13
|
}
|
|
10
14
|
|
|
11
|
-
export function SystemMessage({
|
|
15
|
+
export function SystemMessage({
|
|
16
|
+
variant,
|
|
17
|
+
title,
|
|
18
|
+
timestamp,
|
|
19
|
+
facets,
|
|
20
|
+
}: SystemMessageProps) {
|
|
12
21
|
return (
|
|
13
22
|
<View style={[w.percent[100], px[2], pb[2]]}>
|
|
14
23
|
<Code color="muted" tracking="widest" style={[pl[12], ml[1]]}>
|
|
@@ -18,7 +27,7 @@ export function SystemMessage({ title, timestamp }: SystemMessageProps) {
|
|
|
18
27
|
<Text
|
|
19
28
|
style={{
|
|
20
29
|
fontVariant: ["tabular-nums"],
|
|
21
|
-
color:
|
|
30
|
+
color: colors.gray[400],
|
|
22
31
|
}}
|
|
23
32
|
>
|
|
24
33
|
{timestamp.toLocaleTimeString([], {
|
|
@@ -28,7 +37,7 @@ export function SystemMessage({ title, timestamp }: SystemMessageProps) {
|
|
|
28
37
|
})}
|
|
29
38
|
</Text>
|
|
30
39
|
<Text weight="bold" color="default" style={[flex.shrink[1]]}>
|
|
31
|
-
{title}
|
|
40
|
+
<RichTextMessage facets={facets} text={title} />
|
|
32
41
|
</Text>
|
|
33
42
|
</View>
|
|
34
43
|
</View>
|
|
@@ -1,5 +1,6 @@
|
|
|
1
1
|
import { useMemo } from "react";
|
|
2
|
-
import { PlayerProtocol
|
|
2
|
+
import { PlayerProtocol } from "../../player-store/player-state";
|
|
3
|
+
import { useStreamplaceStore } from "../../streamplace-store";
|
|
3
4
|
|
|
4
5
|
const protocolSuffixes = {
|
|
5
6
|
m3u8: PlayerProtocol.HLS,
|