Browse Source

feat: minimized bottom menu

j8
Bettenbuk Zoltan 5 years ago
parent
commit
411bafb5a6

+ 38
- 0
package-lock.json View File

14830
         "jssha": "^2.2.0"
14830
         "jssha": "^2.2.0"
14831
       }
14831
       }
14832
     },
14832
     },
14833
+    "react-native-collapsible": {
14834
+      "version": "1.5.1",
14835
+      "resolved": "https://registry.npmjs.org/react-native-collapsible/-/react-native-collapsible-1.5.1.tgz",
14836
+      "integrity": "sha512-uQQ2s6l+7+L/pzJroisWsDsyVYVF5bQ+jGbLATNioRh/03SpEL8pcQEVKqVWswcNNR0B9GENixHaLzmuZIwpQg==",
14837
+      "requires": {
14838
+        "prop-types": "^15.6.2"
14839
+      },
14840
+      "dependencies": {
14841
+        "loose-envify": {
14842
+          "version": "1.4.0",
14843
+          "resolved": "https://registry.npmjs.org/loose-envify/-/loose-envify-1.4.0.tgz",
14844
+          "integrity": "sha512-lyuxPGr/Wfhrlem2CL/UcnUc1zcqKAImBDzukY7Y5F/yQiNdko6+fRLevlw1HgMySw7f611UIY408EtxRSoK3Q==",
14845
+          "requires": {
14846
+            "js-tokens": "^3.0.0 || ^4.0.0"
14847
+          }
14848
+        },
14849
+        "prop-types": {
14850
+          "version": "15.7.2",
14851
+          "resolved": "https://registry.npmjs.org/prop-types/-/prop-types-15.7.2.tgz",
14852
+          "integrity": "sha512-8QQikdH7//R2vurIJSutZ1smHYTcLpRWEOlHnzcWHmBYrOGUysKwSsrC89BCiFj3CbrfJ/nXFdJepOVrY1GCHQ==",
14853
+          "requires": {
14854
+            "loose-envify": "^1.4.0",
14855
+            "object-assign": "^4.1.1",
14856
+            "react-is": "^16.8.1"
14857
+          }
14858
+        },
14859
+        "react-is": {
14860
+          "version": "16.12.0",
14861
+          "resolved": "https://registry.npmjs.org/react-is/-/react-is-16.12.0.tgz",
14862
+          "integrity": "sha512-rPCkf/mWBtKc97aLL9/txD8DZdemK0vkA3JMLShjlJB3Pj3s+lpf1KaBzMfQrAmhMQB0n1cU/SUGgKKBCe837Q=="
14863
+        }
14864
+      }
14865
+    },
14833
     "react-native-immersive": {
14866
     "react-native-immersive": {
14834
       "version": "2.0.0",
14867
       "version": "2.0.0",
14835
       "resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
14868
       "resolved": "https://registry.npmjs.org/react-native-immersive/-/react-native-immersive-2.0.0.tgz",
14871
         }
14904
         }
14872
       }
14905
       }
14873
     },
14906
     },
14907
+    "react-native-swipe-gestures": {
14908
+      "version": "1.0.4",
14909
+      "resolved": "https://registry.npmjs.org/react-native-swipe-gestures/-/react-native-swipe-gestures-1.0.4.tgz",
14910
+      "integrity": "sha512-C/vz0KPHNyqHk3uF4Cz/jzd/0N8z34ZgsjAZUh/RsXPH2FtJJf3Fw73pQDWJSoCMtvVadlztb8xQ+/aEQrll7w=="
14911
+    },
14874
     "react-native-swipeout": {
14912
     "react-native-swipeout": {
14875
       "version": "2.3.6",
14913
       "version": "2.3.6",
14876
       "resolved": "https://registry.npmjs.org/react-native-swipeout/-/react-native-swipeout-2.3.6.tgz",
14914
       "resolved": "https://registry.npmjs.org/react-native-swipeout/-/react-native-swipeout-2.3.6.tgz",

+ 2
- 0
package.json View File

72
     "react-native-background-timer": "2.1.1",
72
     "react-native-background-timer": "2.1.1",
73
     "react-native-calendar-events": "github:jitsi/react-native-calendar-events#902e6e92d6bae450a6052f76ba4d02f977ffd8f2",
73
     "react-native-calendar-events": "github:jitsi/react-native-calendar-events#902e6e92d6bae450a6052f76ba4d02f977ffd8f2",
74
     "react-native-callstats": "3.61.0",
74
     "react-native-callstats": "3.61.0",
75
+    "react-native-collapsible": "1.5.1",
75
     "react-native-immersive": "2.0.0",
76
     "react-native-immersive": "2.0.0",
76
     "react-native-keep-awake": "4.0.0",
77
     "react-native-keep-awake": "4.0.0",
77
     "react-native-linear-gradient": "2.5.6",
78
     "react-native-linear-gradient": "2.5.6",
78
     "react-native-sound": "0.11.0",
79
     "react-native-sound": "0.11.0",
79
     "react-native-svg": "9.7.1",
80
     "react-native-svg": "9.7.1",
80
     "react-native-svg-transformer": "0.13.0",
81
     "react-native-svg-transformer": "0.13.0",
82
+    "react-native-swipe-gestures": "1.0.4",
81
     "react-native-swipeout": "2.3.6",
83
     "react-native-swipeout": "2.3.6",
82
     "react-native-watch-connectivity": "0.2.0",
84
     "react-native-watch-connectivity": "0.2.0",
83
     "react-native-webrtc": "1.75.2",
85
     "react-native-webrtc": "1.75.2",

+ 16
- 11
react/features/base/dialog/components/native/BottomSheet.js View File

29
      * Handler for the cancel event, which happens when the user dismisses
29
      * Handler for the cancel event, which happens when the user dismisses
30
      * the sheet.
30
      * the sheet.
31
      */
31
      */
32
-    onCancel: ?Function
32
+    onCancel: ?Function,
33
+
34
+    /**
35
+     * Function to render a bottom sheet header element, if necessary.
36
+     */
37
+    renderHeader: ?Function
33
 };
38
 };
34
 
39
 
35
 /**
40
 /**
43
      * @returns {ReactElement}
48
      * @returns {ReactElement}
44
      */
49
      */
45
     render() {
50
     render() {
46
-        const { _styles } = this.props;
51
+        const { _styles, renderHeader } = this.props;
47
 
52
 
48
         return (
53
         return (
49
             <SlidingView
54
             <SlidingView
56
                     <View
61
                     <View
57
                         pointerEvents = 'box-none'
62
                         pointerEvents = 'box-none'
58
                         style = { styles.sheetAreaCover } />
63
                         style = { styles.sheetAreaCover } />
59
-                    <View
64
+                    { renderHeader && renderHeader() }
65
+                    <SafeAreaView
60
                         style = { [
66
                         style = { [
61
                             styles.sheetItemContainer,
67
                             styles.sheetItemContainer,
62
                             _styles.sheet
68
                             _styles.sheet
63
                         ] }>
69
                         ] }>
64
-                        <SafeAreaView>
65
-                            <ScrollView
66
-                                bounces = { false }
67
-                                showsVerticalScrollIndicator = { false } >
68
-                                { this.props.children }
69
-                            </ScrollView>
70
-                        </SafeAreaView>
71
-                    </View>
70
+                        <ScrollView
71
+                            bounces = { false }
72
+                            showsVerticalScrollIndicator = { false }
73
+                            style = { styles.scrollView } >
74
+                            { this.props.children }
75
+                        </ScrollView>
76
+                    </SafeAreaView>
72
                 </View>
77
                 </View>
73
             </SlidingView>
78
             </SlidingView>
74
         );
79
         );

+ 43
- 33
react/features/base/dialog/components/native/styles.js View File

33
         flex: 1
33
         flex: 1
34
     },
34
     },
35
 
35
 
36
+    scrollView: {
37
+        paddingHorizontal: MD_ITEM_MARGIN_PADDING
38
+    },
39
+
36
     /**
40
     /**
37
      * Style for the container of the sheet.
41
      * Style for the container of the sheet.
38
      */
42
      */
44
     },
48
     },
45
 
49
 
46
     sheetItemContainer: {
50
     sheetItemContainer: {
47
-        flex: -1,
48
-        maxHeight: '60%',
49
-        paddingHorizontal: MD_ITEM_MARGIN_PADDING
51
+        flex: -1
50
     }
52
     }
51
 };
53
 };
52
 
54
 
135
  * {@link https://material.io/guidelines/components/bottom-sheets.html}.
137
  * {@link https://material.io/guidelines/components/bottom-sheets.html}.
136
  */
138
  */
137
 ColorSchemeRegistry.register('BottomSheet', {
139
 ColorSchemeRegistry.register('BottomSheet', {
138
-    /**
139
-     * Style for the {@code Icon} element in a generic item of the menu.
140
-     */
141
-    iconStyle: {
142
-        color: schemeColor('icon'),
143
-        fontSize: 24
140
+    buttons: {
141
+        /**
142
+         * Style for the {@code Icon} element in a generic item of the menu.
143
+         */
144
+        iconStyle: {
145
+            color: schemeColor('icon'),
146
+            fontSize: 24
147
+        },
148
+
149
+        /**
150
+         * Style for the label in a generic item rendered in the menu.
151
+         */
152
+        labelStyle: {
153
+            color: schemeColor('text'),
154
+            flexShrink: 1,
155
+            fontSize: MD_FONT_SIZE,
156
+            marginLeft: 32,
157
+            opacity: 0.90
158
+        },
159
+
160
+        /**
161
+         * Container style for a generic item rendered in the menu.
162
+         */
163
+        style: {
164
+            alignItems: 'center',
165
+            flexDirection: 'row',
166
+            height: MD_ITEM_HEIGHT
167
+        },
168
+
169
+        /**
170
+         * Additional style that is not directly used as a style object.
171
+         */
172
+        underlayColor: ColorPalette.overflowMenuItemUnderlay
144
     },
173
     },
145
 
174
 
146
-    /**
147
-     * Style for the label in a generic item rendered in the menu.
148
-     */
149
-    labelStyle: {
150
-        color: schemeColor('text'),
151
-        flexShrink: 1,
152
-        fontSize: MD_FONT_SIZE,
153
-        marginLeft: 32,
154
-        opacity: 0.90
175
+    expandIcon: {
176
+        color: schemeColor('icon'),
177
+        fontSize: 16,
178
+        opacity: 0.7
155
     },
179
     },
156
 
180
 
157
     /**
181
     /**
159
      */
183
      */
160
     sheet: {
184
     sheet: {
161
         backgroundColor: schemeColor('background')
185
         backgroundColor: schemeColor('background')
162
-    },
163
-
164
-    /**
165
-     * Container style for a generic item rendered in the menu.
166
-     */
167
-    style: {
168
-        alignItems: 'center',
169
-        flexDirection: 'row',
170
-        height: MD_ITEM_HEIGHT
171
-    },
172
-
173
-    /**
174
-     * Additional style that is not directly used as a style object.
175
-     */
176
-    underlayColor: ColorPalette.overflowMenuItemUnderlay
186
+    }
177
 });
187
 });
178
 
188
 
179
 /**
189
 /**

+ 1
- 0
react/features/base/icons/svg/drag-handle.svg View File

1
+<svg xmlns="http://www.w3.org/2000/svg" xmlns:xlink="http://www.w3.org/1999/xlink" width="24" height="24" viewBox="0 0 24 24"><defs><path id="a" d="M0 0h24v24H0V0z"/></defs><clipPath id="b"><use xlink:href="#a" overflow="visible"/></clipPath><path clip-path="url(#b)" d="M20 9H4v2h16V9zM4 15h16v-2H4v2z"/></svg>

+ 1
- 0
react/features/base/icons/svg/index.js View File

25
 export { default as IconDeviceSpeaker } from './volume.svg';
25
 export { default as IconDeviceSpeaker } from './volume.svg';
26
 export { default as IconDominantSpeaker } from './dominant-speaker.svg';
26
 export { default as IconDominantSpeaker } from './dominant-speaker.svg';
27
 export { default as IconDownload } from './download.svg';
27
 export { default as IconDownload } from './download.svg';
28
+export { default as IconDragHandle } from './drag-handle.svg';
28
 export { default as IconEventNote } from './event_note.svg';
29
 export { default as IconEventNote } from './event_note.svg';
29
 export { default as IconExitFullScreen } from './exit-full-screen.svg';
30
 export { default as IconExitFullScreen } from './exit-full-screen.svg';
30
 export { default as IconFeedback } from './feedback.svg';
31
 export { default as IconFeedback } from './feedback.svg';

+ 4
- 4
react/features/mobile/audio-mode/components/AudioRoutePickerDialog.js View File

270
                 <View style = { styles.deviceRow } >
270
                 <View style = { styles.deviceRow } >
271
                     <Icon
271
                     <Icon
272
                         src = { icon }
272
                         src = { icon }
273
-                        style = { [ styles.deviceIcon, _bottomSheetStyles.iconStyle, selectedStyle ] } />
274
-                    <Text style = { [ styles.deviceText, _bottomSheetStyles.labelStyle, selectedStyle ] } >
273
+                        style = { [ styles.deviceIcon, _bottomSheetStyles.buttons.iconStyle, selectedStyle ] } />
274
+                    <Text style = { [ styles.deviceText, _bottomSheetStyles.buttons.labelStyle, selectedStyle ] } >
275
                         { text }
275
                         { text }
276
                     </Text>
276
                     </Text>
277
                 </View>
277
                 </View>
292
             <View style = { styles.deviceRow } >
292
             <View style = { styles.deviceRow } >
293
                 <Icon
293
                 <Icon
294
                     src = { deviceInfoMap.SPEAKER.icon }
294
                     src = { deviceInfoMap.SPEAKER.icon }
295
-                    style = { [ styles.deviceIcon, _bottomSheetStyles.iconStyle ] } />
296
-                <Text style = { [ styles.deviceText, _bottomSheetStyles.labelStyle ] } >
295
+                    style = { [ styles.deviceIcon, _bottomSheetStyles.buttons.iconStyle ] } />
296
+                <Text style = { [ styles.deviceText, _bottomSheetStyles.buttons.labelStyle ] } >
297
                     { t('audioDevices.none') }
297
                     { t('audioDevices.none') }
298
                 </Text>
298
                 </Text>
299
             </View>
299
             </View>

+ 1
- 1
react/features/remote-video-menu/components/native/RemoteVideoMenu.js View File

80
             afterClick: this._onCancel,
80
             afterClick: this._onCancel,
81
             showLabel: true,
81
             showLabel: true,
82
             participantID: participant.id,
82
             participantID: participant.id,
83
-            styles: this.props._bottomSheetStyles
83
+            styles: this.props._bottomSheetStyles.buttons
84
         };
84
         };
85
 
85
 
86
         return (
86
         return (

+ 120
- 22
react/features/toolbox/components/native/OverflowMenu.js View File

1
 // @flow
1
 // @flow
2
 
2
 
3
-import React, { Component } from 'react';
4
-import { Platform } from 'react-native';
3
+import React, { PureComponent } from 'react';
4
+import { Platform, TouchableOpacity } from 'react-native';
5
+import Collapsible from 'react-native-collapsible';
6
+import GestureRecognizer, { swipeDirections } from 'react-native-swipe-gestures';
5
 
7
 
6
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
8
 import { ColorSchemeRegistry } from '../../../base/color-scheme';
7
 import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog';
9
 import { BottomSheet, hideDialog, isDialogOpen } from '../../../base/dialog';
10
+import { IconDragHandle } from '../../../base/icons';
8
 import { CHAT_ENABLED, IOS_RECORDING_ENABLED, getFeatureFlag } from '../../../base/flags';
11
 import { CHAT_ENABLED, IOS_RECORDING_ENABLED, getFeatureFlag } from '../../../base/flags';
9
 import { connect } from '../../../base/redux';
12
 import { connect } from '../../../base/redux';
10
 import { StyleType } from '../../../base/styles';
13
 import { StyleType } from '../../../base/styles';
16
 import { ClosedCaptionButton } from '../../../subtitles';
19
 import { ClosedCaptionButton } from '../../../subtitles';
17
 import { TileViewButton } from '../../../video-layout';
20
 import { TileViewButton } from '../../../video-layout';
18
 
21
 
19
-import AudioOnlyButton from './AudioOnlyButton';
20
 import HelpButton from '../HelpButton';
22
 import HelpButton from '../HelpButton';
23
+
24
+import AudioOnlyButton from './AudioOnlyButton';
21
 import RaiseHandButton from './RaiseHandButton';
25
 import RaiseHandButton from './RaiseHandButton';
22
 import ToggleCameraButton from './ToggleCameraButton';
26
 import ToggleCameraButton from './ToggleCameraButton';
27
+import styles from './styles';
23
 
28
 
24
 /**
29
 /**
25
  * The type of the React {@code Component} props of {@link OverflowMenu}.
30
  * The type of the React {@code Component} props of {@link OverflowMenu}.
52
     dispatch: Function
57
     dispatch: Function
53
 };
58
 };
54
 
59
 
60
+type State = {
61
+
62
+    /**
63
+     * True if the 'more' button set needas to be rendered.
64
+     */
65
+    showMore: boolean
66
+}
67
+
55
 /**
68
 /**
56
  * The exported React {@code Component}. We need it to execute
69
  * The exported React {@code Component}. We need it to execute
57
  * {@link hideDialog}.
70
  * {@link hideDialog}.
65
  * Implements a React {@code Component} with some extra actions in addition to
78
  * Implements a React {@code Component} with some extra actions in addition to
66
  * those in the toolbar.
79
  * those in the toolbar.
67
  */
80
  */
68
-class OverflowMenu extends Component<Props> {
81
+class OverflowMenu extends PureComponent<Props, State> {
69
     /**
82
     /**
70
      * Initializes a new {@code OverflowMenu} instance.
83
      * Initializes a new {@code OverflowMenu} instance.
71
      *
84
      *
74
     constructor(props: Props) {
87
     constructor(props: Props) {
75
         super(props);
88
         super(props);
76
 
89
 
90
+        this.state = {
91
+            showMore: false
92
+        };
93
+
77
         // Bind event handlers so they are only bound once per instance.
94
         // Bind event handlers so they are only bound once per instance.
78
         this._onCancel = this._onCancel.bind(this);
95
         this._onCancel = this._onCancel.bind(this);
96
+        this._onSwipe = this._onSwipe.bind(this);
97
+        this._onToggleMenu = this._onToggleMenu.bind(this);
98
+        this._renderMenuExpandToggle = this._renderMenuExpandToggle.bind(this);
79
     }
99
     }
80
 
100
 
81
     /**
101
     /**
85
      * @returns {ReactElement}
105
      * @returns {ReactElement}
86
      */
106
      */
87
     render() {
107
     render() {
108
+        const { _bottomSheetStyles } = this.props;
109
+        const { showMore } = this.state;
110
+
88
         const buttonProps = {
111
         const buttonProps = {
89
             afterClick: this._onCancel,
112
             afterClick: this._onCancel,
90
             showLabel: true,
113
             showLabel: true,
91
-            styles: this.props._bottomSheetStyles
114
+            styles: _bottomSheetStyles.buttons
92
         };
115
         };
93
 
116
 
94
         return (
117
         return (
95
-            <BottomSheet onCancel = { this._onCancel }>
118
+            <BottomSheet
119
+                onCancel = { this._onCancel }
120
+                renderHeader = { this._renderMenuExpandToggle }>
96
                 <AudioRouteButton { ...buttonProps } />
121
                 <AudioRouteButton { ...buttonProps } />
97
                 <ToggleCameraButton { ...buttonProps } />
122
                 <ToggleCameraButton { ...buttonProps } />
98
                 <AudioOnlyButton { ...buttonProps } />
123
                 <AudioOnlyButton { ...buttonProps } />
99
-                <RoomLockButton { ...buttonProps } />
100
-                <ClosedCaptionButton { ...buttonProps } />
101
-                {
102
-                    this.props._recordingEnabled
103
-                        && <RecordButton { ...buttonProps } />
104
-                }
105
-                <LiveStreamButton { ...buttonProps } />
106
-                <TileViewButton { ...buttonProps } />
107
-                <InviteButton { ...buttonProps } />
108
-                {
109
-                    this.props._chatEnabled
110
-                        && <InfoDialogButton { ...buttonProps } />
111
-                }
112
-                <RaiseHandButton { ...buttonProps } />
113
-                <SharedDocumentButton { ...buttonProps } />
114
-                <HelpButton { ...buttonProps } />
124
+                <Collapsible collapsed = { !showMore }>
125
+                    <RoomLockButton { ...buttonProps } />
126
+                    <ClosedCaptionButton { ...buttonProps } />
127
+                    {
128
+                        this.props._recordingEnabled
129
+                            && <RecordButton { ...buttonProps } />
130
+                    }
131
+                    <LiveStreamButton { ...buttonProps } />
132
+                    <TileViewButton { ...buttonProps } />
133
+                    <InviteButton { ...buttonProps } />
134
+                    {
135
+                        this.props._chatEnabled
136
+                            && <InfoDialogButton { ...buttonProps } />
137
+                    }
138
+                    <RaiseHandButton { ...buttonProps } />
139
+                    <SharedDocumentButton { ...buttonProps } />
140
+                    <HelpButton { ...buttonProps } />
141
+                </Collapsible>
115
             </BottomSheet>
142
             </BottomSheet>
116
         );
143
         );
117
     }
144
     }
118
 
145
 
146
+    _renderMenuExpandToggle: () => React$Element<any>;
147
+
148
+    /**
149
+     * Function to render the menu toggle in the bottom sheet header area.
150
+     *
151
+     * @returns {React$Element}
152
+     */
153
+    _renderMenuExpandToggle() {
154
+        return (
155
+            <GestureRecognizer
156
+                config = {{
157
+                    velocityThreshold: 0.1,
158
+                    directionalOffsetThreshold: 30
159
+                }}
160
+                onSwipe = { this._onSwipe }
161
+                style = { [
162
+                    this.props._bottomSheetStyles.sheet,
163
+                    styles.expandMenuContainer
164
+                ] }>
165
+                <TouchableOpacity onPress = { this._onToggleMenu }>
166
+                    { /* $FlowFixMeProps */ }
167
+                    <IconDragHandle style = { this.props._bottomSheetStyles.expandIcon } />
168
+                </TouchableOpacity>
169
+            </GestureRecognizer>
170
+        );
171
+    }
172
+
119
     _onCancel: () => boolean;
173
     _onCancel: () => boolean;
120
 
174
 
121
     /**
175
     /**
133
 
187
 
134
         return false;
188
         return false;
135
     }
189
     }
190
+
191
+    _onSwipe: (string) => void;
192
+
193
+    /**
194
+     * Callback to be invoked when a swipe gesture is detected on the menu.
195
+     *
196
+     * @param {string} gestureName - The name of the swipe gesture.
197
+     * @returns {void}
198
+     */
199
+    _onSwipe(gestureName) {
200
+        const { showMore } = this.state;
201
+
202
+        switch (gestureName) {
203
+        case swipeDirections.SWIPE_UP:
204
+            !showMore && this.setState({
205
+                showMore: true
206
+            });
207
+            break;
208
+        case swipeDirections.SWIPE_DOWN:
209
+            if (showMore) {
210
+                // If the menu is expanded, we collapse it.
211
+                this.setState({
212
+                    showMore: false
213
+                });
214
+            } else {
215
+                // If the menu is not expanded, we close the menu
216
+                this._onCancel();
217
+            }
218
+            break;
219
+        }
220
+    }
221
+
222
+    _onToggleMenu: () => void;
223
+
224
+    /**
225
+     * Callback to be invoked when the expand menu button is pressed.
226
+     *
227
+     * @returns {void}
228
+     */
229
+    _onToggleMenu() {
230
+        this.setState({
231
+            showMore: !this.state.showMore
232
+        });
233
+    }
136
 }
234
 }
137
 
235
 
138
 /**
236
 /**

+ 5
- 0
react/features/toolbox/components/native/styles.js View File

55
  */
55
  */
56
 const styles = {
56
 const styles = {
57
 
57
 
58
+    expandMenuContainer: {
59
+        alignItems: 'center',
60
+        flexDirection: 'column'
61
+    },
62
+
58
     /**
63
     /**
59
      * The style of the toolbar.
64
      * The style of the toolbar.
60
      */
65
      */

Loading…
Cancel
Save