Explorar el Código

add(highlights): mobile flow (#11168)

* add(highlight): mobile initial flow

* fix(hightlight): get meeting fqn on mobile

* fix(dynamic-branding): extract fqn on mobile

* fix(highlights): remove local fqn extraction and grounp dispatches in batch

* fix(dynamic-branding): check if state is defined in extract fqn
master
Gabriel Borlea hace 3 años
padre
commit
85c505a29d
No account linked to committer's email address

+ 1
- 0
lang/main.json Ver fichero

@@ -865,6 +865,7 @@
865 865
         "expandedPending": "Recording is being started...",
866 866
         "failedToStart": "Recording failed to start",
867 867
         "fileSharingdescription": "Share the recording link with the meeting participants",
868
+        "highlight": "Highlight",
868 869
         "highlightMoment": "Highlight moment",
869 870
         "highlightMomentDisabled": "You can highlight moments when the recording starts",
870 871
         "highlightMomentSuccess": "Moment highlighted",

+ 7
- 0
react/features/base/ui/Tokens.js Ver fichero

@@ -377,6 +377,13 @@ export const typography = {
377 377
         letterSpacing: 0
378 378
     },
379 379
 
380
+    bodyLongRegularLarge: {
381
+        fontSize: 16,
382
+        lineHeight: 26,
383
+        fontWeight: font.weightRegular,
384
+        letterSpacing: 0
385
+    },
386
+
380 387
     bodyLongBold: {
381 388
         fontSize: 14,
382 389
         lineHeight: 24,

+ 31
- 18
react/features/conference/components/native/AlwaysOnLabels.js Ver fichero

@@ -1,10 +1,13 @@
1 1
 // @flow
2 2
 
3
-import React from 'react';
3
+import React, { useCallback } from 'react';
4 4
 import { TouchableOpacity } from 'react-native';
5
+import { useDispatch } from 'react-redux';
5 6
 
6 7
 import { JitsiRecordingConstants } from '../../../base/lib-jitsi-meet';
7 8
 import { RecordingLabel } from '../../../recording';
9
+import { openHighlightDialog } from '../../../recording/actions.native';
10
+import HighlightButton from '../../../recording/components/Recording/native/HighlightButton';
8 11
 
9 12
 import RaisedHandsCountLabel from './RaisedHandsCountLabel';
10 13
 import {
@@ -23,22 +26,32 @@ type Props = {
23 26
     createOnPress: Function
24 27
 }
25 28
 
26
-const AlwaysOnLabels = ({ createOnPress }: Props) => (<>
27
-    <TouchableOpacity
28
-        hitSlop = { LabelHitSlop }
29
-        onPress = { createOnPress(LABEL_ID_RECORDING) } >
30
-        <RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
31
-    </TouchableOpacity>
32
-    <TouchableOpacity
33
-        hitSlop = { LabelHitSlop }
34
-        onPress = { createOnPress(LABEL_ID_STREAMING) } >
35
-        <RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
36
-    </TouchableOpacity>
37
-    <TouchableOpacity
38
-        hitSlop = { LabelHitSlop }
39
-        onPress = { createOnPress(LABEL_ID_RAISED_HANDS_COUNT) } >
40
-        <RaisedHandsCountLabel />
41
-    </TouchableOpacity>
42
-</>);
29
+const AlwaysOnLabels = ({ createOnPress }: Props) => {
30
+    const dispatch = useDispatch();
31
+    const openHighlightDialogCallback = useCallback(() => dispatch(openHighlightDialog()), [ dispatch ]);
32
+
33
+    return (<>
34
+        <TouchableOpacity
35
+            hitSlop = { LabelHitSlop }
36
+            onPress = { createOnPress(LABEL_ID_RECORDING) } >
37
+            <RecordingLabel mode = { JitsiRecordingConstants.mode.FILE } />
38
+        </TouchableOpacity>
39
+        <TouchableOpacity
40
+            hitSlop = { LabelHitSlop }
41
+            onPress = { createOnPress(LABEL_ID_STREAMING) } >
42
+            <RecordingLabel mode = { JitsiRecordingConstants.mode.STREAM } />
43
+        </TouchableOpacity>
44
+        <TouchableOpacity
45
+            hitSlop = { LabelHitSlop }
46
+            onPress = { openHighlightDialogCallback }>
47
+            <HighlightButton />
48
+        </TouchableOpacity>
49
+        <TouchableOpacity
50
+            hitSlop = { LabelHitSlop }
51
+            onPress = { createOnPress(LABEL_ID_RAISED_HANDS_COUNT) } >
52
+            <RaisedHandsCountLabel />
53
+        </TouchableOpacity>
54
+    </>);
55
+};
43 56
 
44 57
 export default AlwaysOnLabels;

+ 13
- 3
react/features/dynamic-branding/functions.any.js Ver fichero

@@ -6,11 +6,21 @@ import { loadConfig } from '../base/lib-jitsi-meet';
6 6
  * Extracts the fqn part from a path, where fqn represents
7 7
  * tenant/roomName.
8 8
  *
9
- * @param {string} path - The URL path.
9
+ * @param {Object} state - A redux state.
10 10
  * @returns {string}
11 11
  */
12
-export function extractFqnFromPath() {
13
-    const parts = window.location.pathname.split('/');
12
+export function extractFqnFromPath(state?: Object) {
13
+    let pathname;
14
+
15
+    if (window.location.pathname) {
16
+        pathname = window.location.pathname;
17
+    } else if (state && state['features/base/connection']) {
18
+        pathname = state['features/base/connection'].locationURL.pathname;
19
+    } else {
20
+        return '';
21
+    }
22
+
23
+    const parts = pathname.split('/');
14 24
     const len = parts.length;
15 25
 
16 26
     return parts.length > 2 ? `${parts[len - 2]}/${parts[len - 1]}` : parts[1];

+ 14
- 0
react/features/recording/actions.native.js Ver fichero

@@ -1,10 +1,24 @@
1 1
 // @flow
2 2
 
3
+import { openDialog } from '../base/dialog';
3 4
 import JitsiMeetJS from '../base/lib-jitsi-meet';
4 5
 import { NOTIFICATION_TIMEOUT_TYPE, showNotification } from '../notifications';
5 6
 
7
+import HighlightDialog from './components/Recording/native/HighlightDialog';
8
+
6 9
 export * from './actions.any';
7 10
 
11
+/**
12
+ * Opens the highlight dialog.
13
+ *
14
+ * @returns {Function}
15
+ */
16
+export function openHighlightDialog() {
17
+    return (dispatch: Function) => {
18
+        dispatch(openDialog(HighlightDialog));
19
+    };
20
+}
21
+
8 22
 /**
9 23
  * Signals that a started recording notification should be shown on the
10 24
  * screen for a given period.

+ 63
- 0
react/features/recording/components/Recording/native/HighlightButton.js Ver fichero

@@ -0,0 +1,63 @@
1
+// @flow
2
+
3
+import React from 'react';
4
+import type { Dispatch } from 'redux';
5
+
6
+import { translate } from '../../../../base/i18n';
7
+import { IconHighlight } from '../../../../base/icons';
8
+import { Label } from '../../../../base/label';
9
+import { connect } from '../../../../base/redux';
10
+import BaseTheme from '../../../../base/ui/components/BaseTheme';
11
+import AbstractHighlightButton, {
12
+    _abstractMapStateToProps,
13
+    type Props as AbstractProps
14
+} from '../AbstractHighlightButton';
15
+import styles from '../styles.native';
16
+
17
+type Props = AbstractProps & {
18
+    _disabled: boolean,
19
+
20
+    /**
21
+     * Flag controlling visibility of the component.
22
+     */
23
+    _visible: boolean,
24
+
25
+    dispatch: Dispatch<any>
26
+};
27
+
28
+/**
29
+ * React {@code Component} responsible for displaying an action that
30
+ * allows users to highlight a meeting moment.
31
+ */
32
+export class HighlightButton extends AbstractHighlightButton<Props> {
33
+
34
+    /**
35
+     * Implements React's {@link Component#render()}.
36
+     *
37
+     * @inheritdoc
38
+     * @returns {ReactElement}
39
+     */
40
+    render() {
41
+        const {
42
+            _disabled,
43
+            _visible,
44
+            t
45
+        } = this.props;
46
+
47
+        if (!_visible || _disabled) {
48
+            return null;
49
+        }
50
+
51
+        return (
52
+            <Label
53
+                icon = { IconHighlight }
54
+                iconColor = { BaseTheme.palette.field01 }
55
+                id = 'highlightMeetingLabel'
56
+                style = { styles.highlightButton }
57
+                text = { t('recording.highlight') }
58
+                textStyle = { styles.highlightButtonText } />
59
+        );
60
+    }
61
+}
62
+
63
+export default translate(connect(_abstractMapStateToProps)(HighlightButton));

+ 52
- 0
react/features/recording/components/Recording/native/HighlightDialog.js Ver fichero

@@ -0,0 +1,52 @@
1
+// @flow
2
+import React, { useCallback } from 'react';
3
+import { useTranslation } from 'react-i18next';
4
+import { Text, View } from 'react-native';
5
+import { Button } from 'react-native-paper';
6
+import { useDispatch, batch } from 'react-redux';
7
+
8
+import { hideDialog, BottomSheet } from '../../../../base/dialog';
9
+import { highlightMeetingMoment } from '../../../actions.any';
10
+import styles from '../styles.native';
11
+
12
+const HighlightDialog = () => {
13
+    const dispatch = useDispatch();
14
+    const { t } = useTranslation();
15
+    const closeDialog = useCallback(() => dispatch(hideDialog()), [ dispatch ]);
16
+    const highlightMoment = useCallback(() => {
17
+        batch(() => {
18
+            dispatch(highlightMeetingMoment());
19
+            dispatch(hideDialog());
20
+        });
21
+    }, [ dispatch ]);
22
+
23
+    return (
24
+        <BottomSheet onCancel = { closeDialog }>
25
+            <View style = { styles.highlightDialog }>
26
+                <Text style = { styles.highlightDialogHeading }>{ `${t('recording.highlightMoment')}?` }</Text>
27
+                <Text style = { styles.highlightDialogText }>
28
+                    { t('recording.highlightMomentSucessDescription') }
29
+                </Text>
30
+                <View style = { styles.highlightDialogButtonsContainer } >
31
+                    <Button
32
+                        accessibilityLabel = { t('dialog.Cancel') }
33
+                        children = { t('dialog.Cancel') }
34
+                        labelStyle = { styles.highlightDialogCancelLabel }
35
+                        mode = 'contained'
36
+                        onPress = { closeDialog }
37
+                        style = { styles.highlightDialogCancelButton } />
38
+                    <View style = { styles.highlightDialogButtonsSpace } />
39
+                    <Button
40
+                        accessibilityLabel = { t('recording.highlight') }
41
+                        children = { t('recording.highlight') }
42
+                        labelStyle = { styles.highlightDialogHighlighLabel }
43
+                        mode = 'contained'
44
+                        onPress = { highlightMoment }
45
+                        style = { styles.highlightDialogHighlightButton } />
46
+                </View>
47
+            </View>
48
+        </BottomSheet>
49
+    );
50
+};
51
+
52
+export default HighlightDialog;

+ 1
- 0
react/features/recording/components/Recording/native/index.js Ver fichero

@@ -1,5 +1,6 @@
1 1
 // @flow
2 2
 
3
+export { default as HighlightButton } from './HighlightButton';
3 4
 export { default as RecordButton } from './RecordButton';
4 5
 export { default as StartRecordingDialog } from './StartRecordingDialog';
5 6
 export { default as StopRecordingDialog } from './StopRecordingDialog';

+ 65
- 0
react/features/recording/components/Recording/styles.native.js Ver fichero

@@ -39,6 +39,17 @@ const title = {
39 39
     paddingLeft: BoxModel.padding
40 40
 };
41 41
 
42
+const baseHighlightDialogButton = {
43
+    borderRadius: BaseTheme.shape.borderRadius,
44
+    height: BaseTheme.spacing[7],
45
+    flex: 1
46
+};
47
+
48
+const baseHighlightDialogLabel = {
49
+    ...BaseTheme.typography.bodyShortBoldLarge,
50
+    textTransform: 'none'
51
+};
52
+
42 53
 export default {
43 54
     /**
44 55
      * Container for the StartRecordingDialog screen.
@@ -58,7 +69,61 @@ export default {
58 69
     startRecordingLabel: {
59 70
         color: BaseTheme.palette.text01,
60 71
         marginRight: 12
72
+    },
73
+    highlightButton: {
74
+        backgroundColor: BaseTheme.palette.section01,
75
+        flexDirection: 'row',
76
+        alignItems: 'center',
77
+        marginLeft: BaseTheme.spacing[0],
78
+        marginBottom: BaseTheme.spacing[0],
79
+        marginRight: BaseTheme.spacing[1]
80
+    },
81
+    highlightButtonText: {
82
+        color: BaseTheme.palette.field01,
83
+        paddingLeft: BaseTheme.spacing[2],
84
+        ...BaseTheme.typography.labelBold
85
+    },
86
+    highlightDialog: {
87
+        paddingLeft: BaseTheme.spacing[3],
88
+        paddingRight: BaseTheme.spacing[3],
89
+        paddingTop: BaseTheme.spacing[4],
90
+        paddingBottom: BaseTheme.spacing[7]
91
+    },
92
+    highlightDialogHeading: {
93
+        ...BaseTheme.typography.heading5,
94
+        color: BaseTheme.palette.text01,
95
+        marginBottom: BaseTheme.spacing[3]
96
+    },
97
+    highlightDialogText: {
98
+        ...BaseTheme.typography.bodyLongRegularLarge,
99
+        color: BaseTheme.palette.text01,
100
+        marginBottom: BaseTheme.spacing[5]
101
+    },
102
+    highlightDialogButtonsContainer: {
103
+        display: 'flex',
104
+        flexDirection: 'row'
105
+    },
106
+    highlightDialogCancelButton: {
107
+        ...baseHighlightDialogButton,
108
+        backgroundColor: BaseTheme.palette.section01
109
+    },
110
+    highlightDialogHighlightButton: {
111
+        ...baseHighlightDialogButton,
112
+        backgroundColor: BaseTheme.palette.action01
113
+    },
114
+    highlightDialogCancelLabel: {
115
+        ...baseHighlightDialogLabel,
116
+        color: BaseTheme.palette.field01
117
+    },
118
+    highlightDialogHighlighLabel: {
119
+        ...baseHighlightDialogLabel,
120
+        color: BaseTheme.palette.text01
121
+    },
122
+    highlightDialogButtonsSpace: {
123
+        width: 16,
124
+        height: '100%'
61 125
     }
126
+
62 127
 };
63 128
 
64 129
 /**

+ 1
- 1
react/features/recording/functions.js Ver fichero

@@ -154,7 +154,7 @@ export async function sendMeetingHighlight(state: Object) {
154 154
     };
155 155
 
156 156
     const reqBody = {
157
-        meetingFqn: extractFqnFromPath(),
157
+        meetingFqn: extractFqnFromPath(state),
158 158
         sessionId: conference.sessionId,
159 159
         submitted: Date.now(),
160 160
         participantId: localParticipant.jwtId,

Loading…
Cancelar
Guardar