浏览代码

[RN] Join password-protected rooms

master
Lyubomir Marinov 8 年前
父节点
当前提交
7ecafb1e69

+ 1
- 0
package.json 查看文件

35
     "react": "15.4.1",
35
     "react": "15.4.1",
36
     "react-dom": "15.4.1",
36
     "react-dom": "15.4.1",
37
     "react-native": "0.39.0",
37
     "react-native": "0.39.0",
38
+    "react-native-prompt": "^1.0.0",
38
     "react-native-vector-icons": "^3.0.0",
39
     "react-native-vector-icons": "^3.0.0",
39
     "react-native-webrtc": "jitsi/react-native-webrtc",
40
     "react-native-webrtc": "jitsi/react-native-webrtc",
40
     "react-redux": "^4.4.6",
41
     "react-redux": "^4.4.6",

+ 25
- 0
react/features/base/conference/actionTypes.js 查看文件

1
 import { Symbol } from '../react';
1
 import { Symbol } from '../react';
2
 
2
 
3
+/**
4
+ * The type of the Redux action which signals that a specific conference has
5
+ * failed.
6
+ *
7
+ * {
8
+ *     type: CONFERENCE_FAILED,
9
+ *     conference: JitsiConference,
10
+ *     error: string
11
+ * }
12
+ */
13
+export const CONFERENCE_FAILED = Symbol('CONFERENCE_FAILED');
14
+
3
 /**
15
 /**
4
  * The type of the Redux action which signals that a specific conference has
16
  * The type of the Redux action which signals that a specific conference has
5
  * been joined.
17
  * been joined.
33
  */
45
  */
34
 export const CONFERENCE_WILL_LEAVE = Symbol('CONFERENCE_WILL_LEAVE');
46
 export const CONFERENCE_WILL_LEAVE = Symbol('CONFERENCE_WILL_LEAVE');
35
 
47
 
48
+/**
49
+ * The type of the Redux action which sets the password to join or lock a
50
+ * specific JitsiConference.
51
+ *
52
+ * {
53
+ *     type: SET_PASSWORD,
54
+ *     conference: JitsiConference,
55
+ *     method: Function
56
+ *     password: string
57
+ * }
58
+ */
59
+export const SET_PASSWORD = Symbol('SET_PASSWORD');
60
+
36
 /**
61
 /**
37
  * The type of the Redux action which sets the name of the room of the
62
  * The type of the Redux action which sets the name of the room of the
38
  * conference to be joined.
63
  * conference to be joined.

+ 52
- 2
react/features/base/conference/actions.js 查看文件

9
 import { trackAdded, trackRemoved } from '../tracks';
9
 import { trackAdded, trackRemoved } from '../tracks';
10
 
10
 
11
 import {
11
 import {
12
+    CONFERENCE_FAILED,
12
     CONFERENCE_JOINED,
13
     CONFERENCE_JOINED,
13
     CONFERENCE_LEFT,
14
     CONFERENCE_LEFT,
14
     CONFERENCE_WILL_LEAVE,
15
     CONFERENCE_WILL_LEAVE,
16
+    SET_PASSWORD,
15
     SET_ROOM
17
     SET_ROOM
16
 } from './actionTypes';
18
 } from './actionTypes';
17
 import { EMAIL_COMMAND } from './constants';
19
 import { EMAIL_COMMAND } from './constants';
30
 function _addConferenceListeners(conference, dispatch) {
32
 function _addConferenceListeners(conference, dispatch) {
31
     const JitsiConferenceEvents = JitsiMeetJS.events.conference;
33
     const JitsiConferenceEvents = JitsiMeetJS.events.conference;
32
 
34
 
35
+    conference.on(
36
+            JitsiConferenceEvents.CONFERENCE_FAILED,
37
+            (...args) => dispatch(_conferenceFailed(conference, ...args)));
33
     conference.on(
38
     conference.on(
34
             JitsiConferenceEvents.CONFERENCE_JOINED,
39
             JitsiConferenceEvents.CONFERENCE_JOINED,
35
             (...args) => dispatch(_conferenceJoined(conference, ...args)));
40
             (...args) => dispatch(_conferenceJoined(conference, ...args)));
67
             (data, id) => dispatch(changeParticipantEmail(id, data.value)));
72
             (data, id) => dispatch(changeParticipantEmail(id, data.value)));
68
 }
73
 }
69
 
74
 
75
+/**
76
+ * Signals that a specific conference has failed.
77
+ *
78
+ * @param {JitsiConference} conference - The JitsiConference that has failed.
79
+ * @param {string} error - The error describing/detailing the cause of the
80
+ * failure.
81
+ * @returns {{
82
+ *     type: CONFERENCE_FAILED,
83
+ *     conference: JitsiConference,
84
+ *     error: string
85
+ * }}
86
+ */
87
+function _conferenceFailed(conference, error) {
88
+    return {
89
+        type: CONFERENCE_FAILED,
90
+        conference,
91
+        error
92
+    };
93
+}
94
+
70
 /**
95
 /**
71
  * Attach any pre-existing local media to the conference once the conference has
96
  * Attach any pre-existing local media to the conference once the conference has
72
  * been joined.
97
  * been joined.
144
             throw new Error('Cannot create conference without connection');
169
             throw new Error('Cannot create conference without connection');
145
         }
170
         }
146
 
171
 
147
-        const room = state['features/base/conference'].room;
172
+        const { password, room } = state['features/base/conference'];
148
 
173
 
149
         if (typeof room === 'undefined' || room === '') {
174
         if (typeof room === 'undefined' || room === '') {
150
             throw new Error('Cannot join conference without room name');
175
             throw new Error('Cannot join conference without room name');
156
 
181
 
157
         _addConferenceListeners(conference, dispatch);
182
         _addConferenceListeners(conference, dispatch);
158
 
183
 
159
-        conference.join();
184
+        conference.join(password);
185
+    };
186
+}
187
+
188
+/**
189
+ * Sets the password to join or lock a specific JitsiConference.
190
+ *
191
+ * @param {JitsiConference} conference - The JitsiConference which requires a
192
+ * password to join or is to be locked with the specified password.
193
+ * @param {Function} method - The JitsiConference method of password protection
194
+ * such as join or lock.
195
+ * @param {string} password - The password with which the specified conference
196
+ * is to be joined or locked.
197
+ * @returns {{
198
+ *     type: SET_PASSWORD,
199
+ *     conference: JitsiConference,
200
+ *     method: Function,
201
+ *     password: string
202
+ * }}
203
+ */
204
+export function setPassword(conference, method, password) {
205
+    return {
206
+        type: SET_PASSWORD,
207
+        conference,
208
+        method,
209
+        password
160
     };
210
     };
161
 }
211
 }
162
 
212
 

+ 54
- 0
react/features/base/conference/middleware.js 查看文件

8
 import { TRACK_ADDED, TRACK_REMOVED } from '../tracks';
8
 import { TRACK_ADDED, TRACK_REMOVED } from '../tracks';
9
 
9
 
10
 import { createConference } from './actions';
10
 import { createConference } from './actions';
11
+import { SET_PASSWORD } from './actionTypes';
11
 import {
12
 import {
12
     _addLocalTracksToConference,
13
     _addLocalTracksToConference,
13
     _handleParticipantError,
14
     _handleParticipantError,
28
     case PIN_PARTICIPANT:
29
     case PIN_PARTICIPANT:
29
         return _pinParticipant(store, next, action);
30
         return _pinParticipant(store, next, action);
30
 
31
 
32
+    case SET_PASSWORD:
33
+        return _setPassword(store, next, action);
34
+
31
     case TRACK_ADDED:
35
     case TRACK_ADDED:
32
     case TRACK_REMOVED:
36
     case TRACK_REMOVED:
33
         return _trackAddedOrRemoved(store, next, action);
37
         return _trackAddedOrRemoved(store, next, action);
107
     return next(action);
111
     return next(action);
108
 }
112
 }
109
 
113
 
114
+/**
115
+ * Notifies the feature base/conference that the action <tt>SET_PASSWORD</tt> is
116
+ * being dispatched within a specific Redux store. Joins or locks a specific
117
+ * <tt>JitsiConference</tt> with a specific password.
118
+ *
119
+ * @param {Store} store - The Redux store in which the specified action is being
120
+ * dispatched.
121
+ * @param {Dispatch} next - The Redux dispatch function to dispatch the
122
+ * specified action to the specified store.
123
+ * @param {Action} action - The Redux action <tt>SET_PASSWORD</tt> which is
124
+ * being dispatched in the specified store.
125
+ * @private
126
+ * @returns {Object} The new state that is the result of the reduction of the
127
+ * specified action.
128
+ */
129
+function _setPassword(store, next, action) {
130
+    const { conference, method } = action;
131
+
132
+    switch (method) {
133
+    case conference.join: {
134
+        let state = store.getState()['features/base/conference'];
135
+
136
+        // Make sure that the action will set a password for a conference that
137
+        // the application wants joined.
138
+        if (state.passwordRequired === conference) {
139
+            const result = next(action);
140
+
141
+            // Join the conference with the newly-set password.
142
+            const password = action.password;
143
+
144
+            // Make sure that the action did set the password.
145
+            state = store.getState()['features/base/conference'];
146
+            if (state.password === password
147
+                    && !state.passwordRequired
148
+
149
+                    // Make sure that the application still wants the conference
150
+                    // joined.
151
+                    && !state.conference) {
152
+                method.call(conference, password);
153
+            }
154
+
155
+            return result;
156
+        }
157
+        break;
158
+    }
159
+    }
160
+
161
+    return next(action);
162
+}
163
+
110
 /**
164
 /**
111
  * Synchronizes local tracks from state with local tracks in JitsiConference
165
  * Synchronizes local tracks from state with local tracks in JitsiConference
112
  * instance.
166
  * instance.

+ 85
- 3
react/features/base/conference/reducer.js 查看文件

6
 } from '../redux';
6
 } from '../redux';
7
 
7
 
8
 import {
8
 import {
9
+    CONFERENCE_FAILED,
9
     CONFERENCE_JOINED,
10
     CONFERENCE_JOINED,
10
     CONFERENCE_LEFT,
11
     CONFERENCE_LEFT,
11
     CONFERENCE_WILL_LEAVE,
12
     CONFERENCE_WILL_LEAVE,
13
+    SET_PASSWORD,
12
     SET_ROOM
14
     SET_ROOM
13
 } from './actionTypes';
15
 } from './actionTypes';
14
 import { isRoomValid } from './functions';
16
 import { isRoomValid } from './functions';
19
  */
21
  */
20
 ReducerRegistry.register('features/base/conference', (state = {}, action) => {
22
 ReducerRegistry.register('features/base/conference', (state = {}, action) => {
21
     switch (action.type) {
23
     switch (action.type) {
24
+    case CONFERENCE_FAILED:
25
+        return _conferenceFailed(state, action);
26
+
22
     case CONFERENCE_JOINED:
27
     case CONFERENCE_JOINED:
23
         return _conferenceJoined(state, action);
28
         return _conferenceJoined(state, action);
24
 
29
 
28
     case CONFERENCE_WILL_LEAVE:
33
     case CONFERENCE_WILL_LEAVE:
29
         return _conferenceWillLeave(state, action);
34
         return _conferenceWillLeave(state, action);
30
 
35
 
36
+    case SET_PASSWORD:
37
+        return _setPassword(state, action);
38
+
31
     case SET_ROOM:
39
     case SET_ROOM:
32
         return _setRoom(state, action);
40
         return _setRoom(state, action);
33
     }
41
     }
35
     return state;
43
     return state;
36
 });
44
 });
37
 
45
 
46
+/**
47
+ * Reduces a specific Redux action CONFERENCE_FAILED of the feature
48
+ * base/conference.
49
+ *
50
+ * @param {Object} state - The Redux state of the feature base/conference.
51
+ * @param {Action} action - The Redux action CONFERENCE_FAILED to reduce.
52
+ * @private
53
+ * @returns {Object} The new state of the feature base/conference after the
54
+ * reduction of the specified action.
55
+ */
56
+function _conferenceFailed(state, action) {
57
+    const conference = action.conference;
58
+
59
+    if (state.conference && state.conference !== conference) {
60
+        return state;
61
+    }
62
+
63
+    const JitsiConferenceErrors = JitsiMeetJS.errors.conference;
64
+    const passwordRequired
65
+        = JitsiConferenceErrors.PASSWORD_REQUIRED === action.error
66
+            ? conference
67
+            : undefined;
68
+
69
+    return (
70
+        setStateProperties(state, {
71
+            conference: undefined,
72
+            leaving: undefined,
73
+            password: undefined,
74
+
75
+            /**
76
+             * The JitsiConference instance which requires a password to join.
77
+             *
78
+             * @type {JitsiConference}
79
+             */
80
+            passwordRequired
81
+        }));
82
+}
83
+
38
 /**
84
 /**
39
  * Reduces a specific Redux action CONFERENCE_JOINED of the feature
85
  * Reduces a specific Redux action CONFERENCE_JOINED of the feature
40
  * base/conference.
86
  * base/conference.
55
              * @type {JitsiConference}
101
              * @type {JitsiConference}
56
              */
102
              */
57
             conference: action.conference,
103
             conference: action.conference,
58
-            leaving: undefined
104
+            leaving: undefined,
105
+            passwordRequired: undefined
59
         }));
106
         }));
60
 }
107
 }
61
 
108
 
79
     return (
126
     return (
80
         setStateProperties(state, {
127
         setStateProperties(state, {
81
             conference: undefined,
128
             conference: undefined,
82
-            leaving: undefined
129
+            leaving: undefined,
130
+            password: undefined,
131
+            passwordRequired: undefined
83
         }));
132
         }));
84
 }
133
 }
85
 
134
 
108
              *
157
              *
109
              * @type {JitsiConference}
158
              * @type {JitsiConference}
110
              */
159
              */
111
-            leaving: conference
160
+            leaving: conference,
161
+            passwordRequired: undefined
112
         }));
162
         }));
113
 }
163
 }
114
 
164
 
165
+/**
166
+ * Reduces a specific Redux action SET_PASSWORD of the feature base/conference.
167
+ *
168
+ * @param {Object} state - The Redux state of the feature base/conference.
169
+ * @param {Action} action - The Redux action SET_PASSWORD to reduce.
170
+ * @private
171
+ * @returns {Object} The new state of the feature base/conference after the
172
+ * reduction of the specified action.
173
+ */
174
+function _setPassword(state, action) {
175
+    const conference = action.conference;
176
+
177
+    switch (action.method) {
178
+    case conference.join:
179
+        if (state.passwordRequired === conference) {
180
+            return (
181
+                setStateProperties(state, {
182
+                    /**
183
+                     * The password with which the conference is to be joined.
184
+                     *
185
+                     * @type {string}
186
+                     */
187
+                    password: action.password,
188
+                    passwordRequired: undefined
189
+                }));
190
+        }
191
+        break;
192
+    }
193
+
194
+    return state;
195
+}
196
+
115
 /**
197
 /**
116
  * Reduces a specific Redux action SET_ROOM of the feature base/conference.
198
  * Reduces a specific Redux action SET_ROOM of the feature base/conference.
117
  *
199
  *

+ 54
- 1
react/features/conference/components/Conference.native.js 查看文件

7
 import { LargeVideo } from '../../largeVideo';
7
 import { LargeVideo } from '../../largeVideo';
8
 import { Toolbar } from '../../toolbar';
8
 import { Toolbar } from '../../toolbar';
9
 
9
 
10
+import PasswordRequiredPrompt from './PasswordRequiredPrompt';
10
 import { styles } from './styles';
11
 import { styles } from './styles';
11
 
12
 
12
 /**
13
 /**
24
      * @static
25
      * @static
25
      */
26
      */
26
     static propTypes = {
27
     static propTypes = {
28
+        /**
29
+         * The indicator which determines whether a password is required to join
30
+         * the conference and has not been provided yet.
31
+         *
32
+         * @private
33
+         * @type {JitsiConference}
34
+         */
35
+        _passwordRequired: React.PropTypes.object,
27
         dispatch: React.PropTypes.func
36
         dispatch: React.PropTypes.func
28
     }
37
     }
29
 
38
 
92
                 <LargeVideo />
101
                 <LargeVideo />
93
                 <Toolbar visible = { toolbarVisible } />
102
                 <Toolbar visible = { toolbarVisible } />
94
                 <FilmStrip visible = { !toolbarVisible } />
103
                 <FilmStrip visible = { !toolbarVisible } />
104
+
105
+                {
106
+                    this._renderPrompt()
107
+                }
95
             </Container>
108
             </Container>
96
         );
109
         );
97
     }
110
     }
128
                 = setTimeout(this._onClick, TOOLBAR_TIMEOUT_MS);
141
                 = setTimeout(this._onClick, TOOLBAR_TIMEOUT_MS);
129
         }
142
         }
130
     }
143
     }
144
+
145
+    /**
146
+     * Renders a prompt if necessary such as when a password is required to join
147
+     * the conference.
148
+     *
149
+     * @private
150
+     * @returns {ReactElement}
151
+     */
152
+    _renderPrompt() {
153
+        const passwordRequired = this.props._passwordRequired;
154
+
155
+        if (passwordRequired) {
156
+            return (
157
+                <PasswordRequiredPrompt conference = { passwordRequired } />
158
+            );
159
+        }
160
+
161
+        return null;
162
+    }
163
+}
164
+
165
+/**
166
+ * Maps (parts of) the Redux state to the associated Conference's props.
167
+ *
168
+ * @param {Object} state - The Redux state.
169
+ * @returns {{
170
+ *     _passwordRequired: boolean
171
+ * }}
172
+ */
173
+function mapStateToProps(state) {
174
+    return {
175
+        /**
176
+         * The indicator which determines whether a password is required to join
177
+         * the conference and has not been provided yet.
178
+         *
179
+         * @private
180
+         * @type {JitsiConference}
181
+         */
182
+        _passwordRequired: state['features/base/conference'].passwordRequired
183
+    };
131
 }
184
 }
132
 
185
 
133
-export default reactReduxConnect()(Conference);
186
+export default reactReduxConnect(mapStateToProps)(Conference);

+ 87
- 0
react/features/conference/components/PasswordRequiredPrompt.native.js 查看文件

1
+import React, { Component } from 'react';
2
+import Prompt from 'react-native-prompt';
3
+import { connect } from 'react-redux';
4
+
5
+import { setPassword } from '../../base/conference';
6
+
7
+/**
8
+ * Implements a React Component which prompts the user when a password is
9
+ * required to join a conference.
10
+ */
11
+class PasswordRequiredPrompt extends Component {
12
+    /**
13
+     * PasswordRequiredPrompt component's property types.
14
+     *
15
+     * @static
16
+     */
17
+    static propTypes = {
18
+        /**
19
+         * The JitsiConference which requires a password.
20
+         *
21
+         * @type {JitsiConference}
22
+         */
23
+        conference: React.PropTypes.object,
24
+        dispatch: React.PropTypes.func
25
+    }
26
+
27
+    /**
28
+     * Initializes a new PasswordRequiredPrompt instance.
29
+     *
30
+     * @param {Object} props - The read-only properties with which the new
31
+     * instance is to be initialized.
32
+     */
33
+    constructor(props) {
34
+        super(props);
35
+
36
+        // Bind event handlers so they are only bound once for every instance.
37
+        this._onCancel = this._onCancel.bind(this);
38
+        this._onSubmit = this._onSubmit.bind(this);
39
+    }
40
+
41
+    /**
42
+     * Implements React's {@link Component#render()}.
43
+     *
44
+     * @inheritdoc
45
+     * @returns {ReactElement}
46
+     */
47
+    render() {
48
+        return (
49
+            <Prompt
50
+                onCancel = { this._onCancel }
51
+                onSubmit = { this._onSubmit }
52
+                placeholder = 'Password'
53
+                title = 'Password required'
54
+                visible = { true } />
55
+        );
56
+    }
57
+
58
+    /**
59
+     * Notifies this prompt that it has been dismissed by cancel.
60
+     *
61
+     * @private
62
+     * @returns {void}
63
+     */
64
+    _onCancel() {
65
+        // XXX The user has canceled this prompt for a password so we are to
66
+        // attempt joining the conference without a password. If the conference
67
+        // still requires a password to join, the user will be prompted again
68
+        // later.
69
+        this._onSubmit(undefined);
70
+    }
71
+
72
+    /**
73
+     * Notifies this prompt that it has been dismissed by submitting a specific
74
+     * value.
75
+     *
76
+     * @param {string} value - The submitted value.
77
+     * @private
78
+     * @returns {void}
79
+     */
80
+    _onSubmit(value) {
81
+        const conference = this.props.conference;
82
+
83
+        this.props.dispatch(setPassword(conference, conference.join, value));
84
+    }
85
+}
86
+
87
+export default connect()(PasswordRequiredPrompt);

正在加载...
取消
保存