Selaa lähdekoodia

fix(calendar): show error message if authorization fails on event fetch

master
Leonard Kim 7 vuotta sitten
vanhempi
commit
7a2c465c4a

+ 7
- 1
css/_meetings_list.scss Näytä tiedosto

29
         background: #0074E0;
29
         background: #0074E0;
30
         border-radius: 4px;
30
         border-radius: 4px;
31
         color: #FFFFFF;
31
         color: #FFFFFF;
32
-        display: flex;
32
+        display: inline-block;
33
         justify-content: center;
33
         justify-content: center;
34
         align-items: center;
34
         align-items: center;
35
         padding: 5px 10px;
35
         padding: 5px 10px;
36
         cursor: pointer;
36
         cursor: pointer;
37
     }
37
     }
38
 
38
 
39
+    .calendar-action-buttons {
40
+        .button {
41
+            margin: 0px 10px;
42
+        }
43
+    }
44
+
39
     .item {
45
     .item {
40
         background: rgba(255,255,255,0.50);
46
         background: rgba(255,255,255,0.50);
41
         box-sizing: border-box;
47
         box-sizing: border-box;

+ 5
- 0
lang/main.json Näytä tiedosto

662
         "addMeetingURL": "Add a meeting link",
662
         "addMeetingURL": "Add a meeting link",
663
         "confirmAddLink": "Do you want to add a Jitsi link to this event?",
663
         "confirmAddLink": "Do you want to add a Jitsi link to this event?",
664
         "confirmAddLinkTitle": "Calendar",
664
         "confirmAddLinkTitle": "Calendar",
665
+        "error": {
666
+            "appConfiguration": "Calendar integration is not properly configured.",
667
+            "generic": "An error has occurred. Please check your calendar settings or try refreshing the calendar.",
668
+            "notSignedIn": "An error occurred while authenticating to see calendar events. Please check your calendar settings and try logging in again."
669
+        },
665
         "join": "Join",
670
         "join": "Join",
666
         "joinTooltip": "Join the meeting",
671
         "joinTooltip": "Join the meeting",
667
         "nextMeeting": "next meeting",
672
         "nextMeeting": "next meeting",

+ 11
- 0
react/features/calendar-sync/actionTypes.js Näytä tiedosto

33
  */
33
  */
34
 export const SET_CALENDAR_AUTHORIZATION = Symbol('SET_CALENDAR_AUTHORIZATION');
34
 export const SET_CALENDAR_AUTHORIZATION = Symbol('SET_CALENDAR_AUTHORIZATION');
35
 
35
 
36
+/**
37
+ * Action to update the last error that occurred while trying to authenticate
38
+ * with or fetch data from the calendar integration.
39
+ *
40
+ * {
41
+ *     type: SET_CALENDAR_ERROR,
42
+ *     error: ?Object
43
+ * }
44
+ */
45
+export const SET_CALENDAR_ERROR = Symbol('SET_CALENDAR_ERROR');
46
+
36
 /**
47
 /**
37
  * Action to update the current calendar entry list in the store.
48
  * Action to update the current calendar entry list in the store.
38
  *
49
  *

+ 17
- 0
react/features/calendar-sync/actions.web.js Näytä tiedosto

8
 import {
8
 import {
9
     CLEAR_CALENDAR_INTEGRATION,
9
     CLEAR_CALENDAR_INTEGRATION,
10
     SET_CALENDAR_AUTH_STATE,
10
     SET_CALENDAR_AUTH_STATE,
11
+    SET_CALENDAR_ERROR,
11
     SET_CALENDAR_INTEGRATION,
12
     SET_CALENDAR_INTEGRATION,
12
     SET_CALENDAR_PROFILE_EMAIL,
13
     SET_CALENDAR_PROFILE_EMAIL,
13
     SET_LOADING_CALENDAR_EVENTS
14
     SET_LOADING_CALENDAR_EVENTS
119
     };
120
     };
120
 }
121
 }
121
 
122
 
123
+/**
124
+ * Sends an action to update the calendar error state in redux.
125
+ *
126
+ * @param {Object} error - An object with error details.
127
+ * @returns {{
128
+ *     type: SET_CALENDAR_ERROR,
129
+ *     error: Object
130
+ * }}
131
+ */
132
+export function setCalendarError(error: ?Object) {
133
+    return {
134
+        type: SET_CALENDAR_ERROR,
135
+        error
136
+    };
137
+}
138
+
122
 /**
139
 /**
123
  * Sends an action to update the current calendar profile email state in redux.
140
  * Sends an action to update the current calendar profile email state in redux.
124
  *
141
  *

+ 70
- 3
react/features/calendar-sync/components/CalendarList.web.js Näytä tiedosto

13
 } from '../../analytics';
13
 } from '../../analytics';
14
 
14
 
15
 import { refreshCalendar } from '../actions';
15
 import { refreshCalendar } from '../actions';
16
+import { ERRORS } from '../constants';
16
 import { isCalendarEnabled } from '../functions';
17
 import { isCalendarEnabled } from '../functions';
17
 
18
 
18
 import CalendarListContent from './CalendarListContent';
19
 import CalendarListContent from './CalendarListContent';
24
  */
25
  */
25
 type Props = {
26
 type Props = {
26
 
27
 
28
+    /**
29
+     * The error object containing details about any error that has occurred
30
+     * while interacting with calendar integration.
31
+     */
32
+    _calendarError: ?Object,
33
+
27
     /**
34
     /**
28
      * Whether or not a calendar may be connected for fetching calendar events.
35
      * Whether or not a calendar may be connected for fetching calendar events.
29
      */
36
      */
87
         );
94
         );
88
     }
95
     }
89
 
96
 
97
+    /**
98
+     * Returns a component for showing the error message related to calendar
99
+     * sync.
100
+     *
101
+     * @private
102
+     * @returns {React$Component}
103
+     */
104
+    _getErrorMessage() {
105
+        const { _calendarError = {}, t } = this.props;
106
+
107
+        let errorMessageKey = 'calendarSync.error.generic';
108
+        let showRefreshButton = true;
109
+        let showSettingsButton = true;
110
+
111
+        if (_calendarError.error === ERRORS.GOOGLE_APP_MISCONFIGURED) {
112
+            errorMessageKey = 'calendarSync.error.appConfiguration';
113
+            showRefreshButton = false;
114
+            showSettingsButton = false;
115
+        } else if (_calendarError.error === ERRORS.AUTH_FAILED) {
116
+            errorMessageKey = 'calendarSync.error.notSignedIn';
117
+            showRefreshButton = false;
118
+        }
119
+
120
+        return (
121
+            <div className = 'meetings-list-empty'>
122
+                <p className = 'description'>
123
+                    { t(errorMessageKey) }
124
+                </p>
125
+                <div className = 'calendar-action-buttons'>
126
+                    { showSettingsButton
127
+                        && <div
128
+                            className = 'button'
129
+                            onClick = { this._onOpenSettings }>
130
+                            { t('calendarSync.permissionButton') }
131
+                        </div>
132
+                    }
133
+                    { showRefreshButton
134
+                        && <div
135
+                            className = 'button'
136
+                            onClick = { this._onRefreshEvents }>
137
+                            { t('calendarSync.refresh') }
138
+                        </div>
139
+                    }
140
+                </div>
141
+            </div>
142
+        );
143
+    }
144
+
90
     _getRenderListEmptyComponent: () => Object;
145
     _getRenderListEmptyComponent: () => Object;
91
 
146
 
92
     /**
147
     /**
97
      * @returns {React$Component}
152
      * @returns {React$Component}
98
      */
153
      */
99
     _getRenderListEmptyComponent() {
154
     _getRenderListEmptyComponent() {
100
-        const { _hasIntegrationSelected, _hasLoadedEvents, t } = this.props;
155
+        const {
156
+            _calendarError,
157
+            _hasIntegrationSelected,
158
+            _hasLoadedEvents,
159
+            t
160
+        } = this.props;
101
 
161
 
102
-        if (_hasIntegrationSelected && _hasLoadedEvents) {
162
+        if (_calendarError) {
163
+            return this._getErrorMessage();
164
+        } else if (_hasIntegrationSelected && _hasLoadedEvents) {
103
             return (
165
             return (
104
                 <div className = 'meetings-list-empty'>
166
                 <div className = 'meetings-list-empty'>
105
-                    <div>{ t('calendarSync.noEvents') }</div>
167
+                    <p className = 'description'>
168
+                        { t('calendarSync.noEvents') }
169
+                    </p>
106
                     <div
170
                     <div
107
                         className = 'button'
171
                         className = 'button'
108
                         onClick = { this._onRefreshEvents }>
172
                         onClick = { this._onRefreshEvents }>
172
  * @param {Object} state - The Redux state.
236
  * @param {Object} state - The Redux state.
173
  * @private
237
  * @private
174
  * @returns {{
238
  * @returns {{
239
+ *     _calendarError: Object,
175
  *     _hasIntegrationSelected: boolean,
240
  *     _hasIntegrationSelected: boolean,
176
  *     _hasLoadedEvents: boolean
241
  *     _hasLoadedEvents: boolean
177
  * }}
242
  * }}
178
  */
243
  */
179
 function _mapStateToProps(state) {
244
 function _mapStateToProps(state) {
180
     const {
245
     const {
246
+        error,
181
         events,
247
         events,
182
         integrationType,
248
         integrationType,
183
         isLoadingEvents
249
         isLoadingEvents
184
     } = state['features/calendar-sync'];
250
     } = state['features/calendar-sync'];
185
 
251
 
186
     return {
252
     return {
253
+        _calendarError: error,
187
         _hasIntegrationSelected: Boolean(integrationType),
254
         _hasIntegrationSelected: Boolean(integrationType),
188
         _hasLoadedEvents: Boolean(events) || !isLoadingEvents
255
         _hasLoadedEvents: Boolean(events) || !isLoadingEvents
189
     };
256
     };

+ 11
- 0
react/features/calendar-sync/constants.js Näytä tiedosto

10
     MICROSOFT: 'microsoft'
10
     MICROSOFT: 'microsoft'
11
 };
11
 };
12
 
12
 
13
+/**
14
+ * An enumeration of known errors that can occur while interacting with the
15
+ * calendar integration.
16
+ *
17
+ * @enum {string}
18
+ */
19
+export const ERRORS = {
20
+    AUTH_FAILED: 'sign_in_failed',
21
+    GOOGLE_APP_MISCONFIGURED: 'idpiframe_initialization_failed'
22
+};
23
+
13
 /**
24
 /**
14
  * The number of days to fetch.
25
  * The number of days to fetch.
15
  */
26
  */

+ 20
- 4
react/features/calendar-sync/functions.web.js Näytä tiedosto

1
 // @flow
1
 // @flow
2
 
2
 
3
-import { setLoadingCalendarEvents } from './actions';
3
+import {
4
+    clearCalendarIntegration,
5
+    setCalendarError,
6
+    setLoadingCalendarEvents
7
+} from './actions';
4
 export * from './functions.any';
8
 export * from './functions.any';
5
 
9
 
6
 import {
10
 import {
7
     CALENDAR_TYPE,
11
     CALENDAR_TYPE,
12
+    ERRORS,
8
     FETCH_END_DAYS,
13
     FETCH_END_DAYS,
9
     FETCH_START_DAYS
14
     FETCH_START_DAYS
10
 } from './constants';
15
 } from './constants';
66
                 return Promise.resolve();
71
                 return Promise.resolve();
67
             }
72
             }
68
 
73
 
69
-            return Promise.reject('Not authorized, please sign in!');
74
+            return Promise.reject({
75
+                error: ERRORS.AUTH_FAILED
76
+            });
70
         })
77
         })
71
         .then(() => dispatch(integration.getCalendarEntries(
78
         .then(() => dispatch(integration.getCalendarEntries(
72
             FETCH_START_DAYS, FETCH_END_DAYS)))
79
             FETCH_START_DAYS, FETCH_END_DAYS)))
74
             dispatch,
81
             dispatch,
75
             getState
82
             getState
76
         }, events))
83
         }, events))
77
-        .catch(error =>
78
-            logger.error('Error fetching calendar.', error))
84
+        .then(() => {
85
+            dispatch(setCalendarError());
86
+        }, error => {
87
+            logger.error('Error fetching calendar.', error);
88
+
89
+            if (error.error === ERRORS.AUTH_FAILED) {
90
+                dispatch(clearCalendarIntegration());
91
+            }
92
+
93
+            dispatch(setCalendarError(error));
94
+        })
79
         .then(() => dispatch(setLoadingCalendarEvents(false)));
95
         .then(() => dispatch(setLoadingCalendarEvents(false)));
80
 }
96
 }
81
 
97
 

+ 4
- 0
react/features/calendar-sync/reducer.js Näytä tiedosto

8
     CLEAR_CALENDAR_INTEGRATION,
8
     CLEAR_CALENDAR_INTEGRATION,
9
     SET_CALENDAR_AUTH_STATE,
9
     SET_CALENDAR_AUTH_STATE,
10
     SET_CALENDAR_AUTHORIZATION,
10
     SET_CALENDAR_AUTHORIZATION,
11
+    SET_CALENDAR_ERROR,
11
     SET_CALENDAR_EVENTS,
12
     SET_CALENDAR_EVENTS,
12
     SET_CALENDAR_INTEGRATION,
13
     SET_CALENDAR_INTEGRATION,
13
     SET_CALENDAR_PROFILE_EMAIL,
14
     SET_CALENDAR_PROFILE_EMAIL,
86
         case SET_CALENDAR_AUTHORIZATION:
87
         case SET_CALENDAR_AUTHORIZATION:
87
             return set(state, 'authorization', action.authorization);
88
             return set(state, 'authorization', action.authorization);
88
 
89
 
90
+        case SET_CALENDAR_ERROR:
91
+            return set(state, 'error', action.error);
92
+
89
         case SET_CALENDAR_EVENTS:
93
         case SET_CALENDAR_EVENTS:
90
             return set(state, 'events', action.events);
94
             return set(state, 'events', action.events);
91
 
95
 

Loading…
Peruuta
Tallenna