agora-appbuilder-core 4.1.16 → 4.1.17

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 (34) hide show
  1. package/package.json +1 -1
  2. package/template/defaultConfig.js +3 -2
  3. package/template/global.d.ts +1 -0
  4. package/template/package.json +1 -0
  5. package/template/src/assets/live-reactions/1f389.gif +0 -0
  6. package/template/src/assets/live-reactions/1f44d.gif +0 -0
  7. package/template/src/assets/live-reactions/1f44f.gif +0 -0
  8. package/template/src/assets/live-reactions/1f496.gif +0 -0
  9. package/template/src/assets/live-reactions/1f602.gif +0 -0
  10. package/template/src/assets/live-reactions/1f622.gif +0 -0
  11. package/template/src/assets/live-reactions/1f62e.gif +0 -0
  12. package/template/src/assets/live-reactions/1f914.gif +0 -0
  13. package/template/src/assets/live-reactions/animated/1f389.json +1 -0
  14. package/template/src/assets/live-reactions/animated/1f44d.json +1 -0
  15. package/template/src/assets/live-reactions/animated/1f44f.json +1 -0
  16. package/template/src/assets/live-reactions/animated/1f496.json +1 -0
  17. package/template/src/assets/live-reactions/animated/1f602.json +1 -0
  18. package/template/src/assets/live-reactions/animated/1f622.json +1 -0
  19. package/template/src/assets/live-reactions/animated/1f62e.json +1 -0
  20. package/template/src/assets/live-reactions/animated/1f914.json +1 -0
  21. package/template/src/components/Controls.tsx +21 -5
  22. package/template/src/components/reactions/LiveReactionBadge.tsx +57 -0
  23. package/template/src/components/reactions/LiveReactionButton.tsx +257 -0
  24. package/template/src/components/reactions/LiveReactionStageOverlay.native.tsx +256 -0
  25. package/template/src/components/reactions/LiveReactionStageOverlay.tsx +326 -0
  26. package/template/src/components/reactions/catalog.ts +79 -0
  27. package/template/src/components/useVideoCall.tsx +219 -1
  28. package/template/src/language/default-labels/videoCallScreenLabels.ts +3 -0
  29. package/template/src/pages/video-call/ActionSheetContent.tsx +14 -1
  30. package/template/src/pages/video-call/VideoComponent.tsx +9 -1
  31. package/template/src/pages/video-call/VideoRenderer.tsx +8 -0
  32. package/template/src/rtm-events/constants.ts +2 -0
  33. package/template/webpack.commons.js +1 -1
  34. package/template/webpack.ts.config.js +1 -1
@@ -0,0 +1,256 @@
1
+ import React from 'react';
2
+ import {
3
+ Animated,
4
+ Easing,
5
+ LayoutChangeEvent,
6
+ StyleSheet,
7
+ Text,
8
+ View,
9
+ } from 'react-native';
10
+ import {useVideoCall} from '../useVideoCall';
11
+ import {LIVE_REACTION_LANE_COUNT} from './catalog';
12
+
13
+ const NATIVE_REACTION_START_BOTTOM = 18;
14
+ const NATIVE_REACTION_TOP_MARGIN = 120;
15
+ const NATIVE_REACTION_MIN_TRAVEL = 220;
16
+
17
+ const AnimatedReaction = ({
18
+ emoji,
19
+ sender,
20
+ left,
21
+ travel,
22
+ }: {
23
+ emoji: string;
24
+ sender: string;
25
+ left: number;
26
+ travel: number;
27
+ }) => {
28
+ const progress = React.useRef(new Animated.Value(0)).current;
29
+
30
+ React.useEffect(() => {
31
+ progress.setValue(0);
32
+ Animated.timing(progress, {
33
+ toValue: 1,
34
+ duration: 4200,
35
+ easing: Easing.out(Easing.cubic),
36
+ useNativeDriver: true,
37
+ }).start();
38
+ }, [progress, travel]);
39
+
40
+ const translateY = progress.interpolate({
41
+ inputRange: [0, 1],
42
+ outputRange: [0, -travel],
43
+ });
44
+
45
+ const opacity = progress.interpolate({
46
+ inputRange: [0, 0.12, 0.8, 1],
47
+ outputRange: [0, 1, 1, 0],
48
+ });
49
+
50
+ return (
51
+ <Animated.View
52
+ style={[
53
+ styles.reactionContainer,
54
+ {
55
+ left,
56
+ bottom: NATIVE_REACTION_START_BOTTOM,
57
+ opacity,
58
+ transform: [{translateY}],
59
+ },
60
+ ]}>
61
+ <Text style={styles.reactionEmoji}>{emoji}</Text>
62
+ <View style={styles.reactionSenderPill}>
63
+ <Text numberOfLines={1} style={styles.reactionSender}>
64
+ {sender}
65
+ </Text>
66
+ </View>
67
+ </Animated.View>
68
+ );
69
+ };
70
+
71
+ const LiveReactionDebugGuides = ({travel}: {travel: number}) => {
72
+ const mid = NATIVE_REACTION_START_BOTTOM + travel / 2;
73
+ const end = NATIVE_REACTION_START_BOTTOM + travel;
74
+
75
+ return (
76
+ <View pointerEvents="none" style={styles.debugOverlay}>
77
+ <View style={styles.debugLanes}>
78
+ {Array.from({length: LIVE_REACTION_LANE_COUNT}).map((_, index) => (
79
+ <View key={`debug-lane-${index}`} style={styles.debugLane} />
80
+ ))}
81
+ </View>
82
+ <View
83
+ style={[styles.debugStartLine, {bottom: NATIVE_REACTION_START_BOTTOM}]}
84
+ />
85
+ <View style={[styles.debugMidLine, {bottom: mid}]} />
86
+ <View style={[styles.debugEndLine, {bottom: end}]} />
87
+ <Text
88
+ style={[
89
+ styles.debugLabel,
90
+ {bottom: NATIVE_REACTION_START_BOTTOM},
91
+ styles.debugLabelStart,
92
+ ]}>
93
+ 0% opacity
94
+ </Text>
95
+ <Text style={[styles.debugLabel, {bottom: mid}, styles.debugLabelMid]}>
96
+ 100% opacity
97
+ </Text>
98
+ <Text style={[styles.debugLabel, {bottom: end}, styles.debugLabelEnd]}>
99
+ 0% opacity
100
+ </Text>
101
+ </View>
102
+ );
103
+ };
104
+
105
+ const LiveReactionStageOverlay = () => {
106
+ const {floatingReactions} = useVideoCall();
107
+ const [overlayHeight, setOverlayHeight] = React.useState(0);
108
+ const travel = Math.max(
109
+ NATIVE_REACTION_MIN_TRAVEL,
110
+ overlayHeight - NATIVE_REACTION_START_BOTTOM - NATIVE_REACTION_TOP_MARGIN,
111
+ );
112
+ // const isDebugVisible = __DEV__ && $config.ENABLE_LIVE_REACTIONS;
113
+ const isDebugVisible = false;
114
+
115
+ const handleLayout = React.useCallback((event: LayoutChangeEvent) => {
116
+ const nextHeight = Math.round(event.nativeEvent.layout.height);
117
+ if (nextHeight > 0) {
118
+ setOverlayHeight(nextHeight);
119
+ }
120
+ }, []);
121
+
122
+ if (!$config.ENABLE_LIVE_REACTIONS || floatingReactions.length === 0) {
123
+ return null;
124
+ }
125
+
126
+ return (
127
+ <View onLayout={handleLayout} pointerEvents="none" style={styles.overlay}>
128
+ {isDebugVisible ? <LiveReactionDebugGuides travel={travel} /> : null}
129
+ {floatingReactions.map((reaction, index) => {
130
+ const lane =
131
+ typeof reaction.lane === 'number'
132
+ ? reaction.lane
133
+ : index % LIVE_REACTION_LANE_COUNT;
134
+ const left = 16 + lane * 48;
135
+
136
+ return (
137
+ <AnimatedReaction
138
+ key={reaction.reactionId}
139
+ left={left}
140
+ emoji={reaction.emoji}
141
+ sender={reaction.senderDisplayName || reaction.senderUid}
142
+ travel={travel}
143
+ />
144
+ );
145
+ })}
146
+ </View>
147
+ );
148
+ };
149
+
150
+ const styles = StyleSheet.create({
151
+ overlay: {
152
+ position: 'absolute',
153
+ top: 0,
154
+ left: 0,
155
+ right: 0,
156
+ bottom: 0,
157
+ zIndex: 1000,
158
+ },
159
+ debugOverlay: {
160
+ position: 'absolute',
161
+ top: 0,
162
+ left: 0,
163
+ right: 0,
164
+ bottom: 0,
165
+ },
166
+ debugLanes: {
167
+ position: 'absolute',
168
+ top: 0,
169
+ left: 16,
170
+ width: 240,
171
+ height: '100%',
172
+ flexDirection: 'row',
173
+ },
174
+ debugLane: {
175
+ width: 48,
176
+ height: '100%',
177
+ borderLeftWidth: StyleSheet.hairlineWidth,
178
+ borderRightWidth: StyleSheet.hairlineWidth,
179
+ borderColor: 'rgba(104, 227, 255, 0.45)',
180
+ },
181
+ debugStartLine: {
182
+ position: 'absolute',
183
+ left: 16,
184
+ width: 240,
185
+ height: 1,
186
+ backgroundColor: 'rgba(255, 91, 91, 0.9)',
187
+ },
188
+ debugMidLine: {
189
+ position: 'absolute',
190
+ left: 16,
191
+ width: 240,
192
+ height: 1,
193
+ backgroundColor: 'rgba(255, 206, 86, 0.95)',
194
+ },
195
+ debugEndLine: {
196
+ position: 'absolute',
197
+ left: 16,
198
+ width: 240,
199
+ height: 1,
200
+ backgroundColor: 'rgba(104, 227, 255, 0.9)',
201
+ },
202
+ debugLabel: {
203
+ position: 'absolute',
204
+ left: 4,
205
+ paddingHorizontal: 6,
206
+ paddingVertical: 2,
207
+ borderRadius: 999,
208
+ fontSize: 10,
209
+ lineHeight: 12,
210
+ color: '#fff',
211
+ },
212
+ debugLabelStart: {
213
+ backgroundColor: 'rgba(255, 91, 91, 0.95)',
214
+ },
215
+ debugLabelMid: {
216
+ backgroundColor: 'rgba(255, 206, 86, 0.95)',
217
+ },
218
+ debugLabelEnd: {
219
+ backgroundColor: 'rgba(104, 227, 255, 0.95)',
220
+ },
221
+ reactionContainer: {
222
+ position: 'absolute',
223
+ width: 64,
224
+ height: 86,
225
+ alignItems: 'center',
226
+ justifyContent: 'flex-end',
227
+ paddingTop: 4,
228
+ paddingBottom: 4,
229
+ },
230
+ reactionEmoji: {
231
+ fontSize: 32,
232
+ lineHeight: 38,
233
+ },
234
+ reactionSenderPill: {
235
+ minWidth: 48,
236
+ width: 88,
237
+ marginTop: 4,
238
+ paddingHorizontal: 10,
239
+ paddingVertical: 4,
240
+ borderRadius: 20,
241
+ backgroundColor: $config.VIDEO_AUDIO_TILE_AVATAR_COLOR,
242
+ alignItems: 'center',
243
+ justifyContent: 'center',
244
+ },
245
+ reactionSender: {
246
+ maxWidth: 68,
247
+ fontSize: 12,
248
+ lineHeight: 12,
249
+ fontFamily: 'Source Sans 3',
250
+ fontWeight: '400',
251
+ color: $config.BACKGROUND_COLOR,
252
+ textAlign: 'center',
253
+ },
254
+ });
255
+
256
+ export default LiveReactionStageOverlay;
@@ -0,0 +1,326 @@
1
+ import React from 'react';
2
+ import {isWebInternal} from '../../utils/common';
3
+ import {useVideoCall} from '../useVideoCall';
4
+ import {
5
+ LIVE_REACTION_FLOAT_DURATION,
6
+ LIVE_REACTION_LANE_COUNT,
7
+ LIVE_REACTION_MAP,
8
+ } from './catalog';
9
+
10
+ // Use the bundled Lottie player directly instead of relying on a global.
11
+ // eslint-disable-next-line @typescript-eslint/no-var-requires
12
+ const lottie = require('lottie-web/build/player/lottie.js');
13
+
14
+ const hashReactionId = (reactionId: string) => {
15
+ let hash = 0;
16
+ for (let i = 0; i < reactionId.length; i += 1) {
17
+ hash = (hash * 31 + reactionId.charCodeAt(i)) | 0;
18
+ }
19
+ return Math.abs(hash);
20
+ };
21
+
22
+ const css = `
23
+ .live-reaction-stage-overlay {
24
+ position: absolute;
25
+ inset: 0;
26
+ pointer-events: none;
27
+ overflow: hidden;
28
+ z-index: 1000;
29
+ }
30
+ .live-reaction-debug-guides {
31
+ position: absolute;
32
+ inset: 0;
33
+ pointer-events: none;
34
+ z-index: 999;
35
+ }
36
+ .live-reaction-debug-lanes {
37
+ position: absolute;
38
+ top: 0;
39
+ left: 16px;
40
+ width: 240px;
41
+ height: 100%;
42
+ display: flex;
43
+ gap: 0;
44
+ }
45
+ .live-reaction-debug-lane {
46
+ width: 48px;
47
+ height: 100%;
48
+ box-sizing: border-box;
49
+ border-left: 1px dashed rgba(104, 227, 255, 0.75);
50
+ border-right: 1px dashed rgba(104, 227, 255, 0.35);
51
+ }
52
+ .live-reaction-debug-start-line,
53
+ .live-reaction-debug-end-line {
54
+ position: absolute;
55
+ left: 16px;
56
+ width: 240px;
57
+ height: 1px;
58
+ }
59
+ .live-reaction-debug-start-line {
60
+ bottom: 18px;
61
+ background: rgba(255, 91, 91, 0.9);
62
+ box-shadow: 0 0 0 1px rgba(255, 91, 91, 0.15);
63
+ }
64
+ .live-reaction-debug-mid-line {
65
+ position: absolute;
66
+ left: 16px;
67
+ width: 240px;
68
+ height: 1px;
69
+ bottom: 50%;
70
+ background: rgba(255, 206, 86, 0.95);
71
+ box-shadow: 0 0 0 1px rgba(255, 206, 86, 0.15);
72
+ }
73
+ .live-reaction-debug-end-line {
74
+ bottom: calc(100% - 96px);
75
+ background: rgba(104, 227, 255, 0.9);
76
+ box-shadow: 0 0 0 1px rgba(104, 227, 255, 0.15);
77
+ }
78
+ .live-reaction-debug-label {
79
+ position: absolute;
80
+ left: 4px;
81
+ transform: translateY(-50%);
82
+ padding: 2px 6px;
83
+ border-radius: 999px;
84
+ font-size: 10px;
85
+ line-height: 1;
86
+ color: #fff;
87
+ white-space: nowrap;
88
+ }
89
+ .live-reaction-debug-label--start {
90
+ bottom: 18px;
91
+ background: rgba(255, 91, 91, 0.95);
92
+ }
93
+ .live-reaction-debug-label--mid {
94
+ bottom: 50%;
95
+ background: rgba(255, 206, 86, 0.95);
96
+ }
97
+ .live-reaction-debug-label--end {
98
+ bottom: calc(100% - 96px);
99
+ background: rgba(104, 227, 255, 0.95);
100
+ }
101
+ .live-reaction-stage-item {
102
+ position: absolute;
103
+ width: 64px;
104
+ height: 80px;
105
+ left: var(--reaction-left);
106
+ bottom: 18px;
107
+ opacity: 0;
108
+ animation: live-reaction-float var(--reaction-duration) cubic-bezier(.18,.72,.22,1) forwards;
109
+ will-change: transform, opacity, bottom;
110
+ }
111
+ .live-reaction-stage-item-content {
112
+ width: 64px;
113
+ height: 80px;
114
+ display: flex;
115
+ flex-direction: column;
116
+ align-items: center;
117
+ justify-content: flex-start;
118
+ gap: 4px;
119
+ }
120
+ .live-reaction-stage-item img {
121
+ width: 48px;
122
+ height: 48px;
123
+ object-fit: contain;
124
+ display: block;
125
+ }
126
+ .live-reaction-clap {
127
+ width: 48px;
128
+ height: 48px;
129
+ }
130
+ .live-reaction-sender-name {
131
+ display: flex;
132
+ min-width: 48px;
133
+ max-width: 88px;
134
+ padding: 4px 10px;
135
+ box-sizing: border-box;
136
+ justify-content: center;
137
+ align-items: center;
138
+ gap: 10px;
139
+ border-radius: 20px;
140
+ background: ${$config.VIDEO_AUDIO_TILE_AVATAR_COLOR};
141
+ font-size: 12px;
142
+ line-height: 12px;
143
+ font-family: 'Source Sans 3', 'Source Sans Pro';
144
+ font-weight: 400;
145
+ color: ${$config.BACKGROUND_COLOR};
146
+ text-align: center;
147
+ }
148
+ .live-reaction-sender-name-text {
149
+ min-width: 0;
150
+ max-width: 68px;
151
+ white-space: nowrap;
152
+ overflow: hidden;
153
+ text-overflow: ellipsis;
154
+ }
155
+ @keyframes live-reaction-float {
156
+ 0% {
157
+ bottom: 18px;
158
+ opacity: 0;
159
+ transform: translate3d(0, 0, 0) scale(0.86);
160
+ }
161
+ 12% {
162
+ opacity: 1;
163
+ transform: translate3d(0, -4px, 0) scale(1);
164
+ }
165
+ 50% {
166
+ opacity: 1;
167
+ transform: translate3d(var(--reaction-drift), -8px, 0) scale(1.02);
168
+ }
169
+ 80% {
170
+ opacity: 0.55;
171
+ transform: translate3d(var(--reaction-drift), -10px, 0) scale(1.05);
172
+ }
173
+ 100% {
174
+ bottom: calc(100% - 96px);
175
+ opacity: 0;
176
+ transform: translate3d(var(--reaction-drift), 0, 0) scale(1.08);
177
+ }
178
+ }
179
+ `;
180
+
181
+ const ReactionArt = ({
182
+ fallbackSrc,
183
+ lottieData,
184
+ }: {
185
+ fallbackSrc: string;
186
+ lottieData?: any;
187
+ }) => {
188
+ const containerRef = React.useRef<HTMLDivElement | null>(null);
189
+ const animationRef = React.useRef<any>(null);
190
+ const [shouldFallback, setShouldFallback] = React.useState(false);
191
+
192
+ React.useEffect(() => {
193
+ const container = containerRef.current;
194
+ if (!container || shouldFallback || !lottieData) {
195
+ return;
196
+ }
197
+ if (!lottie?.loadAnimation) {
198
+ setShouldFallback(true);
199
+ return;
200
+ }
201
+
202
+ animationRef.current?.destroy();
203
+ animationRef.current = lottie.loadAnimation({
204
+ container,
205
+ renderer: 'svg',
206
+ loop: true,
207
+ autoplay: true,
208
+ animationData: lottieData,
209
+ rendererSettings: {
210
+ preserveAspectRatio: 'xMidYMid meet',
211
+ },
212
+ });
213
+
214
+ animationRef.current.addEventListener('data_failed', () => {
215
+ setShouldFallback(true);
216
+ });
217
+
218
+ return () => {
219
+ animationRef.current?.destroy();
220
+ animationRef.current = null;
221
+ };
222
+ }, [lottieData, shouldFallback]);
223
+
224
+ if (shouldFallback) {
225
+ return <img src={fallbackSrc} alt="" aria-hidden="true" />;
226
+ }
227
+
228
+ return (
229
+ <div ref={containerRef} className="live-reaction-clap" aria-hidden="true" />
230
+ );
231
+ };
232
+
233
+ const LiveReactionStageOverlay = () => {
234
+ const {floatingReactions} = useVideoCall();
235
+ // const isDev =
236
+ // typeof __DEV__ !== 'undefined'
237
+ // ? __DEV__
238
+ // : process.env.NODE_ENV !== 'production';
239
+ // const isDebugVisible = isDev && $config.ENABLE_LIVE_REACTIONS;
240
+ const isDebugVisible = false;
241
+
242
+ if (
243
+ !$config.ENABLE_LIVE_REACTIONS ||
244
+ !isWebInternal() ||
245
+ floatingReactions.length === 0
246
+ ) {
247
+ return null;
248
+ }
249
+
250
+ return (
251
+ <>
252
+ <style type="text/css">{css}</style>
253
+ <div className="live-reaction-stage-overlay">
254
+ {isDebugVisible ? (
255
+ <div className="live-reaction-debug-guides" aria-hidden="true">
256
+ <div className="live-reaction-debug-lanes">
257
+ {Array.from({length: LIVE_REACTION_LANE_COUNT}).map(
258
+ (_, index) => (
259
+ <div
260
+ key={`debug-lane-${index}`}
261
+ className="live-reaction-debug-lane"
262
+ />
263
+ ),
264
+ )}
265
+ </div>
266
+ <div className="live-reaction-debug-start-line" />
267
+ <div className="live-reaction-debug-mid-line" />
268
+ <div className="live-reaction-debug-end-line" />
269
+ <div className="live-reaction-debug-label live-reaction-debug-label--start">
270
+ 0% opacity
271
+ </div>
272
+ <div className="live-reaction-debug-label live-reaction-debug-label--mid">
273
+ 100% opacity
274
+ </div>
275
+ <div className="live-reaction-debug-label live-reaction-debug-label--end">
276
+ 0% opacity
277
+ </div>
278
+ </div>
279
+ ) : null}
280
+ {floatingReactions.map((reaction, index) => {
281
+ const reactionDefinition = LIVE_REACTION_MAP[reaction.assetKey];
282
+ if (!reactionDefinition) {
283
+ return null;
284
+ }
285
+ const lane =
286
+ typeof reaction.lane === 'number'
287
+ ? reaction.lane
288
+ : index % LIVE_REACTION_LANE_COUNT;
289
+ const left = `${16 + lane * 48}px`;
290
+ const drift = `${
291
+ ((hashReactionId(reaction.reactionId) % 3) - 1) * 10
292
+ }px`;
293
+ return (
294
+ <div
295
+ key={reaction.reactionId}
296
+ className="live-reaction-stage-item"
297
+ style={
298
+ {
299
+ '--reaction-left': left,
300
+ '--reaction-drift': drift,
301
+ '--reaction-duration': `${LIVE_REACTION_FLOAT_DURATION}ms`,
302
+ '--reaction-size': '48px',
303
+ } as React.CSSProperties
304
+ }>
305
+ <div className="live-reaction-stage-item-content">
306
+ <ReactionArt
307
+ fallbackSrc={reactionDefinition.asset}
308
+ lottieData={reactionDefinition.lottieData}
309
+ />
310
+ <div
311
+ className="live-reaction-sender-name"
312
+ title={reaction.senderDisplayName || reaction.senderUid}>
313
+ <span className="live-reaction-sender-name-text">
314
+ {reaction.senderDisplayName || reaction.senderUid}
315
+ </span>
316
+ </div>
317
+ </div>
318
+ </div>
319
+ );
320
+ })}
321
+ </div>
322
+ </>
323
+ );
324
+ };
325
+
326
+ export default LiveReactionStageOverlay;
@@ -0,0 +1,79 @@
1
+ export interface LiveReactionDefinition {
2
+ key: string;
3
+ emoji: string;
4
+ asset: any;
5
+ lottieData?: any;
6
+ }
7
+
8
+ export interface LiveReactionEvent {
9
+ reactionId: string;
10
+ assetKey: string;
11
+ emoji: string;
12
+ senderUid: string;
13
+ senderDisplayName?: string;
14
+ timestamp: number;
15
+ lane?: number;
16
+ }
17
+
18
+ export const LIVE_REACTION_LANE_COUNT = 5;
19
+ export const LIVE_REACTION_BADGE_DURATION = 10_000;
20
+ export const LIVE_REACTION_FLOAT_DURATION = 4_200;
21
+ export const LIVE_REACTION_MAX_FLOATING_ITEMS = 20;
22
+
23
+ export const LIVE_REACTIONS: LiveReactionDefinition[] = [
24
+ {
25
+ key: 'sparkling-heart',
26
+ emoji: '💖',
27
+ asset: require('../../assets/live-reactions/1f496.gif'),
28
+ lottieData: require('../../assets/live-reactions/animated/1f496.json'),
29
+ },
30
+ {
31
+ key: 'thumbs-up',
32
+ emoji: '👍',
33
+ asset: require('../../assets/live-reactions/1f44d.gif'),
34
+ lottieData: require('../../assets/live-reactions/animated/1f44d.json'),
35
+ },
36
+ {
37
+ key: 'party-popper',
38
+ emoji: '🎉',
39
+ asset: require('../../assets/live-reactions/1f389.gif'),
40
+ lottieData: require('../../assets/live-reactions/animated/1f389.json'),
41
+ },
42
+ {
43
+ key: 'clap',
44
+ emoji: '👏',
45
+ asset: require('../../assets/live-reactions/1f44f.gif'),
46
+ lottieData: require('../../assets/live-reactions/animated/1f44f.json'),
47
+ },
48
+ {
49
+ key: 'joy',
50
+ emoji: '😂',
51
+ asset: require('../../assets/live-reactions/1f602.gif'),
52
+ lottieData: require('../../assets/live-reactions/animated/1f602.json'),
53
+ },
54
+ {
55
+ key: 'wow',
56
+ emoji: '😮',
57
+ asset: require('../../assets/live-reactions/1f62e.gif'),
58
+ lottieData: require('../../assets/live-reactions/animated/1f62e.json'),
59
+ },
60
+ {
61
+ key: 'cry',
62
+ emoji: '😢',
63
+ asset: require('../../assets/live-reactions/1f622.gif'),
64
+ lottieData: require('../../assets/live-reactions/animated/1f622.json'),
65
+ },
66
+ {
67
+ key: 'thinking',
68
+ emoji: '🤔',
69
+ asset: require('../../assets/live-reactions/1f914.gif'),
70
+ lottieData: require('../../assets/live-reactions/animated/1f914.json'),
71
+ },
72
+ ];
73
+
74
+ export const LIVE_REACTION_MAP = LIVE_REACTIONS.reduce<
75
+ Record<string, LiveReactionDefinition>
76
+ >((acc, reaction) => {
77
+ acc[reaction.key] = reaction;
78
+ return acc;
79
+ }, {});