瀏覽代碼

feat(info): automatically show the info dialog (#2018)

* ref(info): be able to open dialog through store

* feat(info): automatically show the info dialog

Conditions:
- Lonely call
- Has not opened the info dialog yet

* squash: change to show on start, hide later

* squash: update naming and comment
j8
virtuacoplenny 8 年之前
父節點
當前提交
cfe4564ab3

+ 1
- 0
css/modals/invite/_info.scss 查看文件

@@ -1,4 +1,5 @@
1 1
 .info-dialog {
2
+    cursor: default;
2 3
     display: flex;
3 4
 
4 5
     .info-dialog-action-link {

+ 11
- 0
react/features/invite/actionTypes.js 查看文件

@@ -1,3 +1,14 @@
1
+/**
2
+ * The type of the action which signals a request to display the inline
3
+ * conference info dialog.
4
+ *
5
+ * {
6
+ *     type: SET_INFO_DIALOG_VISIBILITY,
7
+ *     visible: boolean
8
+ * }
9
+ */
10
+export const SET_INFO_DIALOG_VISIBILITY = Symbol('SET_INFO_DIALOG_VISIBILITY');
11
+
1 12
 /**
2 13
  * The type of the action which signals an error occurred while requesting dial-
3 14
  * in numbers.

+ 17
- 1
react/features/invite/actions.js 查看文件

@@ -1,13 +1,13 @@
1 1
 import { openDialog } from '../../features/base/dialog';
2 2
 
3 3
 import {
4
+    SET_INFO_DIALOG_VISIBILITY,
4 5
     UPDATE_DIAL_IN_NUMBERS_FAILED,
5 6
     UPDATE_DIAL_IN_NUMBERS_SUCCESS
6 7
 } from './actionTypes';
7 8
 import { AddPeopleDialog, InviteDialog } from './components';
8 9
 
9 10
 declare var $: Function;
10
-declare var APP: Object;
11 11
 
12 12
 /**
13 13
  * Opens the Invite Dialog.
@@ -27,6 +27,22 @@ export function openAddPeopleDialog() {
27 27
     return openDialog(AddPeopleDialog);
28 28
 }
29 29
 
30
+/**
31
+ * Opens the inline conference info dialog.
32
+ *
33
+ * @param {boolean} visible - Whether or not the dialog should be displayed.
34
+ * @returns {{
35
+ *     type: SET_INFO_DIALOG_VISIBILITY,
36
+ *     visible: boolean
37
+ * }}
38
+ */
39
+export function setInfoDialogVisibility(visible) {
40
+    return {
41
+        type: SET_INFO_DIALOG_VISIBILITY,
42
+        visible
43
+    };
44
+}
45
+
30 46
 /**
31 47
  * Sends AJAX requests for dial-in numbers and conference ID.
32 48
  *

+ 8
- 1
react/features/invite/components/InfoDialog.web.js 查看文件

@@ -45,6 +45,11 @@ class InfoDialog extends Component {
45 45
          */
46 46
         onClose: PropTypes.func,
47 47
 
48
+        /**
49
+         * Callback invoked when a mouse-related event has been detected.
50
+         */
51
+        onMouseOver: PropTypes.func,
52
+
48 53
         /**
49 54
          * Invoked to obtain translated strings.
50 55
          */
@@ -84,7 +89,9 @@ class InfoDialog extends Component {
84 89
      */
85 90
     render() {
86 91
         return (
87
-            <div className = 'info-dialog'>
92
+            <div
93
+                className = 'info-dialog'
94
+                onMouseOver = { this.props.onMouseOver } >
88 95
                 <div className = 'info-dialog-column'>
89 96
                     <h4 className = 'info-dialog-icon'>
90 97
                         <i className = 'icon-info' />

+ 195
- 6
react/features/invite/components/InfoDialogButton.web.js 查看文件

@@ -1,9 +1,18 @@
1
+import InlineDialog from '@atlaskit/inline-dialog';
2
+import PropTypes from 'prop-types';
1 3
 import React, { Component } from 'react';
4
+import { connect } from 'react-redux';
2 5
 
3
-import { ToolbarButtonWithDialog } from '../../toolbox';
6
+import { ToolbarButton, TOOLTIP_TO_POPUP_POSITION } from '../../toolbox';
7
+
8
+import { setInfoDialogVisibility } from '../actions';
4 9
 
5 10
 import InfoDialog from './InfoDialog';
6 11
 
12
+declare var interfaceConfig: Object;
13
+
14
+const { INITIAL_TOOLBAR_TIMEOUT } = interfaceConfig;
15
+
7 16
 /**
8 17
  * A configuration object to describe how {@code ToolbarButton} should render
9 18
  * the button.
@@ -25,6 +34,103 @@ const DEFAULT_BUTTON_CONFIGURATION = {
25 34
  * @extends Component
26 35
  */
27 36
 class InfoDialogButton extends Component {
37
+    /**
38
+     * {@code InfoDialogButton} component's property types.
39
+     *
40
+     * @static
41
+     */
42
+    static propTypes = {
43
+        /**
44
+         * Whether or not {@code InfoDialog} should be displayed.
45
+         */
46
+        _showDialog: PropTypes.bool,
47
+
48
+        /**
49
+         * Whether or not the toolbox, in which this component exists, are
50
+         * visible.
51
+         */
52
+        _toolboxVisible: PropTypes.bool,
53
+
54
+        /**
55
+         * Invoked to toggle display of the info dialog
56
+         */
57
+        dispatch: PropTypes.func,
58
+
59
+        /**
60
+         * From which side tooltips should display. Will be re-used for
61
+         * displaying the inline dialog for video quality adjustment.
62
+         */
63
+        tooltipPosition: PropTypes.string
64
+    };
65
+
66
+    /**
67
+     * Initializes new {@code ToolbarButtonWithDialog} instance.
68
+     *
69
+     * @param {Object} props - The read-only properties with which the new
70
+     * instance is to be initialized.
71
+     */
72
+    constructor(props) {
73
+        super(props);
74
+
75
+        /**
76
+         * The timeout to automatically hide the {@code InfoDialog} if it has
77
+         * not been interacted with.
78
+         *
79
+         * @type {timeoutID}
80
+         */
81
+        this._autoHideDialogTimeout = null;
82
+
83
+        this.state = {
84
+            /**
85
+             * Whether or not the dialog has been interacted with somehow, such
86
+             * as clicking or toggle display. A value of true will prevent the
87
+             * dialog from being automatically hidden.
88
+             */
89
+            hasInteractedWithDialog: false
90
+        };
91
+
92
+        // Bind event handlers so they are only bound once for every instance.
93
+        this._onDialogClose = this._onDialogClose.bind(this);
94
+        this._onDialogMouseOver = this._onDialogMouseOver.bind(this);
95
+        this._onDialogToggle = this._onDialogToggle.bind(this);
96
+    }
97
+
98
+    /**
99
+     * Set a timeout to automatically hide the {@code InfoDialog}.
100
+     *
101
+     * @inheritdoc
102
+     */
103
+    componentDidMount() {
104
+        this._autoHideDialogTimeout = setTimeout(() => {
105
+            this._maybeHideDialog();
106
+        }, INITIAL_TOOLBAR_TIMEOUT);
107
+    }
108
+
109
+    /**
110
+     * Update the state when the {@code InfoDialog} visibility has been updated.
111
+     *
112
+     * @inheritdoc
113
+     */
114
+    componentWillReceiveProps(nextProps) {
115
+        if (!this.state.hasInteractedWithDialog
116
+            && (nextProps._showDialog !== this.props._showDialog)) {
117
+            this.setState({ hasInteractedWithDialog: true });
118
+        }
119
+
120
+        if (!nextProps._toolboxVisible && this.props._toolboxVisible) {
121
+            this._onDialogClose();
122
+        }
123
+    }
124
+
125
+    /**
126
+     * Clear the timeout to automatically show the {@code InfoDialog}.
127
+     *
128
+     * @inheritdoc
129
+     */
130
+    componentWillUnmount() {
131
+        clearTimeout(this._autoHideDialogTimeout);
132
+    }
133
+
28 134
     /**
29 135
      * Implements React's {@link Component#render()}.
30 136
      *
@@ -32,13 +138,96 @@ class InfoDialogButton extends Component {
32 138
      * @returns {ReactElement}
33 139
      */
34 140
     render() {
141
+        const { _showDialog, _toolboxVisible, tooltipPosition } = this.props;
142
+        const buttonConfiguration = {
143
+            ...DEFAULT_BUTTON_CONFIGURATION,
144
+            classNames: [
145
+                ...DEFAULT_BUTTON_CONFIGURATION.classNames,
146
+                _showDialog ? 'toggled button-active' : ''
147
+            ]
148
+        };
149
+
35 150
         return (
36
-            <ToolbarButtonWithDialog
37
-                { ...this.props }
38
-                button = { DEFAULT_BUTTON_CONFIGURATION }
39
-                content = { InfoDialog } />
151
+            <InlineDialog
152
+                content = { <InfoDialog
153
+                    onClose = { this._onDialogClose }
154
+                    onMouseOver = { this._onDialogMouseOver } /> }
155
+                isOpen = { _toolboxVisible && _showDialog }
156
+                onClose = { this._onDialogClose }
157
+                onContentClick = { this._onDialogInteract }
158
+                position = { TOOLTIP_TO_POPUP_POSITION[tooltipPosition] }>
159
+                <ToolbarButton
160
+                    button = { buttonConfiguration }
161
+                    onClick = { this._onDialogToggle }
162
+                    tooltipPosition = { tooltipPosition } />
163
+            </InlineDialog>
40 164
         );
41 165
     }
166
+
167
+    /**
168
+     * Callback invoked after a timeout to trigger hiding of the
169
+     * {@code InfoDialog} if there has been no interaction with the dialog
170
+     * and the dialog is currently showing.
171
+     *
172
+     * @private
173
+     * @returns {void}
174
+     */
175
+    _maybeHideDialog() {
176
+        if (!this.state.hasInteractedWithDialog && this.props._showDialog) {
177
+            this._onDialogToggle();
178
+        }
179
+    }
180
+
181
+    /**
182
+     * Hides {@code InfoDialog}.
183
+     *
184
+     * @private
185
+     * @returns {void}
186
+     */
187
+    _onDialogClose() {
188
+        this.props.dispatch(setInfoDialogVisibility(false));
189
+    }
190
+
191
+    /**
192
+     * Updates the internal state to mark the {@code InfoDialog} as having been
193
+     * interacted with.
194
+     *
195
+     * @private
196
+     * @returns {void}
197
+     */
198
+    _onDialogMouseOver() {
199
+        if (!this.state.hasInteractedWithDialog) {
200
+            this.setState({ hasInteractedWithDialog: true });
201
+        }
202
+    }
203
+
204
+    /**
205
+     * Toggles the display of {@code InfoDialog}.
206
+     *
207
+     * @private
208
+     * @returns {void}
209
+     */
210
+    _onDialogToggle() {
211
+        this.props.dispatch(setInfoDialogVisibility(!this.props._showDialog));
212
+    }
213
+}
214
+
215
+/**
216
+ * Maps (parts of) the Redux state to the associated {@code InfoDialogButton}
217
+ * component's props.
218
+ *
219
+ * @param {Object} state - The Redux state.
220
+ * @private
221
+ * @returns {{
222
+ *     _showDialog: boolean,
223
+ *     _toolboxVisible: boolean
224
+ * }}
225
+ */
226
+function _mapStateToProps(state) {
227
+    return {
228
+        _showDialog: state['features/invite'].infoDialogVisible,
229
+        _toolboxVisible: state['features/toolbox'].visible
230
+    };
42 231
 }
43 232
 
44
-export default InfoDialogButton;
233
+export default connect(_mapStateToProps)(InfoDialogButton);

+ 11
- 0
react/features/invite/reducer.js 查看文件

@@ -1,16 +1,27 @@
1 1
 import { ReducerRegistry } from '../base/redux';
2 2
 
3 3
 import {
4
+    SET_INFO_DIALOG_VISIBILITY,
4 5
     UPDATE_DIAL_IN_NUMBERS_FAILED,
5 6
     UPDATE_DIAL_IN_NUMBERS_SUCCESS
6 7
 } from './actionTypes';
7 8
 
8 9
 const DEFAULT_STATE = {
10
+
11
+    // By default show the info dialog when joining the conference.
12
+    infoDialogVisible: true,
13
+
9 14
     numbersEnabled: true
10 15
 };
11 16
 
12 17
 ReducerRegistry.register('features/invite', (state = DEFAULT_STATE, action) => {
13 18
     switch (action.type) {
19
+    case SET_INFO_DIALOG_VISIBILITY:
20
+        return {
21
+            ...state,
22
+            infoDialogVisible: action.visible
23
+        };
24
+
14 25
     case UPDATE_DIAL_IN_NUMBERS_FAILED:
15 26
         return {
16 27
             ...state,

+ 1
- 12
react/features/toolbox/components/ToolbarButton.web.js 查看文件

@@ -7,23 +7,12 @@ import React, { Component } from 'react';
7 7
 
8 8
 import { translate } from '../../base/i18n';
9 9
 
10
+import { TOOLTIP_TO_POPUP_POSITION } from '../constants';
10 11
 import { isButtonEnabled } from '../functions';
11 12
 import StatelessToolbarButton from './StatelessToolbarButton';
12 13
 
13 14
 declare var APP: Object;
14 15
 
15
-/**
16
- * Mapping of tooltip positions to equivalent {@code AKInlineDialog} positions.
17
- *
18
- * @private
19
- */
20
-const TOOLTIP_TO_POPUP_POSITION = {
21
-    bottom: 'bottom center',
22
-    left: 'left middle',
23
-    top: 'top center',
24
-    right: 'right middle'
25
-};
26
-
27 16
 /**
28 17
  * Represents a button in Toolbar on React.
29 18
  *

+ 2
- 14
react/features/toolbox/components/ToolbarButtonWithDialog.web.js 查看文件

@@ -3,21 +3,9 @@ import PropTypes from 'prop-types';
3 3
 import React, { Component } from 'react';
4 4
 import { connect } from 'react-redux';
5 5
 
6
+import { TOOLTIP_TO_POPUP_POSITION } from '../constants';
6 7
 import ToolbarButton from './ToolbarButton';
7 8
 
8
-/**
9
- * Maps AtlasKit {@code Tooltip} positions to equivalent {@code InlineDialog}
10
- * positions. The {@code InlineDialog} will appear from the the same side of
11
- * the button as the tooltip.
12
- *
13
- */
14
-const TOOLTIP_TO_DIALOG_POSITION = {
15
-    bottom: 'bottom center',
16
-    left: 'left middle',
17
-    right: 'right middle',
18
-    top: 'top center'
19
-};
20
-
21 9
 /**
22 10
  * React {@code Component} for displaying a button which will open an inline
23 11
  * dialog.
@@ -113,7 +101,7 @@ class ToolbarButtonWithDialog extends Component {
113 101
                 content = { <Content onClose = { this._onDialogClose } /> }
114 102
                 isOpen = { _visible && this.state.showDialog }
115 103
                 onClose = { this._onDialogClose }
116
-                position = { TOOLTIP_TO_DIALOG_POSITION[tooltipPosition] }>
104
+                position = { TOOLTIP_TO_POPUP_POSITION[tooltipPosition] }>
117 105
                 <ToolbarButton
118 106
                     button = { buttonConfiguration }
119 107
                     onClick = { this._onDialogToggle }

+ 11
- 0
react/features/toolbox/constants.js 查看文件

@@ -0,0 +1,11 @@
1
+/**
2
+ * Mapping of tooltip positions to equivalent {@code AKInlineDialog} positions.
3
+ *
4
+ * @private
5
+ */
6
+export const TOOLTIP_TO_POPUP_POSITION = {
7
+    bottom: 'bottom center',
8
+    left: 'left middle',
9
+    top: 'top center',
10
+    right: 'right middle'
11
+};

+ 1
- 0
react/features/toolbox/index.js 查看文件

@@ -1,6 +1,7 @@
1 1
 export * from './actions';
2 2
 export * from './actionTypes';
3 3
 export * from './components';
4
+export * from './constants';
4 5
 export * from './functions';
5 6
 
6 7
 import './middleware';

Loading…
取消
儲存