@streamplace/components 0.7.30 → 0.7.34
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.js +4 -4
- package/dist/components/chat/mod-view.js +9 -2
- package/dist/components/dashboard/chat-panel.js +2 -2
- package/dist/livestream-store/chat.js +6 -6
- package/dist/player-store/player-store.js +11 -1
- package/node-compile-cache/v22.15.0-x64-efe9a9df-0/37be0eec +0 -0
- package/package.json +2 -2
- package/src/components/chat/chat.tsx +90 -104
- package/src/components/chat/mod-view.tsx +15 -7
- package/src/components/dashboard/chat-panel.tsx +1 -3
- package/src/livestream-store/chat.tsx +6 -6
- package/src/player-store/player-store.tsx +10 -0
- package/tsconfig.tsbuildinfo +1 -1
|
@@ -81,7 +81,7 @@ const ActionsBar = (0, react_1.memo)(({ item, visible, hoverTimeoutRef, }) => {
|
|
|
81
81
|
}
|
|
82
82
|
}, children: (0, jsx_runtime_1.jsx)(lucide_react_native_1.Ellipsis, { color: "white", size: 16 }) })] }));
|
|
83
83
|
});
|
|
84
|
-
const ChatLine = (0, react_1.memo)(({ item
|
|
84
|
+
const ChatLine = (0, react_1.memo)(({ item }) => {
|
|
85
85
|
const setReply = (0, __1.useSetReplyToMessage)();
|
|
86
86
|
const setModMsg = (0, __1.usePlayerStore)((state) => state.setModMessage);
|
|
87
87
|
const swipeableRef = (0, react_1.useRef)(null);
|
|
@@ -136,7 +136,7 @@ const ChatLine = (0, react_1.memo)(({ item, canModerate, }) => {
|
|
|
136
136
|
}
|
|
137
137
|
}, children: (0, jsx_runtime_1.jsx)(chat_message_1.RenderChatMessage, { item: item }) }) }));
|
|
138
138
|
});
|
|
139
|
-
function Chat({ shownMessages = SHOWN_MSGS, style: propsStyle,
|
|
139
|
+
function Chat({ shownMessages = SHOWN_MSGS, style: propsStyle, ...props }) {
|
|
140
140
|
const chat = (0, __1.useChat)();
|
|
141
141
|
const [isScrolledUp, setIsScrolledUp] = (0, react_1.useState)(false);
|
|
142
142
|
const handleScroll = (event) => {
|
|
@@ -151,10 +151,10 @@ function Chat({ shownMessages = SHOWN_MSGS, style: propsStyle, canModerate = fal
|
|
|
151
151
|
}
|
|
152
152
|
};
|
|
153
153
|
if (!chat)
|
|
154
|
-
return ((0, jsx_runtime_1.jsx)(__1.View, { style: [atoms_1.flex.shrink[1], { minWidth: 0, maxWidth: "100%" }], children: (0, jsx_runtime_1.jsx)(__1.Text, { children: "Loading
|
|
154
|
+
return ((0, jsx_runtime_1.jsx)(__1.View, { style: [atoms_1.flex.shrink[1], { minWidth: 0, maxWidth: "100%" }], children: (0, jsx_runtime_1.jsx)(__1.Text, { children: "Loading chat..." }) }));
|
|
155
155
|
return ((0, jsx_runtime_1.jsxs)(__1.View, { style: [atoms_1.flex.shrink[1], { minWidth: 0, maxWidth: "100%" }].concat(propsStyle || []), children: [(0, jsx_runtime_1.jsx)(react_native_gesture_handler_1.FlatList, { style: [
|
|
156
156
|
atoms_1.flex.grow[1],
|
|
157
157
|
atoms_1.flex.shrink[1],
|
|
158
158
|
{ minWidth: 0, maxWidth: "100%" },
|
|
159
|
-
], data: chat.slice(0, shownMessages), inverted: true, keyExtractor: keyExtractor, renderItem: ({ item, index }) => (
|
|
159
|
+
], data: chat.slice(0, shownMessages), inverted: true, keyExtractor: keyExtractor, renderItem: ({ item, index }) => (0, jsx_runtime_1.jsx)(ChatLine, { item: item }), removeClippedSubviews: true, maxToRenderPerBatch: 10, initialNumToRender: 10, updateCellsBatchingPeriod: 50, onScroll: handleScroll, scrollEventThrottle: 16, nestedScrollEnabled: true }), (0, jsx_runtime_1.jsx)(mod_view_1.ModView, {})] }));
|
|
160
160
|
}
|
|
@@ -26,6 +26,7 @@ exports.ModView = (0, react_1.forwardRef)(() => {
|
|
|
26
26
|
const setReportSubject = (0, player_store_1.usePlayerStore)((x) => x.setReportSubject);
|
|
27
27
|
const setModMessage = (0, player_store_1.usePlayerStore)((x) => x.setModMessage);
|
|
28
28
|
const deleteChatMessage = (0, livestream_store_1.useDeleteChatMessage)();
|
|
29
|
+
const isMyStream = (0, player_store_1.useIsMyStream)();
|
|
29
30
|
// get the channel did
|
|
30
31
|
const channelId = (0, player_store_1.usePlayerStore)((state) => state.src);
|
|
31
32
|
// get the logged in user's identity
|
|
@@ -60,7 +61,7 @@ exports.ModView = (0, react_1.forwardRef)(() => {
|
|
|
60
61
|
hour: "2-digit",
|
|
61
62
|
minute: "2-digit",
|
|
62
63
|
hour12: false,
|
|
63
|
-
}), " ", "@", message.author.handle, ": ", message.record.text] }) }) }) }),
|
|
64
|
+
}), " ", "@", message.author.handle, ": ", message.record.text] }) }) }) }), isMyStream() && ((0, jsx_runtime_1.jsxs)(ui_1.DropdownMenuGroup, { title: `Moderation actions`, children: [(0, jsx_runtime_1.jsx)(ui_1.DropdownMenuItem, { disabled: isHideLoading || messageRemoved, onPress: () => {
|
|
64
65
|
if (isHideLoading || messageRemoved)
|
|
65
66
|
return;
|
|
66
67
|
createHideChat(message.uri)
|
|
@@ -89,6 +90,7 @@ var DeleteState;
|
|
|
89
90
|
function DeleteButton({ message, deleteChatMessage, }) {
|
|
90
91
|
const [confirming, setConfirming] = (0, react_1.useState)(DeleteState.None);
|
|
91
92
|
const { onOpenChange } = (0, dropdown_menu_1.useRootContext)();
|
|
93
|
+
const toast = (0, ui_1.useToast)();
|
|
92
94
|
return ((0, jsx_runtime_1.jsx)(ui_1.DropdownMenuItem, { onPress: () => {
|
|
93
95
|
if (!message)
|
|
94
96
|
return;
|
|
@@ -99,10 +101,15 @@ function DeleteButton({ message, deleteChatMessage, }) {
|
|
|
99
101
|
if (confirming === DeleteState.Confirmed) {
|
|
100
102
|
setConfirming(DeleteState.Deleting);
|
|
101
103
|
}
|
|
102
|
-
deleteChatMessage(message.uri)
|
|
104
|
+
deleteChatMessage(message.uri)
|
|
105
|
+
.then(() => {
|
|
103
106
|
// wait ~a second before resetting state to allow deletion to take effect
|
|
104
107
|
setTimeout(() => setConfirming(DeleteState.None), 1000);
|
|
105
108
|
onOpenChange?.(false);
|
|
109
|
+
})
|
|
110
|
+
.catch((e) => {
|
|
111
|
+
toast.show("Couldn't delete the message", e);
|
|
112
|
+
setConfirming(DeleteState.None);
|
|
106
113
|
});
|
|
107
114
|
}, children: (0, jsx_runtime_1.jsx)(ui_1.Text, { color: "destructive", children: confirming === DeleteState.Confirmed
|
|
108
115
|
? "Are you sure? Click again to confirm."
|
|
@@ -9,7 +9,7 @@ const zero = tslib_1.__importStar(require("../../ui"));
|
|
|
9
9
|
const chat_1 = require("../chat/chat");
|
|
10
10
|
const chat_box_1 = require("../chat/chat-box");
|
|
11
11
|
const { flex, bg, r, borders, p, px, py, text, layout } = zero;
|
|
12
|
-
function ChatPanel({ isLive, isConnected, messagesPerMinute = 0,
|
|
12
|
+
function ChatPanel({ isLive, isConnected, messagesPerMinute = 0, shownMessages = 50, }) {
|
|
13
13
|
return ((0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [
|
|
14
14
|
flex.values[1],
|
|
15
15
|
bg.neutral[900],
|
|
@@ -27,7 +27,7 @@ function ChatPanel({ isLive, isConnected, messagesPerMinute = 0, canModerate = f
|
|
|
27
27
|
], children: [(0, jsx_runtime_1.jsx)(react_native_1.Text, { style: [text.white, { fontSize: 18, fontWeight: "600" }], children: "Chat" }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [layout.flex.row, layout.flex.alignCenter], children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [
|
|
28
28
|
{ width: 6, height: 6, borderRadius: 3 },
|
|
29
29
|
isLive && isConnected ? bg.green[500] : bg.gray[500],
|
|
30
|
-
] }), (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [text.gray[400], { fontSize: 12, marginLeft: 8 }], children: [messagesPerMinute, " msg/min"] })] })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [flex.values[1], px[2], { minHeight: 0 }], children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [flex.values[1], { minHeight: 0 }], children: (0, jsx_runtime_1.jsx)(chat_1.Chat, {
|
|
30
|
+
] }), (0, jsx_runtime_1.jsxs)(react_native_1.Text, { style: [text.gray[400], { fontSize: 12, marginLeft: 8 }], children: [messagesPerMinute, " msg/min"] })] })] }), (0, jsx_runtime_1.jsxs)(react_native_1.View, { style: [flex.values[1], px[2], { minHeight: 0 }], children: [(0, jsx_runtime_1.jsx)(react_native_1.View, { style: [flex.values[1], { minHeight: 0 }], children: (0, jsx_runtime_1.jsx)(chat_1.Chat, { shownMessages: shownMessages }) }), (0, jsx_runtime_1.jsx)(react_native_1.View, { style: [{ flexShrink: 0 }], children: (0, jsx_runtime_1.jsx)(chat_box_1.ChatBox, { emojiData: emoji_data_json_1.default, chatBoxStyle: [
|
|
31
31
|
bg.gray[700],
|
|
32
32
|
borders.width.thin,
|
|
33
33
|
borders.color.gray[600],
|
|
@@ -102,14 +102,14 @@ const useCreateChatMessage = () => {
|
|
|
102
102
|
exports.useCreateChatMessage = useCreateChatMessage;
|
|
103
103
|
const useDeleteChatMessage = () => {
|
|
104
104
|
const pdsAgent = (0, xrpc_1.usePDSAgent)();
|
|
105
|
-
if (!pdsAgent) {
|
|
106
|
-
throw new Error("No PDS agent found");
|
|
107
|
-
}
|
|
108
105
|
const userDID = (0, streamplace_store_1.useDID)();
|
|
109
|
-
if (!userDID) {
|
|
110
|
-
throw new Error("No user DID found");
|
|
111
|
-
}
|
|
112
106
|
return async (uri) => {
|
|
107
|
+
if (!pdsAgent) {
|
|
108
|
+
throw new Error("No PDS agent found");
|
|
109
|
+
}
|
|
110
|
+
if (!userDID) {
|
|
111
|
+
throw new Error("No user DID found");
|
|
112
|
+
}
|
|
113
113
|
const rkey = uri.split("/").pop();
|
|
114
114
|
if (!rkey) {
|
|
115
115
|
throw new Error("No rkey found");
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
"use strict";
|
|
2
2
|
Object.defineProperty(exports, "__esModule", { value: true });
|
|
3
|
-
exports.useOffline = exports.intoPlayerProtocol = exports.usePlayerProtocol = exports.makePlayerStore = void 0;
|
|
3
|
+
exports.useIsMyStream = exports.useOffline = exports.intoPlayerProtocol = exports.usePlayerProtocol = exports.makePlayerStore = void 0;
|
|
4
4
|
exports.usePlayerContext = usePlayerContext;
|
|
5
5
|
exports.getPlayerStoreById = getPlayerStoreById;
|
|
6
6
|
exports.getFirstPlayerID = getFirstPlayerID;
|
|
@@ -9,6 +9,7 @@ exports.usePlayerStore = usePlayerStore;
|
|
|
9
9
|
const react_1 = require("react");
|
|
10
10
|
const zustand_1 = require("zustand");
|
|
11
11
|
const livestream_store_1 = require("../livestream-store");
|
|
12
|
+
const streamplace_store_1 = require("../streamplace-store");
|
|
12
13
|
const context_1 = require("./context");
|
|
13
14
|
const player_state_1 = require("./player-state");
|
|
14
15
|
const makePlayerStore = (id) => {
|
|
@@ -195,3 +196,12 @@ const useOffline = () => {
|
|
|
195
196
|
return now - Date.parse(segment.startTime) > 10000;
|
|
196
197
|
};
|
|
197
198
|
exports.useOffline = useOffline;
|
|
199
|
+
const useIsMyStream = () => {
|
|
200
|
+
const myHandle = (0, streamplace_store_1.useStreamplaceStore)((state) => state.handle);
|
|
201
|
+
const myDid = (0, streamplace_store_1.useStreamplaceStore)((state) => state.oauthSession?.did);
|
|
202
|
+
const channelId = usePlayerStore((state) => state.src);
|
|
203
|
+
return () => {
|
|
204
|
+
return myHandle === channelId || myDid === channelId;
|
|
205
|
+
};
|
|
206
|
+
};
|
|
207
|
+
exports.useIsMyStream = useIsMyStream;
|
|
Binary file
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@streamplace/components",
|
|
3
|
-
"version": "0.7.
|
|
3
|
+
"version": "0.7.34",
|
|
4
4
|
"description": "Streamplace React (Native) Components",
|
|
5
5
|
"main": "dist/index.js",
|
|
6
6
|
"types": "src/index.tsx",
|
|
@@ -54,5 +54,5 @@
|
|
|
54
54
|
"start": "tsc --watch --preserveWatchOutput",
|
|
55
55
|
"prepare": "tsc"
|
|
56
56
|
},
|
|
57
|
-
"gitHead": "
|
|
57
|
+
"gitHead": "8d33e4f6129af71ccf3c5d959e347f78b6eb609a"
|
|
58
58
|
}
|
|
@@ -136,124 +136,112 @@ const ActionsBar = memo(
|
|
|
136
136
|
},
|
|
137
137
|
);
|
|
138
138
|
|
|
139
|
-
const ChatLine = memo(
|
|
140
|
-
(
|
|
141
|
-
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
canModerate: boolean;
|
|
146
|
-
}) => {
|
|
147
|
-
const setReply = useSetReplyToMessage();
|
|
148
|
-
const setModMsg = usePlayerStore((state) => state.setModMessage);
|
|
149
|
-
const swipeableRef = useRef<SwipeableMethods | null>(null);
|
|
150
|
-
const [isHovered, setIsHovered] = useState(false);
|
|
151
|
-
const hoverTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
139
|
+
const ChatLine = memo(({ item }: { item: ChatMessageViewHydrated }) => {
|
|
140
|
+
const setReply = useSetReplyToMessage();
|
|
141
|
+
const setModMsg = usePlayerStore((state) => state.setModMessage);
|
|
142
|
+
const swipeableRef = useRef<SwipeableMethods | null>(null);
|
|
143
|
+
const [isHovered, setIsHovered] = useState(false);
|
|
144
|
+
const hoverTimeoutRef = useRef<NodeJS.Timeout | null>(null);
|
|
152
145
|
|
|
153
|
-
|
|
146
|
+
const handleHoverIn = () => {
|
|
147
|
+
if (hoverTimeoutRef.current) {
|
|
148
|
+
clearTimeout(hoverTimeoutRef.current);
|
|
149
|
+
hoverTimeoutRef.current = null;
|
|
150
|
+
}
|
|
151
|
+
setIsHovered(true);
|
|
152
|
+
};
|
|
153
|
+
|
|
154
|
+
const handleHoverOut = () => {
|
|
155
|
+
hoverTimeoutRef.current = setTimeout(() => {
|
|
156
|
+
setIsHovered(false);
|
|
157
|
+
}, 50);
|
|
158
|
+
};
|
|
159
|
+
|
|
160
|
+
useEffect(() => {
|
|
161
|
+
return () => {
|
|
154
162
|
if (hoverTimeoutRef.current) {
|
|
155
163
|
clearTimeout(hoverTimeoutRef.current);
|
|
156
|
-
hoverTimeoutRef.current = null;
|
|
157
164
|
}
|
|
158
|
-
setIsHovered(true);
|
|
159
|
-
};
|
|
160
|
-
|
|
161
|
-
const handleHoverOut = () => {
|
|
162
|
-
hoverTimeoutRef.current = setTimeout(() => {
|
|
163
|
-
setIsHovered(false);
|
|
164
|
-
}, 50);
|
|
165
165
|
};
|
|
166
|
+
}, []);
|
|
166
167
|
|
|
167
|
-
|
|
168
|
-
|
|
169
|
-
|
|
170
|
-
|
|
171
|
-
}
|
|
172
|
-
|
|
173
|
-
|
|
174
|
-
|
|
175
|
-
if (item.author.did === "did:sys:system") {
|
|
176
|
-
return (
|
|
177
|
-
<SystemMessage
|
|
178
|
-
timestamp={new Date(item.record.createdAt)}
|
|
179
|
-
title={item.record.text}
|
|
180
|
-
/>
|
|
181
|
-
);
|
|
182
|
-
}
|
|
183
|
-
|
|
184
|
-
if (Platform.OS === "web") {
|
|
185
|
-
return (
|
|
186
|
-
<View
|
|
187
|
-
style={[
|
|
188
|
-
py[1],
|
|
189
|
-
px[2],
|
|
190
|
-
{
|
|
191
|
-
position: "relative",
|
|
192
|
-
borderRadius: 8,
|
|
193
|
-
minWidth: 0,
|
|
194
|
-
maxWidth: "100%",
|
|
195
|
-
},
|
|
196
|
-
isHovered && bg.gray[950],
|
|
197
|
-
]}
|
|
198
|
-
onPointerEnter={handleHoverIn}
|
|
199
|
-
onPointerLeave={handleHoverOut}
|
|
200
|
-
>
|
|
201
|
-
<Pressable style={[{ minWidth: 0, maxWidth: "100%" }]}>
|
|
202
|
-
<RenderChatMessage item={item} />
|
|
203
|
-
</Pressable>
|
|
204
|
-
<ActionsBar
|
|
205
|
-
item={item}
|
|
206
|
-
visible={isHovered}
|
|
207
|
-
hoverTimeoutRef={hoverTimeoutRef}
|
|
208
|
-
/>
|
|
209
|
-
</View>
|
|
210
|
-
);
|
|
211
|
-
}
|
|
168
|
+
if (item.author.did === "did:sys:system") {
|
|
169
|
+
return (
|
|
170
|
+
<SystemMessage
|
|
171
|
+
timestamp={new Date(item.record.createdAt)}
|
|
172
|
+
title={item.record.text}
|
|
173
|
+
/>
|
|
174
|
+
);
|
|
175
|
+
}
|
|
212
176
|
|
|
177
|
+
if (Platform.OS === "web") {
|
|
213
178
|
return (
|
|
214
|
-
|
|
215
|
-
|
|
216
|
-
|
|
217
|
-
|
|
218
|
-
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
|
|
222
|
-
|
|
223
|
-
}
|
|
224
|
-
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
229
|
-
|
|
230
|
-
}
|
|
231
|
-
if (r === (Platform.OS === "android" ? "left" : "right")) {
|
|
232
|
-
setModMsg(item);
|
|
233
|
-
}
|
|
234
|
-
// close this swipeable
|
|
235
|
-
const swipeable = swipeableRef.current;
|
|
236
|
-
if (swipeable) {
|
|
237
|
-
swipeable.close();
|
|
238
|
-
}
|
|
239
|
-
}}
|
|
240
|
-
>
|
|
179
|
+
<View
|
|
180
|
+
style={[
|
|
181
|
+
py[1],
|
|
182
|
+
px[2],
|
|
183
|
+
{
|
|
184
|
+
position: "relative",
|
|
185
|
+
borderRadius: 8,
|
|
186
|
+
minWidth: 0,
|
|
187
|
+
maxWidth: "100%",
|
|
188
|
+
},
|
|
189
|
+
isHovered && bg.gray[950],
|
|
190
|
+
]}
|
|
191
|
+
onPointerEnter={handleHoverIn}
|
|
192
|
+
onPointerLeave={handleHoverOut}
|
|
193
|
+
>
|
|
194
|
+
<Pressable style={[{ minWidth: 0, maxWidth: "100%" }]}>
|
|
241
195
|
<RenderChatMessage item={item} />
|
|
242
|
-
</
|
|
243
|
-
|
|
196
|
+
</Pressable>
|
|
197
|
+
<ActionsBar
|
|
198
|
+
item={item}
|
|
199
|
+
visible={isHovered}
|
|
200
|
+
hoverTimeoutRef={hoverTimeoutRef}
|
|
201
|
+
/>
|
|
202
|
+
</View>
|
|
244
203
|
);
|
|
245
|
-
}
|
|
246
|
-
|
|
204
|
+
}
|
|
205
|
+
|
|
206
|
+
return (
|
|
207
|
+
<>
|
|
208
|
+
<Swipeable
|
|
209
|
+
containerStyle={[py[1]]}
|
|
210
|
+
friction={2}
|
|
211
|
+
enableTrackpadTwoFingerGesture
|
|
212
|
+
rightThreshold={40}
|
|
213
|
+
leftThreshold={40}
|
|
214
|
+
renderRightActions={Platform.OS === "android" ? undefined : RightAction}
|
|
215
|
+
renderLeftActions={Platform.OS === "android" ? undefined : LeftAction}
|
|
216
|
+
overshootFriction={9}
|
|
217
|
+
ref={swipeableRef}
|
|
218
|
+
onSwipeableOpen={(r) => {
|
|
219
|
+
if (r === (Platform.OS === "android" ? "right" : "left")) {
|
|
220
|
+
setReply(item);
|
|
221
|
+
}
|
|
222
|
+
if (r === (Platform.OS === "android" ? "left" : "right")) {
|
|
223
|
+
setModMsg(item);
|
|
224
|
+
}
|
|
225
|
+
// close this swipeable
|
|
226
|
+
const swipeable = swipeableRef.current;
|
|
227
|
+
if (swipeable) {
|
|
228
|
+
swipeable.close();
|
|
229
|
+
}
|
|
230
|
+
}}
|
|
231
|
+
>
|
|
232
|
+
<RenderChatMessage item={item} />
|
|
233
|
+
</Swipeable>
|
|
234
|
+
</>
|
|
235
|
+
);
|
|
236
|
+
});
|
|
247
237
|
|
|
248
238
|
export function Chat({
|
|
249
239
|
shownMessages = SHOWN_MSGS,
|
|
250
240
|
style: propsStyle,
|
|
251
|
-
canModerate = false,
|
|
252
241
|
...props
|
|
253
242
|
}: ComponentProps<typeof View> & {
|
|
254
243
|
shownMessages?: number;
|
|
255
244
|
style?: ComponentProps<typeof View>["style"];
|
|
256
|
-
canModerate?: boolean;
|
|
257
245
|
}) {
|
|
258
246
|
const chat = useChat();
|
|
259
247
|
const [isScrolledUp, setIsScrolledUp] = useState(false);
|
|
@@ -276,7 +264,7 @@ export function Chat({
|
|
|
276
264
|
if (!chat)
|
|
277
265
|
return (
|
|
278
266
|
<View style={[flex.shrink[1], { minWidth: 0, maxWidth: "100%" }]}>
|
|
279
|
-
<Text>Loading
|
|
267
|
+
<Text>Loading chat...</Text>
|
|
280
268
|
</View>
|
|
281
269
|
);
|
|
282
270
|
|
|
@@ -295,9 +283,7 @@ export function Chat({
|
|
|
295
283
|
data={chat.slice(0, shownMessages)}
|
|
296
284
|
inverted={true}
|
|
297
285
|
keyExtractor={keyExtractor}
|
|
298
|
-
renderItem={({ item, index }) =>
|
|
299
|
-
<ChatLine item={item} canModerate={canModerate} />
|
|
300
|
-
)}
|
|
286
|
+
renderItem={({ item, index }) => <ChatLine item={item} />}
|
|
301
287
|
removeClippedSubviews={true}
|
|
302
288
|
maxToRenderPerBatch={10}
|
|
303
289
|
initialNumToRender={10}
|
|
@@ -1,7 +1,7 @@
|
|
|
1
1
|
import { TriggerRef, useRootContext } from "@rn-primitives/dropdown-menu";
|
|
2
2
|
import { forwardRef, useEffect, useRef, useState } from "react";
|
|
3
3
|
import { gap, mr, w } from "../../lib/theme/atoms";
|
|
4
|
-
import { usePlayerStore } from "../../player-store";
|
|
4
|
+
import { useIsMyStream, usePlayerStore } from "../../player-store";
|
|
5
5
|
import {
|
|
6
6
|
useCreateBlockRecord,
|
|
7
7
|
useCreateHideChatRecord,
|
|
@@ -21,6 +21,7 @@ import {
|
|
|
21
21
|
layout,
|
|
22
22
|
ResponsiveDropdownMenuContent,
|
|
23
23
|
Text,
|
|
24
|
+
useToast,
|
|
24
25
|
View,
|
|
25
26
|
} from "../ui";
|
|
26
27
|
|
|
@@ -50,6 +51,7 @@ export const ModView = forwardRef<ModViewRef, ModViewProps>(() => {
|
|
|
50
51
|
const setReportSubject = usePlayerStore((x) => x.setReportSubject);
|
|
51
52
|
const setModMessage = usePlayerStore((x) => x.setModMessage);
|
|
52
53
|
const deleteChatMessage = useDeleteChatMessage();
|
|
54
|
+
const isMyStream = useIsMyStream();
|
|
53
55
|
|
|
54
56
|
// get the channel did
|
|
55
57
|
const channelId = usePlayerStore((state) => state.src);
|
|
@@ -118,7 +120,7 @@ export const ModView = forwardRef<ModViewRef, ModViewProps>(() => {
|
|
|
118
120
|
</DropdownMenuGroup>
|
|
119
121
|
|
|
120
122
|
{/* TODO: Checking for non-owner moderators */}
|
|
121
|
-
{
|
|
123
|
+
{isMyStream() && (
|
|
122
124
|
<DropdownMenuGroup title={`Moderation actions`}>
|
|
123
125
|
<DropdownMenuItem
|
|
124
126
|
disabled={isHideLoading || messageRemoved}
|
|
@@ -210,6 +212,7 @@ export function DeleteButton({
|
|
|
210
212
|
}) {
|
|
211
213
|
const [confirming, setConfirming] = useState<DeleteState>(DeleteState.None);
|
|
212
214
|
const { onOpenChange } = useRootContext();
|
|
215
|
+
const toast = useToast();
|
|
213
216
|
return (
|
|
214
217
|
<DropdownMenuItem
|
|
215
218
|
onPress={() => {
|
|
@@ -221,11 +224,16 @@ export function DeleteButton({
|
|
|
221
224
|
if (confirming === DeleteState.Confirmed) {
|
|
222
225
|
setConfirming(DeleteState.Deleting);
|
|
223
226
|
}
|
|
224
|
-
deleteChatMessage(message.uri)
|
|
225
|
-
|
|
226
|
-
|
|
227
|
-
|
|
228
|
-
|
|
227
|
+
deleteChatMessage(message.uri)
|
|
228
|
+
.then(() => {
|
|
229
|
+
// wait ~a second before resetting state to allow deletion to take effect
|
|
230
|
+
setTimeout(() => setConfirming(DeleteState.None), 1000);
|
|
231
|
+
onOpenChange?.(false);
|
|
232
|
+
})
|
|
233
|
+
.catch((e) => {
|
|
234
|
+
toast.show("Couldn't delete the message", e);
|
|
235
|
+
setConfirming(DeleteState.None);
|
|
236
|
+
});
|
|
229
237
|
}}
|
|
230
238
|
>
|
|
231
239
|
<Text color="destructive">
|
|
@@ -10,7 +10,6 @@ interface ChatPanelProps {
|
|
|
10
10
|
isLive: boolean;
|
|
11
11
|
isConnected: boolean;
|
|
12
12
|
messagesPerMinute?: number;
|
|
13
|
-
canModerate?: boolean;
|
|
14
13
|
shownMessages?: number;
|
|
15
14
|
}
|
|
16
15
|
|
|
@@ -18,7 +17,6 @@ export default function ChatPanel({
|
|
|
18
17
|
isLive,
|
|
19
18
|
isConnected,
|
|
20
19
|
messagesPerMinute = 0,
|
|
21
|
-
canModerate = false,
|
|
22
20
|
shownMessages = 50,
|
|
23
21
|
}: ChatPanelProps) {
|
|
24
22
|
return (
|
|
@@ -59,7 +57,7 @@ export default function ChatPanel({
|
|
|
59
57
|
</View>
|
|
60
58
|
<View style={[flex.values[1], px[2], { minHeight: 0 }]}>
|
|
61
59
|
<View style={[flex.values[1], { minHeight: 0 }]}>
|
|
62
|
-
<Chat
|
|
60
|
+
<Chat shownMessages={shownMessages} />
|
|
63
61
|
</View>
|
|
64
62
|
<View style={[{ flexShrink: 0 }]}>
|
|
65
63
|
<ChatBox
|
|
@@ -135,14 +135,14 @@ export const useCreateChatMessage = () => {
|
|
|
135
135
|
|
|
136
136
|
export const useDeleteChatMessage = () => {
|
|
137
137
|
const pdsAgent = usePDSAgent();
|
|
138
|
-
if (!pdsAgent) {
|
|
139
|
-
throw new Error("No PDS agent found");
|
|
140
|
-
}
|
|
141
138
|
const userDID = useDID();
|
|
142
|
-
if (!userDID) {
|
|
143
|
-
throw new Error("No user DID found");
|
|
144
|
-
}
|
|
145
139
|
return async (uri: string) => {
|
|
140
|
+
if (!pdsAgent) {
|
|
141
|
+
throw new Error("No PDS agent found");
|
|
142
|
+
}
|
|
143
|
+
if (!userDID) {
|
|
144
|
+
throw new Error("No user DID found");
|
|
145
|
+
}
|
|
146
146
|
const rkey = uri.split("/").pop();
|
|
147
147
|
if (!rkey) {
|
|
148
148
|
throw new Error("No rkey found");
|
|
@@ -3,6 +3,7 @@ import { useContext, useEffect, useState } from "react";
|
|
|
3
3
|
import { ChatMessageViewHydrated } from "streamplace";
|
|
4
4
|
import { createStore, StoreApi, useStore } from "zustand";
|
|
5
5
|
import { useLivestreamStore } from "../livestream-store";
|
|
6
|
+
import { useStreamplaceStore } from "../streamplace-store";
|
|
6
7
|
import { PlayerContext } from "./context";
|
|
7
8
|
import {
|
|
8
9
|
IngestMediaSource,
|
|
@@ -271,3 +272,12 @@ export const useOffline = () => {
|
|
|
271
272
|
}
|
|
272
273
|
return now - Date.parse(segment.startTime) > 10000;
|
|
273
274
|
};
|
|
275
|
+
|
|
276
|
+
export const useIsMyStream = () => {
|
|
277
|
+
const myHandle = useStreamplaceStore((state) => state.handle);
|
|
278
|
+
const myDid = useStreamplaceStore((state) => state.oauthSession?.did);
|
|
279
|
+
const channelId = usePlayerStore((state) => state.src);
|
|
280
|
+
return () => {
|
|
281
|
+
return myHandle === channelId || myDid === channelId;
|
|
282
|
+
};
|
|
283
|
+
};
|