Properly gate calendar feature on-off

This commit is contained in:
zbettenbuk 2018-04-16 18:39:26 +02:00 committed by Lyubo Marinov
parent 09482f053b
commit 374e3ccf2c
11 changed files with 216 additions and 170 deletions

View File

@ -267,7 +267,7 @@ class ConferenceNotification extends Component<Props, State> {
* _eventList: Array * _eventList: Array
* }} * }}
*/ */
export function _mapStateToProps(state: Object) { function _mapStateToProps(state: Object) {
const { locationURL } = state['features/base/connection']; const { locationURL } = state['features/base/connection'];
return { return {

View File

@ -10,6 +10,7 @@ import { NavigateSectionList } from '../../base/react';
import { openSettings } from '../../mobile/permissions'; import { openSettings } from '../../mobile/permissions';
import { refreshCalendar } from '../actions'; import { refreshCalendar } from '../actions';
import { CALENDAR_ENABLED } from '../constants';
import styles from './styles'; import styles from './styles';
type Props = { type Props = {
@ -59,6 +60,23 @@ class MeetingList extends Component<Props> {
_eventList: [] _eventList: []
}; };
/**
* Public API method for {@code Component}s rendered in
* {@link AbstractPagedList}. When invoked, refreshes the calendar entries
* in the app.
*
* Note: It is a static method as the {@code Component} may not be
* initialized yet when the UI invokes refresh (e.g. {@link TabBarIOS} tab
* change).
*
* @param {Function} dispatch - The Redux dispatch function.
* @public
* @returns {void}
*/
static refresh(dispatch) {
dispatch(refreshCalendar());
}
/** /**
* Constructor of the MeetingList component. * Constructor of the MeetingList component.
* *
@ -82,21 +100,6 @@ class MeetingList extends Component<Props> {
this._toDateString = this._toDateString.bind(this); this._toDateString = this._toDateString.bind(this);
} }
/**
* Implements React Component's componentWillReceiveProps.
*
* @inheritdoc
*/
componentWillReceiveProps(newProps) {
const { displayed } = this.props;
if (newProps.displayed && !displayed) {
const { dispatch } = this.props;
dispatch(refreshCalendar());
}
}
/** /**
* Implements the React Components's render. * Implements the React Components's render.
* *
@ -266,7 +269,7 @@ class MeetingList extends Component<Props> {
* _eventList: Array * _eventList: Array
* }} * }}
*/ */
export function _mapStateToProps(state: Object) { function _mapStateToProps(state: Object) {
const calendarSyncState = state['features/calendar-sync']; const calendarSyncState = state['features/calendar-sync'];
return { return {
@ -275,4 +278,6 @@ export function _mapStateToProps(state: Object) {
}; };
} }
export default translate(connect(_mapStateToProps)(MeetingList)); export default CALENDAR_ENABLED
? translate(connect(_mapStateToProps)(MeetingList))
: undefined;

View File

@ -0,0 +1,26 @@
// @flow
import { NativeModules } from 'react-native';
/**
* The indicator which determines whether the calendar feature is enabled by the
* app.
*
* @type {boolean}
*/
export const CALENDAR_ENABLED = _isCalendarEnabled();
/**
* Determines whether the calendar feature is enabled by the app. For
* example, Apple through its App Store requires
* {@code NSCalendarsUsageDescription} in the app's Info.plist or App Store
* rejects the app.
*
* @returns {boolean} If the app has enabled the calendar feature, {@code true};
* otherwise, {@code false}.
*/
function _isCalendarEnabled() {
const { calendarEnabled } = NativeModules.AppInfo;
return typeof calendarEnabled === 'undefined' ? true : calendarEnabled;
}

View File

@ -1,4 +1,3 @@
export * from './actions';
export * from './components'; export * from './components';
import './middleware'; import './middleware';

View File

@ -1,6 +1,5 @@
// @flow // @flow
import { NativeModules } from 'react-native';
import RNCalendarEvents from 'react-native-calendar-events'; import RNCalendarEvents from 'react-native-calendar-events';
import { APP_WILL_MOUNT } from '../app'; import { APP_WILL_MOUNT } from '../app';
@ -15,6 +14,7 @@ import {
setCalendarEvents setCalendarEvents
} from './actions'; } from './actions';
import { REFRESH_CALENDAR } from './actionTypes'; import { REFRESH_CALENDAR } from './actionTypes';
import { CALENDAR_ENABLED } from './constants';
const logger = require('jitsi-meet-logger').getLogger(__filename); const logger = require('jitsi-meet-logger').getLogger(__filename);
@ -22,30 +22,31 @@ const FETCH_END_DAYS = 10;
const FETCH_START_DAYS = -1; const FETCH_START_DAYS = -1;
const MAX_LIST_LENGTH = 10; const MAX_LIST_LENGTH = 10;
MiddlewareRegistry.register(store => next => action => { CALENDAR_ENABLED
const result = next(action); && MiddlewareRegistry.register(store => next => action => {
const result = next(action);
switch (action.type) { switch (action.type) {
case APP_STATE_CHANGED: case APP_STATE_CHANGED:
_maybeClearAccessStatus(store, action); _maybeClearAccessStatus(store, action);
break; break;
case APP_WILL_MOUNT: case APP_WILL_MOUNT:
_ensureDefaultServer(store); _ensureDefaultServer(store);
_fetchCalendarEntries(store, false, false); _fetchCalendarEntries(store, false, false);
break; break;
case REFRESH_CALENDAR: case REFRESH_CALENDAR:
_fetchCalendarEntries(store, true, action.forcePermission); _fetchCalendarEntries(store, true, action.forcePermission);
break; break;
case SET_ROOM: case SET_ROOM:
_parseAndAddKnownDomain(store); _parseAndAddKnownDomain(store);
break; break;
} }
return result; return result;
}); });
/** /**
* Clears the calendar access status when the app comes back from the * Clears the calendar access status when the app comes back from the
@ -123,11 +124,6 @@ function _fetchCalendarEntries(
{ dispatch, getState }, { dispatch, getState },
maybePromptForPermission, maybePromptForPermission,
forcePermission) { forcePermission) {
if (!_isCalendarEnabled()) {
// The calendar feature is not enabled.
return;
}
const state = getState()['features/calendar-sync']; const state = getState()['features/calendar-sync'];
const promptForPermission const promptForPermission
= (maybePromptForPermission && !state.authorization) = (maybePromptForPermission && !state.authorization)
@ -203,20 +199,6 @@ function _getURLFromEvent(event, knownDomains) {
return null; return null;
} }
/**
* Determines whether the calendar feature is enabled by the app. For
* example, Apple through its App Store requires NSCalendarsUsageDescription in
* the app's Info.plist or App Store rejects the app.
*
* @returns {boolean} If the app has enabled the calendar feature, {@code true};
* otherwise, {@code false}.
*/
export function _isCalendarEnabled() {
const { calendarEnabled } = NativeModules.AppInfo;
return typeof calendarEnabled === 'undefined' ? true : calendarEnabled;
}
/** /**
* Retrieves the domain name of a room upon join and stores it in the known * Retrieves the domain name of a room upon join and stores it in the known
* domain list, if not present yet. * domain list, if not present yet.

View File

@ -8,6 +8,7 @@ import {
SET_CALENDAR_AUTHORIZATION, SET_CALENDAR_AUTHORIZATION,
SET_CALENDAR_EVENTS SET_CALENDAR_EVENTS
} from './actionTypes'; } from './actionTypes';
import { CALENDAR_ENABLED } from './constants';
const DEFAULT_STATE = { const DEFAULT_STATE = {
/** /**
@ -24,31 +25,33 @@ const MAX_DOMAIN_LIST_SIZE = 10;
const STORE_NAME = 'features/calendar-sync'; const STORE_NAME = 'features/calendar-sync';
PersistenceRegistry.register(STORE_NAME, { CALENDAR_ENABLED
knownDomains: true && PersistenceRegistry.register(STORE_NAME, {
}); knownDomains: true
});
ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => { CALENDAR_ENABLED
switch (action.type) { && ReducerRegistry.register(STORE_NAME, (state = DEFAULT_STATE, action) => {
case ADD_KNOWN_DOMAIN: switch (action.type) {
return _addKnownDomain(state, action); case ADD_KNOWN_DOMAIN:
return _addKnownDomain(state, action);
case SET_CALENDAR_AUTHORIZATION: case SET_CALENDAR_AUTHORIZATION:
return { return {
...state, ...state,
authorization: action.status authorization: action.status
}; };
case SET_CALENDAR_EVENTS: case SET_CALENDAR_EVENTS:
return { return {
...state, ...state,
events: action.events events: action.events
}; };
default: default:
return state; return state;
} }
}); });
/** /**
* Adds a new domain to the known domain list if not present yet. * Adds a new domain to the known domain list if not present yet.

View File

@ -1,53 +0,0 @@
// @flow
import { Component } from 'react';
/**
* The page to be displayed on render.
*/
export const DEFAULT_PAGE = 0;
type Props = {
/**
* Indicates if the list is disabled or not.
*/
disabled: boolean,
/**
* The Redux dispatch function.
*/
dispatch: Function,
/**
* The i18n translate function
*/
t: Function
}
type State = {
/**
* The currently selected page.
*/
pageIndex: number
}
/**
* Abstract class for the platform specific paged lists.
*/
export default class AbstractPagedList extends Component<Props, State> {
/**
* Constructor of the component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this.state = {
pageIndex: DEFAULT_PAGE
};
}
}

View File

@ -0,0 +1,91 @@
// @flow
import React, { Component } from 'react';
import { View } from 'react-native';
import { isCalendarEnabled } from '../../calendar-sync';
import { RecentList } from '../../recent-list';
import styles from './styles';
/**
* The page to be displayed on render.
*/
export const DEFAULT_PAGE = 0;
type Props = {
/**
* Indicates if the list is disabled or not.
*/
disabled: boolean,
/**
* The Redux dispatch function.
*/
dispatch: Function,
/**
* The i18n translate function
*/
t: Function
}
type State = {
/**
* The currently selected page.
*/
pageIndex: number
}
/**
* Abstract class for the platform specific paged lists.
*/
export default class AbstractPagedList extends Component<Props, State> {
/**
* True if the calendar feature is enabled on the platform, false otherwise.
*/
_calendarEnabled: boolean
/**
* Constructor of the component.
*
* @inheritdoc
*/
constructor(props: Props) {
super(props);
this._calendarEnabled = isCalendarEnabled();
this.state = {
pageIndex: DEFAULT_PAGE
};
}
/**
* Renders the component.
*
* @inheritdoc
*/
render() {
const { disabled } = this.props;
return (
<View
style = { [
styles.pagedListContainer,
disabled ? styles.pagedListContainerDisabled : null
] }>
{
(this._calendarEnabled && this._renderPagedList(disabled))
|| <RecentList
disabled = { disabled }
style = { styles.pagedList } />
}
</View>
);
}
_renderPagedList: boolean => Object
}

View File

@ -35,20 +35,17 @@ export default class PagedList extends AbstractPagedList {
} }
/** /**
* Renders the paged list. * Renders the entire paged list if calendar is enabled.
* *
* @inheritdoc * @param {boolean} disabled - True if the rendered lists should be
* disabled.
* @returns {ReactElement}
*/ */
render() { _renderPagedList(disabled) {
const { disabled } = this.props;
const { pageIndex } = this.state; const { pageIndex } = this.state;
return ( return (
<View <View style = { styles.pagedListContainer }>
style = { [
styles.pagedListContainer,
disabled ? styles.pagedListContainerDisabled : null
] }>
<ViewPagerAndroid <ViewPagerAndroid
initialPage = { DEFAULT_PAGE } initialPage = { DEFAULT_PAGE }
onPageSelected = { this._onPageSelected } onPageSelected = { this._onPageSelected }

View File

@ -1,7 +1,7 @@
// @flow // @flow
import React from 'react'; import React from 'react';
import { View, TabBarIOS } from 'react-native'; import { TabBarIOS } from 'react-native';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { translate } from '../../base/i18n'; import { translate } from '../../base/i18n';
@ -31,39 +31,35 @@ class PagedList extends AbstractPagedList {
} }
/** /**
* Renders the paged list. * Renders the entire paged list if calendar is enabled.
* *
* @inheritdoc * @param {boolean} disabled - True if the rendered lists should be
* disabled.
* @returns {ReactElement}
*/ */
render() { _renderPagedList(disabled) {
const { pageIndex } = this.state; const { pageIndex } = this.state;
const { disabled, t } = this.props; const { t } = this.props;
return ( return (
<View <TabBarIOS
style = { [ itemPositioning = 'fill'
styles.pagedListContainer, style = { styles.pagedList }>
disabled ? styles.pagedListContainerDisabled : null <TabBarIOS.Item
] }> onPress = { this._onTabSelected(0) }
<TabBarIOS selected = { pageIndex === 0 }
itemPositioning = 'fill' systemIcon = 'history' >
style = { styles.pagedList }> <RecentList disabled = { disabled } />
<TabBarIOS.Item </TabBarIOS.Item>
onPress = { this._onTabSelected(0) } <TabBarIOS.Item
selected = { pageIndex === 0 } icon = { CALENDAR_ICON }
systemIcon = 'history' > onPress = { this._onTabSelected(1) }
<RecentList disabled = { disabled } /> selected = { pageIndex === 1 }
</TabBarIOS.Item> title = { t('welcomepage.calendar') } >
<TabBarIOS.Item <MeetingList
icon = { CALENDAR_ICON } disabled = { disabled } />
onPress = { this._onTabSelected(1) } </TabBarIOS.Item>
selected = { pageIndex === 1 } </TabBarIOS>
title = { t('welcomepage.calendar') } >
<MeetingList
disabled = { disabled } />
</TabBarIOS.Item>
</TabBarIOS>
</View>
); );
} }