瀏覽代碼

feature-flags: add flag for enabling calendar integration

master
Saúl Ibarra Corretgé 6 年之前
父節點
當前提交
97e0303065

+ 3
- 2
react/features/base/flags/constants.js 查看文件

@@ -1,9 +1,10 @@
1 1
 // @flow
2 2
 
3 3
 /**
4
- * Flag indicating if calendar integration should be disabled.
4
+ * Flag indicating if calendar integration should be enabled.
5
+ * Default: enabled (true) on Android, auto-detected on iOS.
5 6
  */
6
-export const CALENDAR_DISABLED = 'calendar.disabled';
7
+export const CALENDAR_ENABLED = 'calendar.enabled';
7 8
 
8 9
 /**
9 10
  * Flag indicating if chat should be enabled.

+ 8
- 6
react/features/calendar-sync/actions.web.js 查看文件

@@ -30,17 +30,19 @@ const logger = require('jitsi-meet-logger').getLogger(__filename);
30 30
  */
31 31
 export function bootstrapCalendarIntegration(): Function {
32 32
     return (dispatch, getState) => {
33
+        const state = getState();
34
+
35
+        if (!isCalendarEnabled(state)) {
36
+            return Promise.reject();
37
+        }
38
+
33 39
         const {
34 40
             googleApiApplicationClientID
35
-        } = getState()['features/base/config'];
41
+        } = state['features/base/config'];
36 42
         const {
37 43
             integrationReady,
38 44
             integrationType
39
-        } = getState()['features/calendar-sync'];
40
-
41
-        if (!isCalendarEnabled()) {
42
-            return Promise.reject();
43
-        }
45
+        } = state['features/calendar-sync'];
44 46
 
45 47
         return Promise.resolve()
46 48
             .then(() => {

+ 1
- 4
react/features/calendar-sync/components/CalendarList.native.js 查看文件

@@ -9,7 +9,6 @@ import { AbstractPage } from '../../base/react';
9 9
 import { connect } from '../../base/redux';
10 10
 
11 11
 import { refreshCalendar } from '../actions';
12
-import { isCalendarEnabled } from '../functions';
13 12
 import styles from './styles';
14 13
 
15 14
 import CalendarListContent from './CalendarListContent';
@@ -138,6 +137,4 @@ function _mapStateToProps(state: Object) {
138 137
     };
139 138
 }
140 139
 
141
-export default isCalendarEnabled()
142
-    ? translate(connect(_mapStateToProps)(CalendarList))
143
-    : undefined;
140
+export default translate(connect(_mapStateToProps)(CalendarList));

+ 1
- 4
react/features/calendar-sync/components/CalendarList.web.js 查看文件

@@ -14,7 +14,6 @@ import {
14 14
 
15 15
 import { refreshCalendar } from '../actions';
16 16
 import { ERRORS } from '../constants';
17
-import { isCalendarEnabled } from '../functions';
18 17
 
19 18
 import CalendarListContent from './CalendarListContent';
20 19
 
@@ -257,6 +256,4 @@ function _mapStateToProps(state) {
257 256
     };
258 257
 }
259 258
 
260
-export default isCalendarEnabled()
261
-    ? translate(connect(_mapStateToProps)(CalendarList))
262
-    : undefined;
259
+export default translate(connect(_mapStateToProps)(CalendarList));

+ 1
- 4
react/features/calendar-sync/components/CalendarListContent.native.js 查看文件

@@ -13,7 +13,6 @@ import { NavigateSectionList } from '../../base/react';
13 13
 import { connect } from '../../base/redux';
14 14
 
15 15
 import { refreshCalendar, openUpdateCalendarEventDialog } from '../actions';
16
-import { isCalendarEnabled } from '../functions';
17 16
 
18 17
 
19 18
 /**
@@ -271,6 +270,4 @@ function _mapStateToProps(state: Object) {
271 270
     };
272 271
 }
273 272
 
274
-export default isCalendarEnabled()
275
-    ? translate(connect(_mapStateToProps)(CalendarListContent))
276
-    : undefined;
273
+export default translate(connect(_mapStateToProps)(CalendarListContent));

+ 1
- 5
react/features/calendar-sync/components/CalendarListContent.web.js 查看文件

@@ -11,8 +11,6 @@ import {
11 11
 import { MeetingsList } from '../../base/react';
12 12
 import { connect } from '../../base/redux';
13 13
 
14
-import { isCalendarEnabled } from '../functions';
15
-
16 14
 import AddMeetingUrlButton from './AddMeetingUrlButton';
17 15
 import JoinButton from './JoinButton';
18 16
 
@@ -172,6 +170,4 @@ function _mapStateToProps(state: Object) {
172 170
     };
173 171
 }
174 172
 
175
-export default isCalendarEnabled()
176
-    ? connect(_mapStateToProps)(CalendarListContent)
177
-    : undefined;
173
+export default connect(_mapStateToProps)(CalendarListContent);

+ 1
- 4
react/features/calendar-sync/components/ConferenceNotification.native.js 查看文件

@@ -10,7 +10,6 @@ import { getLocalizedDateFormatter, translate } from '../../base/i18n';
10 10
 import { connect } from '../../base/redux';
11 11
 import { ASPECT_RATIO_NARROW } from '../../base/responsive-ui';
12 12
 
13
-import { isCalendarEnabled } from '../functions';
14 13
 import styles from './styles';
15 14
 
16 15
 const ALERT_MILLISECONDS = 5 * 60 * 1000;
@@ -294,6 +293,4 @@ function _mapStateToProps(state: Object) {
294 293
     };
295 294
 }
296 295
 
297
-export default isCalendarEnabled()
298
-    ? translate(connect(_mapStateToProps)(ConferenceNotification))
299
-    : undefined;
296
+export default translate(connect(_mapStateToProps)(ConferenceNotification));

+ 11
- 2
react/features/calendar-sync/functions.native.js 查看文件

@@ -4,6 +4,7 @@ import { NativeModules, Platform } from 'react-native';
4 4
 import RNCalendarEvents from 'react-native-calendar-events';
5 5
 import type { Store } from 'redux';
6 6
 
7
+import { CALENDAR_ENABLED, getFeatureFlag } from '../base/flags';
7 8
 import { getShareInfoText } from '../invite';
8 9
 
9 10
 import { setCalendarAuthorization } from './actions';
@@ -54,12 +55,20 @@ export function addLinkToCalendarEntry(
54 55
  * Determines whether the calendar feature is enabled by the app. For
55 56
  * example, Apple through its App Store requires
56 57
  * {@code NSCalendarsUsageDescription} in the app's Info.plist or App Store
57
- * rejects the app.
58
+ * rejects the app. It could also be disabled with a feature flag.
58 59
  *
60
+ * @param {Function|Object} stateful - The redux store or {@code getState}
61
+ * function.
59 62
  * @returns {boolean} If the app has enabled the calendar feature, {@code true};
60 63
  * otherwise, {@code false}.
61 64
  */
62
-export function isCalendarEnabled() {
65
+export function isCalendarEnabled(stateful: Function | Object) {
66
+    const flag = getFeatureFlag(stateful, CALENDAR_ENABLED);
67
+
68
+    if (typeof flag !== 'undefined') {
69
+        return flag;
70
+    }
71
+
63 72
     const { calendarEnabled = true } = NativeModules.AppInfo;
64 73
 
65 74
     return calendarEnabled;

+ 11
- 7
react/features/calendar-sync/functions.web.js 查看文件

@@ -16,22 +16,26 @@ import {
16 16
 import { _updateCalendarEntries } from './functions';
17 17
 import { googleCalendarApi } from './web/googleCalendar';
18 18
 import { microsoftCalendarApi } from './web/microsoftCalendar';
19
+import { toState } from '../base/redux';
19 20
 
20 21
 const logger = require('jitsi-meet-logger').getLogger(__filename);
21 22
 
22
-declare var config: Object;
23
-
24 23
 /**
25 24
  * Determines whether the calendar feature is enabled by the web.
26 25
  *
26
+ * @param {Function|Object} stateful - The redux store or {@code getState}
27
+ * function.
27 28
  * @returns {boolean} If the app has enabled the calendar feature, {@code true};
28 29
  * otherwise, {@code false}.
29 30
  */
30
-export function isCalendarEnabled() {
31
-    return Boolean(
32
-        config.enableCalendarIntegration
33
-            && (config.googleApiApplicationClientID
34
-                || config.microsoftApiApplicationClientID));
31
+export function isCalendarEnabled(stateful: Function | Object) {
32
+    const {
33
+        enableCalendarIntegration,
34
+        googleApiApplicationClientID,
35
+        microsoftApiApplicationClientID
36
+    } = toState(stateful)['features/base/config'] || {};
37
+
38
+    return Boolean(enableCalendarIntegration && (googleApiApplicationClientID || microsoftApiApplicationClientID));
35 39
 }
36 40
 
37 41
 /* eslint-disable no-unused-vars */

+ 37
- 33
react/features/calendar-sync/middleware.js 查看文件

@@ -9,51 +9,55 @@ import { setCalendarAuthorization } from './actions';
9 9
 import { REFRESH_CALENDAR } from './actionTypes';
10 10
 import { _fetchCalendarEntries, isCalendarEnabled } from './functions';
11 11
 
12
-isCalendarEnabled()
13
-    && MiddlewareRegistry.register(store => next => action => {
14
-        switch (action.type) {
15
-        case ADD_KNOWN_DOMAINS: {
16
-            // XXX Fetch new calendar entries only when an actual domain has
17
-            // become known.
18
-            const { getState } = store;
19
-            const oldValue = getState()['features/base/known-domains'];
20
-            const result = next(action);
21
-            const newValue = getState()['features/base/known-domains'];
12
+MiddlewareRegistry.register(store => next => action => {
13
+    const { getState } = store;
22 14
 
23
-            equals(oldValue, newValue)
24
-                || _fetchCalendarEntries(store, false, false);
15
+    if (!isCalendarEnabled(getState)) {
16
+        return next(action);
17
+    }
25 18
 
26
-            return result;
27
-        }
19
+    switch (action.type) {
20
+    case ADD_KNOWN_DOMAINS: {
21
+        // XXX Fetch new calendar entries only when an actual domain has
22
+        // become known.
23
+        const oldValue = getState()['features/base/known-domains'];
24
+        const result = next(action);
25
+        const newValue = getState()['features/base/known-domains'];
28 26
 
29
-        case APP_STATE_CHANGED: {
30
-            const result = next(action);
27
+        equals(oldValue, newValue)
28
+            || _fetchCalendarEntries(store, false, false);
31 29
 
32
-            _maybeClearAccessStatus(store, action);
30
+        return result;
31
+    }
33 32
 
34
-            return result;
35
-        }
33
+    case APP_STATE_CHANGED: {
34
+        const result = next(action);
36 35
 
37
-        case SET_CONFIG: {
38
-            const result = next(action);
36
+        _maybeClearAccessStatus(store, action);
39 37
 
40
-            _fetchCalendarEntries(store, false, false);
38
+        return result;
39
+    }
41 40
 
42
-            return result;
43
-        }
41
+    case SET_CONFIG: {
42
+        const result = next(action);
44 43
 
45
-        case REFRESH_CALENDAR: {
46
-            const result = next(action);
44
+        _fetchCalendarEntries(store, false, false);
47 45
 
48
-            _fetchCalendarEntries(
49
-                store, action.isInteractive, action.forcePermission);
46
+        return result;
47
+    }
50 48
 
51
-            return result;
52
-        }
53
-        }
49
+    case REFRESH_CALENDAR: {
50
+        const result = next(action);
54 51
 
55
-        return next(action);
56
-    });
52
+        _fetchCalendarEntries(
53
+            store, action.isInteractive, action.forcePermission);
54
+
55
+        return result;
56
+    }
57
+    }
58
+
59
+    return next(action);
60
+});
57 61
 
58 62
 /**
59 63
  * Clears the calendar access status when the app comes back from the

+ 43
- 41
react/features/calendar-sync/reducer.js 查看文件

@@ -44,52 +44,54 @@ const STORE_NAME = 'features/calendar-sync';
44 44
  * runtime value to see if we need to re-request the calendar permission from
45 45
  * the user.
46 46
  */
47
-isCalendarEnabled()
48
-    && PersistenceRegistry.register(STORE_NAME, {
49
-        integrationType: true,
50
-        msAuthState: true
51
-    });
52
-
53
-isCalendarEnabled()
54
-    && ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
55
-        switch (action.type) {
56
-        case CLEAR_CALENDAR_INTEGRATION:
57
-            return DEFAULT_STATE;
58
-
59
-        case SET_CALENDAR_AUTH_STATE: {
60
-            if (!action.msAuthState) {
61
-                // received request to delete the state
62
-                return set(state, 'msAuthState', undefined);
63
-            }
64
-
65
-            return set(state, 'msAuthState', {
66
-                ...state.msAuthState,
67
-                ...action.msAuthState
68
-            });
47
+PersistenceRegistry.register(STORE_NAME, {
48
+    integrationType: true,
49
+    msAuthState: true
50
+});
51
+
52
+ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
53
+    if (!isCalendarEnabled(state)) {
54
+        return state;
55
+    }
56
+
57
+    switch (action.type) {
58
+    case CLEAR_CALENDAR_INTEGRATION:
59
+        return DEFAULT_STATE;
60
+
61
+    case SET_CALENDAR_AUTH_STATE: {
62
+        if (!action.msAuthState) {
63
+            // received request to delete the state
64
+            return set(state, 'msAuthState', undefined);
69 65
         }
70 66
 
71
-        case SET_CALENDAR_AUTHORIZATION:
72
-            return set(state, 'authorization', action.authorization);
67
+        return set(state, 'msAuthState', {
68
+            ...state.msAuthState,
69
+            ...action.msAuthState
70
+        });
71
+    }
73 72
 
74
-        case SET_CALENDAR_ERROR:
75
-            return set(state, 'error', action.error);
73
+    case SET_CALENDAR_AUTHORIZATION:
74
+        return set(state, 'authorization', action.authorization);
76 75
 
77
-        case SET_CALENDAR_EVENTS:
78
-            return set(state, 'events', action.events);
76
+    case SET_CALENDAR_ERROR:
77
+        return set(state, 'error', action.error);
79 78
 
80
-        case SET_CALENDAR_INTEGRATION:
81
-            return {
82
-                ...state,
83
-                integrationReady: action.integrationReady,
84
-                integrationType: action.integrationType
85
-            };
79
+    case SET_CALENDAR_EVENTS:
80
+        return set(state, 'events', action.events);
86 81
 
87
-        case SET_CALENDAR_PROFILE_EMAIL:
88
-            return set(state, 'profileEmail', action.email);
82
+    case SET_CALENDAR_INTEGRATION:
83
+        return {
84
+            ...state,
85
+            integrationReady: action.integrationReady,
86
+            integrationType: action.integrationType
87
+        };
89 88
 
90
-        case SET_LOADING_CALENDAR_EVENTS:
91
-            return set(state, 'isLoadingEvents', action.isLoadingEvents);
92
-        }
89
+    case SET_CALENDAR_PROFILE_EMAIL:
90
+        return set(state, 'profileEmail', action.email);
93 91
 
94
-        return state;
95
-    });
92
+    case SET_LOADING_CALENDAR_EVENTS:
93
+        return set(state, 'isLoadingEvents', action.isLoadingEvents);
94
+    }
95
+
96
+    return state;
97
+});

+ 19
- 4
react/features/conference/components/native/Conference.js 查看文件

@@ -13,7 +13,7 @@ import {
13 13
     makeAspectRatioAware
14 14
 } from '../../../base/responsive-ui';
15 15
 import { TestConnectionInfo } from '../../../base/testing';
16
-import { ConferenceNotification } from '../../../calendar-sync';
16
+import { ConferenceNotification, isCalendarEnabled } from '../../../calendar-sync';
17 17
 import { Chat } from '../../../chat';
18 18
 import { DisplayNameLabel } from '../../../display-name';
19 19
 import {
@@ -42,6 +42,13 @@ import type { AbstractProps } from '../AbstractConference';
42 42
  */
43 43
 type Props = AbstractProps & {
44 44
 
45
+    /**
46
+     * Wherther the calendar feature is enabled or not.
47
+     *
48
+     * @private
49
+     */
50
+    _calendarEnabled: boolean,
51
+
45 52
     /**
46 53
      * The indicator which determines that we are still connecting to the
47 54
      * conference which includes establishing the XMPP connection and then
@@ -331,10 +338,10 @@ class Conference extends AbstractConference<Props, *> {
331 338
      * @returns {React$Node}
332 339
      */
333 340
     _renderConferenceNotification() {
334
-        // XXX If the calendar feature is disabled on a platform, then we don't
335
-        // have its components exported so an undefined check is necessary.
341
+        const { _calendarEnabled, _reducedUI } = this.props;
342
+
336 343
         return (
337
-            !this.props._reducedUI && ConferenceNotification
344
+            _calendarEnabled && !_reducedUI
338 345
                 ? <ConferenceNotification />
339 346
                 : undefined);
340 347
     }
@@ -417,6 +424,14 @@ function _mapStateToProps(state) {
417 424
     return {
418 425
         ...abstractMapStateToProps(state),
419 426
 
427
+        /**
428
+         * Wherther the calendar feature is enabled or not.
429
+         *
430
+         * @private
431
+         * @type {boolean}
432
+         */
433
+        _calendarEnabled: isCalendarEnabled(state),
434
+
420 435
         /**
421 436
          * The indicator which determines that we are still connecting to the
422 437
          * conference which includes establishing the XMPP connection and then

+ 1
- 1
react/features/settings/components/web/CalendarTab.js 查看文件

@@ -285,7 +285,7 @@ function _mapStateToProps(state) {
285 285
         googleApiApplicationClientID,
286 286
         microsoftApiApplicationClientID
287 287
     } = state['features/base/config'];
288
-    const calendarEnabled = isCalendarEnabled();
288
+    const calendarEnabled = isCalendarEnabled(state);
289 289
 
290 290
     return {
291 291
         _appName: interfaceConfig.APP_NAME,

+ 1
- 1
react/features/settings/components/web/SettingsDialog.js 查看文件

@@ -135,7 +135,7 @@ function _mapStateToProps(state) {
135 135
     const showProfileSettings
136 136
         = configuredTabs.includes('profile') && jwt.isGuest;
137 137
     const showCalendarSettings
138
-        = configuredTabs.includes('calendar') && isCalendarEnabled();
138
+        = configuredTabs.includes('calendar') && isCalendarEnabled(state);
139 139
     const tabs = [];
140 140
 
141 141
     if (showDeviceSettings) {

+ 8
- 0
react/features/welcome/components/AbstractWelcomePage.js 查看文件

@@ -6,6 +6,7 @@ import type { Dispatch } from 'redux';
6 6
 
7 7
 import { createWelcomePageEvent, sendAnalytics } from '../../analytics';
8 8
 import { appNavigate } from '../../app';
9
+import { isCalendarEnabled } from '../../calendar-sync';
9 10
 import { isRoomValid } from '../../base/conference';
10 11
 
11 12
 /**
@@ -13,6 +14,11 @@ import { isRoomValid } from '../../base/conference';
13 14
  */
14 15
 type Props = {
15 16
 
17
+    /**
18
+     * Whether the calendar functionality is enabled or not.
19
+     */
20
+    _calendarEnabled: boolean,
21
+
16 22
     /**
17 23
      * Room name to join to.
18 24
      */
@@ -237,12 +243,14 @@ export class AbstractWelcomePage extends Component<Props, *> {
237 243
  * @param {Object} state - The redux state.
238 244
  * @protected
239 245
  * @returns {{
246
+ *     _calendarEnabled: boolean,
240 247
  *     _room: string,
241 248
  *     _settings: Object
242 249
  * }}
243 250
  */
244 251
 export function _mapStateToProps(state: Object) {
245 252
     return {
253
+        _calendarEnabled: isCalendarEnabled(state),
246 254
         _room: state['features/base/conference'].room,
247 255
         _settings: state['features/base/settings']
248 256
     };

+ 2
- 2
react/features/welcome/components/WelcomePage.web.js 查看文件

@@ -225,11 +225,11 @@ class WelcomePage extends AbstractWelcomePage {
225 225
             return null;
226 226
         }
227 227
 
228
-        const { t } = this.props;
228
+        const { _calendarEnabled, t } = this.props;
229 229
 
230 230
         const tabs = [];
231 231
 
232
-        if (CalendarList) {
232
+        if (_calendarEnabled) {
233 233
             tabs.push({
234 234
                 label: t('welcomepage.calendar'),
235 235
                 content: <CalendarList />

+ 28
- 31
react/features/welcome/components/WelcomePageLists.js 查看文件

@@ -5,7 +5,7 @@ import React, { Component } from 'react';
5 5
 import { translate } from '../../base/i18n';
6 6
 import { PagedList } from '../../base/react';
7 7
 import { connect } from '../../base/redux';
8
-import { CalendarList } from '../../calendar-sync';
8
+import { CalendarList, isCalendarEnabled } from '../../calendar-sync';
9 9
 import { RecentList } from '../../recent-list';
10 10
 
11 11
 import { setWelcomePageListsDefaultPage } from '../actions';
@@ -15,6 +15,11 @@ import { setWelcomePageListsDefaultPage } from '../actions';
15 15
  */
16 16
 type Props = {
17 17
 
18
+    /**
19
+     * Whether the calendar functionality is enabled or not.
20
+     */
21
+    _calendarEnabled: boolean,
22
+
18 23
     /**
19 24
      * The stored default page index.
20 25
      */
@@ -40,19 +45,6 @@ type Props = {
40 45
  * Implements the lists displayed on the mobile welcome screen.
41 46
  */
42 47
 class WelcomePageLists extends Component<Props> {
43
-    /**
44
-     * The pages to be rendered.
45
-     *
46
-     * Note: An element's  {@code component} may be {@code undefined} if a
47
-     * feature (such as Calendar) is disabled, and that means that the page must
48
-     * not be rendered.
49
-     */
50
-    pages: Array<{
51
-        component: ?Object,
52
-        icon: string | number,
53
-        title: string
54
-    }>;
55
-
56 48
     /**
57 49
      * Initializes a new {@code WelcomePageLists} instance.
58 50
      *
@@ -61,21 +53,6 @@ class WelcomePageLists extends Component<Props> {
61 53
     constructor(props) {
62 54
         super(props);
63 55
 
64
-        const { t } = props;
65
-
66
-        this.pages = [
67
-            {
68
-                component: RecentList,
69
-                icon: 'restore',
70
-                title: t('welcomepage.recentList')
71
-            },
72
-            {
73
-                component: CalendarList,
74
-                icon: 'event_note',
75
-                title: t('welcomepage.calendar')
76
-            }
77
-        ];
78
-
79 56
         // Bind event handlers so they are only bound once per instance.
80 57
         this._onSelectPage = this._onSelectPage.bind(this);
81 58
     }
@@ -86,18 +63,36 @@ class WelcomePageLists extends Component<Props> {
86 63
      * @inheritdoc
87 64
      */
88 65
     render() {
89
-        const { _defaultPage } = this.props;
66
+        const { _calendarEnabled, _defaultPage, t } = this.props;
90 67
 
91 68
         if (typeof _defaultPage === 'undefined') {
92 69
             return null;
93 70
         }
94 71
 
72
+        const pages = [
73
+            {
74
+                component: RecentList,
75
+                icon: 'restore',
76
+                title: t('welcomepage.recentList')
77
+            }
78
+        ];
79
+
80
+        if (_calendarEnabled) {
81
+            pages.push(
82
+                {
83
+                    component: CalendarList,
84
+                    icon: 'event_note',
85
+                    title: t('welcomepage.calendar')
86
+                }
87
+            );
88
+        }
89
+
95 90
         return (
96 91
             <PagedList
97 92
                 defaultPage = { _defaultPage }
98 93
                 disabled = { this.props.disabled }
99 94
                 onSelectPage = { this._onSelectPage }
100
-                pages = { this.pages } />
95
+                pages = { pages } />
101 96
         );
102 97
     }
103 98
 
@@ -122,6 +117,7 @@ class WelcomePageLists extends Component<Props> {
122 117
  * @param {Object} state - The redux state.
123 118
  * @protected
124 119
  * @returns {{
120
+ *     _calendarEnabled: boolean,
125 121
  *     _defaultPage: number
126 122
  * }}
127 123
  */
@@ -135,6 +131,7 @@ function _mapStateToProps(state: Object) {
135 131
     }
136 132
 
137 133
     return {
134
+        _calendarEnabled: isCalendarEnabled(state),
138 135
         _defaultPage: defaultPage
139 136
     };
140 137
 }

Loading…
取消
儲存