tangled
alpha
login
or
join now
stream.place
/
streamplace
74
fork
atom
Live video on the AT Protocol
74
fork
atom
overview
issues
1
pulls
pipelines
two column card grid?
Natalie B.
1 month ago
51384b0e
c9f03a16
+159
-54
1 changed file
expand all
collapse all
unified
split
js
components
src
components
chat
teleport-modal.tsx
+159
-54
js/components/src/components/chat/teleport-modal.tsx
···
1
1
+
import { Check } from "lucide-react-native";
1
2
import React, { useEffect, useMemo, useState } from "react";
2
3
import { Image, Pressable, ScrollView, View } from "react-native";
3
4
import { PlaceStreamLivestream } from "streamplace";
···
7
8
Button,
8
9
DialogFooter,
9
10
Input,
10
10
-
MenuGroup,
11
11
-
MenuItem,
12
11
ResponsiveDialog,
13
12
Text,
14
13
useTheme,
···
83
82
title="Teleport to Streamer"
84
83
showCloseButton
85
84
variant="default"
86
86
-
size="md"
85
85
+
size="xl"
87
86
dismissible={false}
88
87
>
89
88
<View style={[zero.py[2]]}>
···
113
112
</View>
114
113
) : (
115
114
<ScrollView style={[{ maxHeight: 400 }]}>
116
116
-
<MenuGroup>
117
117
-
{filteredStreams.map((stream) => (
118
118
-
<Pressable
119
119
-
key={stream.uri}
120
120
-
onPress={() => setSelectedStream(stream)}
121
121
-
>
122
122
-
<MenuItem
123
123
-
style={
124
124
-
[
125
125
-
selectedStream?.uri === stream.uri && {
126
126
-
backgroundColor: "rgba(0, 122, 255, 0.1)",
127
127
-
},
128
128
-
zero.layout.flex.spaceBetween,
129
129
-
zero.r.md,
130
130
-
zero.flex[1],
131
131
-
zero.gap.all[2],
132
132
-
{ width: "100%" },
133
133
-
] as any
134
134
-
}
115
115
+
<View
116
116
+
style={[
117
117
+
{
118
118
+
flexDirection: "row",
119
119
+
flexWrap: "wrap",
120
120
+
gap: 12,
121
121
+
},
122
122
+
]}
123
123
+
>
124
124
+
{filteredStreams.map((stream) => {
125
125
+
const isSelected = selectedStream?.uri === stream.uri;
126
126
+
const profile = profiles[stream.author?.did];
127
127
+
128
128
+
return (
129
129
+
<Pressable
130
130
+
key={stream.uri}
131
131
+
onPress={() => setSelectedStream(stream)}
132
132
+
style={[
133
133
+
{
134
134
+
width: "48%",
135
135
+
minWidth: 200,
136
136
+
},
137
137
+
]}
135
138
>
136
136
-
<Image
137
137
-
source={{
138
138
-
uri: profiles[stream.author.did]?.avatar,
139
139
-
width: 50,
140
140
-
height: 50,
141
141
-
}}
142
142
-
style={[zero.r.full]}
143
143
-
/>
144
139
<View
145
140
style={[
146
146
-
zero.layout.flex.row,
147
147
-
zero.gap.all[2],
148
148
-
zero.layout.flex.alignCenter,
149
149
-
{ flex: 1, minWidth: 0, width: "100%" },
141
141
+
{
142
142
+
backgroundColor: theme.colors.muted,
143
143
+
borderRadius: 12,
144
144
+
overflow: "hidden",
145
145
+
borderWidth: 2,
146
146
+
borderColor: isSelected
147
147
+
? theme.colors.primary
148
148
+
: "transparent",
149
149
+
},
150
150
]}
151
151
>
152
152
<View
153
153
style={[
154
154
-
zero.layout.flex.column,
155
155
-
{ flex: 1, minWidth: 0 },
154
154
+
{
155
155
+
width: "100%",
156
156
+
aspectRatio: 16 / 9,
157
157
+
backgroundColor: theme.colors.card,
158
158
+
position: "relative",
159
159
+
},
160
160
+
]}
161
161
+
>
162
162
+
<Image
163
163
+
source={{
164
164
+
uri:
165
165
+
"/api/playback/" +
166
166
+
stream.author.did +
167
167
+
"/stream.jpg",
168
168
+
}}
169
169
+
style={{
170
170
+
width: "100%",
171
171
+
height: "100%",
172
172
+
}}
173
173
+
resizeMode="cover"
174
174
+
/>
175
175
+
{isSelected && (
176
176
+
<View
177
177
+
style={[
178
178
+
{
179
179
+
position: "absolute",
180
180
+
top: 8,
181
181
+
right: 8,
182
182
+
backgroundColor: theme.colors.primary,
183
183
+
borderRadius: 999,
184
184
+
width: 24,
185
185
+
height: 24,
186
186
+
alignItems: "center",
187
187
+
justifyContent: "center",
188
188
+
boxShadow: "0 2px 4px rgba(0, 0, 0, 0.6)",
189
189
+
},
190
190
+
]}
191
191
+
>
192
192
+
<Check size={16} color="white" />
193
193
+
</View>
194
194
+
)}
195
195
+
{stream.viewerCount && (
196
196
+
<View
197
197
+
style={[
198
198
+
{
199
199
+
position: "absolute",
200
200
+
top: 8,
201
201
+
left: 8,
202
202
+
backgroundColor: "rgba(0, 0, 0, 0.75)",
203
203
+
borderRadius: 999,
204
204
+
paddingHorizontal: 8,
205
205
+
paddingVertical: 4,
206
206
+
},
207
207
+
]}
208
208
+
>
209
209
+
<Text style={[{ fontSize: 12, color: "white" }]}>
210
210
+
{stream.viewerCount.count} viewer
211
211
+
{stream.viewerCount.count !== 1 ? "s" : ""}
212
212
+
</Text>
213
213
+
</View>
214
214
+
)}
215
215
+
</View>
216
216
+
<View
217
217
+
style={[
218
218
+
{
219
219
+
padding: 12,
220
220
+
flexDirection: "row",
221
221
+
gap: 8,
222
222
+
alignItems: "center",
223
223
+
},
156
224
]}
157
225
>
158
158
-
<Text numberOfLines={1} ellipsizeMode="tail">
159
159
-
{stream.author?.handle}
160
160
-
</Text>
161
161
-
{stream.record.title ? (
226
226
+
<View
227
227
+
style={[
228
228
+
{
229
229
+
width: 40,
230
230
+
height: 40,
231
231
+
borderRadius: 20,
232
232
+
overflow: "hidden",
233
233
+
backgroundColor: theme.colors.card,
234
234
+
flexShrink: 0,
235
235
+
},
236
236
+
]}
237
237
+
>
238
238
+
{profile?.avatar ? (
239
239
+
<Image
240
240
+
source={{
241
241
+
uri: profile.avatar,
242
242
+
}}
243
243
+
style={{ width: "100%", height: "100%" }}
244
244
+
resizeMode="cover"
245
245
+
/>
246
246
+
) : (
247
247
+
<View
248
248
+
style={{
249
249
+
width: "100%",
250
250
+
height: "100%",
251
251
+
backgroundColor: theme.colors.muted,
252
252
+
}}
253
253
+
/>
254
254
+
)}
255
255
+
</View>
256
256
+
257
257
+
{/* Text */}
258
258
+
<View style={[{ flex: 1, minWidth: 0 }]}>
162
259
<Text
163
163
-
color="muted"
260
260
+
numberOfLines={1}
164
261
ellipsizeMode="tail"
165
165
-
numberOfLines={1}
262
262
+
style={[{ fontWeight: "600" }]}
166
263
>
167
167
-
{(stream.record.title as any) || ""}
264
264
+
{stream.author?.handle}
168
265
</Text>
169
169
-
) : null}
170
170
-
{stream.viewerCount && (
171
171
-
<Text color="muted">
172
172
-
{stream.viewerCount.count} viewer
173
173
-
{stream.viewerCount.count !== 1 ? "s" : ""}
174
174
-
</Text>
175
175
-
)}
266
266
+
{stream.record.title ? (
267
267
+
<Text
268
268
+
numberOfLines={1}
269
269
+
ellipsizeMode="tail"
270
270
+
style={[
271
271
+
{
272
272
+
fontSize: 12,
273
273
+
color: theme.colors.textMuted,
274
274
+
},
275
275
+
]}
276
276
+
>
277
277
+
{stream.record.title as any}
278
278
+
</Text>
279
279
+
) : null}
280
280
+
</View>
176
281
</View>
177
282
</View>
178
178
-
</MenuItem>
179
179
-
</Pressable>
180
180
-
))}
181
181
-
</MenuGroup>
283
283
+
</Pressable>
284
284
+
);
285
285
+
})}
286
286
+
</View>
182
287
</ScrollView>
183
288
)}
184
289
</View>