Coding style: formatting, naming
This commit is contained in:
parent
eac74aa0b7
commit
f1ab160c62
|
@ -1,24 +1,44 @@
|
|||
// @flow
|
||||
|
||||
/**
|
||||
* Action to signal that calendar access has already been requested
|
||||
* since the app started, so no new request should be done unless the
|
||||
* user explicitly tries to refresh the calendar view.
|
||||
* Action to add a new domain to the list of domain known to the feature
|
||||
* calendar-sync.
|
||||
*
|
||||
* {
|
||||
* type: ADD_KNOWN_DOMAIN,
|
||||
* knownDomain: string
|
||||
* }
|
||||
*/
|
||||
export const CALENDAR_ACCESS_REQUESTED = Symbol('CALENDAR_ACCESS_REQUESTED');
|
||||
|
||||
/**
|
||||
* Action to update the current calendar entry list in the store.
|
||||
*/
|
||||
export const NEW_CALENDAR_ENTRY_LIST = Symbol('NEW_CALENDAR_ENTRY_LIST');
|
||||
|
||||
/**
|
||||
* Action to add a new known domain to the list.
|
||||
*/
|
||||
export const NEW_KNOWN_DOMAIN = Symbol('NEW_KNOWN_DOMAIN');
|
||||
export const ADD_KNOWN_DOMAIN = Symbol('ADD_KNOWN_DOMAIN');
|
||||
|
||||
/**
|
||||
* Action to refresh (re-fetch) the entry list.
|
||||
*
|
||||
* {
|
||||
* type: REFRESH_CALENDAR,
|
||||
* forcePermission: boolean
|
||||
* }
|
||||
*/
|
||||
export const REFRESH_CALENDAR_ENTRY_LIST
|
||||
= Symbol('REFRESH_CALENDAR_ENTRY_LIST');
|
||||
export const REFRESH_CALENDAR = Symbol('REFRESH_CALENDAR');
|
||||
|
||||
/**
|
||||
* Action to signal that calendar access has already been requested since the
|
||||
* app started, so no new request should be done unless the user explicitly
|
||||
* tries to refresh the calendar view.
|
||||
*
|
||||
* {
|
||||
* type: SET_CALENDAR_AUTHORIZATION,
|
||||
* authorization: ?string
|
||||
* }
|
||||
*/
|
||||
export const SET_CALENDAR_AUTHORIZATION = Symbol('SET_CALENDAR_AUTHORIZATION');
|
||||
|
||||
/**
|
||||
* Action to update the current calendar entry list in the store.
|
||||
*
|
||||
* {
|
||||
* type: SET_CALENDAR_EVENTS,
|
||||
* events: Array<Object>
|
||||
* }
|
||||
*/
|
||||
export const SET_CALENDAR_EVENTS = Symbol('SET_CALENDAR_EVENTS');
|
||||
|
|
|
@ -1,58 +1,60 @@
|
|||
// @flow
|
||||
import {
|
||||
CALENDAR_ACCESS_REQUESTED,
|
||||
NEW_CALENDAR_ENTRY_LIST,
|
||||
NEW_KNOWN_DOMAIN,
|
||||
REFRESH_CALENDAR_ENTRY_LIST
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Sends an action to signal that a calendar access has been requested. For
|
||||
* more info see the {@link CALENDAR_ACCESS_REQUESTED}.
|
||||
*
|
||||
* @param {string | undefined} status - The result of the last calendar
|
||||
* access request.
|
||||
* @returns {{
|
||||
* type: CALENDAR_ACCESS_REQUESTED
|
||||
* }}
|
||||
*/
|
||||
export function updateCalendarAccessStatus(status: ?string) {
|
||||
return {
|
||||
status,
|
||||
type: CALENDAR_ACCESS_REQUESTED
|
||||
};
|
||||
}
|
||||
import {
|
||||
SET_CALENDAR_AUTHORIZATION,
|
||||
SET_CALENDAR_EVENTS,
|
||||
ADD_KNOWN_DOMAIN,
|
||||
REFRESH_CALENDAR
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Sends an action to add a new known domain if not present yet.
|
||||
*
|
||||
* @param {string} domainName - The new domain.
|
||||
* @param {string} knownDomain - The new domain.
|
||||
* @returns {{
|
||||
* type: NEW_KNOWN_DOMAIN,
|
||||
* domainName: string
|
||||
* type: ADD_KNOWN_DOMAIN,
|
||||
* knownDomain: string
|
||||
* }}
|
||||
*/
|
||||
export function maybeAddNewKnownDomain(domainName: string) {
|
||||
export function addKnownDomain(knownDomain: string) {
|
||||
return {
|
||||
type: NEW_KNOWN_DOMAIN,
|
||||
domainName
|
||||
type: ADD_KNOWN_DOMAIN,
|
||||
knownDomain
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an action to refresh the entry list (fetches new data).
|
||||
*
|
||||
* @param {boolean|undefined} forcePermission - Whether to force to re-ask
|
||||
* for the permission or not.
|
||||
* @param {boolean|undefined} forcePermission - Whether to force to re-ask for
|
||||
* the permission or not.
|
||||
* @returns {{
|
||||
* type: REFRESH_CALENDAR_ENTRY_LIST,
|
||||
* forcePermission: boolean
|
||||
* type: REFRESH_CALENDAR,
|
||||
* forcePermission: boolean
|
||||
* }}
|
||||
*/
|
||||
export function refreshCalendarEntryList(forcePermission: boolean = false) {
|
||||
export function refreshCalendar(forcePermission: boolean = false) {
|
||||
return {
|
||||
forcePermission,
|
||||
type: REFRESH_CALENDAR_ENTRY_LIST
|
||||
type: REFRESH_CALENDAR,
|
||||
forcePermission
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sends an action to signal that a calendar access has been requested. For more
|
||||
* info, see {@link SET_CALENDAR_AUTHORIZATION}.
|
||||
*
|
||||
* @param {string | undefined} authorization - The result of the last calendar
|
||||
* authorization request.
|
||||
* @returns {{
|
||||
* type: SET_CALENDAR_AUTHORIZATION,
|
||||
* authorization: ?string
|
||||
* }}
|
||||
*/
|
||||
export function setCalendarAuthorization(authorization: ?string) {
|
||||
return {
|
||||
type: SET_CALENDAR_AUTHORIZATION,
|
||||
authorization
|
||||
};
|
||||
}
|
||||
|
||||
|
@ -61,13 +63,13 @@ export function refreshCalendarEntryList(forcePermission: boolean = false) {
|
|||
*
|
||||
* @param {Array<Object>} events - The new list.
|
||||
* @returns {{
|
||||
* type: NEW_CALENDAR_ENTRY_LIST,
|
||||
* events: Array<Object>
|
||||
* type: SET_CALENDAR_EVENTS,
|
||||
* events: Array<Object>
|
||||
* }}
|
||||
*/
|
||||
export function updateCalendarEntryList(events: Array<Object>) {
|
||||
export function setCalendarEvents(events: Array<Object>) {
|
||||
return {
|
||||
type: NEW_CALENDAR_ENTRY_LIST,
|
||||
type: SET_CALENDAR_EVENTS,
|
||||
events
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,16 +1,13 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import {
|
||||
Text,
|
||||
TouchableOpacity,
|
||||
View
|
||||
} from 'react-native';
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import { appNavigate } from '../../app';
|
||||
import { getURLWithoutParamsNormalized } from '../../base/connection';
|
||||
import { getLocalizedDateFormatter, translate } from '../../base/i18n';
|
||||
import { Icon } from '../../base/font-icons';
|
||||
import { getLocalizedDateFormatter, translate } from '../../base/i18n';
|
||||
import { ASPECT_RATIO_NARROW } from '../../base/responsive-ui';
|
||||
|
||||
import styles from './styles';
|
||||
|
@ -19,11 +16,6 @@ const ALERT_MILLISECONDS = 5 * 60 * 1000;
|
|||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* The current aspect ratio of the screen.
|
||||
*/
|
||||
|
@ -39,6 +31,11 @@ type Props = {
|
|||
*/
|
||||
_eventList: Array<Object>,
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* The translate function.
|
||||
*/
|
||||
|
@ -54,8 +51,8 @@ type State = {
|
|||
};
|
||||
|
||||
/**
|
||||
* Component to display a permanent badge-like notification on the
|
||||
* conference screen when another meeting is about to start.
|
||||
* Component to display a permanent badge-like notification on the conference
|
||||
* screen when another meeting is about to start.
|
||||
*/
|
||||
class ConferenceNotification extends Component<Props, State> {
|
||||
updateIntervalId: number;
|
||||
|
@ -103,7 +100,7 @@ class ConferenceNotification extends Component<Props, State> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements the React Components's render method.
|
||||
* Implements the React Components's render.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
|
@ -223,16 +220,14 @@ class ConferenceNotification extends Component<Props, State> {
|
|||
const now = Date.now();
|
||||
|
||||
for (const event of _eventList) {
|
||||
const eventUrl = getURLWithoutParamsNormalized(
|
||||
new URL(event.url)
|
||||
);
|
||||
const eventUrl
|
||||
= getURLWithoutParamsNormalized(new URL(event.url));
|
||||
|
||||
if (eventUrl !== _currentConferenceURL) {
|
||||
if ((!eventToShow
|
||||
&& event.startDate > now
|
||||
&& event.startDate < now + ALERT_MILLISECONDS)
|
||||
|| (event.startDate < now && event.endDate > now)
|
||||
) {
|
||||
&& event.startDate > now
|
||||
&& event.startDate < now + ALERT_MILLISECONDS)
|
||||
|| (event.startDate < now && event.endDate > now)) {
|
||||
eventToShow = event;
|
||||
}
|
||||
}
|
||||
|
@ -249,8 +244,8 @@ class ConferenceNotification extends Component<Props, State> {
|
|||
/**
|
||||
* Opens the meeting URL that the notification shows.
|
||||
*
|
||||
* @private
|
||||
* @param {string} url - The URL to open.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_onGoToNext() {
|
||||
|
@ -260,7 +255,6 @@ class ConferenceNotification extends Component<Props, State> {
|
|||
this.props.dispatch(appNavigate(event.url));
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -268,9 +262,9 @@ class ConferenceNotification extends Component<Props, State> {
|
|||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {{
|
||||
* _aspectRatio: Symbol,
|
||||
* _currentConferenceURL: string,
|
||||
* _eventList: Array
|
||||
* _aspectRatio: Symbol,
|
||||
* _currentConferenceURL: string,
|
||||
* _eventList: Array
|
||||
* }}
|
||||
*/
|
||||
export function _mapStateToProps(state: Object) {
|
||||
|
@ -279,8 +273,7 @@ export function _mapStateToProps(state: Object) {
|
|||
return {
|
||||
_aspectRatio: state['features/base/responsive-ui'].aspectRatio,
|
||||
_currentConferenceURL:
|
||||
locationURL
|
||||
? getURLWithoutParamsNormalized(locationURL) : '',
|
||||
locationURL ? getURLWithoutParamsNormalized(locationURL) : '',
|
||||
_eventList: state['features/calendar-sync'].events
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,19 +1,29 @@
|
|||
// @flow
|
||||
|
||||
import React, { Component } from 'react';
|
||||
import { Text, TouchableOpacity, View } from 'react-native';
|
||||
import { connect } from 'react-redux';
|
||||
|
||||
import styles from './styles';
|
||||
|
||||
import { refreshCalendarEntryList } from '../actions';
|
||||
|
||||
import { appNavigate } from '../../app';
|
||||
import { getLocalizedDateFormatter, translate } from '../../base/i18n';
|
||||
import { NavigateSectionList } from '../../base/react';
|
||||
import { openSettings } from '../../mobile/permissions';
|
||||
|
||||
import { refreshCalendar } from '../actions';
|
||||
import styles from './styles';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* The current state of the calendar access permission.
|
||||
*/
|
||||
_authorization: string,
|
||||
|
||||
/**
|
||||
* The calendar event list.
|
||||
*/
|
||||
_eventList: Array<Object>,
|
||||
|
||||
/**
|
||||
* Indicates if the list is disabled or not.
|
||||
*/
|
||||
|
@ -25,23 +35,13 @@ type Props = {
|
|||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Tells the component if it's being displayed at the moment, or not.
|
||||
* Note: as an example, on Android it can happen that the component
|
||||
* is rendered but not displayed, because components like ViewPagerAndroid
|
||||
* render their children even if they are not visible at the moment.
|
||||
* Tells the component if it's being displayed at the moment, or not. Note:
|
||||
* as an example, on Android it can happen that the component is rendered
|
||||
* but not displayed, because components like ViewPagerAndroid render their
|
||||
* children even if they are not visible at the moment.
|
||||
*/
|
||||
displayed: boolean,
|
||||
|
||||
/**
|
||||
* The current state of the calendar access permission.
|
||||
*/
|
||||
_calendarAccessStatus: string,
|
||||
|
||||
/**
|
||||
* The calendar event list.
|
||||
*/
|
||||
_eventList: Array<Object>,
|
||||
|
||||
/**
|
||||
* The translate function.
|
||||
*/
|
||||
|
@ -70,7 +70,7 @@ class MeetingList extends Component<Props> {
|
|||
const { dispatch, displayed } = props;
|
||||
|
||||
if (displayed) {
|
||||
dispatch(refreshCalendarEntryList());
|
||||
dispatch(refreshCalendar());
|
||||
}
|
||||
|
||||
this._getRenderListEmptyComponent
|
||||
|
@ -93,7 +93,7 @@ class MeetingList extends Component<Props> {
|
|||
if (newProps.displayed && !displayed) {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(refreshCalendarEntryList());
|
||||
dispatch(refreshCalendar());
|
||||
}
|
||||
}
|
||||
|
||||
|
@ -110,9 +110,7 @@ class MeetingList extends Component<Props> {
|
|||
disabled = { disabled }
|
||||
onPress = { this._onPress }
|
||||
onRefresh = { this._onRefresh }
|
||||
renderListEmptyComponent = {
|
||||
this._getRenderListEmptyComponent
|
||||
}
|
||||
renderListEmptyComponent = { this._getRenderListEmptyComponent }
|
||||
sections = { this._toDisplayableList() } />
|
||||
);
|
||||
}
|
||||
|
@ -127,9 +125,9 @@ class MeetingList extends Component<Props> {
|
|||
* @returns {Component}
|
||||
*/
|
||||
_getRenderListEmptyComponent() {
|
||||
const { _calendarAccessStatus, t } = this.props;
|
||||
const { _authorization, t } = this.props;
|
||||
|
||||
if (_calendarAccessStatus === 'denied') {
|
||||
if (_authorization === 'denied') {
|
||||
return (
|
||||
<View style = { styles.noPermissionMessageView }>
|
||||
<Text style = { styles.noPermissionMessageText }>
|
||||
|
@ -159,9 +157,7 @@ class MeetingList extends Component<Props> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onPress(url) {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(appNavigate(url));
|
||||
this.props.dispatch(appNavigate(url));
|
||||
}
|
||||
|
||||
_onRefresh: () => void
|
||||
|
@ -173,9 +169,7 @@ class MeetingList extends Component<Props> {
|
|||
* @returns {void}
|
||||
*/
|
||||
_onRefresh() {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(refreshCalendarEntryList(true));
|
||||
this.props.dispatch(refreshCalendar(true));
|
||||
}
|
||||
|
||||
_toDisplayableItem: Object => Object
|
||||
|
@ -183,8 +177,8 @@ class MeetingList extends Component<Props> {
|
|||
/**
|
||||
* Creates a displayable object from an event.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} event - The calendar event.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
_toDisplayableItem(event) {
|
||||
|
@ -202,8 +196,7 @@ class MeetingList extends Component<Props> {
|
|||
_toDisplayableList: () => Array<Object>
|
||||
|
||||
/**
|
||||
* Transforms the event list to a displayable list
|
||||
* with sections.
|
||||
* Transforms the event list to a displayable list with sections.
|
||||
*
|
||||
* @private
|
||||
* @returns {Array<Object>}
|
||||
|
@ -259,8 +252,8 @@ class MeetingList extends Component<Props> {
|
|||
/**
|
||||
* Generates a date (interval) string for a given event.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} event - The event.
|
||||
* @private
|
||||
* @returns {string}
|
||||
*/
|
||||
_toDateString(event) {
|
||||
|
@ -278,14 +271,14 @@ class MeetingList extends Component<Props> {
|
|||
*
|
||||
* @param {Object} state - The redux state.
|
||||
* @returns {{
|
||||
* _eventList: Array
|
||||
* _eventList: Array
|
||||
* }}
|
||||
*/
|
||||
export function _mapStateToProps(state: Object) {
|
||||
const calendarSyncState = state['features/calendar-sync'];
|
||||
|
||||
return {
|
||||
_calendarAccessStatus: calendarSyncState.calendarAccessStatus,
|
||||
_authorization: calendarSyncState.authorization,
|
||||
_eventList: calendarSyncState.events
|
||||
};
|
||||
}
|
||||
|
|
|
@ -1,2 +1,2 @@
|
|||
export { default as MeetingList } from './MeetingList';
|
||||
export { default as ConferenceNotification } from './ConferenceNotification';
|
||||
export { default as MeetingList } from './MeetingList';
|
||||
|
|
|
@ -1,5 +1,5 @@
|
|||
// @flow
|
||||
import Logger from 'jitsi-meet-logger';
|
||||
|
||||
import RNCalendarEvents from 'react-native-calendar-events';
|
||||
|
||||
import { APP_WILL_MOUNT } from '../app';
|
||||
|
@ -8,18 +8,18 @@ import { MiddlewareRegistry } from '../base/redux';
|
|||
import { APP_LINK_SCHEME, parseURIString } from '../base/util';
|
||||
import { APP_STATE_CHANGED } from '../mobile/background';
|
||||
|
||||
|
||||
import {
|
||||
maybeAddNewKnownDomain,
|
||||
updateCalendarAccessStatus,
|
||||
updateCalendarEntryList
|
||||
addKnownDomain,
|
||||
setCalendarAuthorization,
|
||||
setCalendarEvents
|
||||
} from './actions';
|
||||
import { REFRESH_CALENDAR_ENTRY_LIST } from './actionTypes';
|
||||
import { REFRESH_CALENDAR } from './actionTypes';
|
||||
|
||||
const logger = require('jitsi-meet-logger').getLogger(__filename);
|
||||
|
||||
const FETCH_END_DAYS = 10;
|
||||
const FETCH_START_DAYS = -1;
|
||||
const MAX_LIST_LENGTH = 10;
|
||||
const logger = Logger.getLogger(__filename);
|
||||
|
||||
MiddlewareRegistry.register(store => next => action => {
|
||||
const result = next(action);
|
||||
|
@ -28,48 +28,48 @@ MiddlewareRegistry.register(store => next => action => {
|
|||
case APP_STATE_CHANGED:
|
||||
_maybeClearAccessStatus(store, action);
|
||||
break;
|
||||
|
||||
case APP_WILL_MOUNT:
|
||||
_ensureDefaultServer(store);
|
||||
_fetchCalendarEntries(store, false, false);
|
||||
break;
|
||||
case REFRESH_CALENDAR_ENTRY_LIST:
|
||||
|
||||
case REFRESH_CALENDAR:
|
||||
_fetchCalendarEntries(store, true, action.forcePermission);
|
||||
break;
|
||||
|
||||
case SET_ROOM:
|
||||
_parseAndAddDomain(store);
|
||||
_parseAndAddKnownDomain(store);
|
||||
break;
|
||||
}
|
||||
|
||||
return result;
|
||||
});
|
||||
|
||||
/**
|
||||
* Clears the calendar access status when the app comes back from
|
||||
* the background. This is needed as some users may never quit the
|
||||
* app, but puts it into the background and we need to try to request
|
||||
* for a permission as often as possible, but not annoyingly often.
|
||||
* Clears the calendar access status when the app comes back from the
|
||||
* background. This is needed as some users may never quit the app, but puts it
|
||||
* into the background and we need to try to request for a permission as often
|
||||
* as possible, but not annoyingly often.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} store - The redux store.
|
||||
* @param {Object} action - The Redux action.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _maybeClearAccessStatus(store, action) {
|
||||
const { appState } = action;
|
||||
|
||||
function _maybeClearAccessStatus(store, { appState }) {
|
||||
if (appState === 'background') {
|
||||
const { dispatch } = store;
|
||||
|
||||
dispatch(updateCalendarAccessStatus(undefined));
|
||||
store.dispatch(setCalendarAuthorization(undefined));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures calendar access if possible and resolves the promise if it's granted.
|
||||
*
|
||||
* @private
|
||||
* @param {boolean} promptForPermission - Flag to tell the app if it should
|
||||
* prompt for a calendar permission if it wasn't granted yet.
|
||||
* @param {Function} dispatch - The Redux dispatch function.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _ensureCalendarAccess(promptForPermission, dispatch) {
|
||||
|
@ -81,107 +81,96 @@ function _ensureCalendarAccess(promptForPermission, dispatch) {
|
|||
} else if (promptForPermission) {
|
||||
RNCalendarEvents.authorizeEventStore()
|
||||
.then(result => {
|
||||
dispatch(updateCalendarAccessStatus(result));
|
||||
dispatch(setCalendarAuthorization(result));
|
||||
resolve(result === 'authorized');
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
.catch(reject);
|
||||
} else {
|
||||
resolve(false);
|
||||
}
|
||||
})
|
||||
.catch(error => {
|
||||
reject(error);
|
||||
});
|
||||
.catch(reject);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Ensures presence of the default server in the known domains list.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} store - The redux store.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _ensureDefaultServer(store) {
|
||||
const state = store.getState();
|
||||
const defaultURL = parseURIString(
|
||||
state['features/app'].app._getDefaultURL()
|
||||
);
|
||||
function _ensureDefaultServer({ dispatch, getState }) {
|
||||
const state = getState();
|
||||
const defaultURL
|
||||
= parseURIString(state['features/app'].app._getDefaultURL());
|
||||
|
||||
store.dispatch(maybeAddNewKnownDomain(defaultURL.host));
|
||||
dispatch(addKnownDomain(defaultURL.host));
|
||||
}
|
||||
|
||||
/**
|
||||
* Reads the user's calendar and updates the stored entries if need be.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} store - The redux store.
|
||||
* @param {boolean} maybePromptForPermission - Flag to tell the app if it should
|
||||
* prompt for a calendar permission if it wasn't granted yet.
|
||||
* @param {boolean|undefined} forcePermission - Whether to force to re-ask
|
||||
* for the permission or not.
|
||||
* @param {boolean|undefined} forcePermission - Whether to force to re-ask for
|
||||
* the permission or not.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _fetchCalendarEntries(
|
||||
store,
|
||||
{ dispatch, getState },
|
||||
maybePromptForPermission,
|
||||
forcePermission
|
||||
) {
|
||||
const { dispatch } = store;
|
||||
const state = store.getState()['features/calendar-sync'];
|
||||
const { calendarAccessStatus } = state;
|
||||
forcePermission) {
|
||||
const state = getState()['features/calendar-sync'];
|
||||
const promptForPermission
|
||||
= (maybePromptForPermission && !calendarAccessStatus)
|
||||
|| forcePermission;
|
||||
= (maybePromptForPermission && !state.authorization)
|
||||
|| forcePermission;
|
||||
|
||||
_ensureCalendarAccess(promptForPermission, dispatch)
|
||||
.then(accessGranted => {
|
||||
if (accessGranted) {
|
||||
const startDate = new Date();
|
||||
const endDate = new Date();
|
||||
.then(accessGranted => {
|
||||
if (accessGranted) {
|
||||
const startDate = new Date();
|
||||
const endDate = new Date();
|
||||
|
||||
startDate.setDate(startDate.getDate() + FETCH_START_DAYS);
|
||||
endDate.setDate(endDate.getDate() + FETCH_END_DAYS);
|
||||
startDate.setDate(startDate.getDate() + FETCH_START_DAYS);
|
||||
endDate.setDate(endDate.getDate() + FETCH_END_DAYS);
|
||||
|
||||
RNCalendarEvents.fetchAllEvents(
|
||||
startDate.getTime(),
|
||||
endDate.getTime(),
|
||||
[]
|
||||
)
|
||||
.then(events => {
|
||||
const { knownDomains } = state;
|
||||
|
||||
_updateCalendarEntries(events, knownDomains, dispatch);
|
||||
})
|
||||
.catch(error => {
|
||||
logger.error('Error fetching calendar.', error);
|
||||
});
|
||||
} else {
|
||||
logger.warn('Calendar access not granted.');
|
||||
}
|
||||
})
|
||||
.catch(reason => {
|
||||
logger.error('Error accessing calendar.', reason);
|
||||
});
|
||||
RNCalendarEvents.fetchAllEvents(
|
||||
startDate.getTime(),
|
||||
endDate.getTime(),
|
||||
[])
|
||||
.then(events =>
|
||||
_updateCalendarEntries(
|
||||
events,
|
||||
state.knownDomains,
|
||||
dispatch))
|
||||
.catch(error =>
|
||||
logger.error('Error fetching calendar.', error));
|
||||
} else {
|
||||
logger.warn('Calendar access not granted.');
|
||||
}
|
||||
})
|
||||
.catch(reason => {
|
||||
logger.error('Error accessing calendar.', reason);
|
||||
});
|
||||
}
|
||||
|
||||
/**
|
||||
* Retreives a jitsi URL from an event if present.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} event - The event to parse.
|
||||
* @param {Array<string>} knownDomains - The known domain names.
|
||||
* @private
|
||||
* @returns {string}
|
||||
*
|
||||
*/
|
||||
function _getURLFromEvent(event, knownDomains) {
|
||||
const linkTerminatorPattern = '[^\\s<>$]';
|
||||
/* eslint-disable max-len */
|
||||
const urlRegExp
|
||||
= new RegExp(`http(s)?://(${knownDomains.join('|')})/${linkTerminatorPattern}+`, 'gi');
|
||||
/* eslint-enable max-len */
|
||||
= new RegExp(
|
||||
`http(s)?://(${knownDomains.join('|')})/${linkTerminatorPattern}+`,
|
||||
'gi');
|
||||
const schemeRegExp
|
||||
= new RegExp(`${APP_LINK_SCHEME}${linkTerminatorPattern}+`, 'gi');
|
||||
const fieldsToSearch = [
|
||||
|
@ -191,16 +180,13 @@ function _getURLFromEvent(event, knownDomains) {
|
|||
event.notes,
|
||||
event.description
|
||||
];
|
||||
let matchArray;
|
||||
|
||||
for (const field of fieldsToSearch) {
|
||||
if (typeof field === 'string') {
|
||||
if (
|
||||
(matchArray
|
||||
= urlRegExp.exec(field) || schemeRegExp.exec(field))
|
||||
!== null
|
||||
) {
|
||||
return matchArray[0];
|
||||
const matches = urlRegExp.exec(field) || schemeRegExp.exec(field);
|
||||
|
||||
if (matches) {
|
||||
return matches[0];
|
||||
}
|
||||
}
|
||||
}
|
||||
|
@ -209,36 +195,36 @@ function _getURLFromEvent(event, knownDomains) {
|
|||
}
|
||||
|
||||
/**
|
||||
* Retreives the domain name of a room upon join and stores it
|
||||
* in the known domain list, if not present yet.
|
||||
* Retreives the domain name of a room upon join and stores it in the known
|
||||
* domain list, if not present yet.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} store - The redux store.
|
||||
* @private
|
||||
* @returns {Promise}
|
||||
*/
|
||||
function _parseAndAddDomain(store) {
|
||||
const { locationURL } = store.getState()['features/base/connection'];
|
||||
function _parseAndAddKnownDomain({ dispatch, getState }) {
|
||||
const { locationURL } = getState()['features/base/connection'];
|
||||
|
||||
store.dispatch(maybeAddNewKnownDomain(locationURL.host));
|
||||
dispatch(addKnownDomain(locationURL.host));
|
||||
}
|
||||
|
||||
/**
|
||||
* Updates the calendar entries in Redux when new list is received.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} event - An event returned from the native calendar.
|
||||
* @param {Array<string>} knownDomains - The known domain list.
|
||||
* @private
|
||||
* @returns {CalendarEntry}
|
||||
*/
|
||||
function _parseCalendarEntry(event, knownDomains) {
|
||||
if (event) {
|
||||
const jitsiURL = _getURLFromEvent(event, knownDomains);
|
||||
const url = _getURLFromEvent(event, knownDomains);
|
||||
|
||||
if (jitsiURL) {
|
||||
const eventStartDate = Date.parse(event.startDate);
|
||||
const eventEndDate = Date.parse(event.endDate);
|
||||
if (url) {
|
||||
const startDate = Date.parse(event.startDate);
|
||||
const endDate = Date.parse(event.endDate);
|
||||
|
||||
if (isNaN(eventStartDate) || isNaN(eventEndDate)) {
|
||||
if (isNaN(startDate) || isNaN(endDate)) {
|
||||
logger.warn(
|
||||
'Skipping invalid calendar event',
|
||||
event.title,
|
||||
|
@ -247,11 +233,11 @@ function _parseCalendarEntry(event, knownDomains) {
|
|||
);
|
||||
} else {
|
||||
return {
|
||||
endDate: eventEndDate,
|
||||
endDate,
|
||||
id: event.id,
|
||||
startDate: eventStartDate,
|
||||
startDate,
|
||||
title: event.title,
|
||||
url: jitsiURL
|
||||
url
|
||||
};
|
||||
}
|
||||
}
|
||||
|
@ -263,10 +249,10 @@ function _parseCalendarEntry(event, knownDomains) {
|
|||
/**
|
||||
* Updates the calendar entries in Redux when new list is received.
|
||||
*
|
||||
* @private
|
||||
* @param {Array<CalendarEntry>} events - The new event list.
|
||||
* @param {Array<string>} knownDomains - The known domain list.
|
||||
* @param {Function} dispatch - The Redux dispatch function.
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
function _updateCalendarEntries(events, knownDomains, dispatch) {
|
||||
|
@ -283,8 +269,10 @@ function _updateCalendarEntries(events, knownDomains, dispatch) {
|
|||
}
|
||||
}
|
||||
|
||||
dispatch(updateCalendarEntryList(eventList.sort((a, b) =>
|
||||
a.startDate - b.startDate
|
||||
).slice(0, MAX_LIST_LENGTH)));
|
||||
dispatch(
|
||||
setCalendarEvents(
|
||||
eventList
|
||||
.sort((a, b) => a.startDate - b.startDate)
|
||||
.slice(0, MAX_LIST_LENGTH)));
|
||||
}
|
||||
}
|
||||
|
|
|
@ -4,18 +4,18 @@ import { ReducerRegistry } from '../base/redux';
|
|||
import { PersistenceRegistry } from '../base/storage';
|
||||
|
||||
import {
|
||||
CALENDAR_ACCESS_REQUESTED,
|
||||
NEW_CALENDAR_ENTRY_LIST,
|
||||
NEW_KNOWN_DOMAIN
|
||||
ADD_KNOWN_DOMAIN,
|
||||
SET_CALENDAR_AUTHORIZATION,
|
||||
SET_CALENDAR_EVENTS
|
||||
} from './actionTypes';
|
||||
|
||||
const DEFAULT_STATE = {
|
||||
/**
|
||||
* Note: If features/calendar-sync ever gets persisted, do not persist the
|
||||
* calendarAccessStatus value as it's needed to remain a runtime value to
|
||||
* see if we need to re-request the calendar permission from the user.
|
||||
* authorization value as it's needed to remain a runtime value to see if we
|
||||
* need to re-request the calendar permission from the user.
|
||||
*/
|
||||
calendarAccessStatus: undefined,
|
||||
authorization: undefined,
|
||||
events: [],
|
||||
knownDomains: []
|
||||
};
|
||||
|
@ -28,54 +28,62 @@ PersistenceRegistry.register(STORE_NAME, {
|
|||
knownDomains: true
|
||||
});
|
||||
|
||||
ReducerRegistry.register(
|
||||
STORE_NAME,
|
||||
(state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case CALENDAR_ACCESS_REQUESTED:
|
||||
return {
|
||||
...state,
|
||||
calendarAccessStatus: action.status
|
||||
};
|
||||
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
|
||||
switch (action.type) {
|
||||
case ADD_KNOWN_DOMAIN:
|
||||
return _addKnownDomain(state, action);
|
||||
|
||||
case NEW_CALENDAR_ENTRY_LIST:
|
||||
return {
|
||||
...state,
|
||||
events: action.events
|
||||
};
|
||||
case SET_CALENDAR_AUTHORIZATION:
|
||||
return {
|
||||
...state,
|
||||
authorization: action.status
|
||||
};
|
||||
|
||||
case NEW_KNOWN_DOMAIN:
|
||||
return _maybeAddNewDomain(state, action);
|
||||
case SET_CALENDAR_EVENTS:
|
||||
return {
|
||||
...state,
|
||||
events: action.events
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
});
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
});
|
||||
|
||||
/**
|
||||
* Adds a new domain to the known domain list if not present yet.
|
||||
*
|
||||
* @private
|
||||
* @param {Object} state - The redux state.
|
||||
* @param {Object} action - The redux action.
|
||||
* @private
|
||||
* @returns {Object}
|
||||
*/
|
||||
function _maybeAddNewDomain(state, action) {
|
||||
let { domainName } = action;
|
||||
const { knownDomains } = state;
|
||||
function _addKnownDomain(state, action) {
|
||||
let { knownDomain } = action;
|
||||
|
||||
if (domainName && domainName.length) {
|
||||
domainName = domainName.toLowerCase();
|
||||
if (knownDomains.indexOf(domainName) === -1) {
|
||||
knownDomains.push(domainName);
|
||||
if (knownDomain) {
|
||||
knownDomain = knownDomain.toLowerCase();
|
||||
|
||||
let { knownDomains } = state;
|
||||
|
||||
if (knownDomains.indexOf(knownDomain) === -1) {
|
||||
// Add the specified known domain and at the same time avoid
|
||||
// modifying the knownDomains Array instance referenced by the
|
||||
// current redux state.
|
||||
knownDomains = [
|
||||
...state.knownDomains,
|
||||
knownDomain
|
||||
];
|
||||
|
||||
// Ensure the list doesn't exceed a/the maximum size.
|
||||
knownDomains.splice(0, knownDomains.length - MAX_DOMAIN_LIST_SIZE);
|
||||
|
||||
return {
|
||||
...state,
|
||||
knownDomains
|
||||
};
|
||||
}
|
||||
}
|
||||
|
||||
return {
|
||||
...state,
|
||||
knownDomains
|
||||
};
|
||||
return state;
|
||||
}
|
||||
|
|
|
@ -1,13 +1,17 @@
|
|||
// @flow
|
||||
|
||||
import { NativeModules } from 'react-native';
|
||||
import uuid from 'uuid';
|
||||
|
||||
import {
|
||||
createTrackMutedEvent,
|
||||
sendAnalytics
|
||||
} from '../../analytics';
|
||||
import { APP_WILL_MOUNT, APP_WILL_UNMOUNT, appNavigate } from '../../app';
|
||||
import {
|
||||
APP_WILL_MOUNT,
|
||||
APP_WILL_UNMOUNT,
|
||||
appNavigate,
|
||||
getName
|
||||
} from '../../app';
|
||||
import {
|
||||
CONFERENCE_FAILED,
|
||||
CONFERENCE_LEFT,
|
||||
|
@ -97,7 +101,7 @@ function _appWillMount({ dispatch, getState }, next, action) {
|
|||
|
||||
CallKit.setProviderConfiguration({
|
||||
iconTemplateImageName: 'CallKitIcon',
|
||||
localizedName: NativeModules.AppInfo.name
|
||||
localizedName: getName()
|
||||
});
|
||||
|
||||
const context = {
|
||||
|
|
|
@ -1,7 +1,8 @@
|
|||
/* @flow */
|
||||
// @flow
|
||||
|
||||
import { NativeModules, Share } from 'react-native';
|
||||
import { Share } from 'react-native';
|
||||
|
||||
import { getName } from '../app';
|
||||
import { MiddlewareRegistry } from '../base/redux';
|
||||
|
||||
import { endShareRoom } from './actions';
|
||||
|
@ -37,7 +38,7 @@ function _shareRoom(roomURL: string, dispatch: Function) {
|
|||
// review before i18n was introduces in react/. However, I reviewed it
|
||||
// afterwards. Translate the display/human-readable strings.
|
||||
const message = `Click the following link to join the meeting: ${roomURL}`;
|
||||
const title = `${NativeModules.AppInfo.name} Conference`;
|
||||
const title = `${getName()} Conference`;
|
||||
const onFulfilled
|
||||
= (shared: boolean) => dispatch(endShareRoom(roomURL, shared));
|
||||
|
||||
|
|
|
@ -5,7 +5,7 @@ import { View, TabBarIOS } from 'react-native';
|
|||
import { connect } from 'react-redux';
|
||||
|
||||
import { translate } from '../../base/i18n';
|
||||
import { MeetingList, refreshCalendarEntryList } from '../../calendar-sync';
|
||||
import { MeetingList, refreshCalendar } from '../../calendar-sync';
|
||||
import { RecentList } from '../../recent-list';
|
||||
|
||||
import AbstractPagedList from './AbstractPagedList';
|
||||
|
@ -85,10 +85,10 @@ class PagedList extends AbstractPagedList {
|
|||
if (tabIndex === 1) {
|
||||
/**
|
||||
* This is a workaround as TabBarIOS doesn't invoke
|
||||
* componentWillReciveProps on prop change of the
|
||||
* MeetingList component.
|
||||
* componentWillReciveProps on prop change of the MeetingList
|
||||
* component.
|
||||
*/
|
||||
this.props.dispatch(refreshCalendarEntryList());
|
||||
this.props.dispatch(refreshCalendar());
|
||||
}
|
||||
};
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue