agora-appbuilder-core 4.0.35 → 4.1.0-beta-2
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 +2 -2
- package/template/agora-rn-uikit/README.md +1 -40
- package/template/agora-rn-uikit/src/Contexts/PropsContext.tsx +1 -0
- package/template/agora-rn-uikit/src/Contexts/RtcContext.tsx +1 -0
- package/template/agora-rn-uikit/src/Reducer/Spotlight.ts +11 -0
- package/template/agora-rn-uikit/src/Reducer/index.ts +1 -0
- package/template/agora-rn-uikit/src/RtcConfigure.tsx +7 -0
- package/template/bridge/rtc/webNg/RtcEngine.ts +4 -1
- package/template/customization-api/app-state.ts +11 -7
- package/template/customization-api/{customize.ts → customize.tsx} +116 -11
- package/template/customization-api/sub-components.ts +4 -0
- package/template/customization-api/temp.ts +2 -0
- package/template/customization-api/typeDefinition.ts +2 -1
- package/template/customization-api/utils.ts +6 -1
- package/template/defaultConfig.js +4 -2
- package/template/global.d.ts +2 -0
- package/template/src/AppRoutes.tsx +15 -5
- package/template/src/ai-agent/components/AgentControls/AgentContext.tsx +163 -0
- package/template/src/ai-agent/components/AgentControls/LeaveCall.png +0 -0
- package/template/src/ai-agent/components/AgentControls/Vector.svg +3 -0
- package/template/src/ai-agent/components/AgentControls/const.ts +58 -0
- package/template/src/ai-agent/components/AgentControls/index.tsx +293 -0
- package/template/src/ai-agent/components/AudioVisualizer.tsx +91 -0
- package/template/src/ai-agent/components/Bottombar.tsx +91 -0
- package/template/src/ai-agent/components/CustomCreate.tsx +279 -0
- package/template/src/ai-agent/components/CustomCreateNative.tsx +265 -0
- package/template/src/ai-agent/components/CustomSidePanel.tsx +135 -0
- package/template/src/ai-agent/components/FallbackLogo.tsx +80 -0
- package/template/src/ai-agent/components/LocalAudioWave.tsx +171 -0
- package/template/src/ai-agent/components/agent-chat-panel/agent-chat-ui.tsx +82 -0
- package/template/src/ai-agent/components/icons.tsx +227 -0
- package/template/src/ai-agent/components/mobile/Bottombar.tsx +47 -0
- package/template/src/ai-agent/components/mobile/MobileLayoutComponent.tsx +106 -0
- package/template/src/ai-agent/components/mobile/Topbar.tsx +62 -0
- package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/LiveAudioVisualizer.tsx +173 -0
- package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/index.ts +1 -0
- package/template/src/ai-agent/components/react-audio-visualize/LiveAudioVisualizer/utils.ts +102 -0
- package/template/src/ai-agent/components/react-audio-visualize/index.ts +1 -0
- package/template/src/ai-agent/components/utils.ts +15 -0
- package/template/src/ai-agent/index.tsx +301 -0
- package/template/src/ai-agent/routes/CustomLoginRoute.tsx +25 -0
- package/template/src/ai-agent/routes/CustomValidateRoute.tsx +25 -0
- package/template/src/ai-agent/utils.ts +78 -0
- package/template/src/assets/font-styles.css +4 -0
- package/template/src/assets/fonts/icomoon.ttf +0 -0
- package/template/src/assets/selection.json +1 -1
- package/template/src/atoms/CustomIcon.tsx +1 -0
- package/template/src/atoms/ImageIcon.tsx +3 -0
- package/template/src/atoms/ToolbarItem.tsx +0 -2
- package/template/src/components/ChatContext.ts +7 -0
- package/template/src/components/Controls.tsx +6 -1
- package/template/src/components/ErrorBoundary.tsx +37 -0
- package/template/src/components/ErrorBoundaryFallback.tsx +44 -0
- package/template/src/components/RTMConfigure.tsx +25 -20
- package/template/src/components/participants/Participant.tsx +4 -0
- package/template/src/components/participants/UserActionMenuOptions.tsx +34 -1
- package/template/src/components/precall/PermissionHelper.tsx +11 -8
- package/template/src/language/default-labels/videoCallScreenLabels.ts +8 -0
- package/template/src/logger/AppBuilderLogger.tsx +4 -1
- package/template/src/pages/Create.tsx +11 -12
- package/template/src/pages/VideoCall.tsx +1 -0
- package/template/src/pages/video-call/ActionSheet.tsx +33 -29
- package/template/src/pages/video-call/SidePanelHeader.tsx +8 -3
- package/template/src/pages/video-call/SpotlightHighlighter.tsx +91 -0
- package/template/src/pages/video-call/VideoCallMobileView.tsx +17 -6
- package/template/src/pages/video-call/VideoCallScreen.tsx +0 -1
- package/template/src/pages/video-call/VideoRenderer.tsx +32 -4
- package/template/src/rtm-events/constants.ts +2 -0
- package/template/src/subComponents/ChatBubble.tsx +34 -15
- package/template/src/subComponents/FallbackLogo.tsx +3 -1
- package/template/src/subComponents/LocalAudioMute.tsx +20 -2
- package/template/src/utils/index.tsx +3 -4
- package/template/src/utils/useJoinRoom.ts +14 -0
- package/template/src/utils/useSpotlight.ts +31 -0
- package/template/tsconfig.json +23 -18
|
@@ -0,0 +1,106 @@
|
|
|
1
|
+
import React, {useContext, useEffect, useState} from 'react';
|
|
2
|
+
import {View} from 'react-native';
|
|
3
|
+
import {ILocalAudioTrack, IRemoteAudioTrack} from 'agora-rtc-sdk-ng';
|
|
4
|
+
import {
|
|
5
|
+
MaxVideoView,
|
|
6
|
+
useContent,
|
|
7
|
+
useLocalUid,
|
|
8
|
+
LayoutComponent,
|
|
9
|
+
useRtc,
|
|
10
|
+
useLocalAudio,
|
|
11
|
+
useIsAudioEnabled,
|
|
12
|
+
} from 'customization-api';
|
|
13
|
+
import AudioVisualizer, {DisconnectedView} from '../AudioVisualizer';
|
|
14
|
+
import {ActiveSpeakerAnimation} from '../../components/LocalAudioWave';
|
|
15
|
+
import {AgentContext} from '../AgentControls/AgentContext';
|
|
16
|
+
|
|
17
|
+
const MobileLayoutComponent: LayoutComponent = () => {
|
|
18
|
+
const localUid = useLocalUid();
|
|
19
|
+
const {defaultContent, activeUids} = useContent();
|
|
20
|
+
const {RtcEngineUnsafe} = useRtc();
|
|
21
|
+
const [localTracks, setLocalTrack] = useState<ILocalAudioTrack | null>(null);
|
|
22
|
+
const [remoteTrack, setRemoteTrack] = useState<IRemoteAudioTrack | null>(
|
|
23
|
+
null,
|
|
24
|
+
);
|
|
25
|
+
const {agentUID} = useContext(AgentContext);
|
|
26
|
+
|
|
27
|
+
const {getLocalAudioStream, getRemoteAudioStream} = useLocalAudio();
|
|
28
|
+
const isAudioEnabled = useIsAudioEnabled();
|
|
29
|
+
const connected = activeUids.includes(agentUID);
|
|
30
|
+
console.log({activeUids}, 'active uids');
|
|
31
|
+
|
|
32
|
+
useEffect(() => {
|
|
33
|
+
if (getLocalAudioStream()) {
|
|
34
|
+
setLocalTrack(getLocalAudioStream());
|
|
35
|
+
}
|
|
36
|
+
}, [RtcEngineUnsafe]);
|
|
37
|
+
|
|
38
|
+
useEffect(() => {}, [isAudioEnabled]);
|
|
39
|
+
|
|
40
|
+
useEffect(() => {
|
|
41
|
+
if (getRemoteAudioStream(agentUID)) {
|
|
42
|
+
setRemoteTrack(getRemoteAudioStream(agentUID));
|
|
43
|
+
}
|
|
44
|
+
}, [activeUids]);
|
|
45
|
+
|
|
46
|
+
return (
|
|
47
|
+
<View
|
|
48
|
+
style={{
|
|
49
|
+
flex: 1,
|
|
50
|
+
display: 'flex',
|
|
51
|
+
flexDirection: 'column',
|
|
52
|
+
}}>
|
|
53
|
+
<MaxVideoView
|
|
54
|
+
containerStyle={{borderRadius: 12, borderWidth: 0}}
|
|
55
|
+
innerContainerStyle={{backgroundColor: '#222'}}
|
|
56
|
+
user={{
|
|
57
|
+
...defaultContent[agentUID],
|
|
58
|
+
name: 'OpenAI',
|
|
59
|
+
video: false,
|
|
60
|
+
}}
|
|
61
|
+
CustomChild={() =>
|
|
62
|
+
connected ? (
|
|
63
|
+
<AudioVisualizer audioTrack={remoteTrack} />
|
|
64
|
+
) : (
|
|
65
|
+
<DisconnectedView isConnected={connected} />
|
|
66
|
+
)
|
|
67
|
+
}
|
|
68
|
+
hideMenuOptions={true}
|
|
69
|
+
/>
|
|
70
|
+
<View
|
|
71
|
+
style={{
|
|
72
|
+
position: 'absolute',
|
|
73
|
+
top: 8,
|
|
74
|
+
right: 8,
|
|
75
|
+
display: 'flex',
|
|
76
|
+
flexDirection: 'row',
|
|
77
|
+
height: 100,
|
|
78
|
+
width: 215,
|
|
79
|
+
}}>
|
|
80
|
+
<MaxVideoView
|
|
81
|
+
containerStyle={{borderRadius: 8, borderWidth: 0}}
|
|
82
|
+
user={defaultContent[localUid]}
|
|
83
|
+
avatarRadius={48}
|
|
84
|
+
hideMenuOptions={true}
|
|
85
|
+
/>
|
|
86
|
+
<View
|
|
87
|
+
style={{
|
|
88
|
+
position: 'absolute',
|
|
89
|
+
width: 100,
|
|
90
|
+
height: 50,
|
|
91
|
+
bottom: 16,
|
|
92
|
+
right: -38,
|
|
93
|
+
}}>
|
|
94
|
+
{localTracks && isAudioEnabled(localUid) && (
|
|
95
|
+
<ActiveSpeakerAnimation
|
|
96
|
+
audioTrack={localTracks}
|
|
97
|
+
isMuted={!isAudioEnabled(localUid)}
|
|
98
|
+
/>
|
|
99
|
+
)}
|
|
100
|
+
</View>
|
|
101
|
+
</View>
|
|
102
|
+
</View>
|
|
103
|
+
);
|
|
104
|
+
};
|
|
105
|
+
|
|
106
|
+
export default MobileLayoutComponent;
|
|
@@ -0,0 +1,62 @@
|
|
|
1
|
+
import React from 'react';
|
|
2
|
+
import {View, StyleSheet, Text, TouchableOpacity} from 'react-native';
|
|
3
|
+
import {LogoIcon, SettingsIcon} from '../icons';
|
|
4
|
+
import {SidePanelType, useSidePanel} from 'customization-api';
|
|
5
|
+
import ThemeConfig from '../../../theme';
|
|
6
|
+
|
|
7
|
+
const MobileTopbar = () => {
|
|
8
|
+
const {setSidePanel} = useSidePanel();
|
|
9
|
+
return (
|
|
10
|
+
<View style={style.rootStyle}>
|
|
11
|
+
<View style={style.containerStyle}>
|
|
12
|
+
<View style={style.logContainerStyle}>
|
|
13
|
+
<LogoIcon />
|
|
14
|
+
</View>
|
|
15
|
+
<Text style={style.textStyle}>Agora & OpenAI Demo</Text>
|
|
16
|
+
</View>
|
|
17
|
+
<TouchableOpacity
|
|
18
|
+
onPress={() => {
|
|
19
|
+
setSidePanel(SidePanelType.Settings);
|
|
20
|
+
}}>
|
|
21
|
+
<SettingsIcon />
|
|
22
|
+
</TouchableOpacity>
|
|
23
|
+
</View>
|
|
24
|
+
);
|
|
25
|
+
};
|
|
26
|
+
|
|
27
|
+
const style = StyleSheet.create({
|
|
28
|
+
rootStyle: {
|
|
29
|
+
display: 'flex',
|
|
30
|
+
flexDirection: 'row',
|
|
31
|
+
justifyContent: 'space-between',
|
|
32
|
+
alignItems: 'center',
|
|
33
|
+
paddingBottom: 20,
|
|
34
|
+
},
|
|
35
|
+
containerStyle: {
|
|
36
|
+
display: 'flex',
|
|
37
|
+
flexDirection: 'row',
|
|
38
|
+
justifyContent: 'center',
|
|
39
|
+
alignItems: 'center',
|
|
40
|
+
gap: 4,
|
|
41
|
+
alignSelf: 'stretch',
|
|
42
|
+
},
|
|
43
|
+
logContainerStyle: {
|
|
44
|
+
display: 'flex',
|
|
45
|
+
width: 40,
|
|
46
|
+
height: 40,
|
|
47
|
+
paddingRight: 1,
|
|
48
|
+
justifyContent: 'center',
|
|
49
|
+
alignItems: 'center',
|
|
50
|
+
},
|
|
51
|
+
textStyle: {
|
|
52
|
+
color: '#C3C3C3',
|
|
53
|
+
textAlign: 'center',
|
|
54
|
+
fontFamily: ThemeConfig.FontFamily.sansPro,
|
|
55
|
+
fontSize: 18,
|
|
56
|
+
fontStyle: 'normal',
|
|
57
|
+
fontWeight: '600',
|
|
58
|
+
lineHeight: 18,
|
|
59
|
+
},
|
|
60
|
+
});
|
|
61
|
+
|
|
62
|
+
export default MobileTopbar;
|
|
@@ -0,0 +1,173 @@
|
|
|
1
|
+
import React, {
|
|
2
|
+
type ReactElement,
|
|
3
|
+
useCallback,
|
|
4
|
+
useEffect,
|
|
5
|
+
useRef,
|
|
6
|
+
useState,
|
|
7
|
+
} from 'react';
|
|
8
|
+
import {calculateBarData, draw} from './utils';
|
|
9
|
+
import {ILocalAudioTrack, IRemoteAudioTrack} from 'agora-rtc-sdk-ng';
|
|
10
|
+
|
|
11
|
+
export interface Props {
|
|
12
|
+
/**
|
|
13
|
+
* Width of the visualization. Default: "100%"
|
|
14
|
+
*/
|
|
15
|
+
width?: number | string;
|
|
16
|
+
/**
|
|
17
|
+
* Height of the visualization. Default: "100%"
|
|
18
|
+
*/
|
|
19
|
+
height?: number | string;
|
|
20
|
+
/**
|
|
21
|
+
* Width of each individual bar in the visualization. Default: `2`
|
|
22
|
+
*/
|
|
23
|
+
barWidth?: number;
|
|
24
|
+
/**
|
|
25
|
+
* Gap between each bar in the visualization. Default: `1`
|
|
26
|
+
*/
|
|
27
|
+
gap?: number;
|
|
28
|
+
/**
|
|
29
|
+
* BackgroundColor for the visualization: Default: `transparent`
|
|
30
|
+
*/
|
|
31
|
+
backgroundColor?: string;
|
|
32
|
+
/**
|
|
33
|
+
* Color of the bars drawn in the visualization. Default: `"rgb(160, 198, 255)"`
|
|
34
|
+
*/
|
|
35
|
+
barColor?: string;
|
|
36
|
+
/**
|
|
37
|
+
* An unsigned integer, representing the window size of the FFT, given in number of samples.
|
|
38
|
+
* Default: `1024`
|
|
39
|
+
*/
|
|
40
|
+
fftSize?:
|
|
41
|
+
| 32
|
|
42
|
+
| 64
|
|
43
|
+
| 128
|
|
44
|
+
| 256
|
|
45
|
+
| 512
|
|
46
|
+
| 1024
|
|
47
|
+
| 2048
|
|
48
|
+
| 4096
|
|
49
|
+
| 8192
|
|
50
|
+
| 16384
|
|
51
|
+
| 32768;
|
|
52
|
+
/**
|
|
53
|
+
* A double, representing the maximum decibel value for scaling the FFT analysis data
|
|
54
|
+
* Default: `-10`
|
|
55
|
+
*/
|
|
56
|
+
maxDecibels?: number;
|
|
57
|
+
/**
|
|
58
|
+
* A double, representing the minimum decibel value for scaling the FFT analysis data
|
|
59
|
+
* Default: `-90`
|
|
60
|
+
*/
|
|
61
|
+
minDecibels?: number;
|
|
62
|
+
/**
|
|
63
|
+
* A double within the range 0 to 1 (0 meaning no time averaging). The default value is 0.8.
|
|
64
|
+
* Default: `0.4`
|
|
65
|
+
*/
|
|
66
|
+
smoothingTimeConstant?: number;
|
|
67
|
+
audioTrack?: ILocalAudioTrack | IRemoteAudioTrack;
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
const LiveAudioVisualizer: (props: Props) => ReactElement = ({
|
|
71
|
+
width = '100%',
|
|
72
|
+
height = '100%',
|
|
73
|
+
barWidth = 2,
|
|
74
|
+
gap = 1,
|
|
75
|
+
backgroundColor = 'transparent',
|
|
76
|
+
barColor = 'rgb(160, 198, 255)',
|
|
77
|
+
fftSize = 1024,
|
|
78
|
+
maxDecibels = -10,
|
|
79
|
+
minDecibels = -90,
|
|
80
|
+
smoothingTimeConstant = 0.4,
|
|
81
|
+
audioTrack,
|
|
82
|
+
}: Props) => {
|
|
83
|
+
const [audioContext, setAudioContext] = useState<AudioContext | null>(null);
|
|
84
|
+
const [audioSource, setAudioSource] = useState<MediaStreamAudioSourceNode>();
|
|
85
|
+
const [analyser, setAnalyser] = useState<AnalyserNode>();
|
|
86
|
+
const canvasRef = useRef<HTMLCanvasElement>(null);
|
|
87
|
+
|
|
88
|
+
useEffect(() => {
|
|
89
|
+
if (!audioTrack) {
|
|
90
|
+
// Clean up if track is null
|
|
91
|
+
if (audioContext) {
|
|
92
|
+
audioContext.state !== 'closed' && audioContext.close();
|
|
93
|
+
setAudioContext(null);
|
|
94
|
+
setAnalyser(null);
|
|
95
|
+
setAudioSource(null);
|
|
96
|
+
}
|
|
97
|
+
return;
|
|
98
|
+
}
|
|
99
|
+
|
|
100
|
+
const newAudioContext = new (window.AudioContext ||
|
|
101
|
+
(window as any).webkitAudioContext)();
|
|
102
|
+
const newAnalyser = newAudioContext.createAnalyser();
|
|
103
|
+
newAnalyser.fftSize = fftSize;
|
|
104
|
+
newAnalyser.minDecibels = minDecibels;
|
|
105
|
+
newAnalyser.maxDecibels = maxDecibels;
|
|
106
|
+
newAnalyser.smoothingTimeConstant = smoothingTimeConstant;
|
|
107
|
+
|
|
108
|
+
const source = newAudioContext.createMediaStreamSource(
|
|
109
|
+
new MediaStream([audioTrack.getMediaStreamTrack()]),
|
|
110
|
+
);
|
|
111
|
+
source.connect(newAnalyser);
|
|
112
|
+
|
|
113
|
+
setAudioContext(newAudioContext);
|
|
114
|
+
setAnalyser(newAnalyser);
|
|
115
|
+
setAudioSource(source);
|
|
116
|
+
|
|
117
|
+
return () => {
|
|
118
|
+
source.disconnect();
|
|
119
|
+
newAnalyser.disconnect();
|
|
120
|
+
newAudioContext.state !== 'closed' && newAudioContext.close();
|
|
121
|
+
};
|
|
122
|
+
}, [audioTrack, fftSize, minDecibels, maxDecibels, smoothingTimeConstant]);
|
|
123
|
+
|
|
124
|
+
useEffect(() => {
|
|
125
|
+
if (analyser && audioTrack) {
|
|
126
|
+
requestAnimationFrame(report);
|
|
127
|
+
}
|
|
128
|
+
}, [analyser, audioTrack]);
|
|
129
|
+
|
|
130
|
+
const report = useCallback(() => {
|
|
131
|
+
if (!analyser || !audioContext || !audioTrack) return;
|
|
132
|
+
|
|
133
|
+
const data = new Uint8Array(analyser.frequencyBinCount);
|
|
134
|
+
analyser.getByteFrequencyData(data);
|
|
135
|
+
processFrequencyData(data);
|
|
136
|
+
|
|
137
|
+
if (audioTrack.isPlaying) {
|
|
138
|
+
requestAnimationFrame(report);
|
|
139
|
+
}
|
|
140
|
+
}, [analyser, audioContext, audioTrack]);
|
|
141
|
+
|
|
142
|
+
const processFrequencyData = (data: Uint8Array): void => {
|
|
143
|
+
if (!canvasRef.current) return;
|
|
144
|
+
|
|
145
|
+
const dataPoints = calculateBarData(
|
|
146
|
+
data,
|
|
147
|
+
canvasRef.current.width,
|
|
148
|
+
barWidth,
|
|
149
|
+
gap,
|
|
150
|
+
);
|
|
151
|
+
draw(
|
|
152
|
+
dataPoints,
|
|
153
|
+
canvasRef.current,
|
|
154
|
+
barWidth,
|
|
155
|
+
gap,
|
|
156
|
+
backgroundColor,
|
|
157
|
+
barColor,
|
|
158
|
+
);
|
|
159
|
+
};
|
|
160
|
+
|
|
161
|
+
return (
|
|
162
|
+
<canvas
|
|
163
|
+
ref={canvasRef}
|
|
164
|
+
width={width}
|
|
165
|
+
height={height}
|
|
166
|
+
style={{
|
|
167
|
+
aspectRatio: 'unset',
|
|
168
|
+
}}
|
|
169
|
+
/>
|
|
170
|
+
);
|
|
171
|
+
};
|
|
172
|
+
|
|
173
|
+
export {LiveAudioVisualizer};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {LiveAudioVisualizer} from './LiveAudioVisualizer';
|
|
@@ -0,0 +1,102 @@
|
|
|
1
|
+
interface CustomCanvasRenderingContext2D extends CanvasRenderingContext2D {
|
|
2
|
+
roundRect: (
|
|
3
|
+
x: number,
|
|
4
|
+
y: number,
|
|
5
|
+
w: number,
|
|
6
|
+
h: number,
|
|
7
|
+
radius: number,
|
|
8
|
+
) => void;
|
|
9
|
+
}
|
|
10
|
+
|
|
11
|
+
export const calculateBarData = (
|
|
12
|
+
frequencyData: Uint8Array,
|
|
13
|
+
width: number,
|
|
14
|
+
barWidth: number,
|
|
15
|
+
gap: number,
|
|
16
|
+
): number[] => {
|
|
17
|
+
let units = width / (barWidth + gap);
|
|
18
|
+
let step = Math.floor(frequencyData.length / units);
|
|
19
|
+
|
|
20
|
+
if (units > frequencyData.length) {
|
|
21
|
+
units = frequencyData.length;
|
|
22
|
+
step = 1;
|
|
23
|
+
}
|
|
24
|
+
|
|
25
|
+
const data: number[] = [];
|
|
26
|
+
|
|
27
|
+
for (let i = 0; i < units; i++) {
|
|
28
|
+
let sum = 0;
|
|
29
|
+
|
|
30
|
+
for (let j = 0; j < step && i * step + j < frequencyData.length; j++) {
|
|
31
|
+
sum += frequencyData[i * step + j];
|
|
32
|
+
}
|
|
33
|
+
data.push(sum / step);
|
|
34
|
+
}
|
|
35
|
+
return data;
|
|
36
|
+
};
|
|
37
|
+
|
|
38
|
+
export const draw = (
|
|
39
|
+
data: number[],
|
|
40
|
+
canvas: HTMLCanvasElement,
|
|
41
|
+
barWidth: number,
|
|
42
|
+
gap: number,
|
|
43
|
+
backgroundColor: string,
|
|
44
|
+
barColor: string,
|
|
45
|
+
): void => {
|
|
46
|
+
const amp = canvas.height / 2;
|
|
47
|
+
|
|
48
|
+
const ctx = canvas.getContext('2d') as CustomCanvasRenderingContext2D;
|
|
49
|
+
if (!ctx) return;
|
|
50
|
+
|
|
51
|
+
ctx.clearRect(0, 0, canvas.width, canvas.height);
|
|
52
|
+
|
|
53
|
+
if (backgroundColor !== 'transparent') {
|
|
54
|
+
ctx.fillStyle = backgroundColor;
|
|
55
|
+
ctx.fillRect(0, 0, canvas.width, canvas.height);
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
// const sampled = data.slice(0, 8);
|
|
59
|
+
//
|
|
60
|
+
// const downSampled = new Array(5).fill(0);
|
|
61
|
+
//
|
|
62
|
+
// const downPoint = Math.floor(sampled.length / 5);
|
|
63
|
+
//
|
|
64
|
+
// sampled.forEach((dp, i) => {
|
|
65
|
+
// downSampled[Math.floor(i / downPoint)] += dp;
|
|
66
|
+
// });
|
|
67
|
+
|
|
68
|
+
// const sampled = new Array(Math.ceil(data.length / 5)).fill(0);
|
|
69
|
+
|
|
70
|
+
// for (const dataPoint of data) {
|
|
71
|
+
// const index = Math.floor(dataPoint / 5);
|
|
72
|
+
// sampled[index] += dataPoint;
|
|
73
|
+
// }
|
|
74
|
+
|
|
75
|
+
const displayData = data.slice(0, 5);
|
|
76
|
+
|
|
77
|
+
const newBarWidth =
|
|
78
|
+
(canvas.width - gap * (displayData.length - 1)) / displayData.length;
|
|
79
|
+
|
|
80
|
+
const minHeight = newBarWidth;
|
|
81
|
+
|
|
82
|
+
displayData.forEach((dpo, i) => {
|
|
83
|
+
const dp = dpo;
|
|
84
|
+
|
|
85
|
+
ctx.fillStyle = barColor;
|
|
86
|
+
|
|
87
|
+
const x = i * (newBarWidth + gap);
|
|
88
|
+
const y = amp - dp / 2;
|
|
89
|
+
const w = newBarWidth;
|
|
90
|
+
const h = dp || 1;
|
|
91
|
+
|
|
92
|
+
ctx.beginPath();
|
|
93
|
+
if (ctx.roundRect) {
|
|
94
|
+
// making sure roundRect is supported by the browser
|
|
95
|
+
ctx.roundRect(x, y - minHeight / 2, w, h + minHeight, w / 2);
|
|
96
|
+
ctx.fill();
|
|
97
|
+
} else {
|
|
98
|
+
// fallback for browsers that do not support roundRect
|
|
99
|
+
ctx.fillRect(x, y, w, h);
|
|
100
|
+
}
|
|
101
|
+
});
|
|
102
|
+
};
|
|
@@ -0,0 +1 @@
|
|
|
1
|
+
export {LiveAudioVisualizer} from './LiveAudioVisualizer';
|
|
@@ -0,0 +1,15 @@
|
|
|
1
|
+
import {
|
|
2
|
+
AGENT_PROXY_URL,
|
|
3
|
+
AGORA_SSO_LOGIN_PATH,
|
|
4
|
+
AGORA_SSO_CLIENT_ID,
|
|
5
|
+
AGORA_SSO_BASE,
|
|
6
|
+
} from '../components/AgentControls/const';
|
|
7
|
+
|
|
8
|
+
export const handleSSOLogin = () => {
|
|
9
|
+
const REDIRECT_URL = `${AGENT_PROXY_URL}/login`;
|
|
10
|
+
const originURL = window.location.origin + '/validate';
|
|
11
|
+
|
|
12
|
+
const ssoUrl = `${AGORA_SSO_BASE}/${AGORA_SSO_LOGIN_PATH}?scope=basic_info&response_type=code&state=url=${originURL}&redirect_uri=${REDIRECT_URL}&client_id=${AGORA_SSO_CLIENT_ID}`;
|
|
13
|
+
console.log('sso', ssoUrl);
|
|
14
|
+
window.location.href = ssoUrl;
|
|
15
|
+
};
|