agora-appbuilder-core 4.1.8-beta.5 → 4.1.8-beta.7
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/defaultConfig.js +4 -3
- package/template/global.d.ts +1 -0
- package/template/src/components/Controls.tsx +17 -15
- package/template/src/components/common/GenericPopup.tsx +0 -1
- package/template/src/components/common/data-table.tsx +20 -2
- package/template/src/components/controls/useControlPermissionMatrix.tsx +3 -2
- package/template/src/components/recordings/RecordingItemRow.tsx +285 -0
- package/template/src/components/recordings/RecordingsDateTable.tsx +89 -26
- package/template/src/components/recordings/TextTrackItemRow.tsx +120 -0
- package/template/src/components/{stt-transcript/STTTranscriptTable.tsx → text-tracks/TextTracksTable.tsx} +56 -50
- package/template/src/components/{stt-transcript/ViewSTTTranscriptModal.tsx → text-tracks/ViewTextTracksModal.tsx} +7 -7
- package/template/src/components/text-tracks/useFetchSTTTranscript.tsx +266 -0
- package/template/src/language/default-labels/videoCallScreenLabels.ts +7 -7
- package/template/src/subComponents/recording/useRecording.tsx +19 -4
- package/template/src/components/stt-transcript/useFetchSTTTranscript.tsx +0 -194
package/package.json
CHANGED
|
@@ -77,8 +77,8 @@ const DefaultConfig = {
|
|
|
77
77
|
CHAT_ORG_NAME: '',
|
|
78
78
|
CHAT_APP_NAME: '',
|
|
79
79
|
CHAT_URL: '',
|
|
80
|
-
CLI_VERSION: '3.1.8-beta.
|
|
81
|
-
CORE_VERSION: '4.1.8-beta.
|
|
80
|
+
CLI_VERSION: '3.1.8-beta.7',
|
|
81
|
+
CORE_VERSION: '4.1.8-beta.7',
|
|
82
82
|
DISABLE_LANDSCAPE_MODE: false,
|
|
83
83
|
STT_AUTO_START: false,
|
|
84
84
|
CLOUD_RECORDING_AUTO_START: false,
|
|
@@ -89,7 +89,8 @@ const DefaultConfig = {
|
|
|
89
89
|
AI_LAYOUT: 'LAYOUT_TYPE_1',
|
|
90
90
|
SDK_CODEC: 'vp8',
|
|
91
91
|
ENABLE_WAITING_ROOM_AUTO_APPROVAL: false,
|
|
92
|
-
ENABLE_WAITING_ROOM_AUTO_REQUEST: false
|
|
92
|
+
ENABLE_WAITING_ROOM_AUTO_REQUEST: false,
|
|
93
|
+
ENABLE_TEXT_TRACKS: true,
|
|
93
94
|
};
|
|
94
95
|
|
|
95
96
|
module.exports = DefaultConfig;
|
package/template/global.d.ts
CHANGED
|
@@ -177,6 +177,7 @@ interface ConfigInterface {
|
|
|
177
177
|
SDK_CODEC: string;
|
|
178
178
|
ENABLE_WAITING_ROOM_AUTO_APPROVAL: boolean;
|
|
179
179
|
ENABLE_WAITING_ROOM_AUTO_REQUEST: boolean;
|
|
180
|
+
ENABLE_TEXT_TRACKS: boolean;
|
|
180
181
|
}
|
|
181
182
|
declare var $config: ConfigInterface;
|
|
182
183
|
declare module 'customization' {
|
|
@@ -103,7 +103,7 @@ import {
|
|
|
103
103
|
toolbarItemTranscriptText,
|
|
104
104
|
toolbarItemVirtualBackgroundText,
|
|
105
105
|
toolbarItemWhiteboardText,
|
|
106
|
-
|
|
106
|
+
toolbarItemManageTextTracksText,
|
|
107
107
|
} from '../language/default-labels/videoCallScreenLabels';
|
|
108
108
|
import {LogSource, logger} from '../logger/AppBuilderLogger';
|
|
109
109
|
import {useModal} from '../utils/useModal';
|
|
@@ -116,7 +116,7 @@ import {
|
|
|
116
116
|
InviteToolbarItem,
|
|
117
117
|
ScreenshareToolbarItem,
|
|
118
118
|
} from './controls/toolbar-items';
|
|
119
|
-
import
|
|
119
|
+
import ViewTextTracksModal from './text-tracks/ViewTextTracksModal';
|
|
120
120
|
|
|
121
121
|
export const useToggleWhiteboard = () => {
|
|
122
122
|
const {
|
|
@@ -278,7 +278,9 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
|
|
|
278
278
|
const viewRecordingsLabel = useString<boolean>(
|
|
279
279
|
toolbarItemViewRecordingText,
|
|
280
280
|
)();
|
|
281
|
-
const
|
|
281
|
+
const viewTextTracksLabel = useString<boolean>(
|
|
282
|
+
toolbarItemManageTextTracksText,
|
|
283
|
+
)();
|
|
282
284
|
const moreButtonLabel = useString(toolbarItemMoreText)();
|
|
283
285
|
const virtualBackgroundLabel = useString(toolbarItemVirtualBackgroundText)();
|
|
284
286
|
const chatLabel = useString(toolbarItemChatText)();
|
|
@@ -297,9 +299,9 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
|
|
|
297
299
|
toggle: toggleVRModal,
|
|
298
300
|
} = useModal();
|
|
299
301
|
const {
|
|
300
|
-
modalOpen:
|
|
301
|
-
setModalOpen:
|
|
302
|
-
toggle:
|
|
302
|
+
modalOpen: isTextTrackModalOpen,
|
|
303
|
+
setModalOpen: setTextTrackModalOpen,
|
|
304
|
+
toggle: toggleTextTrackModal,
|
|
303
305
|
} = useModal();
|
|
304
306
|
const moreBtnRef = useRef(null);
|
|
305
307
|
const {width: globalWidth, height: globalHeight} = useWindowDimensions();
|
|
@@ -814,20 +816,20 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
|
|
|
814
816
|
});
|
|
815
817
|
}
|
|
816
818
|
|
|
817
|
-
// 13.
|
|
818
|
-
const
|
|
819
|
-
useControlPermissionMatrix('
|
|
819
|
+
// 13. Text-tracks to download
|
|
820
|
+
const canAccessAllTextTracks =
|
|
821
|
+
useControlPermissionMatrix('viewAllTextTracks');
|
|
820
822
|
|
|
821
|
-
if (
|
|
823
|
+
if (canAccessAllTextTracks) {
|
|
822
824
|
actionMenuitems.push({
|
|
823
|
-
componentName: 'view-all-
|
|
825
|
+
componentName: 'view-all-text-tracks',
|
|
824
826
|
order: 13,
|
|
825
827
|
icon: 'transcript',
|
|
826
828
|
iconColor: $config.SECONDARY_ACTION_COLOR,
|
|
827
829
|
textColor: $config.FONT_COLOR,
|
|
828
|
-
title:
|
|
830
|
+
title: viewTextTracksLabel,
|
|
829
831
|
onPress: () => {
|
|
830
|
-
|
|
832
|
+
toggleTextTrackModal();
|
|
831
833
|
},
|
|
832
834
|
});
|
|
833
835
|
}
|
|
@@ -978,8 +980,8 @@ const MoreButton = (props: {fields: ToolbarMoreButtonDefaultFields}) => {
|
|
|
978
980
|
)}
|
|
979
981
|
</>
|
|
980
982
|
)}
|
|
981
|
-
{
|
|
982
|
-
<
|
|
983
|
+
{canAccessAllTextTracks && isTextTrackModalOpen ? (
|
|
984
|
+
<ViewTextTracksModal setModalOpen={setTextTrackModalOpen} />
|
|
983
985
|
) : (
|
|
984
986
|
<></>
|
|
985
987
|
)}
|
|
@@ -151,7 +151,7 @@ const TableFooter: React.FC<TableFooterProps> = ({
|
|
|
151
151
|
|
|
152
152
|
export {TableHeader, TableFooter, TableBody};
|
|
153
153
|
|
|
154
|
-
const style = StyleSheet.create({
|
|
154
|
+
export const style = StyleSheet.create({
|
|
155
155
|
scrollgrow: {
|
|
156
156
|
flexGrow: 1,
|
|
157
157
|
},
|
|
@@ -249,7 +249,6 @@ const style = StyleSheet.create({
|
|
|
249
249
|
flex: 1,
|
|
250
250
|
alignSelf: 'center',
|
|
251
251
|
justifyContent: 'center',
|
|
252
|
-
// height: 100,
|
|
253
252
|
gap: 10,
|
|
254
253
|
},
|
|
255
254
|
tpreview: {
|
|
@@ -275,6 +274,8 @@ const style = StyleSheet.create({
|
|
|
275
274
|
tactions: {
|
|
276
275
|
display: 'flex',
|
|
277
276
|
flexDirection: 'row',
|
|
277
|
+
alignItems: 'center',
|
|
278
|
+
justifyContent: 'flex-end',
|
|
278
279
|
},
|
|
279
280
|
tlink: {
|
|
280
281
|
color: $config.PRIMARY_ACTION_BRAND_COLOR,
|
|
@@ -382,4 +383,21 @@ const style = StyleSheet.create({
|
|
|
382
383
|
pl15: {
|
|
383
384
|
paddingLeft: 15,
|
|
384
385
|
},
|
|
386
|
+
// icon celles
|
|
387
|
+
tdIconCell: {
|
|
388
|
+
flex: 0,
|
|
389
|
+
flexShrink: 0,
|
|
390
|
+
alignItems: 'flex-start',
|
|
391
|
+
justifyContent: 'center',
|
|
392
|
+
minWidth: 50,
|
|
393
|
+
paddingRight: 50 + 12,
|
|
394
|
+
},
|
|
395
|
+
thIconCell: {
|
|
396
|
+
flex: 0,
|
|
397
|
+
flexShrink: 0,
|
|
398
|
+
alignSelf: 'stretch',
|
|
399
|
+
justifyContent: 'center',
|
|
400
|
+
minWidth: 50,
|
|
401
|
+
paddingHorizontal: 12,
|
|
402
|
+
},
|
|
385
403
|
});
|
|
@@ -15,7 +15,7 @@ export type ControlPermissionKey =
|
|
|
15
15
|
| 'participantControl'
|
|
16
16
|
| 'screenshareControl'
|
|
17
17
|
| 'settingsControl'
|
|
18
|
-
| '
|
|
18
|
+
| 'viewAllTextTracks';
|
|
19
19
|
|
|
20
20
|
/**
|
|
21
21
|
* ControlPermissionRule defines the properties used to evaluate permission rules.
|
|
@@ -36,10 +36,11 @@ export const controlPermissionMatrix: Record<
|
|
|
36
36
|
settingsControl: ({preference}) => !preference.disableSettings,
|
|
37
37
|
screenshareControl: ({preference}) =>
|
|
38
38
|
$config.SCREEN_SHARING && !preference.disableScreenShare,
|
|
39
|
-
|
|
39
|
+
viewAllTextTracks: ({isHost}) =>
|
|
40
40
|
isHost &&
|
|
41
41
|
$config.ENABLE_STT &&
|
|
42
42
|
$config.ENABLE_MEETING_TRANSCRIPT &&
|
|
43
|
+
$config.ENABLE_TEXT_TRACKS &&
|
|
43
44
|
isWeb(),
|
|
44
45
|
};
|
|
45
46
|
|
|
@@ -0,0 +1,285 @@
|
|
|
1
|
+
import React, {useEffect, useState} from 'react';
|
|
2
|
+
import {View, Text, Linking, TouchableOpacity, StyleSheet} from 'react-native';
|
|
3
|
+
import {downloadRecording, getDuration, getRecordedDateTime} from './utils';
|
|
4
|
+
import IconButtonWithToolTip from '../../atoms/IconButton';
|
|
5
|
+
import Tooltip from '../../atoms/Tooltip';
|
|
6
|
+
import Clipboard from '../../subComponents/Clipboard';
|
|
7
|
+
import Spacer from '../../atoms/Spacer';
|
|
8
|
+
import PlatformWrapper from '../../utils/PlatformWrapper';
|
|
9
|
+
import {useFetchSTTTranscript} from '../text-tracks/useFetchSTTTranscript';
|
|
10
|
+
import {style} from '../common/data-table';
|
|
11
|
+
import {FetchRecordingData} from '../../subComponents/recording/useRecording';
|
|
12
|
+
import ImageIcon from '../../atoms/ImageIcon';
|
|
13
|
+
import TextTrackItemRow from './TextTrackItemRow';
|
|
14
|
+
|
|
15
|
+
interface RecordingItemRowProps {
|
|
16
|
+
item: FetchRecordingData['recordings'][0];
|
|
17
|
+
onDeleteAction: (id: string) => void;
|
|
18
|
+
onTextTrackDownload: (textTrackLink: string) => void;
|
|
19
|
+
}
|
|
20
|
+
export default function RecordingItemRow({
|
|
21
|
+
item,
|
|
22
|
+
onDeleteAction,
|
|
23
|
+
onTextTrackDownload,
|
|
24
|
+
}: RecordingItemRowProps) {
|
|
25
|
+
const [expanded, setIsExpanded] = useState(false);
|
|
26
|
+
|
|
27
|
+
const [date, time] = getRecordedDateTime(item.created_at);
|
|
28
|
+
const recordingStatus = item.status;
|
|
29
|
+
|
|
30
|
+
const {sttRecState, getSTTsForRecording} = useFetchSTTTranscript();
|
|
31
|
+
const {
|
|
32
|
+
status,
|
|
33
|
+
error,
|
|
34
|
+
data: {stts = []},
|
|
35
|
+
} = sttRecState;
|
|
36
|
+
console.log('supriya sttRecState', sttRecState);
|
|
37
|
+
useEffect(() => {
|
|
38
|
+
if (expanded) {
|
|
39
|
+
if (item.id) {
|
|
40
|
+
getSTTsForRecording(item.id);
|
|
41
|
+
}
|
|
42
|
+
}
|
|
43
|
+
}, [expanded, item.id, getSTTsForRecording]);
|
|
44
|
+
|
|
45
|
+
if (
|
|
46
|
+
recordingStatus === 'STOPPING' ||
|
|
47
|
+
recordingStatus === 'STARTED' ||
|
|
48
|
+
(recordingStatus === 'INPROGRESS' && !item?.download_url)
|
|
49
|
+
) {
|
|
50
|
+
return (
|
|
51
|
+
<View key={item.id} style={style.pt12}>
|
|
52
|
+
<View style={[style.infotextContainer, style.captionContainer]}>
|
|
53
|
+
<ImageIcon
|
|
54
|
+
iconSize={20}
|
|
55
|
+
iconType="plain"
|
|
56
|
+
name="info"
|
|
57
|
+
tintColor={$config.SEMANTIC_NEUTRAL}
|
|
58
|
+
/>
|
|
59
|
+
<Text style={[style.captionText]}>
|
|
60
|
+
Current recording is ongoing. Once it concludes, we'll generate the
|
|
61
|
+
link
|
|
62
|
+
</Text>
|
|
63
|
+
</View>
|
|
64
|
+
</View>
|
|
65
|
+
);
|
|
66
|
+
}
|
|
67
|
+
|
|
68
|
+
// Collapsible Row
|
|
69
|
+
return (
|
|
70
|
+
<View>
|
|
71
|
+
{/* ========== PARENT ROW ========== */}
|
|
72
|
+
<View style={style.tbrow} key={item.id}>
|
|
73
|
+
<View style={style.tdIconCell}>
|
|
74
|
+
<IconButtonWithToolTip
|
|
75
|
+
hoverEffect={true}
|
|
76
|
+
hoverEffectStyle={style.iconButtonHoverEffect}
|
|
77
|
+
containerStyle={style.iconButton}
|
|
78
|
+
iconProps={{
|
|
79
|
+
name: expanded ? 'arrow-up' : 'arrow-down',
|
|
80
|
+
iconType: 'plain',
|
|
81
|
+
iconSize: 20,
|
|
82
|
+
tintColor: `${$config.FONT_COLOR}`,
|
|
83
|
+
}}
|
|
84
|
+
onPress={() => setIsExpanded(prev => !prev)}
|
|
85
|
+
/>
|
|
86
|
+
</View>
|
|
87
|
+
<View style={[style.td, style.plzero]}>
|
|
88
|
+
<Text style={style.ttime}>
|
|
89
|
+
{date}
|
|
90
|
+
<br />
|
|
91
|
+
<Text style={style.ttime}>{time}</Text>
|
|
92
|
+
</Text>
|
|
93
|
+
</View>
|
|
94
|
+
<View style={[style.td]}>
|
|
95
|
+
<Text style={style.ttime}>
|
|
96
|
+
{getDuration(item.created_at, item.ended_at)}
|
|
97
|
+
</Text>
|
|
98
|
+
</View>
|
|
99
|
+
<View style={style.td}>
|
|
100
|
+
{!item.download_url ? (
|
|
101
|
+
<View style={(style.tactions, {marginTop: 0})}>
|
|
102
|
+
<Text style={style.placeHolder}>{'No recording found'}</Text>
|
|
103
|
+
</View>
|
|
104
|
+
) : item?.download_url?.length > 0 ? (
|
|
105
|
+
<View style={style.tactions}>
|
|
106
|
+
<View>
|
|
107
|
+
{item?.download_url?.map((link: string, i: number) => (
|
|
108
|
+
<View
|
|
109
|
+
style={[
|
|
110
|
+
style.tactions,
|
|
111
|
+
//if recording contains multiple parts then we need to add some space each row
|
|
112
|
+
i >= 1 ? {marginTop: 8} : {},
|
|
113
|
+
]}>
|
|
114
|
+
<View>
|
|
115
|
+
<IconButtonWithToolTip
|
|
116
|
+
hoverEffect={true}
|
|
117
|
+
hoverEffectStyle={style.iconButtonHoverEffect}
|
|
118
|
+
containerStyle={style.iconButton}
|
|
119
|
+
iconProps={{
|
|
120
|
+
name: 'download',
|
|
121
|
+
iconType: 'plain',
|
|
122
|
+
iconSize: 20,
|
|
123
|
+
tintColor: `${$config.SECONDARY_ACTION_COLOR}`,
|
|
124
|
+
}}
|
|
125
|
+
onPress={() => {
|
|
126
|
+
downloadRecording(link);
|
|
127
|
+
}}
|
|
128
|
+
/>
|
|
129
|
+
</View>
|
|
130
|
+
<View style={style.pl10}>
|
|
131
|
+
<IconButtonWithToolTip
|
|
132
|
+
hoverEffect={true}
|
|
133
|
+
hoverEffectStyle={style.iconButtonHoverEffect}
|
|
134
|
+
containerStyle={style.iconButton}
|
|
135
|
+
iconProps={{
|
|
136
|
+
name: 'link-share',
|
|
137
|
+
iconType: 'plain',
|
|
138
|
+
iconSize: 20,
|
|
139
|
+
tintColor: `${$config.SECONDARY_ACTION_COLOR}`,
|
|
140
|
+
}}
|
|
141
|
+
onPress={async () => {
|
|
142
|
+
if (await Linking.canOpenURL(link)) {
|
|
143
|
+
await Linking.openURL(link);
|
|
144
|
+
}
|
|
145
|
+
}}
|
|
146
|
+
/>
|
|
147
|
+
</View>
|
|
148
|
+
<View style={[style.pl10]}>
|
|
149
|
+
<Tooltip
|
|
150
|
+
isClickable
|
|
151
|
+
placement="left"
|
|
152
|
+
toolTipMessage="Link Copied"
|
|
153
|
+
onPress={() => {
|
|
154
|
+
Clipboard.setString(link);
|
|
155
|
+
}}
|
|
156
|
+
toolTipIcon={
|
|
157
|
+
<>
|
|
158
|
+
<ImageIcon
|
|
159
|
+
iconType="plain"
|
|
160
|
+
name="tick-fill"
|
|
161
|
+
tintColor={$config.SEMANTIC_SUCCESS}
|
|
162
|
+
iconSize={20}
|
|
163
|
+
/>
|
|
164
|
+
<Spacer size={8} horizontal={true} />
|
|
165
|
+
</>
|
|
166
|
+
}
|
|
167
|
+
fontSize={12}
|
|
168
|
+
renderContent={() => {
|
|
169
|
+
return (
|
|
170
|
+
<PlatformWrapper>
|
|
171
|
+
{(isHovered: boolean) => (
|
|
172
|
+
<TouchableOpacity
|
|
173
|
+
style={[
|
|
174
|
+
isHovered
|
|
175
|
+
? style.iconButtonHoverEffect
|
|
176
|
+
: {},
|
|
177
|
+
style.iconShareLink,
|
|
178
|
+
]}
|
|
179
|
+
onPress={() => {
|
|
180
|
+
Clipboard.setString(link);
|
|
181
|
+
}}>
|
|
182
|
+
<ImageIcon
|
|
183
|
+
iconType="plain"
|
|
184
|
+
name="copy-link"
|
|
185
|
+
iconSize={20}
|
|
186
|
+
tintColor={$config.SECONDARY_ACTION_COLOR}
|
|
187
|
+
/>
|
|
188
|
+
</TouchableOpacity>
|
|
189
|
+
)}
|
|
190
|
+
</PlatformWrapper>
|
|
191
|
+
);
|
|
192
|
+
}}
|
|
193
|
+
/>
|
|
194
|
+
</View>
|
|
195
|
+
</View>
|
|
196
|
+
))}
|
|
197
|
+
</View>
|
|
198
|
+
<View style={[style.pl10]}>
|
|
199
|
+
<IconButtonWithToolTip
|
|
200
|
+
hoverEffect={true}
|
|
201
|
+
hoverEffectStyle={style.iconButtonHoverEffect}
|
|
202
|
+
containerStyle={style.iconButton}
|
|
203
|
+
iconProps={{
|
|
204
|
+
name: 'delete',
|
|
205
|
+
iconType: 'plain',
|
|
206
|
+
iconSize: 20,
|
|
207
|
+
tintColor: `${$config.SEMANTIC_ERROR}`,
|
|
208
|
+
}}
|
|
209
|
+
onPress={() => {
|
|
210
|
+
onDeleteAction && onDeleteAction(item.id);
|
|
211
|
+
}}
|
|
212
|
+
/>
|
|
213
|
+
</View>
|
|
214
|
+
</View>
|
|
215
|
+
) : (
|
|
216
|
+
<View style={(style.tactions, {marginTop: 0})}>
|
|
217
|
+
<Text style={style.placeHolder}>No recordings found</Text>
|
|
218
|
+
</View>
|
|
219
|
+
)}
|
|
220
|
+
</View>
|
|
221
|
+
</View>
|
|
222
|
+
{/* ========== CHILDREN ROW ========== */}
|
|
223
|
+
{expanded && (
|
|
224
|
+
<View style={expanedStyles.expandedContainer}>
|
|
225
|
+
<View>
|
|
226
|
+
<Text style={expanedStyles.expandedHeaderText}>STT's</Text>
|
|
227
|
+
</View>
|
|
228
|
+
<View style={expanedStyles.expandedHeaderBody}>
|
|
229
|
+
{status === 'idle' || status === 'pending' ? (
|
|
230
|
+
<Text style={style.ttime}>Fetching text-tracks....</Text>
|
|
231
|
+
) : status === 'rejected' ? (
|
|
232
|
+
<Text style={style.ttime}>
|
|
233
|
+
{error?.message ||
|
|
234
|
+
'There was an error while fetching the text-tracks'}
|
|
235
|
+
</Text>
|
|
236
|
+
) : status === 'resolved' && stts?.length === 0 ? (
|
|
237
|
+
<Text style={style.ttime}>
|
|
238
|
+
There are no STT's for this recording
|
|
239
|
+
</Text>
|
|
240
|
+
) : (
|
|
241
|
+
<>
|
|
242
|
+
<Text style={style.ttime}>Found {stts.length} text tracks</Text>
|
|
243
|
+
<View>
|
|
244
|
+
{stts.map(item => (
|
|
245
|
+
<TextTrackItemRow
|
|
246
|
+
key={item.id}
|
|
247
|
+
item={item}
|
|
248
|
+
onTextTrackDownload={onTextTrackDownload}
|
|
249
|
+
/>
|
|
250
|
+
))}
|
|
251
|
+
</View>
|
|
252
|
+
</>
|
|
253
|
+
)}
|
|
254
|
+
</View>
|
|
255
|
+
</View>
|
|
256
|
+
)}
|
|
257
|
+
</View>
|
|
258
|
+
);
|
|
259
|
+
}
|
|
260
|
+
|
|
261
|
+
const expanedStyles = StyleSheet.create({
|
|
262
|
+
expandedContainer: {
|
|
263
|
+
display: 'flex',
|
|
264
|
+
flexDirection: 'column',
|
|
265
|
+
gap: 5,
|
|
266
|
+
color: $config.FONT_COLOR,
|
|
267
|
+
borderColor: $config.CARD_LAYER_3_COLOR,
|
|
268
|
+
backgroundColor: $config.CARD_LAYER_2_COLOR,
|
|
269
|
+
paddingHorizontal: 12,
|
|
270
|
+
paddingVertical: 15,
|
|
271
|
+
borderRadius: 5,
|
|
272
|
+
},
|
|
273
|
+
expandedHeaderText: {
|
|
274
|
+
fontSize: 15,
|
|
275
|
+
lineHeight: 32,
|
|
276
|
+
fontWeight: '500',
|
|
277
|
+
color: $config.FONT_COLOR,
|
|
278
|
+
},
|
|
279
|
+
expandedHeaderBody: {
|
|
280
|
+
display: 'flex',
|
|
281
|
+
flexDirection: 'row',
|
|
282
|
+
justifyContent: 'space-between',
|
|
283
|
+
alignItems: 'flex-start',
|
|
284
|
+
},
|
|
285
|
+
});
|
|
@@ -1,30 +1,65 @@
|
|
|
1
1
|
import React, {useState, useEffect} from 'react';
|
|
2
2
|
import {View, Text} from 'react-native';
|
|
3
|
-
import {
|
|
4
|
-
|
|
5
|
-
|
|
3
|
+
import {
|
|
4
|
+
APIStatus,
|
|
5
|
+
FetchRecordingData,
|
|
6
|
+
useRecording,
|
|
7
|
+
} from '../../subComponents/recording/useRecording';
|
|
6
8
|
import events from '../../rtm-events-api';
|
|
7
9
|
import {EventNames} from '../../rtm-events';
|
|
10
|
+
import {style, TableBody, TableHeader} from '../common/data-table';
|
|
11
|
+
import Loading from '../../subComponents/Loading';
|
|
12
|
+
import ImageIcon from '../../atoms/ImageIcon';
|
|
13
|
+
import RecordingItemRow from './RecordingItemRow';
|
|
14
|
+
import GenericPopup from '../common/GenericPopup';
|
|
15
|
+
import {downloadS3Link} from '../../utils/common';
|
|
16
|
+
|
|
17
|
+
function EmptyRecordingState() {
|
|
18
|
+
return (
|
|
19
|
+
<View style={style.infotextContainer}>
|
|
20
|
+
<View>
|
|
21
|
+
<ImageIcon
|
|
22
|
+
iconType="plain"
|
|
23
|
+
name="info"
|
|
24
|
+
tintColor={'#777777'}
|
|
25
|
+
iconSize={32}
|
|
26
|
+
/>
|
|
27
|
+
</View>
|
|
28
|
+
<View>
|
|
29
|
+
<Text style={[style.infoText, style.pt10, style.pl10]}>
|
|
30
|
+
No recording found for this meeting
|
|
31
|
+
</Text>
|
|
32
|
+
</View>
|
|
33
|
+
</View>
|
|
34
|
+
);
|
|
35
|
+
}
|
|
36
|
+
|
|
37
|
+
const headers = ['', 'Date/Time', 'Duration', 'Actions'];
|
|
38
|
+
const defaultPageNumber = 1;
|
|
8
39
|
|
|
9
40
|
function RecordingsDateTable(props) {
|
|
10
|
-
const [state, setState] = React.useState
|
|
41
|
+
const [state, setState] = React.useState<{
|
|
42
|
+
status: APIStatus;
|
|
43
|
+
data: {
|
|
44
|
+
recordings: FetchRecordingData['recordings'];
|
|
45
|
+
pagination: FetchRecordingData['pagination'];
|
|
46
|
+
};
|
|
47
|
+
error: Error;
|
|
48
|
+
}>({
|
|
11
49
|
status: 'idle',
|
|
12
50
|
data: {
|
|
13
|
-
pagination: {},
|
|
14
51
|
recordings: [],
|
|
52
|
+
pagination: {total: 0, limit: 10, page: defaultPageNumber},
|
|
15
53
|
},
|
|
16
54
|
error: null,
|
|
17
55
|
});
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
data: {pagination, recordings},
|
|
21
|
-
error,
|
|
22
|
-
} = state;
|
|
56
|
+
|
|
57
|
+
const [currentPage, setCurrentPage] = useState(defaultPageNumber);
|
|
23
58
|
|
|
24
59
|
const {fetchRecordings} = useRecording();
|
|
25
60
|
|
|
26
|
-
|
|
27
|
-
const [
|
|
61
|
+
// message for any download‐error popup
|
|
62
|
+
const [errorSnack, setErrorSnack] = React.useState<string | undefined>();
|
|
28
63
|
|
|
29
64
|
const onRecordingDeleteCallback = () => {
|
|
30
65
|
setCurrentPage(defaultPageNumber);
|
|
@@ -38,7 +73,7 @@ function RecordingsDateTable(props) {
|
|
|
38
73
|
};
|
|
39
74
|
}, []);
|
|
40
75
|
|
|
41
|
-
const getRecordings = pageNumber => {
|
|
76
|
+
const getRecordings = (pageNumber: number) => {
|
|
42
77
|
setState(prev => ({...prev, status: 'pending'}));
|
|
43
78
|
fetchRecordings(pageNumber).then(
|
|
44
79
|
response =>
|
|
@@ -47,8 +82,13 @@ function RecordingsDateTable(props) {
|
|
|
47
82
|
status: 'resolved',
|
|
48
83
|
data: {
|
|
49
84
|
recordings: response?.recordings || [],
|
|
50
|
-
pagination: response?.pagination || {
|
|
85
|
+
pagination: response?.pagination || {
|
|
86
|
+
total: 0,
|
|
87
|
+
limit: 10,
|
|
88
|
+
page: defaultPageNumber,
|
|
89
|
+
},
|
|
51
90
|
},
|
|
91
|
+
error: null,
|
|
52
92
|
})),
|
|
53
93
|
error => setState(prev => ({...prev, status: 'rejected', error})),
|
|
54
94
|
);
|
|
@@ -58,26 +98,49 @@ function RecordingsDateTable(props) {
|
|
|
58
98
|
getRecordings(currentPage);
|
|
59
99
|
}, [currentPage]);
|
|
60
100
|
|
|
61
|
-
if (status === 'rejected') {
|
|
101
|
+
if (state.status === 'rejected') {
|
|
62
102
|
return (
|
|
63
103
|
<Text style={[style.ttime, style.pv10, style.ph20]}>
|
|
64
|
-
{error?.message}
|
|
104
|
+
{state.error?.message}
|
|
65
105
|
</Text>
|
|
66
106
|
);
|
|
67
107
|
}
|
|
108
|
+
const onTextTrackDownload = (textTrackLink: string) => {
|
|
109
|
+
downloadS3Link(textTrackLink).catch((err: Error) => {
|
|
110
|
+
setErrorSnack(err.message || 'Download failed');
|
|
111
|
+
});
|
|
112
|
+
};
|
|
113
|
+
|
|
68
114
|
return (
|
|
69
115
|
<View style={style.ttable}>
|
|
70
|
-
<
|
|
71
|
-
<
|
|
72
|
-
status={status}
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
|
|
77
|
-
|
|
78
|
-
|
|
79
|
-
|
|
116
|
+
<TableHeader columns={headers} firstCellStyle={style.thIconCell} />
|
|
117
|
+
<TableBody
|
|
118
|
+
status={state.status}
|
|
119
|
+
items={state.data.recordings}
|
|
120
|
+
loadingComponent={
|
|
121
|
+
<Loading background="transparent" text="Fetching recordingss.." />
|
|
122
|
+
}
|
|
123
|
+
renderRow={item => (
|
|
124
|
+
<RecordingItemRow
|
|
125
|
+
key={item.id}
|
|
126
|
+
item={item}
|
|
127
|
+
onDeleteAction={props?.onDeleteAction}
|
|
128
|
+
onTextTrackDownload={onTextTrackDownload}
|
|
129
|
+
/>
|
|
130
|
+
)}
|
|
131
|
+
emptyComponent={<EmptyRecordingState />}
|
|
80
132
|
/>
|
|
133
|
+
{/** ERROR POPUP **/}
|
|
134
|
+
{errorSnack && (
|
|
135
|
+
<GenericPopup
|
|
136
|
+
title="Error"
|
|
137
|
+
variant="error"
|
|
138
|
+
message={errorSnack}
|
|
139
|
+
visible={true}
|
|
140
|
+
setVisible={() => setErrorSnack(undefined)}
|
|
141
|
+
onConfirm={() => setErrorSnack(undefined)}
|
|
142
|
+
/>
|
|
143
|
+
)}
|
|
81
144
|
</View>
|
|
82
145
|
);
|
|
83
146
|
}
|