tangled
alpha
login
or
join now
stream.place
/
streamplace
Live video on the AT Protocol
74
fork
atom
overview
issues
1
pulls
pipelines
redo live dashboard UI w color and style updates
Natalie B.
5 months ago
a638af39
6247d501
+235
-171
9 changed files
expand all
collapse all
unified
split
js
app
components
live-dashboard
bento-grid.tsx
chat-panel.tsx
header.tsx
livestream-panel
index.tsx
stream-monitor.tsx
ui
button-selector.tsx
components
src
components
chat
chat-message.tsx
chat.tsx
lib
theme
tokens.ts
+2
-8
js/app/components/live-dashboard/bento-grid.tsx
···
22
22
const isWeb = Platform.OS === "web";
23
23
24
24
return (
25
25
-
<View style={[flex.values[1], gap.all[4], p[4]]}>
26
26
-
<View
27
27
-
style={[
28
28
-
layout.flex.column,
29
29
-
bg.gray[900],
30
30
-
{ minWidth: isWeb ? 400 : "100%" },
31
31
-
]}
32
32
-
>
25
25
+
<View style={[flex.values[1], gap.all[4], p[4], bg.black]}>
26
26
+
<View style={[layout.flex.column, { minWidth: isWeb ? 400 : "100%" }]}>
33
27
<Header isLive={isLive} />
34
28
</View>
35
29
<View style={[flex.values[1], layout.flex.row, gap.all[4]]}>
+26
-22
js/app/components/live-dashboard/chat-panel.tsx
···
9
9
import { useState } from "react";
10
10
import { Text, View } from "react-native";
11
11
import { useLiveUser } from "../../hooks/useLiveUser";
12
12
-
13
13
-
const { flex, bg, r, borders, p, text, layout } = zero;
12
12
+
const { flex, bg, r, borders, p, px, py, text, layout } = zero;
14
13
15
14
export default function ChatPanel() {
16
15
// Get real data from hooks
···
38
37
<View
39
38
style={[
40
39
flex.values[1],
41
41
-
bg.gray[800],
42
42
-
r[3],
40
40
+
bg.neutral[900],
43
41
borders.width.thin,
44
44
-
borders.color.gray[700],
42
42
+
borders.color.neutral[700],
45
43
layout.flex.column,
44
44
+
r.lg,
45
45
+
{ minWidth: 300, maxWidth: 600, flexShrink: 0 },
46
46
]}
47
47
>
48
48
<View
···
50
50
layout.flex.row,
51
51
layout.flex.spaceBetween,
52
52
layout.flex.alignCenter,
53
53
-
p[4],
54
53
borders.bottom.width.thin,
55
55
-
borders.bottom.color.gray[700],
54
54
+
borders.bottom.color.neutral[700],
55
55
+
p[4],
56
56
]}
57
57
>
58
58
<Text style={[text.white, { fontSize: 18, fontWeight: "600" }]}>
59
59
Live Chat
60
60
</Text>
61
61
-
<View style={[layout.flex.row, layout.flex.alignCenter, { gap: 8 }]}>
61
61
+
<View style={[layout.flex.row, layout.flex.alignCenter]}>
62
62
<View
63
63
style={[
64
64
{ width: 6, height: 6, borderRadius: 3 },
65
65
isLive && isConnected ? bg.green[500] : bg.gray[500],
66
66
]}
67
67
/>
68
68
-
<Text style={[text.gray[400], { fontSize: 12 }]}>
68
68
+
<Text style={[text.gray[400], { fontSize: 12, marginLeft: 8 }]}>
69
69
{messagesPerMinute} msg/min
70
70
</Text>
71
71
</View>
72
72
</View>
73
73
-
<View style={[flex.values[1], p[2]]}>
74
74
-
<Chat canModerate={canModerate} shownMessages={50} />
75
75
-
<ChatBox
76
76
-
emojiData={emojiData}
77
77
-
chatBoxStyle={[
78
78
-
bg.gray[700],
79
79
-
borders.width.thin,
80
80
-
borders.color.gray[600],
81
81
-
r[2],
82
82
-
p[3],
83
83
-
!isConnected && { opacity: 0.6 },
84
84
-
]}
85
85
-
/>
73
73
+
<View style={[flex.values[1], px[2], { minHeight: 0 }]}>
74
74
+
<View style={[flex.values[1], { minHeight: 0 }]}>
75
75
+
<Chat canModerate={canModerate} shownMessages={50} />
76
76
+
</View>
77
77
+
<View style={[{ flexShrink: 0 }]}>
78
78
+
<ChatBox
79
79
+
emojiData={emojiData}
80
80
+
chatBoxStyle={[
81
81
+
bg.gray[700],
82
82
+
borders.width.thin,
83
83
+
borders.color.gray[600],
84
84
+
r.md,
85
85
+
p[3],
86
86
+
!isConnected && { opacity: 0.6 },
87
87
+
]}
88
88
+
/>
89
89
+
</View>
86
90
</View>
87
91
</View>
88
92
);
+5
-5
js/app/components/live-dashboard/header.tsx
···
10
10
import { useLiveUser } from "../../hooks/useLiveUser";
11
11
import { useSegmentTiming } from "../../hooks/useSegmentTiming";
12
12
13
13
-
const { flex, bg, r, borders, p, px, py, text, layout, gap } = zero;
13
13
+
const { bg, r, borders, px, py, text, layout, gap } = zero;
14
14
15
15
interface MetricItemProps {
16
16
icon: any;
···
151
151
return (
152
152
<View
153
153
style={[
154
154
-
bg.gray[800],
155
155
-
borders.bottom.width.thin,
156
156
-
borders.bottom.color.gray[700],
157
154
px[4],
158
155
py[3],
156
156
+
r.lg,
159
157
layout.flex.row,
160
160
-
layout.flex.alignCenter,
161
158
layout.flex.spaceBetween,
159
159
+
bg.neutral[900],
160
160
+
borders.width.thin,
161
161
+
borders.color.neutral[700],
162
162
]}
163
163
>
164
164
{/* Left side - Stream title and status */}
+58
-52
js/app/components/live-dashboard/livestream-panel/index.tsx
···
1
1
-
import { useLivestream, useToast, zero } from "@streamplace/components";
1
1
+
import {
2
2
+
Button,
3
3
+
Textarea,
4
4
+
useLivestream,
5
5
+
useToast,
6
6
+
zero,
7
7
+
} from "@streamplace/components";
2
8
import ThumbnailSelector from "components/thumbnail-selector";
3
9
import ButtonSelector from "components/ui/button-selector";
4
10
import {
···
12
18
import { useEffect, useState } from "react";
13
19
import {
14
20
Platform,
15
15
-
Pressable,
16
21
ScrollView,
17
22
Text,
18
18
-
TextInput,
19
23
useWindowDimensions,
20
24
View,
21
25
} from "react-native";
22
26
import { useAppDispatch, useAppSelector } from "store/hooks";
23
27
24
24
-
const { flex, p, px, py, mt, gap, layout, bg, borders, text, r, w } = zero;
28
28
+
const {
29
29
+
flex,
30
30
+
p,
31
31
+
px,
32
32
+
py,
33
33
+
h,
34
34
+
gap,
35
35
+
layout,
36
36
+
bg,
37
37
+
borders,
38
38
+
mt,
39
39
+
pt,
40
40
+
pb,
41
41
+
text,
42
42
+
r,
43
43
+
w,
44
44
+
typography,
45
45
+
} = zero;
25
46
const isWeb = Platform.OS === "web";
26
47
27
48
interface LivestreamPanelProps {
···
161
182
<View
162
183
style={[
163
184
flex.values[1],
164
164
-
bg.gray[800],
165
165
-
r[3],
185
185
+
bg.neutral[900],
186
186
+
r.lg,
166
187
borders.width.thin,
167
167
-
borders.color.gray[700],
188
188
+
borders.color.neutral[700],
168
189
layout.flex.column,
169
190
]}
170
191
>
···
172
193
style={[
173
194
layout.flex.row,
174
195
layout.flex.spaceBetween,
175
175
-
layout.flex.alignCenter,
176
176
-
p[4],
196
196
+
layout.flex.align.center,
197
197
+
px[4],
198
198
+
pt[4],
199
199
+
pb[4],
177
200
borders.bottom.width.thin,
178
178
-
borders.bottom.color.gray[700],
201
201
+
borders.bottom.color.neutral[700],
179
202
]}
180
203
>
181
204
<Text style={[text.white, { fontSize: 18, fontWeight: "600" }]}>
182
182
-
Live Chat
205
205
+
Stream settings
183
206
</Text>
184
207
<ButtonSelector
185
208
values={[
186
209
{ label: "Create", value: "create" },
187
210
{ label: "Edit", value: "edit" },
188
211
]}
212
212
+
style={[{ marginVertical: -2 }]}
189
213
selectedValue={mode}
190
214
setSelectedValue={setSelectedMode}
191
215
disabledValues={livestream ? [] : ["edit"]}
192
216
/>
193
217
</View>
194
218
{mode === "edit" && (
195
195
-
<Text
196
196
-
style={[px[4], text.white, { fontSize: 20, fontWeight: "bold" }]}
197
197
-
>
219
219
+
<Text style={[p[4], text.white, typography.universal["xl"]]}>
198
220
Change your Current Livestream Title
199
221
</Text>
200
222
)}
201
223
{mode === "edit" && noLivestream ? (
202
224
<View style={[layout.flex.center, p[4]]}>
203
203
-
<Text style={[text.gray[400], { fontSize: 16 }]}>
225
225
+
<Text style={[text.neutral[400], { fontSize: 16 }]}>
204
226
No active livestream to edit. Start a livestream first!
205
227
</Text>
206
228
</View>
···
208
230
<View
209
231
style={[
210
232
{ flexDirection: useTwoColumns ? "row" : "column" },
211
211
-
useTwoColumns ? gap.row[12] : gap.column[4],
233
233
+
useTwoColumns ? gap.row[12] : gap.all[8],
212
234
w.percent[100],
213
235
{ alignSelf: "center" },
214
236
p[4],
···
219
241
]}
220
242
>
221
243
{/* Left column: labels and fields */}
222
222
-
<View
223
223
-
style={[
224
224
-
flex.values[2],
225
225
-
{ minWidth: 0 },
226
226
-
gap.column[3],
227
227
-
w.percent[100],
228
228
-
]}
229
229
-
>
244
244
+
<View style={[{ minWidth: 0 }, gap.column[3], w.percent[100]]}>
230
245
<View
231
246
style={[
232
247
layout.flex.row,
···
236
251
>
237
252
<Text
238
253
style={[
239
239
-
text.gray[300],
254
254
+
text.neutral[300],
240
255
{ minWidth: 100, textAlign: "left", paddingBottom: 8 },
241
256
]}
242
257
>
···
260
275
>
261
276
<Text
262
277
style={[
263
263
-
text.gray[300],
278
278
+
text.neutral[300],
264
279
{ minWidth: 100, textAlign: "left", paddingBottom: 8 },
265
280
]}
266
281
>
267
282
Title
268
283
</Text>
269
284
<View style={[flex.values[1]]}>
270
270
-
<TextInput
285
285
+
<Textarea
271
286
value={title}
272
287
onChangeText={setTitle}
273
288
style={[
274
289
p[2],
275
290
r[1],
276
276
-
bg.gray[900],
291
291
+
bg.neutral[800],
277
292
text.white,
278
293
w.percent[100],
279
294
{ minHeight: 100, fontSize: 16 },
···
288
303
style={[
289
304
layout.flex.row,
290
305
layout.flex.alignCenter,
306
306
+
mt[2],
291
307
w.percent[100],
292
292
-
{ marginTop: -16 },
293
308
]}
294
309
>
295
310
<View style={[flex.values[1]]}>
296
296
-
<Text style={[text.gray[400], { fontSize: 12 }]}>
311
311
+
<Text style={[text.neutral[400], { fontSize: 12 }]}>
297
312
Updating will not send out notifications to viewers or
298
313
create a new social media post.
299
314
</Text>
···
310
325
gap.column[4],
311
326
{ alignItems: "center" },
312
327
{ justifyContent: "flex-start" },
313
313
-
{ marginTop: 12 },
328
328
+
h.percent[100],
314
329
]}
315
330
>
316
331
<Text
···
328
343
</View>
329
344
</View>
330
345
)}
331
331
-
<View
346
346
+
<Button
347
347
+
disabled={disabled}
332
348
style={[
349
349
+
bg.primary[500],
350
350
+
r[1],
351
351
+
py[2],
333
352
w.percent[100],
334
353
{ alignItems: "center" },
335
335
-
mode === "edit" ? { marginTop: -16 } : mt[4],
354
354
+
{ opacity: disabled ? 0.5 : 1 },
336
355
]}
356
356
+
onPress={handleSubmit}
337
357
>
338
338
-
<Pressable
339
339
-
disabled={disabled}
340
340
-
style={[
341
341
-
bg.primary[500],
342
342
-
r[1],
343
343
-
px[4],
344
344
-
py[2],
345
345
-
w.percent[100],
346
346
-
{ alignItems: "center" },
347
347
-
{ opacity: disabled ? 0.5 : 1 },
348
348
-
]}
349
349
-
onPress={handleSubmit}
358
358
+
<Text
359
359
+
style={[text.white, { fontSize: 16, fontWeight: "bold" }]}
350
360
>
351
351
-
<Text
352
352
-
style={[text.white, { fontSize: 16, fontWeight: "bold" }]}
353
353
-
>
354
354
-
{buttonText}
355
355
-
</Text>
356
356
-
</Pressable>
357
357
-
</View>
361
361
+
{buttonText}
362
362
+
</Text>
363
363
+
</Button>
358
364
</View>
359
365
)}
360
366
</View>
+57
-31
js/app/components/live-dashboard/stream-monitor.tsx
···
12
12
import { useSegmentTiming } from "../../hooks/useSegmentTiming";
13
13
import StreamScreen from "./live-selector";
14
14
15
15
-
const { flex, bg, r, borders, layout, p, text, w, h } = zero;
15
15
+
const { flex, bg, r, borders, layout, p, text, w, h, mt } = zero;
16
16
17
17
interface StreamMonitorProps {
18
18
userProfile?: any;
···
89
89
style={[
90
90
flex.values[2],
91
91
bg.gray[800],
92
92
-
r[3],
92
92
+
r.lg,
93
93
+
bg.neutral[900],
93
94
borders.width.thin,
94
94
-
borders.color.gray[700],
95
95
+
borders.color.neutral[700],
95
96
layout.flex.column,
96
97
]}
97
98
>
98
98
-
<View style={[flex.values[1], layout.flex.center, bg.gray[900]]}>
99
99
+
<View style={[flex.values[1], layout.flex.center, bg.neutral[900]]}>
99
100
{isLive && userProfile ? (
100
101
isStreamVisible ? (
101
102
<Player src={userProfile.did} name={userProfile.handle} />
···
141
142
layout.flex.spaceBetween,
142
143
layout.flex.alignCenter,
143
144
p[4],
144
144
-
borders.bottom.width.thin,
145
145
-
borders.bottom.color.gray[700],
145
145
+
borders.top.width.thin,
146
146
+
borders.top.color.gray[700],
146
147
]}
147
148
>
148
148
-
<View style={[layout.flex.row, layout.flex.alignCenter, { gap: 12 }]}>
149
149
+
<View
150
150
+
style={[
151
151
+
layout.flex.row,
152
152
+
layout.flex.spaceBetween,
153
153
+
layout.flex.alignCenter,
154
154
+
flex.grow[1],
155
155
+
{ gap: 12 },
156
156
+
]}
157
157
+
>
149
158
<Text style={[text.white, { fontSize: 18, fontWeight: "600" }]}>
150
159
{ls?.record.title || "Stream Title"}
151
160
</Text>
152
152
-
{isLive && userProfile && (
153
153
-
<TouchableOpacity
154
154
-
onPress={() => setIsStreamVisible(!isStreamVisible)}
155
155
-
style={{
156
156
-
padding: 4,
157
157
-
borderRadius: 4,
158
158
-
backgroundColor: "rgba(255, 255, 255, 0.1)",
159
159
-
}}
160
160
-
>
161
161
-
{isStreamVisible ? (
162
162
-
<EyeOff size={16} color="#9ca3af" />
163
163
-
) : (
164
164
-
<Eye size={16} color="#9ca3af" />
165
165
-
)}
166
166
-
</TouchableOpacity>
167
167
-
)}
168
168
-
<View style={[w[2], h[2], r[1], bg[getConnectionColor()][500]]} />
169
169
-
<Text style={[text.gray[400], { fontSize: 14 }]}>
170
170
-
{isLive ? "LIVE" : "OFFLINE"}
171
171
-
</Text>
172
172
-
{isLive && segmentTiming.timeBetweenSegments && (
173
173
-
<Text style={[text.gray[500], { fontSize: 12 }]}>
174
174
-
{Math.round(segmentTiming.timeBetweenSegments)}ms
161
161
+
<View
162
162
+
style={[
163
163
+
layout.flex.row,
164
164
+
layout.flex.justify.end,
165
165
+
layout.flex.align.start,
166
166
+
{ gap: 8, flexShrink: 0 },
167
167
+
]}
168
168
+
>
169
169
+
{isLive && userProfile && (
170
170
+
<TouchableOpacity
171
171
+
onPress={() => setIsStreamVisible(!isStreamVisible)}
172
172
+
style={{
173
173
+
padding: 4,
174
174
+
borderRadius: 4,
175
175
+
backgroundColor: "rgba(255, 255, 255, 0.1)",
176
176
+
}}
177
177
+
>
178
178
+
{isStreamVisible ? (
179
179
+
<EyeOff size={16} color="#9ca3af" />
180
180
+
) : (
181
181
+
<Eye size={16} color="#9ca3af" />
182
182
+
)}
183
183
+
</TouchableOpacity>
184
184
+
)}
185
185
+
<View
186
186
+
style={[
187
187
+
w[3],
188
188
+
h[3],
189
189
+
r.full,
190
190
+
{ marginTop: 3 },
191
191
+
bg[getConnectionColor()][500],
192
192
+
]}
193
193
+
/>
194
194
+
<Text style={[text.gray[400], { fontSize: 14 }]}>
195
195
+
{isLive ? "LIVE" : "OFFLINE"}
175
196
</Text>
176
176
-
)}
197
197
+
{isLive && segmentTiming.timeBetweenSegments && (
198
198
+
<Text style={[text.gray[500], { fontSize: 12 }]}>
199
199
+
{Math.round(segmentTiming.timeBetweenSegments)}ms
200
200
+
</Text>
201
201
+
)}
202
202
+
</View>
177
203
</View>
178
204
</View>
179
205
</View>
+43
-32
js/app/components/ui/button-selector.tsx
···
1
1
-
import { Button, Text, XStack, YStack, YStackProps } from "tamagui";
1
1
+
import {
2
2
+
Button,
3
3
+
Text,
4
4
+
useTheme,
5
5
+
View,
6
6
+
ViewProps,
7
7
+
zero,
8
8
+
} from "@streamplace/components";
9
9
+
10
10
+
const { gap, pt, w, bg, r, spacing, layout, colors } = zero;
2
11
3
12
export default function ButtonSelector({
4
13
text,
···
6
15
selectedValue,
7
16
setSelectedValue,
8
17
disabledValues,
18
18
+
style,
9
19
...props
10
20
}: {
11
21
text?: string;
···
13
23
selectedValue: string;
14
24
setSelectedValue: (value: any) => void;
15
25
disabledValues?: string[];
16
16
-
} & YStackProps) {
26
26
+
} & ViewProps) {
27
27
+
let theme = useTheme();
17
28
return (
18
18
-
<YStack ai="flex-start" gap="$2" pt="$2" {...props}>
29
29
+
<View align="start" style={[gap.all[2], style as any]} {...props}>
19
30
{text && (
20
20
-
<Text fontSize="$base" fontWeight="semibold">
31
31
+
<Text variant="body1" weight="semibold">
21
32
{text}
22
33
</Text>
23
34
)}
24
24
-
<XStack
25
25
-
ai="center"
26
26
-
jc="space-around"
27
27
-
gap="$1"
28
28
-
w="100%"
29
29
-
bg="$background"
30
30
-
borderRadius="$xl"
35
35
+
<View
36
36
+
direction="row"
37
37
+
align="center"
38
38
+
justify="around"
39
39
+
style={[gap.all[1], w.percent[100], r.full]}
31
40
>
32
32
-
{values.map(({ label, value }) => (
33
33
-
<Button
34
34
-
key={value}
35
35
-
onPress={() => setSelectedValue(value)}
36
36
-
f={1}
37
37
-
height="$2"
38
38
-
disabled={disabledValues?.includes(value)}
39
39
-
opacity={disabledValues?.includes(value) ? 0.5 : 1}
40
40
-
variant={selectedValue === value ? "outlined" : undefined}
41
41
-
>
42
42
-
<Text
43
43
-
color={
44
44
-
selectedValue === value
45
45
-
? "$color.foreground"
46
46
-
: "$color.mutedForeground"
47
47
-
}
41
41
+
{values.map(({ label, value }) => {
42
42
+
const isSelected = selectedValue === value;
43
43
+
const isDisabled = disabledValues?.includes(value);
44
44
+
45
45
+
return (
46
46
+
<Button
47
47
+
key={value}
48
48
+
onPress={() => setSelectedValue(value)}
49
49
+
variant={isSelected ? "outline" : "ghost"}
50
50
+
size="pill"
51
51
+
disabled={isDisabled}
52
52
+
style={[
53
53
+
{ flex: 1, maxHeight: 20 },
54
54
+
isSelected
55
55
+
? { backgroundColor: theme.theme.colors.primary }
56
56
+
: { backgroundColor: theme.theme.colors.secondary },
57
57
+
isDisabled && { opacity: 0.5 },
58
58
+
]}
48
59
>
49
60
{label}
50
50
-
</Text>
51
51
-
</Button>
52
52
-
))}
53
53
-
</XStack>
54
54
-
</YStack>
61
61
+
</Button>
62
62
+
);
63
63
+
})}
64
64
+
</View>
65
65
+
</View>
55
66
);
56
67
}
+22
-14
js/components/src/components/chat/chat-message.tsx
···
7
7
import { Linking, View } from "react-native";
8
8
import { ChatMessageViewHydrated } from "streamplace";
9
9
import { RichtextSegment, segmentize } from "../../lib/facet";
10
10
-
import {
11
11
-
borders,
12
12
-
flex,
13
13
-
gap,
14
14
-
ml,
15
15
-
mr,
16
16
-
opacity,
17
17
-
pl,
18
18
-
w,
19
19
-
} from "../../lib/theme/atoms";
10
10
+
import { borders, flex, gap, ml, mr, opacity, pl } from "../../lib/theme/atoms";
20
11
import { atoms, colors, layout } from "../ui";
21
12
22
13
interface Facet {
···
121
112
style={[
122
113
gap.all[2],
123
114
layout.flex.row,
124
124
-
w.percent[100],
115
115
+
{ minWidth: 0, maxWidth: "100%" },
125
116
borders.left.width.medium,
126
117
borders.left.color.gray[700],
127
118
ml[4],
···
129
120
opacity[80],
130
121
]}
131
122
>
132
132
-
<Text numberOfLines={1} style={[flex.shrink[1], mr[4]]}>
123
123
+
<Text
124
124
+
numberOfLines={1}
125
125
+
style={[
126
126
+
flex.shrink[1],
127
127
+
mr[4],
128
128
+
{ minWidth: 0, overflow: "hidden" },
129
129
+
]}
130
130
+
>
133
131
<Text
134
132
style={{
135
133
color: getRgbColor((item.replyTo.chatProfile as any).color),
···
149
147
</Text>
150
148
</View>
151
149
)}
152
152
-
<View style={[gap.all[2], layout.flex.row, w.percent[100]]}>
150
150
+
<View
151
151
+
style={[
152
152
+
gap.all[2],
153
153
+
layout.flex.row,
154
154
+
{ minWidth: 0, maxWidth: "100%" },
155
155
+
]}
156
156
+
>
153
157
{showTime && (
154
158
<Text
155
159
style={{
···
160
164
{formatTime(item.record.createdAt)}
161
165
</Text>
162
166
)}
163
163
-
<Text weight="bold" color="default" style={[flex.shrink[1]]}>
167
167
+
<Text
168
168
+
weight="bold"
169
169
+
color="default"
170
170
+
style={[flex.shrink[1], { minWidth: 0, overflow: "hidden" }]}
171
171
+
>
164
172
<Text
165
173
style={[
166
174
{
+21
-6
js/components/src/components/chat/chat.tsx
···
17
17
useSetReplyToMessage,
18
18
View,
19
19
} from "../../";
20
20
-
import { bg, flex, px, py, w } from "../../lib/theme/atoms";
20
20
+
import { bg, flex, px, py } from "../../lib/theme/atoms";
21
21
import { RenderChatMessage } from "./chat-message";
22
22
import { ModView } from "./mod-view";
23
23
···
87
87
padding: 1,
88
88
gap: 4,
89
89
zIndex: 10,
90
90
+
maxWidth: 120,
91
91
+
flexShrink: 0,
90
92
},
91
93
]}
92
94
>
···
184
186
style={[
185
187
py[1],
186
188
px[2],
187
187
-
{ position: "relative", borderRadius: 8 },
189
189
+
{
190
190
+
position: "relative",
191
191
+
borderRadius: 8,
192
192
+
minWidth: 0,
193
193
+
maxWidth: "100%",
194
194
+
},
188
195
isHovered && bg.gray[950],
189
196
]}
190
197
onPointerEnter={handleHoverIn}
191
198
onPointerLeave={handleHoverOut}
192
199
>
193
193
-
<Pressable>
200
200
+
<Pressable style={[{ minWidth: 0, maxWidth: "100%" }]}>
194
201
<RenderChatMessage item={item} />
195
202
</Pressable>
196
203
<ActionsBar
···
253
260
254
261
if (!chat)
255
262
return (
256
256
-
<View style={[flex.shrink[1]]}>
263
263
+
<View style={[flex.shrink[1], { minWidth: 0, maxWidth: "100%" }]}>
257
264
<Text>Loading chaat...</Text>
258
265
</View>
259
266
);
260
267
261
268
return (
262
262
-
<View style={[flex.shrink[1]].concat(propsStyle || [])}>
269
269
+
<View
270
270
+
style={[flex.shrink[1], { minWidth: 0, maxWidth: "100%" }].concat(
271
271
+
propsStyle || [],
272
272
+
)}
273
273
+
>
263
274
<FlatList
264
264
-
style={[flex.grow[1], flex.shrink[1], w.percent[100]]}
275
275
+
style={[
276
276
+
flex.grow[1],
277
277
+
flex.shrink[1],
278
278
+
{ minWidth: 0, maxWidth: "100%" },
279
279
+
]}
265
280
data={chat.slice(0, shownMessages)}
266
281
inverted={true}
267
282
keyExtractor={keyExtractor}
+1
-1
js/components/src/lib/theme/tokens.ts
···
427
427
428
428
export const borderRadius = {
429
429
none: 0,
430
430
-
sm: 4,
430
430
+
sm: 3,
431
431
md: 8,
432
432
lg: 12,
433
433
xl: 16,