[RN] Change default WelcomeScreen tab and persist user choice
This commit is contained in:
parent
4ab8d98cd1
commit
dcfebf746f
|
@ -23,6 +23,11 @@ type Props = {
|
|||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* Callback to execute on page change.
|
||||
*/
|
||||
onSelectPage: ?Function,
|
||||
|
||||
/**
|
||||
* The pages of the PagedList component to be rendered.
|
||||
* Note: page.component may be undefined and then they don't need to be
|
||||
|
@ -61,18 +66,12 @@ export default class AbstractPagedList extends Component<Props, State> {
|
|||
}
|
||||
|
||||
/**
|
||||
* Implements React {@code Component}'s componentWillReceiveProps.
|
||||
* Implements React's {@code Component} componentDidMount.
|
||||
*
|
||||
* @inheritdoc
|
||||
*/
|
||||
componentWillReceiveProps(newProps: Props) {
|
||||
const { defaultPage } = newProps;
|
||||
|
||||
if (defaultPage !== this.props.defaultPage) {
|
||||
// Default page changed due to a redux update. This is likely to
|
||||
// happen after APP_WILL_MOUNT. So we update the active tab.
|
||||
this._platformSpecificPageSelect(defaultPage);
|
||||
}
|
||||
componentDidMount() {
|
||||
this._maybeRefreshActivePage();
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -119,6 +118,26 @@ export default class AbstractPagedList extends Component<Props, State> {
|
|||
this._selectPage(pageIndex);
|
||||
}
|
||||
|
||||
_maybeRefreshActivePage: () => void
|
||||
|
||||
/**
|
||||
* Components that this PagedList displays may have a refresh function to
|
||||
* refresh its content when displayed (or based on custom logic). This
|
||||
* function invokes this logic if it's present.
|
||||
*
|
||||
* @private
|
||||
* @returns {void}
|
||||
*/
|
||||
_maybeRefreshActivePage() {
|
||||
const selectedPage = this.props.pages[this.state.pageIndex];
|
||||
|
||||
if (selectedPage && selectedPage.component) {
|
||||
const { refresh } = selectedPage.component;
|
||||
|
||||
typeof refresh === 'function' && refresh(this.props.dispatch);
|
||||
}
|
||||
}
|
||||
|
||||
_renderPagedList: boolean => React$Node;
|
||||
|
||||
_selectPage: number => void;
|
||||
|
@ -133,19 +152,15 @@ export default class AbstractPagedList extends Component<Props, State> {
|
|||
_selectPage(pageIndex: number) {
|
||||
const validatedPageIndex = this._validatePageIndex(pageIndex);
|
||||
|
||||
const { onSelectPage } = this.props;
|
||||
|
||||
if (typeof onSelectPage === 'function') {
|
||||
onSelectPage(validatedPageIndex);
|
||||
}
|
||||
|
||||
this.setState({
|
||||
pageIndex: validatedPageIndex
|
||||
});
|
||||
|
||||
// The page's Component may have a refresh(dispatch) function which we
|
||||
// invoke when the page is selected.
|
||||
const selectedPage = this.props.pages[validatedPageIndex];
|
||||
|
||||
if (selectedPage && selectedPage.component) {
|
||||
const { refresh } = selectedPage.component;
|
||||
|
||||
typeof refresh === 'function' && refresh(this.props.dispatch);
|
||||
}
|
||||
}, () => this._maybeRefreshActivePage());
|
||||
}
|
||||
|
||||
_validatePageIndex: number => number
|
||||
|
|
|
@ -96,14 +96,21 @@ class MeetingList extends Component<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { disabled } = this.props;
|
||||
const { _authorization, disabled } = this.props;
|
||||
|
||||
return (
|
||||
<NavigateSectionList
|
||||
disabled = { disabled }
|
||||
onPress = { this._onPress }
|
||||
onRefresh = { this._onRefresh }
|
||||
renderListEmptyComponent = { this._getRenderListEmptyComponent }
|
||||
|
||||
// If we don't provide a list specific renderListEmptyComponent,
|
||||
// then the default empty component of the NavigateSectionList
|
||||
// will be rendered, which (atm) is a simple "Pull to refresh"
|
||||
// message.
|
||||
renderListEmptyComponent
|
||||
= { _authorization === 'denied'
|
||||
? this._getRenderListEmptyComponent() : undefined }
|
||||
sections = { this._toDisplayableList() } />
|
||||
);
|
||||
}
|
||||
|
@ -115,29 +122,25 @@ class MeetingList extends Component<Props> {
|
|||
* of the default one in the {@link NavigateSectionList}.
|
||||
*
|
||||
* @private
|
||||
* @returns {Component}
|
||||
* @returns {Function}
|
||||
*/
|
||||
_getRenderListEmptyComponent() {
|
||||
const { _authorization, t } = this.props;
|
||||
const { t } = this.props;
|
||||
|
||||
if (_authorization === 'denied') {
|
||||
return (
|
||||
<View style = { styles.noPermissionMessageView }>
|
||||
<Text style = { styles.noPermissionMessageText }>
|
||||
{ t('calendarSync.permissionMessage') }
|
||||
return (
|
||||
<View style = { styles.noPermissionMessageView }>
|
||||
<Text style = { styles.noPermissionMessageText }>
|
||||
{ t('calendarSync.permissionMessage') }
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
onPress = { openSettings }
|
||||
style = { styles.noPermissionMessageButton } >
|
||||
<Text style = { styles.noPermissionMessageButtonText }>
|
||||
{ t('calendarSync.permissionButton') }
|
||||
</Text>
|
||||
<TouchableOpacity
|
||||
onPress = { openSettings }
|
||||
style = { styles.noPermissionMessageButton } >
|
||||
<Text style = { styles.noPermissionMessageButtonText }>
|
||||
{ t('calendarSync.permissionButton') }
|
||||
</Text>
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
return null;
|
||||
</TouchableOpacity>
|
||||
</View>
|
||||
);
|
||||
}
|
||||
|
||||
_onPress: string => Function;
|
||||
|
|
|
@ -45,7 +45,7 @@ CALENDAR_ENABLED
|
|||
case SET_CALENDAR_AUTHORIZATION:
|
||||
return {
|
||||
...state,
|
||||
authorization: action.status
|
||||
authorization: action.authorization
|
||||
};
|
||||
|
||||
case SET_CALENDAR_EVENTS:
|
||||
|
|
|
@ -1,3 +1,5 @@
|
|||
// @flow
|
||||
|
||||
/**
|
||||
* The type of the (redux) action which sets the visibility of
|
||||
* {@link WelcomePageSideBar}.
|
||||
|
@ -8,3 +10,15 @@
|
|||
* }
|
||||
*/
|
||||
export const SET_SIDEBAR_VISIBLE = Symbol('SET_SIDEBAR_VISIBLE');
|
||||
|
||||
/**
|
||||
* Action to update the default page index of the {@code WelcomePageLists}
|
||||
* component.
|
||||
*
|
||||
* {
|
||||
* type: SET_WELCOME_PAGE_LIST_DEFAULT_PAGE,
|
||||
* pageIndex: number
|
||||
* }
|
||||
*/
|
||||
export const SET_WELCOME_PAGE_LIST_DEFAULT_PAGE
|
||||
= Symbol('SET_WELCOME_PAGE_LIST_DEFAULT_PAGE');
|
||||
|
|
|
@ -1,6 +1,26 @@
|
|||
// @flow
|
||||
|
||||
import { SET_SIDEBAR_VISIBLE } from './actionTypes';
|
||||
import {
|
||||
SET_SIDEBAR_VISIBLE,
|
||||
SET_WELCOME_PAGE_LIST_DEFAULT_PAGE
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* Action to update the default page index of the {@code WelcomePageLists}
|
||||
* component.
|
||||
*
|
||||
* @param {number} pageIndex - The index of the selected page.
|
||||
* @returns {{
|
||||
* type: SET_WELCOME_PAGE_LIST_DEFAULT_PAGE,
|
||||
* pageIndex: number
|
||||
* }}
|
||||
*/
|
||||
export function setWelcomePageListDefaultPage(pageIndex: number) {
|
||||
return {
|
||||
type: SET_WELCOME_PAGE_LIST_DEFAULT_PAGE,
|
||||
pageIndex
|
||||
};
|
||||
}
|
||||
|
||||
/**
|
||||
* Sets the visibility of {@link WelcomePageSideBar}.
|
||||
|
|
|
@ -9,18 +9,25 @@ import { PagedList } from '../../base/react';
|
|||
import { MeetingList } from '../../calendar-sync';
|
||||
import { RecentList } from '../../recent-list';
|
||||
|
||||
import { setWelcomePageListDefaultPage } from '../actions';
|
||||
|
||||
type Props = {
|
||||
|
||||
/**
|
||||
* True if the calendar feature has fetched entries, false otherwise
|
||||
* The stored default page index.
|
||||
*/
|
||||
_hasCalendarEntries: boolean,
|
||||
_defaultPage: number,
|
||||
|
||||
/**
|
||||
* Renders the lists disabled.
|
||||
*/
|
||||
disabled: boolean,
|
||||
|
||||
/**
|
||||
* The Redux dispatch function.
|
||||
*/
|
||||
dispatch: Function,
|
||||
|
||||
/**
|
||||
* The i18n translate function.
|
||||
*/
|
||||
|
@ -72,6 +79,8 @@ class WelcomePageLists extends Component<Props> {
|
|||
icon: isAndroid ? 'event_note' : IOS_CALENDAR_ICON,
|
||||
title: t('welcomepage.calendar')
|
||||
} ];
|
||||
|
||||
this._onSelectPage = this._onSelectPage.bind(this);
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -80,15 +89,35 @@ class WelcomePageLists extends Component<Props> {
|
|||
* @inheritdoc
|
||||
*/
|
||||
render() {
|
||||
const { disabled, _hasCalendarEntries } = this.props;
|
||||
const { disabled, _defaultPage } = this.props;
|
||||
|
||||
if (typeof _defaultPage === 'undefined') {
|
||||
return null;
|
||||
}
|
||||
|
||||
return (
|
||||
<PagedList
|
||||
defaultPage = { _hasCalendarEntries ? 1 : 0 }
|
||||
defaultPage = { _defaultPage }
|
||||
disabled = { disabled }
|
||||
onSelectPage = { this._onSelectPage }
|
||||
pages = { this.pages } />
|
||||
);
|
||||
}
|
||||
|
||||
_onSelectPage: number => void
|
||||
|
||||
/**
|
||||
* Callback for the {@code PagedList} page select action.
|
||||
*
|
||||
* @private
|
||||
* @param {number} pageIndex - The index of the selected page.
|
||||
* @returns {void}
|
||||
*/
|
||||
_onSelectPage(pageIndex) {
|
||||
const { dispatch } = this.props;
|
||||
|
||||
dispatch(setWelcomePageListDefaultPage(pageIndex));
|
||||
}
|
||||
}
|
||||
|
||||
/**
|
||||
|
@ -98,14 +127,18 @@ class WelcomePageLists extends Component<Props> {
|
|||
* @param {Object} state - The redux state.
|
||||
* @protected
|
||||
* @returns {{
|
||||
* _hasCalendarEntries: boolean
|
||||
* _hasRecentListEntries: boolean
|
||||
* }}
|
||||
*/
|
||||
function _mapStateToProps(state: Object) {
|
||||
const { events } = state['features/calendar-sync'];
|
||||
const { defaultPage } = state['features/welcome'];
|
||||
const recentList = state['features/recent-list'];
|
||||
const _hasRecentListEntries = Boolean(recentList && recentList.length);
|
||||
|
||||
return {
|
||||
_hasCalendarEntries: Boolean(events && events.length)
|
||||
_defaultPage: defaultPage === 'undefined'
|
||||
? _hasRecentListEntries ? 0 : 1
|
||||
: defaultPage
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
@ -1,12 +1,28 @@
|
|||
// @flow
|
||||
|
||||
import { ReducerRegistry } from '../base/redux';
|
||||
import { SET_SIDEBAR_VISIBLE } from './actionTypes';
|
||||
import { PersistenceRegistry } from '../base/storage';
|
||||
import {
|
||||
SET_SIDEBAR_VISIBLE,
|
||||
SET_WELCOME_PAGE_LIST_DEFAULT_PAGE
|
||||
} from './actionTypes';
|
||||
|
||||
/**
|
||||
* The Redux store name this feature uses.
|
||||
*/
|
||||
const STORE_NAME = 'features/welcome';
|
||||
|
||||
/**
|
||||
* Sets up the persistence of the feature {@code features/welcome}.
|
||||
*/
|
||||
PersistenceRegistry.register(STORE_NAME, {
|
||||
defaultPage: true
|
||||
});
|
||||
|
||||
/**
|
||||
* Reduces redux actions for the purposes of {@code features/welcome}.
|
||||
*/
|
||||
ReducerRegistry.register('features/welcome', (state = {}, action) => {
|
||||
ReducerRegistry.register(STORE_NAME, (state = {}, action) => {
|
||||
switch (action.type) {
|
||||
case SET_SIDEBAR_VISIBLE:
|
||||
return {
|
||||
|
@ -14,6 +30,12 @@ ReducerRegistry.register('features/welcome', (state = {}, action) => {
|
|||
sideBarVisible: action.visible
|
||||
};
|
||||
|
||||
case SET_WELCOME_PAGE_LIST_DEFAULT_PAGE:
|
||||
return {
|
||||
...state,
|
||||
defaultPage: action.pageIndex
|
||||
};
|
||||
|
||||
default:
|
||||
return state;
|
||||
}
|
||||
|
|
Loading…
Reference in New Issue