agora-appbuilder-core 4.1.20 → 4.1.21
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/package.json +1 -1
- package/template/bridge/rtm/web/__tests__/storage.test.ts +130 -0
- package/template/bridge/rtm/web/index.ts +44 -19
- package/template/customization-api/customize.tsx +6 -0
- package/template/customization-api/index.ts +11 -0
- package/template/customization-api/typeDefinition.ts +6 -0
- package/template/defaultConfig.js +2 -2
- package/template/src/assets/live-reactions/1f440.gif +0 -0
- package/template/src/assets/live-reactions/1f44d_1f3fb.gif +0 -0
- package/template/src/assets/live-reactions/1f44d_1f3fc.gif +0 -0
- package/template/src/assets/live-reactions/1f44d_1f3fd.gif +0 -0
- package/template/src/assets/live-reactions/1f44d_1f3fe.gif +0 -0
- package/template/src/assets/live-reactions/1f44d_1f3ff.gif +0 -0
- package/template/src/assets/live-reactions/1f44f_1f3fb.gif +0 -0
- package/template/src/assets/live-reactions/1f44f_1f3fc.gif +0 -0
- package/template/src/assets/live-reactions/1f44f_1f3fd.gif +0 -0
- package/template/src/assets/live-reactions/1f44f_1f3fe.gif +0 -0
- package/template/src/assets/live-reactions/1f44f_1f3ff.gif +0 -0
- package/template/src/assets/live-reactions/1f4a1.gif +0 -0
- package/template/src/assets/live-reactions/1f4af.gif +0 -0
- package/template/src/assets/live-reactions/1f525.gif +0 -0
- package/template/src/assets/live-reactions/1f60e.gif +0 -0
- package/template/src/assets/live-reactions/1f643.gif +0 -0
- package/template/src/assets/live-reactions/1f64c.gif +0 -0
- package/template/src/assets/live-reactions/1f64c_1f3fb.gif +0 -0
- package/template/src/assets/live-reactions/1f64c_1f3fc.gif +0 -0
- package/template/src/assets/live-reactions/1f64c_1f3fd.gif +0 -0
- package/template/src/assets/live-reactions/1f64c_1f3fe.gif +0 -0
- package/template/src/assets/live-reactions/1f64c_1f3ff.gif +0 -0
- package/template/src/assets/live-reactions/1f64f.gif +0 -0
- package/template/src/assets/live-reactions/1f64f_1f3fb.gif +0 -0
- package/template/src/assets/live-reactions/1f64f_1f3fc.gif +0 -0
- package/template/src/assets/live-reactions/1f64f_1f3fd.gif +0 -0
- package/template/src/assets/live-reactions/1f64f_1f3fe.gif +0 -0
- package/template/src/assets/live-reactions/1f64f_1f3ff.gif +0 -0
- package/template/src/assets/live-reactions/1f680.gif +0 -0
- package/template/src/assets/live-reactions/2705.gif +0 -0
- package/template/src/assets/live-reactions/animated/1f440.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44d_1f3fb.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44d_1f3fc.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44d_1f3fd.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44d_1f3fe.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44d_1f3ff.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44f_1f3fb.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44f_1f3fc.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44f_1f3fd.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44f_1f3fe.json +1 -0
- package/template/src/assets/live-reactions/animated/1f44f_1f3ff.json +1 -0
- package/template/src/assets/live-reactions/animated/1f4a1.json +1 -0
- package/template/src/assets/live-reactions/animated/1f4af.json +1 -0
- package/template/src/assets/live-reactions/animated/1f525.json +1 -0
- package/template/src/assets/live-reactions/animated/1f60e.json +1 -0
- package/template/src/assets/live-reactions/animated/1f643.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64c.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64c_1f3fb.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64c_1f3fc.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64c_1f3fd.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64c_1f3fe.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64c_1f3ff.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64f.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64f_1f3fb.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64f_1f3fc.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64f_1f3fd.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64f_1f3fe.json +1 -0
- package/template/src/assets/live-reactions/animated/1f64f_1f3ff.json +1 -0
- package/template/src/assets/live-reactions/animated/1f680.json +1 -0
- package/template/src/assets/live-reactions/animated/2705.json +1 -0
- package/template/src/assets/live-reactions/custom/ty.png +0 -0
- package/template/src/components/RTMConfigure.tsx +15 -10
- package/template/src/components/StorageContext.tsx +3 -0
- package/template/src/components/reactions/LiveReactionBadge.tsx +20 -2
- package/template/src/components/reactions/LiveReactionButton.tsx +200 -44
- package/template/src/components/reactions/LiveReactionStageOverlay.native.tsx +34 -10
- package/template/src/components/reactions/LiveReactionStageOverlay.tsx +30 -7
- package/template/src/components/reactions/catalog.ts +241 -0
- package/template/src/components/useVideoCall.tsx +20 -2
- package/template/src/rtm/__tests__/isRemoteDeparture.test.ts +19 -0
- package/template/src/rtm/isRemoteDeparture.ts +4 -0
- package/template/src/subComponents/caption/__tests__/sttRequestBody.test.ts +61 -0
- package/template/src/subComponents/caption/__tests__/sttSessionId.native.test.ts +76 -0
- package/template/src/subComponents/caption/__tests__/sttSessionId.web.test.ts +190 -0
- package/template/src/subComponents/caption/__tests__/useSTTAPI.test.tsx +129 -0
- package/template/src/subComponents/caption/sttRequestBody.ts +54 -0
- package/template/src/subComponents/caption/sttSessionCoordinator.ts +315 -0
- package/template/src/subComponents/caption/sttSessionId.ts +21 -0
- package/template/src/subComponents/caption/sttSessionId.web.ts +21 -0
- package/template/src/subComponents/caption/useSTTAPI.tsx +13 -33
- package/template/src/utils/__tests__/useEndCall.test.tsx +282 -0
- package/template/src/utils/useEndCall.ts +63 -22
|
@@ -4,7 +4,8 @@ import {useVideoCall} from '../useVideoCall';
|
|
|
4
4
|
import {
|
|
5
5
|
LIVE_REACTION_FLOAT_DURATION,
|
|
6
6
|
LIVE_REACTION_LANE_COUNT,
|
|
7
|
-
|
|
7
|
+
getLiveReactionMap,
|
|
8
|
+
resolveReactionVisual,
|
|
8
9
|
} from './catalog';
|
|
9
10
|
|
|
10
11
|
// Use the bundled Lottie player directly instead of relying on a global.
|
|
@@ -123,6 +124,11 @@ const css = `
|
|
|
123
124
|
object-fit: contain;
|
|
124
125
|
display: block;
|
|
125
126
|
}
|
|
127
|
+
.live-reaction-stage-item img.live-reaction-custom {
|
|
128
|
+
width: 36px;
|
|
129
|
+
height: 36px;
|
|
130
|
+
border-radius: 6px;
|
|
131
|
+
}
|
|
126
132
|
.live-reaction-clap {
|
|
127
133
|
width: 48px;
|
|
128
134
|
height: 48px;
|
|
@@ -181,13 +187,15 @@ const css = `
|
|
|
181
187
|
const ReactionArt = ({
|
|
182
188
|
fallbackSrc,
|
|
183
189
|
lottieData,
|
|
190
|
+
isCustom,
|
|
184
191
|
}: {
|
|
185
192
|
fallbackSrc: string;
|
|
186
193
|
lottieData?: any;
|
|
194
|
+
isCustom?: boolean;
|
|
187
195
|
}) => {
|
|
188
196
|
const containerRef = React.useRef<HTMLDivElement | null>(null);
|
|
189
197
|
const animationRef = React.useRef<any>(null);
|
|
190
|
-
const [shouldFallback, setShouldFallback] = React.useState(
|
|
198
|
+
const [shouldFallback, setShouldFallback] = React.useState(!lottieData);
|
|
191
199
|
|
|
192
200
|
React.useEffect(() => {
|
|
193
201
|
const container = containerRef.current;
|
|
@@ -222,7 +230,14 @@ const ReactionArt = ({
|
|
|
222
230
|
}, [lottieData, shouldFallback]);
|
|
223
231
|
|
|
224
232
|
if (shouldFallback) {
|
|
225
|
-
return
|
|
233
|
+
return (
|
|
234
|
+
<img
|
|
235
|
+
src={fallbackSrc}
|
|
236
|
+
alt=""
|
|
237
|
+
aria-hidden="true"
|
|
238
|
+
className={isCustom ? 'live-reaction-custom' : undefined}
|
|
239
|
+
/>
|
|
240
|
+
);
|
|
226
241
|
}
|
|
227
242
|
|
|
228
243
|
return (
|
|
@@ -278,8 +293,15 @@ const LiveReactionStageOverlay = () => {
|
|
|
278
293
|
</div>
|
|
279
294
|
) : null}
|
|
280
295
|
{floatingReactions.map((reaction, index) => {
|
|
281
|
-
const
|
|
282
|
-
if (!
|
|
296
|
+
const catalogEntry = getLiveReactionMap()[reaction.assetKey];
|
|
297
|
+
if (!catalogEntry) {
|
|
298
|
+
return null;
|
|
299
|
+
}
|
|
300
|
+
const visual = resolveReactionVisual(
|
|
301
|
+
reaction.assetKey,
|
|
302
|
+
reaction.skinTone,
|
|
303
|
+
);
|
|
304
|
+
if (!visual) {
|
|
283
305
|
return null;
|
|
284
306
|
}
|
|
285
307
|
const lane =
|
|
@@ -304,8 +326,9 @@ const LiveReactionStageOverlay = () => {
|
|
|
304
326
|
}>
|
|
305
327
|
<div className="live-reaction-stage-item-content">
|
|
306
328
|
<ReactionArt
|
|
307
|
-
fallbackSrc={
|
|
308
|
-
lottieData={
|
|
329
|
+
fallbackSrc={visual.asset}
|
|
330
|
+
lottieData={visual.lottieData}
|
|
331
|
+
isCustom={!!catalogEntry.custom}
|
|
309
332
|
/>
|
|
310
333
|
<div
|
|
311
334
|
className="live-reaction-sender-name"
|
|
@@ -1,8 +1,26 @@
|
|
|
1
|
+
// @ts-ignore
|
|
2
|
+
import tyCustom from '../../assets/live-reactions/custom/ty.png';
|
|
3
|
+
|
|
4
|
+
export type SkinToneCode = '1f3fb' | '1f3fc' | '1f3fd' | '1f3fe' | '1f3ff';
|
|
5
|
+
export type SkinTonePreference = 'default' | SkinToneCode;
|
|
6
|
+
|
|
7
|
+
export interface LiveReactionSkinToneAsset {
|
|
8
|
+
asset: any;
|
|
9
|
+
lottieData?: any;
|
|
10
|
+
}
|
|
11
|
+
|
|
12
|
+
export interface LiveReactionCustomMeta {
|
|
13
|
+
label: string;
|
|
14
|
+
fallbackEmoji: string;
|
|
15
|
+
}
|
|
16
|
+
|
|
1
17
|
export interface LiveReactionDefinition {
|
|
2
18
|
key: string;
|
|
3
19
|
emoji: string;
|
|
4
20
|
asset: any;
|
|
5
21
|
lottieData?: any;
|
|
22
|
+
skinToneVariants?: Record<SkinToneCode, LiveReactionSkinToneAsset>;
|
|
23
|
+
custom?: LiveReactionCustomMeta;
|
|
6
24
|
}
|
|
7
25
|
|
|
8
26
|
export interface LiveReactionEvent {
|
|
@@ -13,6 +31,7 @@ export interface LiveReactionEvent {
|
|
|
13
31
|
senderDisplayName?: string;
|
|
14
32
|
timestamp: number;
|
|
15
33
|
lane?: number;
|
|
34
|
+
skinTone?: SkinToneCode;
|
|
16
35
|
}
|
|
17
36
|
|
|
18
37
|
export const LIVE_REACTION_LANE_COUNT = 5;
|
|
@@ -20,6 +39,22 @@ export const LIVE_REACTION_BADGE_DURATION = 10_000;
|
|
|
20
39
|
export const LIVE_REACTION_FLOAT_DURATION = 4_200;
|
|
21
40
|
export const LIVE_REACTION_MAX_FLOATING_ITEMS = 20;
|
|
22
41
|
|
|
42
|
+
export const SKIN_TONE_CODES: SkinToneCode[] = [
|
|
43
|
+
'1f3fb',
|
|
44
|
+
'1f3fc',
|
|
45
|
+
'1f3fd',
|
|
46
|
+
'1f3fe',
|
|
47
|
+
'1f3ff',
|
|
48
|
+
];
|
|
49
|
+
|
|
50
|
+
export const SKIN_TONE_MODIFIER: Record<SkinToneCode, string> = {
|
|
51
|
+
'1f3fb': '\u{1F3FB}',
|
|
52
|
+
'1f3fc': '\u{1F3FC}',
|
|
53
|
+
'1f3fd': '\u{1F3FD}',
|
|
54
|
+
'1f3fe': '\u{1F3FE}',
|
|
55
|
+
'1f3ff': '\u{1F3FF}',
|
|
56
|
+
};
|
|
57
|
+
|
|
23
58
|
export const LIVE_REACTIONS: LiveReactionDefinition[] = [
|
|
24
59
|
{
|
|
25
60
|
key: 'sparkling-heart',
|
|
@@ -32,6 +67,28 @@ export const LIVE_REACTIONS: LiveReactionDefinition[] = [
|
|
|
32
67
|
emoji: '👍',
|
|
33
68
|
asset: require('../../assets/live-reactions/1f44d.gif'),
|
|
34
69
|
lottieData: require('../../assets/live-reactions/animated/1f44d.json'),
|
|
70
|
+
skinToneVariants: {
|
|
71
|
+
'1f3fb': {
|
|
72
|
+
asset: require('../../assets/live-reactions/1f44d_1f3fb.gif'),
|
|
73
|
+
lottieData: require('../../assets/live-reactions/animated/1f44d_1f3fb.json'),
|
|
74
|
+
},
|
|
75
|
+
'1f3fc': {
|
|
76
|
+
asset: require('../../assets/live-reactions/1f44d_1f3fc.gif'),
|
|
77
|
+
lottieData: require('../../assets/live-reactions/animated/1f44d_1f3fc.json'),
|
|
78
|
+
},
|
|
79
|
+
'1f3fd': {
|
|
80
|
+
asset: require('../../assets/live-reactions/1f44d_1f3fd.gif'),
|
|
81
|
+
lottieData: require('../../assets/live-reactions/animated/1f44d_1f3fd.json'),
|
|
82
|
+
},
|
|
83
|
+
'1f3fe': {
|
|
84
|
+
asset: require('../../assets/live-reactions/1f44d_1f3fe.gif'),
|
|
85
|
+
lottieData: require('../../assets/live-reactions/animated/1f44d_1f3fe.json'),
|
|
86
|
+
},
|
|
87
|
+
'1f3ff': {
|
|
88
|
+
asset: require('../../assets/live-reactions/1f44d_1f3ff.gif'),
|
|
89
|
+
lottieData: require('../../assets/live-reactions/animated/1f44d_1f3ff.json'),
|
|
90
|
+
},
|
|
91
|
+
},
|
|
35
92
|
},
|
|
36
93
|
{
|
|
37
94
|
key: 'party-popper',
|
|
@@ -44,6 +101,28 @@ export const LIVE_REACTIONS: LiveReactionDefinition[] = [
|
|
|
44
101
|
emoji: '👏',
|
|
45
102
|
asset: require('../../assets/live-reactions/1f44f.gif'),
|
|
46
103
|
lottieData: require('../../assets/live-reactions/animated/1f44f.json'),
|
|
104
|
+
skinToneVariants: {
|
|
105
|
+
'1f3fb': {
|
|
106
|
+
asset: require('../../assets/live-reactions/1f44f_1f3fb.gif'),
|
|
107
|
+
lottieData: require('../../assets/live-reactions/animated/1f44f_1f3fb.json'),
|
|
108
|
+
},
|
|
109
|
+
'1f3fc': {
|
|
110
|
+
asset: require('../../assets/live-reactions/1f44f_1f3fc.gif'),
|
|
111
|
+
lottieData: require('../../assets/live-reactions/animated/1f44f_1f3fc.json'),
|
|
112
|
+
},
|
|
113
|
+
'1f3fd': {
|
|
114
|
+
asset: require('../../assets/live-reactions/1f44f_1f3fd.gif'),
|
|
115
|
+
lottieData: require('../../assets/live-reactions/animated/1f44f_1f3fd.json'),
|
|
116
|
+
},
|
|
117
|
+
'1f3fe': {
|
|
118
|
+
asset: require('../../assets/live-reactions/1f44f_1f3fe.gif'),
|
|
119
|
+
lottieData: require('../../assets/live-reactions/animated/1f44f_1f3fe.json'),
|
|
120
|
+
},
|
|
121
|
+
'1f3ff': {
|
|
122
|
+
asset: require('../../assets/live-reactions/1f44f_1f3ff.gif'),
|
|
123
|
+
lottieData: require('../../assets/live-reactions/animated/1f44f_1f3ff.json'),
|
|
124
|
+
},
|
|
125
|
+
},
|
|
47
126
|
},
|
|
48
127
|
{
|
|
49
128
|
key: 'joy',
|
|
@@ -69,11 +148,173 @@ export const LIVE_REACTIONS: LiveReactionDefinition[] = [
|
|
|
69
148
|
asset: require('../../assets/live-reactions/1f914.gif'),
|
|
70
149
|
lottieData: require('../../assets/live-reactions/animated/1f914.json'),
|
|
71
150
|
},
|
|
151
|
+
{
|
|
152
|
+
key: 'eyes',
|
|
153
|
+
emoji: '👀',
|
|
154
|
+
asset: require('../../assets/live-reactions/1f440.gif'),
|
|
155
|
+
lottieData: require('../../assets/live-reactions/animated/1f440.json'),
|
|
156
|
+
},
|
|
157
|
+
{
|
|
158
|
+
key: 'rocket',
|
|
159
|
+
emoji: '🚀',
|
|
160
|
+
asset: require('../../assets/live-reactions/1f680.gif'),
|
|
161
|
+
lottieData: require('../../assets/live-reactions/animated/1f680.json'),
|
|
162
|
+
},
|
|
163
|
+
{
|
|
164
|
+
key: 'upside-down-face',
|
|
165
|
+
emoji: '🙃',
|
|
166
|
+
asset: require('../../assets/live-reactions/1f643.gif'),
|
|
167
|
+
lottieData: require('../../assets/live-reactions/animated/1f643.json'),
|
|
168
|
+
},
|
|
169
|
+
{
|
|
170
|
+
key: 'fire',
|
|
171
|
+
emoji: '🔥',
|
|
172
|
+
asset: require('../../assets/live-reactions/1f525.gif'),
|
|
173
|
+
lottieData: require('../../assets/live-reactions/animated/1f525.json'),
|
|
174
|
+
},
|
|
175
|
+
{
|
|
176
|
+
key: 'hundred',
|
|
177
|
+
emoji: '💯',
|
|
178
|
+
asset: require('../../assets/live-reactions/1f4af.gif'),
|
|
179
|
+
lottieData: require('../../assets/live-reactions/animated/1f4af.json'),
|
|
180
|
+
},
|
|
181
|
+
{
|
|
182
|
+
key: 'raising-hands',
|
|
183
|
+
emoji: '🙌',
|
|
184
|
+
asset: require('../../assets/live-reactions/1f64c.gif'),
|
|
185
|
+
lottieData: require('../../assets/live-reactions/animated/1f64c.json'),
|
|
186
|
+
skinToneVariants: {
|
|
187
|
+
'1f3fb': {
|
|
188
|
+
asset: require('../../assets/live-reactions/1f64c_1f3fb.gif'),
|
|
189
|
+
lottieData: require('../../assets/live-reactions/animated/1f64c_1f3fb.json'),
|
|
190
|
+
},
|
|
191
|
+
'1f3fc': {
|
|
192
|
+
asset: require('../../assets/live-reactions/1f64c_1f3fc.gif'),
|
|
193
|
+
lottieData: require('../../assets/live-reactions/animated/1f64c_1f3fc.json'),
|
|
194
|
+
},
|
|
195
|
+
'1f3fd': {
|
|
196
|
+
asset: require('../../assets/live-reactions/1f64c_1f3fd.gif'),
|
|
197
|
+
lottieData: require('../../assets/live-reactions/animated/1f64c_1f3fd.json'),
|
|
198
|
+
},
|
|
199
|
+
'1f3fe': {
|
|
200
|
+
asset: require('../../assets/live-reactions/1f64c_1f3fe.gif'),
|
|
201
|
+
lottieData: require('../../assets/live-reactions/animated/1f64c_1f3fe.json'),
|
|
202
|
+
},
|
|
203
|
+
'1f3ff': {
|
|
204
|
+
asset: require('../../assets/live-reactions/1f64c_1f3ff.gif'),
|
|
205
|
+
lottieData: require('../../assets/live-reactions/animated/1f64c_1f3ff.json'),
|
|
206
|
+
},
|
|
207
|
+
},
|
|
208
|
+
},
|
|
209
|
+
{
|
|
210
|
+
key: 'light-bulb',
|
|
211
|
+
emoji: '💡',
|
|
212
|
+
asset: require('../../assets/live-reactions/1f4a1.gif'),
|
|
213
|
+
lottieData: require('../../assets/live-reactions/animated/1f4a1.json'),
|
|
214
|
+
},
|
|
215
|
+
{
|
|
216
|
+
key: 'folded-hands',
|
|
217
|
+
emoji: '🙏',
|
|
218
|
+
asset: require('../../assets/live-reactions/1f64f.gif'),
|
|
219
|
+
lottieData: require('../../assets/live-reactions/animated/1f64f.json'),
|
|
220
|
+
skinToneVariants: {
|
|
221
|
+
'1f3fb': {
|
|
222
|
+
asset: require('../../assets/live-reactions/1f64f_1f3fb.gif'),
|
|
223
|
+
lottieData: require('../../assets/live-reactions/animated/1f64f_1f3fb.json'),
|
|
224
|
+
},
|
|
225
|
+
'1f3fc': {
|
|
226
|
+
asset: require('../../assets/live-reactions/1f64f_1f3fc.gif'),
|
|
227
|
+
lottieData: require('../../assets/live-reactions/animated/1f64f_1f3fc.json'),
|
|
228
|
+
},
|
|
229
|
+
'1f3fd': {
|
|
230
|
+
asset: require('../../assets/live-reactions/1f64f_1f3fd.gif'),
|
|
231
|
+
lottieData: require('../../assets/live-reactions/animated/1f64f_1f3fd.json'),
|
|
232
|
+
},
|
|
233
|
+
'1f3fe': {
|
|
234
|
+
asset: require('../../assets/live-reactions/1f64f_1f3fe.gif'),
|
|
235
|
+
lottieData: require('../../assets/live-reactions/animated/1f64f_1f3fe.json'),
|
|
236
|
+
},
|
|
237
|
+
'1f3ff': {
|
|
238
|
+
asset: require('../../assets/live-reactions/1f64f_1f3ff.gif'),
|
|
239
|
+
lottieData: require('../../assets/live-reactions/animated/1f64f_1f3ff.json'),
|
|
240
|
+
},
|
|
241
|
+
},
|
|
242
|
+
},
|
|
243
|
+
{
|
|
244
|
+
key: 'check-mark',
|
|
245
|
+
emoji: '✅',
|
|
246
|
+
asset: require('../../assets/live-reactions/2705.gif'),
|
|
247
|
+
lottieData: require('../../assets/live-reactions/animated/2705.json'),
|
|
248
|
+
},
|
|
249
|
+
{
|
|
250
|
+
key: 'sunglasses',
|
|
251
|
+
emoji: '😎',
|
|
252
|
+
asset: require('../../assets/live-reactions/1f60e.gif'),
|
|
253
|
+
lottieData: require('../../assets/live-reactions/animated/1f60e.json'),
|
|
254
|
+
},
|
|
255
|
+
{
|
|
256
|
+
key: 'thank-you',
|
|
257
|
+
emoji: '🙏',
|
|
258
|
+
asset: tyCustom,
|
|
259
|
+
custom: {label: 'Thank you', fallbackEmoji: '🙏'},
|
|
260
|
+
},
|
|
72
261
|
];
|
|
73
262
|
|
|
263
|
+
let _customLiveReactions: LiveReactionDefinition[] = [];
|
|
264
|
+
|
|
265
|
+
export function registerCustomLiveReactions(
|
|
266
|
+
reactions: LiveReactionDefinition[],
|
|
267
|
+
): void {
|
|
268
|
+
_customLiveReactions = Array.isArray(reactions) ? [...reactions] : [];
|
|
269
|
+
}
|
|
270
|
+
|
|
271
|
+
export function getLiveReactions(): LiveReactionDefinition[] {
|
|
272
|
+
return [...LIVE_REACTIONS, ..._customLiveReactions];
|
|
273
|
+
}
|
|
274
|
+
|
|
275
|
+
export function getLiveReactionMap(): Record<string, LiveReactionDefinition> {
|
|
276
|
+
return getLiveReactions().reduce<Record<string, LiveReactionDefinition>>(
|
|
277
|
+
(acc, reaction) => {
|
|
278
|
+
acc[reaction.key] = reaction;
|
|
279
|
+
return acc;
|
|
280
|
+
},
|
|
281
|
+
{},
|
|
282
|
+
);
|
|
283
|
+
}
|
|
284
|
+
|
|
74
285
|
export const LIVE_REACTION_MAP = LIVE_REACTIONS.reduce<
|
|
75
286
|
Record<string, LiveReactionDefinition>
|
|
76
287
|
>((acc, reaction) => {
|
|
77
288
|
acc[reaction.key] = reaction;
|
|
78
289
|
return acc;
|
|
79
290
|
}, {});
|
|
291
|
+
|
|
292
|
+
export function supportsSkinTone(
|
|
293
|
+
reaction: LiveReactionDefinition | undefined,
|
|
294
|
+
): boolean {
|
|
295
|
+
return !!reaction?.skinToneVariants;
|
|
296
|
+
}
|
|
297
|
+
|
|
298
|
+
export function resolveReactionVisual(
|
|
299
|
+
assetKey: string,
|
|
300
|
+
skinTone?: SkinToneCode,
|
|
301
|
+
): LiveReactionSkinToneAsset | undefined {
|
|
302
|
+
const reaction = getLiveReactionMap()[assetKey];
|
|
303
|
+
if (!reaction) {
|
|
304
|
+
return undefined;
|
|
305
|
+
}
|
|
306
|
+
if (skinTone && reaction.skinToneVariants?.[skinTone]) {
|
|
307
|
+
return reaction.skinToneVariants[skinTone];
|
|
308
|
+
}
|
|
309
|
+
return {asset: reaction.asset, lottieData: reaction.lottieData};
|
|
310
|
+
}
|
|
311
|
+
|
|
312
|
+
export function applySkinToneToEmoji(
|
|
313
|
+
reaction: LiveReactionDefinition,
|
|
314
|
+
tone: SkinTonePreference,
|
|
315
|
+
): string {
|
|
316
|
+
if (tone === 'default' || !reaction.skinToneVariants?.[tone]) {
|
|
317
|
+
return reaction.emoji;
|
|
318
|
+
}
|
|
319
|
+
return `${reaction.emoji}${SKIN_TONE_MODIFIER[tone]}`;
|
|
320
|
+
}
|
|
@@ -45,7 +45,10 @@ import {
|
|
|
45
45
|
LIVE_REACTION_MAX_FLOATING_ITEMS,
|
|
46
46
|
LiveReactionDefinition,
|
|
47
47
|
LiveReactionEvent,
|
|
48
|
+
SkinToneCode,
|
|
49
|
+
applySkinToneToEmoji,
|
|
48
50
|
} from './reactions/catalog';
|
|
51
|
+
import StorageContext from './StorageContext';
|
|
49
52
|
import {useString} from '../utils/useString';
|
|
50
53
|
import {videoRoomUserFallbackText} from '../language/default-labels/videoCallScreenLabels';
|
|
51
54
|
|
|
@@ -117,6 +120,7 @@ const VideoCallProvider = (props: VideoCallProviderProps) => {
|
|
|
117
120
|
const {uids} = useUserPreference();
|
|
118
121
|
const remoteUserFallbackName = useString(videoRoomUserFallbackText)();
|
|
119
122
|
const {deviceList} = useContext(DeviceContext);
|
|
123
|
+
const {store} = useContext(StorageContext);
|
|
120
124
|
const setUsername = useSetName();
|
|
121
125
|
//const videoTileInViewPortStateRef = useRef({});
|
|
122
126
|
const [videoTileInViewPortState, setVideoTileInViewPortStateL] = useState({});
|
|
@@ -254,13 +258,20 @@ const VideoCallProvider = (props: VideoCallProviderProps) => {
|
|
|
254
258
|
reaction.key
|
|
255
259
|
}-${Date.now()}-${nanoid(4)}`;
|
|
256
260
|
const senderDisplayName = getReactionSenderName(String(localUser.uid));
|
|
261
|
+
const tonePreference = store.liveReactionSkinTone ?? 'default';
|
|
262
|
+
const skinTone: SkinToneCode | undefined =
|
|
263
|
+
tonePreference !== 'default' && reaction.skinToneVariants?.[tonePreference]
|
|
264
|
+
? tonePreference
|
|
265
|
+
: undefined;
|
|
266
|
+
const emoji = applySkinToneToEmoji(reaction, tonePreference);
|
|
257
267
|
const nextReaction: LiveReactionEvent = {
|
|
258
268
|
reactionId,
|
|
259
269
|
assetKey: reaction.key,
|
|
260
|
-
emoji
|
|
270
|
+
emoji,
|
|
261
271
|
senderUid: String(localUser.uid),
|
|
262
272
|
senderDisplayName,
|
|
263
273
|
timestamp: Date.now(),
|
|
274
|
+
skinTone,
|
|
264
275
|
};
|
|
265
276
|
|
|
266
277
|
ingestReaction(nextReaction);
|
|
@@ -271,11 +282,17 @@ const VideoCallProvider = (props: VideoCallProviderProps) => {
|
|
|
271
282
|
assetKey: nextReaction.assetKey,
|
|
272
283
|
emoji: nextReaction.emoji,
|
|
273
284
|
timestamp: nextReaction.timestamp,
|
|
285
|
+
skinTone: nextReaction.skinTone,
|
|
274
286
|
}),
|
|
275
287
|
PersistanceLevel.None,
|
|
276
288
|
);
|
|
277
289
|
},
|
|
278
|
-
[
|
|
290
|
+
[
|
|
291
|
+
getReactionSenderName,
|
|
292
|
+
ingestReaction,
|
|
293
|
+
localUser.uid,
|
|
294
|
+
store.liveReactionSkinTone,
|
|
295
|
+
],
|
|
279
296
|
);
|
|
280
297
|
|
|
281
298
|
useEffect(() => {
|
|
@@ -312,6 +329,7 @@ const VideoCallProvider = (props: VideoCallProviderProps) => {
|
|
|
312
329
|
emoji: payload.emoji,
|
|
313
330
|
senderUid: String(data.sender),
|
|
314
331
|
timestamp: payload.timestamp || data.ts || Date.now(),
|
|
332
|
+
skinTone: payload.skinTone,
|
|
315
333
|
});
|
|
316
334
|
} catch (error) {
|
|
317
335
|
console.warn('Failed to parse live reaction payload', error);
|
|
@@ -0,0 +1,19 @@
|
|
|
1
|
+
import {describe, expect, it} from '@jest/globals';
|
|
2
|
+
import {isRemoteDeparture} from '../isRemoteDeparture';
|
|
3
|
+
|
|
4
|
+
describe('isRemoteDeparture', () => {
|
|
5
|
+
it('always treats an explicit remote leave as a departure', () => {
|
|
6
|
+
expect(isRemoteDeparture(4, false)).toBe(true);
|
|
7
|
+
expect(isRemoteDeparture(4, true)).toBe(true);
|
|
8
|
+
});
|
|
9
|
+
|
|
10
|
+
it('treats a remote timeout as a departure only when enabled', () => {
|
|
11
|
+
expect(isRemoteDeparture(5, true)).toBe(true);
|
|
12
|
+
expect(isRemoteDeparture(5, false)).toBe(false);
|
|
13
|
+
});
|
|
14
|
+
|
|
15
|
+
it('ignores unrelated presence event types', () => {
|
|
16
|
+
expect(isRemoteDeparture(3, true)).toBe(false);
|
|
17
|
+
expect(isRemoteDeparture(6, true)).toBe(false);
|
|
18
|
+
});
|
|
19
|
+
});
|
|
@@ -0,0 +1,61 @@
|
|
|
1
|
+
import {describe, expect, it, jest} from '@jest/globals';
|
|
2
|
+
import {buildSTTRequestBody, type STTMethod} from '../sttRequestBody';
|
|
3
|
+
|
|
4
|
+
const createOptions = (method: STTMethod) => ({
|
|
5
|
+
method,
|
|
6
|
+
botUid: 900000123,
|
|
7
|
+
passphrase: 'phrase',
|
|
8
|
+
encryptionMode: 8,
|
|
9
|
+
localUid: 123,
|
|
10
|
+
channelName: 'room',
|
|
11
|
+
translationConfig:
|
|
12
|
+
method === 'stopv7'
|
|
13
|
+
? undefined
|
|
14
|
+
: {source: ['en-US'] as any, targets: ['de-DE'] as any},
|
|
15
|
+
resolveSessionId: jest.fn(async () => 'shared-session'),
|
|
16
|
+
});
|
|
17
|
+
|
|
18
|
+
describe('buildSTTRequestBody', () => {
|
|
19
|
+
it.each(['startv7', 'update', 'stopv7'] as STTMethod[])(
|
|
20
|
+
'adds the coordinated session ID to %s on every client platform',
|
|
21
|
+
async method => {
|
|
22
|
+
const body = await buildSTTRequestBody(createOptions(method));
|
|
23
|
+
|
|
24
|
+
expect(body.session_id).toBe('shared-session');
|
|
25
|
+
expect(body.passphrase).toBe('phrase');
|
|
26
|
+
expect(body.dataStream_uid).toBe(900000123);
|
|
27
|
+
expect(body.encryption_mode).toBe(8);
|
|
28
|
+
},
|
|
29
|
+
);
|
|
30
|
+
|
|
31
|
+
it('keeps the existing update translation payload behavior', async () => {
|
|
32
|
+
const body = await buildSTTRequestBody({
|
|
33
|
+
...createOptions('update'),
|
|
34
|
+
translationConfig: {
|
|
35
|
+
source: ['en-US'],
|
|
36
|
+
targets: ['en-US', 'de-DE'],
|
|
37
|
+
},
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
expect(body).toMatchObject({
|
|
41
|
+
lang: ['en-US'],
|
|
42
|
+
translate: true,
|
|
43
|
+
translate_config: [
|
|
44
|
+
{
|
|
45
|
+
source_lang: 'en-US',
|
|
46
|
+
target_lang: ['de-DE'],
|
|
47
|
+
},
|
|
48
|
+
],
|
|
49
|
+
subscribeAudioUids: ['123'],
|
|
50
|
+
});
|
|
51
|
+
});
|
|
52
|
+
|
|
53
|
+
it('rejects construction when the shared ID is unavailable', async () => {
|
|
54
|
+
await expect(
|
|
55
|
+
buildSTTRequestBody({
|
|
56
|
+
...createOptions('stopv7'),
|
|
57
|
+
resolveSessionId: jest.fn(async () => undefined),
|
|
58
|
+
}),
|
|
59
|
+
).rejects.toThrow('Unable to resolve the shared STT session ID');
|
|
60
|
+
});
|
|
61
|
+
});
|
|
@@ -0,0 +1,76 @@
|
|
|
1
|
+
import {afterEach, beforeEach, describe, expect, it, jest} from '@jest/globals';
|
|
2
|
+
|
|
3
|
+
const mockStorage = {
|
|
4
|
+
getChannelMetadata: jest.fn<any>(),
|
|
5
|
+
setChannelMetadata: jest.fn<any>(),
|
|
6
|
+
removeChannelMetadata: jest.fn<any>(),
|
|
7
|
+
};
|
|
8
|
+
const mockPresence = {
|
|
9
|
+
getOnlineUsers: jest.fn<any>(),
|
|
10
|
+
};
|
|
11
|
+
|
|
12
|
+
jest.mock('../../../rtm/RTMEngine', () => ({
|
|
13
|
+
__esModule: true,
|
|
14
|
+
default: {
|
|
15
|
+
getInstance: () => ({
|
|
16
|
+
engine: {storage: mockStorage, presence: mockPresence},
|
|
17
|
+
}),
|
|
18
|
+
},
|
|
19
|
+
}));
|
|
20
|
+
|
|
21
|
+
jest.mock('../../../utils/getUniqueID', () => ({
|
|
22
|
+
__esModule: true,
|
|
23
|
+
default: jest.fn(() => 'native-session'),
|
|
24
|
+
}));
|
|
25
|
+
|
|
26
|
+
import {
|
|
27
|
+
ensureSTTSessionId,
|
|
28
|
+
resetSTTSessionIdCache,
|
|
29
|
+
STT_SESSION_ID_KEY,
|
|
30
|
+
} from '../sttSessionId';
|
|
31
|
+
|
|
32
|
+
const emptyChannelMetadata = {
|
|
33
|
+
timestamp: 100,
|
|
34
|
+
channelName: 'room',
|
|
35
|
+
channelType: 1,
|
|
36
|
+
majorRevision: 0,
|
|
37
|
+
itemCount: 0,
|
|
38
|
+
items: [],
|
|
39
|
+
};
|
|
40
|
+
|
|
41
|
+
describe('native STT session coordination', () => {
|
|
42
|
+
beforeEach(() => {
|
|
43
|
+
jest.clearAllMocks();
|
|
44
|
+
jest.spyOn(console, 'log').mockImplementation(() => {});
|
|
45
|
+
resetSTTSessionIdCache();
|
|
46
|
+
mockStorage.getChannelMetadata.mockResolvedValue(emptyChannelMetadata);
|
|
47
|
+
mockStorage.setChannelMetadata.mockResolvedValue({
|
|
48
|
+
timestamp: 100,
|
|
49
|
+
channelName: 'room',
|
|
50
|
+
channelType: 1,
|
|
51
|
+
});
|
|
52
|
+
});
|
|
53
|
+
|
|
54
|
+
afterEach(() => {
|
|
55
|
+
jest.restoreAllMocks();
|
|
56
|
+
});
|
|
57
|
+
|
|
58
|
+
it('creates the shared session ID through the native RTM client', async () => {
|
|
59
|
+
await expect(ensureSTTSessionId('room')).resolves.toBe('native-session');
|
|
60
|
+
|
|
61
|
+
expect(mockStorage.setChannelMetadata).toHaveBeenCalledWith(
|
|
62
|
+
'room',
|
|
63
|
+
1,
|
|
64
|
+
{
|
|
65
|
+
items: [
|
|
66
|
+
{
|
|
67
|
+
key: STT_SESSION_ID_KEY,
|
|
68
|
+
value: 'native-session',
|
|
69
|
+
revision: 0,
|
|
70
|
+
},
|
|
71
|
+
],
|
|
72
|
+
},
|
|
73
|
+
{addUserId: true, addTimeStamp: true},
|
|
74
|
+
);
|
|
75
|
+
});
|
|
76
|
+
});
|