소스 검색

[RN] Fix base/profile and recent-list bugs

master
Lyubo Marinov 7 년 전
부모
커밋
d727ee80b2

+ 4
- 6
react/features/app/actions.js 파일 보기

4
 import { configWillLoad, loadConfigError, setConfig } from '../base/config';
4
 import { configWillLoad, loadConfigError, setConfig } from '../base/config';
5
 import { setLocationURL } from '../base/connection';
5
 import { setLocationURL } from '../base/connection';
6
 import { loadConfig } from '../base/lib-jitsi-meet';
6
 import { loadConfig } from '../base/lib-jitsi-meet';
7
-import { getProfile } from '../base/profile';
8
 import { parseURIString } from '../base/util';
7
 import { parseURIString } from '../base/util';
9
 
8
 
10
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
9
 import { APP_WILL_MOUNT, APP_WILL_UNMOUNT } from './actionTypes';
15
  * Triggers an in-app navigation to a specific route. Allows navigation to be
14
  * Triggers an in-app navigation to a specific route. Allows navigation to be
16
  * abstracted between the mobile/React Native and Web/React applications.
15
  * abstracted between the mobile/React Native and Web/React applications.
17
  *
16
  *
18
- * @param {(string|undefined)} uri - The URI to which to navigate. It may be a
17
+ * @param {string|undefined} uri - The URI to which to navigate. It may be a
19
  * full URL with an HTTP(S) scheme, a full or partial URI with the app-specific
18
  * full URL with an HTTP(S) scheme, a full or partial URI with the app-specific
20
  * scheme, or a mere room name.
19
  * scheme, or a mere room name.
21
  * @returns {Function}
20
  * @returns {Function}
83
             });
82
             });
84
         }
83
         }
85
 
84
 
86
-        const profile = getProfile(getState());
85
+        const profile = getState()['features/base/profile'];
87
 
86
 
88
-        return promise.then(() => dispatch(setConfig(
89
-            _mergeConfigWithProfile(config, profile)
90
-        )));
87
+        return promise.then(() =>
88
+            dispatch(setConfig(_mergeConfigWithProfile(config, profile))));
91
     }
89
     }
92
 }
90
 }
93
 
91
 

+ 5
- 4
react/features/app/components/AbstractApp.js 파일 보기

12
     localParticipantJoined,
12
     localParticipantJoined,
13
     localParticipantLeft
13
     localParticipantLeft
14
 } from '../../base/participants';
14
 } from '../../base/participants';
15
-import { getProfile } from '../../base/profile';
15
+import '../../base/profile';
16
 import { Fragment, RouteRegistry } from '../../base/react';
16
 import { Fragment, RouteRegistry } from '../../base/react';
17
 import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux';
17
 import { MiddlewareRegistry, ReducerRegistry } from '../../base/redux';
18
 import { PersistenceRegistry } from '../../base/storage';
18
 import { PersistenceRegistry } from '../../base/storage';
123
      */
123
      */
124
     componentWillMount() {
124
     componentWillMount() {
125
         this._init.then(() => {
125
         this._init.then(() => {
126
-            const { dispatch } = this._getStore();
126
+            const { dispatch, getState } = this._getStore();
127
 
127
 
128
             dispatch(appWillMount(this));
128
             dispatch(appWillMount(this));
129
 
129
 
144
             }
144
             }
145
 
145
 
146
             // Profile is the new React compatible settings.
146
             // Profile is the new React compatible settings.
147
-            const profile = getProfile(this._getStore().getState());
147
+            const profile = getState()['features/base/profile'];
148
 
148
 
149
             if (profile) {
149
             if (profile) {
150
                 localParticipant.email
150
                 localParticipant.email
381
 
381
 
382
         return (
382
         return (
383
             this.props.defaultURL
383
             this.props.defaultURL
384
-                || getProfile(this._getStore().getState()).serverURL
384
+                || this._getStore().getState()['features/base/profile']
385
+                    .serverURL
385
                 || DEFAULT_URL);
386
                 || DEFAULT_URL);
386
     }
387
     }
387
 
388
 

+ 1
- 1
react/features/base/conference/middleware.js 파일 보기

128
 
128
 
129
     const state = getState();
129
     const state = getState();
130
     const { audioOnly } = state['features/base/conference'];
130
     const { audioOnly } = state['features/base/conference'];
131
-    const { startAudioOnly } = state['features/base/profile'].profile;
131
+    const { startAudioOnly } = state['features/base/profile'];
132
 
132
 
133
     // FIXME: Consider implementing a standalone audio-only feature that handles
133
     // FIXME: Consider implementing a standalone audio-only feature that handles
134
     // all these state changes.
134
     // all these state changes.

+ 0
- 15
react/features/base/profile/functions.js 파일 보기

1
-/* @flow */
2
-
3
-/**
4
- * Retreives the current profile settings from redux store. The profile
5
- * is persisted to localStorage so it's a good candidate to store settings
6
- * in it.
7
- *
8
- * @param {Object} state - The Redux state.
9
- * @returns {Object}
10
- */
11
-export function getProfile(state: Object) {
12
-    const profileStateSlice = state['features/base/profile'];
13
-
14
-    return profileStateSlice || {};
15
-}

+ 0
- 1
react/features/base/profile/index.js 파일 보기

1
 export * from './actions';
1
 export * from './actions';
2
-export * from './functions';
3
 
2
 
4
 import './middleware';
3
 import './middleware';
5
 import './reducer';
4
 import './reducer';

+ 6
- 7
react/features/base/profile/middleware.js 파일 보기

2
 
2
 
3
 import { setAudioOnly } from '../conference';
3
 import { setAudioOnly } from '../conference';
4
 import { getLocalParticipant, participantUpdated } from '../participants';
4
 import { getLocalParticipant, participantUpdated } from '../participants';
5
-import { getProfile } from '../profile';
6
 import { MiddlewareRegistry, toState } from '../redux';
5
 import { MiddlewareRegistry, toState } from '../redux';
7
 
6
 
8
 import { PROFILE_UPDATED } from './actionTypes';
7
 import { PROFILE_UPDATED } from './actionTypes';
33
  * @param {Object} action - The redux action.
32
  * @param {Object} action - The redux action.
34
  * @returns {void}
33
  * @returns {void}
35
  */
34
  */
36
-function _maybeUpdateStartAudioOnly(store, action) {
37
-    const { profile } = action;
38
-
39
-    if (typeof profile.startAudioOnly === 'boolean') {
40
-        store.dispatch(setAudioOnly(profile.startAudioOnly));
35
+function _maybeUpdateStartAudioOnly(
36
+        { dispatch },
37
+        { profile: { startAudioOnly } }) {
38
+    if (typeof startAudioOnly === 'boolean') {
39
+        dispatch(setAudioOnly(startAudioOnly));
41
     }
40
     }
42
 }
41
 }
43
 
42
 
50
 function _updateLocalParticipant(store) {
49
 function _updateLocalParticipant(store) {
51
     const state = toState(store);
50
     const state = toState(store);
52
     const localParticipant = getLocalParticipant(state);
51
     const localParticipant = getLocalParticipant(state);
53
-    const profile = getProfile(state);
52
+    const profile = state['features/base/profile'];
54
 
53
 
55
     store.dispatch(participantUpdated({
54
     store.dispatch(participantUpdated({
56
         // Identify that the participant to update i.e. the local participant:
55
         // Identify that the participant to update i.e. the local participant:

+ 38
- 11
react/features/base/profile/reducer.js 파일 보기

1
 // @flow
1
 // @flow
2
 
2
 
3
+import { APP_WILL_MOUNT } from '../../app';
3
 import { ReducerRegistry } from '../redux';
4
 import { ReducerRegistry } from '../redux';
4
 import { PersistenceRegistry } from '../storage';
5
 import { PersistenceRegistry } from '../storage';
5
 
6
 
6
 import { PROFILE_UPDATED } from './actionTypes';
7
 import { PROFILE_UPDATED } from './actionTypes';
7
 
8
 
9
+/**
10
+ * The default/initial redux state of the feature {@code base/profile}.
11
+ *
12
+ * @type Object
13
+ */
14
+const DEFAULT_STATE = {};
15
+
8
 const STORE_NAME = 'features/base/profile';
16
 const STORE_NAME = 'features/base/profile';
9
 
17
 
10
 /**
18
 /**
11
- * Sets up the persistence of the feature base/profile.
19
+ * Sets up the persistence of the feature {@code base/profile}.
12
  */
20
  */
13
 PersistenceRegistry.register(STORE_NAME);
21
 PersistenceRegistry.register(STORE_NAME);
14
 
22
 
15
-ReducerRegistry.register(
16
-    STORE_NAME, (state = {}, action) => {
17
-        switch (action.type) {
18
-        case PROFILE_UPDATED:
19
-            return {
20
-                ...state,
21
-                ...action.profile
22
-            };
23
+ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
24
+    switch (action.type) {
25
+    case APP_WILL_MOUNT:
26
+        // XXX APP_WILL_MOUNT is the earliest redux action of ours dispatched in
27
+        // the store. For the purposes of legacy support, make sure that the
28
+        // deserialized base/profile's state is in the format deemed current by
29
+        // the current app revision.
30
+        if (state && typeof state === 'object') {
31
+            // In an enterprise/internal build of Jitsi Meet for Android and iOS
32
+            // we had base/profile's state as an object with property profile.
33
+            const { profile } = state;
34
+
35
+            if (profile && typeof profile === 'object') {
36
+                return { ...profile };
37
+            }
38
+        } else {
39
+            // In the weird case that we have previously persisted/serialized
40
+            // null.
41
+            return DEFAULT_STATE;
23
         }
42
         }
43
+        break;
44
+
45
+    case PROFILE_UPDATED:
46
+        return {
47
+            ...state,
48
+            ...action.profile
49
+        };
50
+    }
24
 
51
 
25
-        return state;
26
-    });
52
+    return state;
53
+});

+ 109
- 123
react/features/base/storage/PersistenceRegistry.js 파일 보기

1
 // @flow
1
 // @flow
2
 
2
 
3
-import Logger from 'jitsi-meet-logger';
4
 import md5 from 'js-md5';
3
 import md5 from 'js-md5';
5
 
4
 
6
-const logger = Logger.getLogger(__filename);
5
+const logger = require('jitsi-meet-logger').getLogger(__filename);
7
 
6
 
8
 /**
7
 /**
9
- * The name of the localStorage store where the app persists its values to.
8
+ * The name of the {@code localStorage} store where the app persists its values.
10
  */
9
  */
11
 const PERSISTED_STATE_NAME = 'jitsi-state';
10
 const PERSISTED_STATE_NAME = 'jitsi-state';
12
 
11
 
13
 /**
12
 /**
14
- * Mixed type of the element (subtree) config. If it's a boolean,
15
- * (and is true) we persist the entire subtree. If it's an object,
16
- * we perist a filtered subtree based on the properties in the
17
- * config object.
13
+ * Mixed type of the element (subtree) config. If it's a {@code boolean} (and is
14
+ * {@code true}), we persist the entire subtree. If it's an {@code Object}, we
15
+ * perist a filtered subtree based on the properties of the config object.
18
  */
16
  */
19
-declare type ElementConfig = Object | boolean;
17
+declare type ElementConfig = boolean | Object;
20
 
18
 
21
 /**
19
 /**
22
- * The type of the name-config pairs stored in this reducer.
20
+ * The type of the name-config pairs stored in {@code PersistenceRegistry}.
23
  */
21
  */
24
 declare type PersistencyConfigMap = { [name: string]: ElementConfig };
22
 declare type PersistencyConfigMap = { [name: string]: ElementConfig };
25
 
23
 
30
 class PersistenceRegistry {
28
 class PersistenceRegistry {
31
     _checksum: string;
29
     _checksum: string;
32
 
30
 
33
-    _elements: PersistencyConfigMap;
31
+    _elements: PersistencyConfigMap = {};
34
 
32
 
35
     /**
33
     /**
36
-     * Initializes a new {@ code PersistenceRegistry} instance.
37
-     */
38
-    constructor() {
39
-        this._elements = {};
40
-    }
41
-
42
-    /**
43
-     * Returns the persisted redux state. This function takes the
44
-     * {@link #_elements} into account as we may have persisted something in the
45
-     * past that we don't want to retreive anymore. The next
46
-     * {@link #persistState} will remove those values.
34
+     * Returns the persisted redux state. Takes the {@link #_elements} into
35
+     * account as we may have persisted something in the past that we don't want
36
+     * to retreive anymore. The next {@link #persistState} will remove such
37
+     * values.
47
      *
38
      *
48
      * @returns {Object}
39
      * @returns {Object}
49
      */
40
      */
50
     getPersistedState() {
41
     getPersistedState() {
51
         let filteredPersistedState = {};
42
         let filteredPersistedState = {};
52
-        let persistedState = window.localStorage.getItem(PERSISTED_STATE_NAME);
53
 
43
 
54
-        if (persistedState) {
55
-            // This is the legacy implementation,
56
-            // must be removed in a later version.
57
-            try {
58
-                persistedState = JSON.parse(persistedState);
59
-            } catch (error) {
60
-                logger.error(
61
-                    'Error parsing persisted state',
62
-                    persistedState,
63
-                    error);
64
-                persistedState = {};
44
+        // localStorage key per feature
45
+        for (const subtreeName of Object.keys(this._elements)) {
46
+            // Assumes that the persisted value is stored under the same key as
47
+            // the feature's redux state name.
48
+            // TODO We'll need to introduce functions later that can control the
49
+            // persist key's name. Similar to control serialization and
50
+            // deserialization. But that should be a straightforward change.
51
+            const persistedSubtree
52
+                = this._getPersistedSubtree(
53
+                    subtreeName,
54
+                    this._elements[subtreeName]);
55
+
56
+            if (persistedSubtree !== undefined) {
57
+                filteredPersistedState[subtreeName] = persistedSubtree;
65
             }
58
             }
59
+        }
66
 
60
 
67
-            filteredPersistedState
68
-                = this._getFilteredState(persistedState);
69
-
70
-            // legacy values must be written to the new store format and
71
-            // old values to be deleted, so then it'll never be used again.
72
-            this.persistState(filteredPersistedState);
73
-            window.localStorage.removeItem(PERSISTED_STATE_NAME);
74
-        } else {
75
-            // new, split-keys implementation
76
-            for (const subtreeName of Object.keys(this._elements)) {
77
-                /*
78
-                 * this assumes that the persisted value is stored under the
79
-                 * same key as the feature's redux state name.
80
-                 * We'll need to introduce functions later that can control
81
-                 * the persist key's name. Similar to control serialization
82
-                 * and deserialization.
83
-                 * But that should be a straightforward change.
84
-                 */
85
-                const persistedSubtree
86
-                    = this._getPersistedSubtree(
87
-                        subtreeName,
88
-                        this._elements[subtreeName]
89
-                    );
61
+        // legacy
62
+        if (Object.keys(filteredPersistedState).length === 0) {
63
+            const { localStorage } = window;
64
+            let persistedState = localStorage.getItem(PERSISTED_STATE_NAME);
90
 
65
 
91
-                if (persistedSubtree !== undefined) {
92
-                    filteredPersistedState[subtreeName] = persistedSubtree;
66
+            if (persistedState) {
67
+                try {
68
+                    persistedState = JSON.parse(persistedState);
69
+                } catch (error) {
70
+                    logger.error(
71
+                        'Error parsing persisted state',
72
+                        persistedState,
73
+                        error);
74
+                    persistedState = {};
93
                 }
75
                 }
76
+
77
+                filteredPersistedState = this._getFilteredState(persistedState);
78
+
79
+                // Store into the new format and delete the old format so that
80
+                // it's not used again.
81
+                this.persistState(filteredPersistedState);
82
+                localStorage.removeItem(PERSISTED_STATE_NAME);
94
             }
83
             }
95
         }
84
         }
96
 
85
 
97
-        // initialize checksum
86
+        // Initialize the checksum.
98
         this._checksum = this._calculateChecksum(filteredPersistedState);
87
         this._checksum = this._calculateChecksum(filteredPersistedState);
99
 
88
 
100
-        this._checksum = this._calculateChecksum(filteredPersistedState);
101
         logger.info('redux state rehydrated as', filteredPersistedState);
89
         logger.info('redux state rehydrated as', filteredPersistedState);
102
 
90
 
103
         return filteredPersistedState;
91
         return filteredPersistedState;
112
      */
100
      */
113
     persistState(state: Object) {
101
     persistState(state: Object) {
114
         const filteredState = this._getFilteredState(state);
102
         const filteredState = this._getFilteredState(state);
115
-        const newCheckSum = this._calculateChecksum(filteredState);
103
+        const checksum = this._calculateChecksum(filteredState);
116
 
104
 
117
-        if (newCheckSum !== this._checksum) {
105
+        if (checksum !== this._checksum) {
118
             for (const subtreeName of Object.keys(filteredState)) {
106
             for (const subtreeName of Object.keys(filteredState)) {
119
                 try {
107
                 try {
120
                     window.localStorage.setItem(
108
                     window.localStorage.setItem(
121
                         subtreeName,
109
                         subtreeName,
122
                         JSON.stringify(filteredState[subtreeName]));
110
                         JSON.stringify(filteredState[subtreeName]));
123
                 } catch (error) {
111
                 } catch (error) {
124
-                    logger.error('Error persisting redux subtree',
112
+                    logger.error(
113
+                        'Error persisting redux subtree',
125
                         subtreeName,
114
                         subtreeName,
126
                         filteredState[subtreeName],
115
                         filteredState[subtreeName],
127
-                        error
128
-                    );
116
+                        error);
129
                 }
117
                 }
130
             }
118
             }
131
             logger.info(
119
             logger.info(
132
-                `redux state persisted. ${this._checksum} -> ${
133
-                    newCheckSum}`);
134
-            this._checksum = newCheckSum;
120
+                `redux state persisted. ${this._checksum} -> ${checksum}`);
121
+            this._checksum = checksum;
135
         }
122
         }
136
     }
123
     }
137
 
124
 
139
      * Registers a new subtree config to be used for the persistency.
126
      * Registers a new subtree config to be used for the persistency.
140
      *
127
      *
141
      * @param {string} name - The name of the subtree the config belongs to.
128
      * @param {string} name - The name of the subtree the config belongs to.
142
-     * @param {ElementConfig} config - The config object, or boolean
143
-     * if the entire subtree needs to be persisted.
129
+     * @param {ElementConfig} config - The config {@code Object}, or
130
+     * {@code boolean} if the entire subtree needs to be persisted.
144
      * @returns {void}
131
      * @returns {void}
145
      */
132
      */
146
     register(name: string, config?: ElementConfig = true) {
133
     register(name: string, config?: ElementConfig = true) {
148
     }
135
     }
149
 
136
 
150
     /**
137
     /**
151
-     * Calculates the checksum of the current or the new values of the state.
138
+     * Calculates the checksum of a specific state.
152
      *
139
      *
140
+     * @param {Object} state - The redux state to calculate the checksum of.
153
      * @private
141
      * @private
154
-     * @param {Object} filteredState - The filtered/persisted redux state.
155
-     * @returns {string}
142
+     * @returns {string} The checksum of the specified {@code state}.
156
      */
143
      */
157
-    _calculateChecksum(filteredState: Object) {
144
+    _calculateChecksum(state: Object) {
158
         try {
145
         try {
159
-            return md5.hex(JSON.stringify(filteredState) || '');
146
+            return md5.hex(JSON.stringify(state) || '');
160
         } catch (error) {
147
         } catch (error) {
161
-            logger.error(
162
-                'Error calculating checksum for state',
163
-                filteredState,
164
-                error);
148
+            logger.error('Error calculating checksum for state', state, error);
165
 
149
 
166
             return '';
150
             return '';
167
         }
151
         }
168
     }
152
     }
169
 
153
 
170
-    /**
171
-     * Retreives a persisted subtree from the storage.
172
-     *
173
-     * @private
174
-     * @param {string} subtreeName - The name of the subtree.
175
-     * @param {Object} subtreeConfig - The config of the subtree
176
-     * from this._elements.
177
-     * @returns {Object}
178
-     */
179
-    _getPersistedSubtree(subtreeName, subtreeConfig) {
180
-        let persistedSubtree = window.localStorage.getItem(subtreeName);
181
-
182
-        if (persistedSubtree) {
183
-            try {
184
-                persistedSubtree = JSON.parse(persistedSubtree);
185
-                const filteredSubtree
186
-                    = this._getFilteredSubtree(persistedSubtree, subtreeConfig);
187
-
188
-                if (filteredSubtree !== undefined) {
189
-                    return filteredSubtree;
190
-                }
191
-            } catch (error) {
192
-                logger.error(
193
-                    'Error parsing persisted subtree',
194
-                    subtreeName,
195
-                    persistedSubtree,
196
-                    error);
197
-            }
198
-        }
199
-
200
-        return null;
201
-    }
202
-
203
     /**
154
     /**
204
      * Prepares a filtered state from the actual or the persisted redux state,
155
      * Prepares a filtered state from the actual or the persisted redux state,
205
      * based on this registry.
156
      * based on this registry.
206
      *
157
      *
207
-     * @private
208
      * @param {Object} state - The actual or persisted redux state.
158
      * @param {Object} state - The actual or persisted redux state.
159
+     * @private
209
      * @returns {Object}
160
      * @returns {Object}
210
      */
161
      */
211
     _getFilteredState(state: Object) {
162
     _getFilteredState(state: Object) {
213
 
164
 
214
         for (const name of Object.keys(this._elements)) {
165
         for (const name of Object.keys(this._elements)) {
215
             if (state[name]) {
166
             if (state[name]) {
216
-                filteredState[name] = this._getFilteredSubtree(
217
-                    state[name],
218
-                    this._elements[name]);
167
+                filteredState[name]
168
+                    = this._getFilteredSubtree(
169
+                        state[name],
170
+                        this._elements[name]);
219
             }
171
             }
220
         }
172
         }
221
 
173
 
226
      * Prepares a filtered subtree based on the config for persisting or for
178
      * Prepares a filtered subtree based on the config for persisting or for
227
      * retrieval.
179
      * retrieval.
228
      *
180
      *
229
-     * @private
230
      * @param {Object} subtree - The redux state subtree.
181
      * @param {Object} subtree - The redux state subtree.
231
      * @param {ElementConfig} subtreeConfig - The related config.
182
      * @param {ElementConfig} subtreeConfig - The related config.
183
+     * @private
232
      * @returns {Object}
184
      * @returns {Object}
233
      */
185
      */
234
     _getFilteredSubtree(subtree, subtreeConfig) {
186
     _getFilteredSubtree(subtree, subtreeConfig) {
235
         let filteredSubtree;
187
         let filteredSubtree;
236
 
188
 
237
-        if (subtreeConfig === true) {
238
-            // we persist the entire subtree
239
-            filteredSubtree = subtree;
240
-        } else if (typeof subtreeConfig === 'object') {
241
-            // only a filtered subtree gets persisted, based on the
242
-            // subtreeConfig object.
189
+        if (typeof subtreeConfig === 'object') {
190
+            // Only a filtered subtree gets persisted as specified by
191
+            // subtreeConfig.
243
             filteredSubtree = {};
192
             filteredSubtree = {};
244
             for (const persistedKey of Object.keys(subtree)) {
193
             for (const persistedKey of Object.keys(subtree)) {
245
                 if (subtreeConfig[persistedKey]) {
194
                 if (subtreeConfig[persistedKey]) {
246
                     filteredSubtree[persistedKey] = subtree[persistedKey];
195
                     filteredSubtree[persistedKey] = subtree[persistedKey];
247
                 }
196
                 }
248
             }
197
             }
198
+        } else if (subtreeConfig) {
199
+            // Persist the entire subtree.
200
+            filteredSubtree = subtree;
249
         }
201
         }
250
 
202
 
251
         return filteredSubtree;
203
         return filteredSubtree;
252
     }
204
     }
205
+
206
+    /**
207
+     * Retreives a persisted subtree from the storage.
208
+     *
209
+     * @param {string} subtreeName - The name of the subtree.
210
+     * @param {Object} subtreeConfig - The config of the subtree from
211
+     * {@link #_elements}.
212
+     * @private
213
+     * @returns {Object}
214
+     */
215
+    _getPersistedSubtree(subtreeName, subtreeConfig) {
216
+        let persistedSubtree = window.localStorage.getItem(subtreeName);
217
+
218
+        if (persistedSubtree) {
219
+            try {
220
+                persistedSubtree = JSON.parse(persistedSubtree);
221
+
222
+                const filteredSubtree
223
+                    = this._getFilteredSubtree(persistedSubtree, subtreeConfig);
224
+
225
+                if (filteredSubtree !== undefined) {
226
+                    return filteredSubtree;
227
+                }
228
+            } catch (error) {
229
+                logger.error(
230
+                    'Error parsing persisted subtree',
231
+                    subtreeName,
232
+                    persistedSubtree,
233
+                    error);
234
+            }
235
+        }
236
+
237
+        return undefined;
238
+    }
253
 }
239
 }
254
 
240
 
255
 export default new PersistenceRegistry();
241
 export default new PersistenceRegistry();

+ 16
- 10
react/features/recent-list/components/AbstractRecentList.js 파일 보기

8
  * The type of the React {@code Component} props of {@link AbstractRecentList}
8
  * The type of the React {@code Component} props of {@link AbstractRecentList}
9
  */
9
  */
10
 type Props = {
10
 type Props = {
11
+    _defaultURL: string,
12
+
13
+    _recentList: Array<Object>,
11
 
14
 
12
     /**
15
     /**
13
-     * Indicates if the list is disabled or not.
16
+     * The redux store's {@code dispatch} function.
14
      */
17
      */
15
-    disabled: boolean,
18
+    dispatch: Dispatch<*>,
16
 
19
 
17
     /**
20
     /**
18
-     * The redux store's {@code dispatch} function.
21
+     * Whether {@code AbstractRecentList} is enabled.
19
      */
22
      */
20
-    dispatch: Dispatch<*>
23
+    enabled: boolean
21
 };
24
 };
22
 
25
 
23
 /**
26
 /**
33
      * Joins the selected room.
36
      * Joins the selected room.
34
      *
37
      *
35
      * @param {string} room - The selected room.
38
      * @param {string} room - The selected room.
39
+     * @protected
36
      * @returns {void}
40
      * @returns {void}
37
      */
41
      */
38
     _onJoin(room) {
42
     _onJoin(room) {
39
-        const { disabled, dispatch } = this.props;
43
+        const { dispatch, enabled } = this.props;
40
 
44
 
41
-        !disabled && room && dispatch(appNavigate(room));
45
+        enabled && room && dispatch(appNavigate(room));
42
     }
46
     }
43
 
47
 
44
     /**
48
     /**
45
      * Creates a bound onPress action for the list item.
49
      * Creates a bound onPress action for the list item.
46
      *
50
      *
47
      * @param {string} room - The selected room.
51
      * @param {string} room - The selected room.
52
+     * @protected
48
      * @returns {Function}
53
      * @returns {Function}
49
      */
54
      */
50
     _onSelect(room) {
55
     _onSelect(room) {
53
 }
58
 }
54
 
59
 
55
 /**
60
 /**
56
- * Maps redux state to component props.
61
+ * Maps (parts of) the redux state into {@code AbstractRecentList}'s React
62
+ * {@code Component} props.
57
  *
63
  *
58
  * @param {Object} state - The redux state.
64
  * @param {Object} state - The redux state.
59
  * @returns {{
65
  * @returns {{
60
- *      _homeServer: string,
61
- *      _recentList: Array
66
+ *     _defaultURL: string,
67
+ *     _recentList: Array
62
  * }}
68
  * }}
63
  */
69
  */
64
 export function _mapStateToProps(state: Object) {
70
 export function _mapStateToProps(state: Object) {
65
     return {
71
     return {
66
-        _homeServer: state['features/app'].app._getDefaultURL(),
72
+        _defaultURL: state['features/app'].app._getDefaultURL(),
67
         _recentList: state['features/recent-list']
73
         _recentList: state['features/recent-list']
68
     };
74
     };
69
 }
75
 }

+ 39
- 44
react/features/recent-list/components/RecentList.native.js 파일 보기

47
      * @returns {ReactElement}
47
      * @returns {ReactElement}
48
      */
48
      */
49
     render() {
49
     render() {
50
-        const { _recentList, disabled } = this.props;
50
+        const { enabled, _recentList } = this.props;
51
 
51
 
52
         if (!_recentList) {
52
         if (!_recentList) {
53
             return null;
53
             return null;
54
         }
54
         }
55
 
55
 
56
         const listViewDataSource
56
         const listViewDataSource
57
-            = this.dataSource.cloneWithRows(
58
-                getRecentRooms(_recentList));
57
+            = this.dataSource.cloneWithRows(getRecentRooms(_recentList));
59
 
58
 
60
         return (
59
         return (
61
             <View
60
             <View
62
                 style = { [
61
                 style = { [
63
                     styles.container,
62
                     styles.container,
64
-                    disabled ? styles.containerDisabled : null
63
+                    enabled ? null : styles.containerDisabled
65
                 ] }>
64
                 ] }>
66
                 <ListView
65
                 <ListView
67
                     dataSource = { listViewDataSource }
66
                     dataSource = { listViewDataSource }
72
     }
71
     }
73
 
72
 
74
     /**
73
     /**
75
-     * Assembles the style array of the avatar based on if the conference was a
76
-     * home or remote server conference (based on current app setting).
74
+     * Assembles the style array of the avatar based on if the conference was
75
+     * hosted on the default Jitsi Meet deployment or on a non-default one
76
+     * (based on current app setting).
77
      *
77
      *
78
      * @param {Object} recentListEntry - The recent list entry being rendered.
78
      * @param {Object} recentListEntry - The recent list entry being rendered.
79
      * @private
79
      * @private
80
      * @returns {Array<Object>}
80
      * @returns {Array<Object>}
81
      */
81
      */
82
-    _getAvatarStyle(recentListEntry) {
82
+    _getAvatarStyle({ baseURL, serverName }) {
83
         const avatarStyles = [ styles.avatar ];
83
         const avatarStyles = [ styles.avatar ];
84
 
84
 
85
-        if (recentListEntry.baseURL !== this.props._homeServer) {
86
-            avatarStyles.push(
87
-                this._getColorForServerName(recentListEntry.serverName));
85
+        if (baseURL !== this.props._defaultURL) {
86
+            avatarStyles.push(this._getColorForServerName(serverName));
88
         }
87
         }
89
 
88
 
90
         return avatarStyles;
89
         return avatarStyles;
115
      * @private
114
      * @private
116
      * @returns {ReactElement}
115
      * @returns {ReactElement}
117
      */
116
      */
118
-    _renderConfDuration({ conferenceDurationString }) {
119
-        if (conferenceDurationString) {
117
+    _renderConfDuration({ durationString }) {
118
+        if (durationString) {
120
             return (
119
             return (
121
                 <View style = { styles.infoWithIcon } >
120
                 <View style = { styles.infoWithIcon } >
122
                     <Icon
121
                     <Icon
123
                         name = 'timer'
122
                         name = 'timer'
124
                         style = { styles.inlineIcon } />
123
                         style = { styles.inlineIcon } />
125
                     <Text style = { styles.confLength }>
124
                     <Text style = { styles.confLength }>
126
-                        { conferenceDurationString }
127
-                    </Text>
128
-                </View>
129
-            );
130
-        }
131
-
132
-        return null;
133
-    }
134
-
135
-    /**
136
-     * Renders the server info component based on if the entry was on a
137
-     * different server or not.
138
-     *
139
-     * @param {Object} recentListEntry - The recent list entry being rendered.
140
-     * @private
141
-     * @returns {ReactElement}
142
-     */
143
-    _renderServerInfo(recentListEntry) {
144
-        if (recentListEntry.baseURL !== this.props._homeServer) {
145
-            return (
146
-                <View style = { styles.infoWithIcon } >
147
-                    <Icon
148
-                        name = 'public'
149
-                        style = { styles.inlineIcon } />
150
-                    <Text style = { styles.serverName }>
151
-                        { recentListEntry.serverName }
125
+                        { durationString }
152
                     </Text>
126
                     </Text>
153
                 </View>
127
                 </View>
154
             );
128
             );
191
                                 { data.dateString }
165
                                 { data.dateString }
192
                             </Text>
166
                             </Text>
193
                         </View>
167
                         </View>
194
-                        {
195
-                            this._renderConfDuration(data)
196
-                        }
197
-                        {
198
-                            this._renderServerInfo(data)
199
-                        }
168
+                        { this._renderConfDuration(data) }
169
+                        { this._renderServerInfo(data) }
200
                     </View>
170
                     </View>
201
                 </View>
171
                 </View>
202
             </TouchableHighlight>
172
             </TouchableHighlight>
203
         );
173
         );
204
     }
174
     }
175
+
176
+    /**
177
+     * Renders the server info component based on whether the entry was on a
178
+     * different server.
179
+     *
180
+     * @param {Object} recentListEntry - The recent list entry being rendered.
181
+     * @private
182
+     * @returns {ReactElement}
183
+     */
184
+    _renderServerInfo({ baseURL, serverName }) {
185
+        if (baseURL !== this.props._defaultURL) {
186
+            return (
187
+                <View style = { styles.infoWithIcon } >
188
+                    <Icon
189
+                        name = 'public'
190
+                        style = { styles.inlineIcon } />
191
+                    <Text style = { styles.serverName }>
192
+                        { serverName }
193
+                    </Text>
194
+                </View>
195
+            );
196
+        }
197
+
198
+        return null;
199
+    }
205
 }
200
 }
206
 
201
 
207
 export default connect(_mapStateToProps)(RecentList);
202
 export default connect(_mapStateToProps)(RecentList);

+ 12
- 13
react/features/recent-list/functions.js 파일 보기

17
 require('moment/locale/nb');
17
 require('moment/locale/nb');
18
 
18
 
19
 // OC is not available. Please submit OC translation to the MomentJS project.
19
 // OC is not available. Please submit OC translation to the MomentJS project.
20
-
21
 require('moment/locale/pl');
20
 require('moment/locale/pl');
22
 require('moment/locale/pt');
21
 require('moment/locale/pt');
23
 require('moment/locale/pt-br');
22
 require('moment/locale/pt-br');
47
         const locale = _getSupportedLocale();
46
         const locale = _getSupportedLocale();
48
 
47
 
49
         for (const e of list) {
48
         for (const e of list) {
50
-            const location = parseURIString(e.conference);
49
+            const uri = parseURIString(e.conference);
50
+
51
+            if (uri && uri.room && uri.hostname) {
52
+                const duration
53
+                    = e.duration || /* legacy */ e.conferenceDuration;
51
 
54
 
52
-            if (location && location.room && location.hostname) {
53
                 recentRoomDS.push({
55
                 recentRoomDS.push({
54
-                    baseURL: `${location.protocol}//${location.host}`,
56
+                    baseURL: `${uri.protocol}//${uri.host}`,
55
                     conference: e.conference,
57
                     conference: e.conference,
56
-                    conferenceDuration: e.conferenceDuration,
57
-                    conferenceDurationString:
58
-                        _getDurationString(
59
-                            e.conferenceDuration,
60
-                            locale),
61
                     dateString: _getDateString(e.date, locale),
58
                     dateString: _getDateString(e.date, locale),
62
                     dateTimeStamp: e.date,
59
                     dateTimeStamp: e.date,
63
-                    initials: _getInitials(location.room),
64
-                    room: location.room,
65
-                    serverName: location.hostname
60
+                    duration,
61
+                    durationString: _getDurationString(duration, locale),
62
+                    initials: _getInitials(uri.room),
63
+                    room: uri.room,
64
+                    serverName: uri.hostname
66
                 });
65
                 });
67
             }
66
             }
68
         }
67
         }
124
  * or duration ({@code number}).
123
  * or duration ({@code number}).
125
  *
124
  *
126
  * @private
125
  * @private
127
- * @param {Date | number} dateOrDuration - The date or duration to format.
126
+ * @param {Date|number} dateOrDuration - The date or duration to format.
128
  * @param {string} locale - The locale to init the formatter with. Note: The
127
  * @param {string} locale - The locale to init the formatter with. Note: The
129
  * specified locale must be supported by the formatter so ensure the
128
  * specified locale must be supported by the formatter so ensure the
130
  * prerequisite is met before invoking the function.
129
  * prerequisite is met before invoking the function.

+ 9
- 12
react/features/recent-list/middleware.js 파일 보기

15
 MiddlewareRegistry.register(store => next => action => {
15
 MiddlewareRegistry.register(store => next => action => {
16
     switch (action.type) {
16
     switch (action.type) {
17
     case CONFERENCE_WILL_LEAVE:
17
     case CONFERENCE_WILL_LEAVE:
18
-        _updateConferenceDuration(store, next);
18
+        _updateConferenceDuration(store);
19
         break;
19
         break;
20
 
20
 
21
     case SET_ROOM:
21
     case SET_ROOM:
22
-        _maybeStoreCurrentConference(store, next, action);
22
+        _maybeStoreCurrentConference(store, action);
23
         break;
23
         break;
24
     }
24
     }
25
 
25
 
36
  * @private
36
  * @private
37
  * @returns {void}
37
  * @returns {void}
38
  */
38
  */
39
-function _maybeStoreCurrentConference(store, next, action) {
40
-    const { locationURL } = store.getState()['features/base/connection'];
41
-    const { room } = action;
42
-
39
+function _maybeStoreCurrentConference({ dispatch, getState }, { room }) {
43
     if (room) {
40
     if (room) {
44
-        next(storeCurrentConference(locationURL));
41
+        const { locationURL } = getState()['features/base/connection'];
42
+
43
+        dispatch(storeCurrentConference(locationURL));
45
     }
44
     }
46
 }
45
 }
47
 
46
 
49
  * Updates the duration of the last conference stored in the list.
48
  * Updates the duration of the last conference stored in the list.
50
  *
49
  *
51
  * @param {Store} store - The redux store.
50
  * @param {Store} store - The redux store.
52
- * @param {Dispatch} next - The redux {@code dispatch} function.
53
- * @param {Action} action - The redux action.
54
  * @private
51
  * @private
55
  * @returns {void}
52
  * @returns {void}
56
  */
53
  */
57
-function _updateConferenceDuration(store, next) {
58
-    const { locationURL } = store.getState()['features/base/connection'];
54
+function _updateConferenceDuration({ dispatch, getState }) {
55
+    const { locationURL } = getState()['features/base/connection'];
59
 
56
 
60
-    next(updateConferenceDuration(locationURL));
57
+    dispatch(updateConferenceDuration(locationURL));
61
 }
58
 }

+ 97
- 38
react/features/recent-list/reducer.js 파일 보기

1
 // @flow
1
 // @flow
2
 
2
 
3
+import { APP_WILL_MOUNT } from '../app';
3
 import { ReducerRegistry } from '../base/redux';
4
 import { ReducerRegistry } from '../base/redux';
4
 import { PersistenceRegistry } from '../base/storage';
5
 import { PersistenceRegistry } from '../base/storage';
5
 
6
 
10
 
11
 
11
 const logger = require('jitsi-meet-logger').getLogger(__filename);
12
 const logger = require('jitsi-meet-logger').getLogger(__filename);
12
 
13
 
14
+/**
15
+ * The default/initial redux state of the feature {@code recent-list}.
16
+ *
17
+ * @type {Array<Object>}
18
+ */
19
+const DEFAULT_STATE = [];
20
+
13
 /**
21
 /**
14
  * The name of the {@code window.localStorage} item where recent rooms are
22
  * The name of the {@code window.localStorage} item where recent rooms are
15
  * stored.
23
  * stored.
31
 const STORE_NAME = 'features/recent-list';
39
 const STORE_NAME = 'features/recent-list';
32
 
40
 
33
 /**
41
 /**
34
- * Sets up the persistence of the feature recent-list.
42
+ * Sets up the persistence of the feature {@code recent-list}.
35
  */
43
  */
36
 PersistenceRegistry.register(STORE_NAME);
44
 PersistenceRegistry.register(STORE_NAME);
37
 
45
 
38
 /**
46
 /**
39
- * Reduces the redux actions of the feature recent-list.
47
+ * Reduces redux actions for the purposes of the feature {@code recent-list}.
40
  */
48
  */
41
 ReducerRegistry.register(
49
 ReducerRegistry.register(
42
     STORE_NAME,
50
     STORE_NAME,
43
     (state = _getLegacyRecentRoomList(), action) => {
51
     (state = _getLegacyRecentRoomList(), action) => {
44
         switch (action.type) {
52
         switch (action.type) {
53
+        case APP_WILL_MOUNT:
54
+            return _appWillMount(state);
55
+
45
         case STORE_CURRENT_CONFERENCE:
56
         case STORE_CURRENT_CONFERENCE:
46
             return _storeCurrentConference(state, action);
57
             return _storeCurrentConference(state, action);
47
 
58
 
53
         }
64
         }
54
     });
65
     });
55
 
66
 
67
+/**
68
+ * Reduces the redux action {@link APP_WILL_MOUNT}.
69
+ *
70
+ * @param {Object} state - The redux state of the feature {@code recent-list}.
71
+ * @param {Action} action - The redux action {@code APP_WILL_MOUNT}.
72
+ * @returns {Array<Object>} The next redux state of the feature
73
+ * {@code recent-list}.
74
+ */
75
+function _appWillMount(state) {
76
+    // XXX APP_WILL_MOUNT is the earliest redux action of ours dispatched in the
77
+    // store. For the purposes of legacy support, make sure that the
78
+    // deserialized recent-list's state is in the format deemed current by the
79
+    // current app revision.
80
+    if (state && typeof state === 'object') {
81
+        if (Array.isArray(state)) {
82
+            return state;
83
+        }
84
+
85
+        // In an enterprise/internal build of Jitsi Meet for Android and iOS we
86
+        // had recent-list's state as an object with property list.
87
+        const { list } = state;
88
+
89
+        if (Array.isArray(list) && list.length) {
90
+            return list.slice();
91
+        }
92
+    }
93
+
94
+    // In the weird case that we have previously persisted/serialized null.
95
+    return DEFAULT_STATE;
96
+}
97
+
56
 /**
98
 /**
57
  * Retrieves the recent room list that was stored using the legacy way.
99
  * Retrieves the recent room list that was stored using the legacy way.
58
  *
100
  *
59
  * @returns {Array<Object>}
101
  * @returns {Array<Object>}
60
  */
102
  */
61
-export function _getLegacyRecentRoomList(): Array<Object> {
103
+function _getLegacyRecentRoomList(): Array<Object> {
62
     try {
104
     try {
63
-        const list
64
-            = JSON.parse(window.localStorage.getItem(LEGACY_STORAGE_KEY));
105
+        const str = window.localStorage.getItem(LEGACY_STORAGE_KEY);
65
 
106
 
66
-        if (list && list.length) {
67
-            return list;
107
+        if (str) {
108
+            return JSON.parse(str);
68
         }
109
         }
69
     } catch (error) {
110
     } catch (error) {
70
         logger.warn('Failed to parse legacy recent-room list!');
111
         logger.warn('Failed to parse legacy recent-room list!');
74
 }
115
 }
75
 
116
 
76
 /**
117
 /**
77
-* Adds a new list entry to the redux store.
78
-*
79
-* @param {Object} state - The redux state.
80
-* @param {Object} action - The redux action.
81
-* @returns {Object}
82
-*/
83
-function _storeCurrentConference(state, action) {
84
-    const { locationURL } = action;
118
+ * Adds a new list entry to the redux store.
119
+ *
120
+ * @param {Object} state - The redux state of the feature {@code recent-list}.
121
+ * @param {Object} action - The redux action.
122
+ * @returns {Object}
123
+ */
124
+function _storeCurrentConference(state, { locationURL }) {
85
     const conference = locationURL.href;
125
     const conference = locationURL.href;
86
 
126
 
87
     // If the current conference is already in the list, we remove it to re-add
127
     // If the current conference is already in the list, we remove it to re-add
88
     // it to the top.
128
     // it to the top.
89
-    const list = (Array.isArray(state) ? state : [])
90
-        .filter(e => e.conference !== conference);
129
+    const nextState
130
+        = state.filter(e => !_urlStringEquals(e.conference, conference));
91
 
131
 
92
     // The list is a reverse-sorted (i.e. the newer elements are at the end).
132
     // The list is a reverse-sorted (i.e. the newer elements are at the end).
93
-    list.push({
133
+    nextState.push({
94
         conference,
134
         conference,
95
-        conferenceDuration: 0, // We don't have this data yet!
96
-        date: Date.now()
135
+        date: Date.now(),
136
+        duration: 0 // We don't have the duration yet!
97
     });
137
     });
98
 
138
 
99
     // Ensure the list doesn't exceed a/the maximum size.
139
     // Ensure the list doesn't exceed a/the maximum size.
100
-    list.splice(0, list.length - MAX_LIST_SIZE);
140
+    nextState.splice(0, nextState.length - MAX_LIST_SIZE);
101
 
141
 
102
-    return list;
142
+    return nextState;
103
 }
143
 }
104
 
144
 
105
 /**
145
 /**
106
  * Updates the conference length when left.
146
  * Updates the conference length when left.
107
  *
147
  *
108
- * @param {Object} state - The redux state.
148
+ * @param {Object} state - The redux state of the feature {@code recent-list}.
109
  * @param {Object} action - The redux action.
149
  * @param {Object} action - The redux action.
110
- * @returns {Object}
150
+ * @returns {Object} The next redux state of the feature {@code recent-list}.
111
  */
151
  */
112
-function _updateConferenceDuration(state, action) {
113
-    const { locationURL } = action;
152
+function _updateConferenceDuration(state, { locationURL }) {
153
+    if (locationURL && locationURL.href && state.length) {
154
+        const mostRecentIndex = state.length - 1;
155
+        const mostRecent = state[mostRecentIndex];
156
+
157
+        if (_urlStringEquals(mostRecent.conference, locationURL.href)) {
158
+            // The last conference start was stored so we need to update the
159
+            // length.
160
+            const nextMostRecent = {
161
+                ...mostRecent,
162
+                duration: Date.now() - mostRecent.date
163
+            };
114
 
164
 
115
-    if (locationURL && locationURL.href) {
116
-        // shallow copy to avoid in-place modification.
117
-        const list = (Array.isArray(state) ? state : []).slice();
165
+            delete nextMostRecent.conferenceDuration; // legacy
118
 
166
 
119
-        if (list.length > 0) {
120
-            const mostRecentURL = list[list.length - 1];
167
+            // Shallow copy to avoid in-place modification.
168
+            const nextState = state.slice();
121
 
169
 
122
-            if (mostRecentURL.conference === locationURL.href) {
123
-                // The last conference start was stored so we need to update the
124
-                // length.
125
-                mostRecentURL.conferenceDuration
126
-                    = Date.now() - mostRecentURL.date;
170
+            nextState[mostRecentIndex] = nextMostRecent;
127
 
171
 
128
-                return list;
129
-            }
172
+            return nextState;
130
         }
173
         }
131
     }
174
     }
132
 
175
 
133
     return state;
176
     return state;
134
 }
177
 }
178
+
179
+/**
180
+ * Determines whether two specific URL {@code strings} are equal in the sense
181
+ * that they identify one and the same conference resource (irrespective of
182
+ * time) for the purposes of the feature {@code recent-list}.
183
+ *
184
+ * @param {string} a - The URL {@code string} to test for equality to {@code b}.
185
+ * @param {string} b - The URL {@code string} to test for equality to {@code a}.
186
+ * @returns {boolean}
187
+ */
188
+function _urlStringEquals(a: string, b: string) {
189
+    // FIXME Case-sensitive comparison is wrong because the room name at least
190
+    // is case insensitive on the server and elsewhere (where it matters) in the
191
+    // client. I don't think domain names are case-sensitive either.
192
+    return a === b;
193
+}

+ 8
- 2
react/features/settings/components/AbstractSettingsView.js 파일 보기

2
 
2
 
3
 import { Component } from 'react';
3
 import { Component } from 'react';
4
 
4
 
5
-import { getProfile, updateProfile } from '../../base/profile';
5
+import { updateProfile } from '../../base/profile';
6
 
6
 
7
 /**
7
 /**
8
  * The type of the React {@code Component} props of
8
  * The type of the React {@code Component} props of
12
 
12
 
13
     /**
13
     /**
14
      * The current profile object.
14
      * The current profile object.
15
+     *
16
+     * @protected
15
      */
17
      */
16
     _profile: Object,
18
     _profile: Object,
17
 
19
 
18
     /**
20
     /**
19
      * The default URL for when there is no custom URL set in the profile.
21
      * The default URL for when there is no custom URL set in the profile.
22
+     *
23
+     * @protected
20
      */
24
      */
21
     _serverURL: string,
25
     _serverURL: string,
22
 
26
 
23
     /**
27
     /**
24
      * Whether {@link AbstractSettingsView} is visible.
28
      * Whether {@link AbstractSettingsView} is visible.
29
+     *
30
+     * @protected
25
      */
31
      */
26
     _visible: boolean,
32
     _visible: boolean,
27
 
33
 
168
  */
174
  */
169
 export function _mapStateToProps(state: Object) {
175
 export function _mapStateToProps(state: Object) {
170
     return {
176
     return {
171
-        _profile: getProfile(state),
177
+        _profile: state['features/base/profile'],
172
         _serverURL: state['features/app'].app._getDefaultURL(),
178
         _serverURL: state['features/app'].app._getDefaultURL(),
173
         _visible: state['features/settings'].visible
179
         _visible: state['features/settings'].visible
174
     };
180
     };

+ 3
- 18
react/features/welcome/components/AbstractWelcomePage.js 파일 보기

1
 // @flow
1
 // @flow
2
 
2
 
3
-import PropTypes from 'prop-types';
4
 import { Component } from 'react';
3
 import { Component } from 'react';
5
 
4
 
6
 import { createWelcomePageEvent, sendAnalytics } from '../../analytics';
5
 import { createWelcomePageEvent, sendAnalytics } from '../../analytics';
14
  */
13
  */
15
 type Props = {
14
 type Props = {
16
 
15
 
17
-    /**
18
-     * Boolean to indicate if the room field is focused or not.
19
-     */
20
-    _fieldFocused: boolean,
21
-
22
     /**
16
     /**
23
      * The user's profile.
17
      * The user's profile.
24
      */
18
      */
32
  *
26
  *
33
  * @abstract
27
  * @abstract
34
  */
28
  */
35
-export class AbstractWelcomePage extends Component<*, *> {
36
-    /**
37
-     * {@code AbstractWelcomePage}'s React {@code Component} prop types.
38
-     *
39
-     * @static
40
-     */
41
-    static propTypes = {
42
-        _room: PropTypes.string,
43
-        dispatch: PropTypes.func
44
-    };
45
-
29
+export class AbstractWelcomePage extends Component<Props, *> {
46
     _mounted: ?boolean;
30
     _mounted: ?boolean;
47
 
31
 
48
     /**
32
     /**
245
  * @param {Object} state - The redux state.
229
  * @param {Object} state - The redux state.
246
  * @protected
230
  * @protected
247
  * @returns {{
231
  * @returns {{
232
+ *     _profile: Object,
248
  *     _room: string
233
  *     _room: string
249
  * }}
234
  * }}
250
  */
235
  */
251
 export function _mapStateToProps(state: Object) {
236
 export function _mapStateToProps(state: Object) {
252
     return {
237
     return {
253
-        _profile: state['features/base/profile'].profile,
238
+        _profile: state['features/base/profile'],
254
         _room: state['features/base/conference'].room
239
         _room: state['features/base/conference'].room
255
     };
240
     };
256
 }
241
 }

+ 1
- 8
react/features/welcome/components/WelcomePage.native.js 파일 보기

40
  * @extends AbstractWelcomePage
40
  * @extends AbstractWelcomePage
41
  */
41
  */
42
 class WelcomePage extends AbstractWelcomePage {
42
 class WelcomePage extends AbstractWelcomePage {
43
-    /**
44
-     * WelcomePage component's property types.
45
-     *
46
-     * @static
47
-     */
48
-    static propTypes = AbstractWelcomePage.propTypes;
49
-
50
     /**
43
     /**
51
      * Constructor of the Component.
44
      * Constructor of the Component.
52
      *
45
      *
140
                         {
133
                         {
141
                             this._renderHintBox()
134
                             this._renderHintBox()
142
                         }
135
                         }
143
-                        <RecentList disabled = { this.state._fieldFocused } />
136
+                        <RecentList enabled = { !this.state._fieldFocused } />
144
                     </SafeAreaView>
137
                     </SafeAreaView>
145
                     <SettingsView />
138
                     <SettingsView />
146
                 </View>
139
                 </View>

Loading…
취소
저장