[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,
|
dispatch: Function,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* Callback to execute on page change.
|
||||||
|
*/
|
||||||
|
onSelectPage: ?Function,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The pages of the PagedList component to be rendered.
|
* The pages of the PagedList component to be rendered.
|
||||||
* Note: page.component may be undefined and then they don't need to be
|
* 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
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
componentWillReceiveProps(newProps: Props) {
|
componentDidMount() {
|
||||||
const { defaultPage } = newProps;
|
this._maybeRefreshActivePage();
|
||||||
|
|
||||||
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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -119,6 +118,26 @@ export default class AbstractPagedList extends Component<Props, State> {
|
||||||
this._selectPage(pageIndex);
|
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;
|
_renderPagedList: boolean => React$Node;
|
||||||
|
|
||||||
_selectPage: number => void;
|
_selectPage: number => void;
|
||||||
|
@ -133,19 +152,15 @@ export default class AbstractPagedList extends Component<Props, State> {
|
||||||
_selectPage(pageIndex: number) {
|
_selectPage(pageIndex: number) {
|
||||||
const validatedPageIndex = this._validatePageIndex(pageIndex);
|
const validatedPageIndex = this._validatePageIndex(pageIndex);
|
||||||
|
|
||||||
|
const { onSelectPage } = this.props;
|
||||||
|
|
||||||
|
if (typeof onSelectPage === 'function') {
|
||||||
|
onSelectPage(validatedPageIndex);
|
||||||
|
}
|
||||||
|
|
||||||
this.setState({
|
this.setState({
|
||||||
pageIndex: validatedPageIndex
|
pageIndex: validatedPageIndex
|
||||||
});
|
}, () => this._maybeRefreshActivePage());
|
||||||
|
|
||||||
// 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);
|
|
||||||
}
|
|
||||||
}
|
}
|
||||||
|
|
||||||
_validatePageIndex: number => number
|
_validatePageIndex: number => number
|
||||||
|
|
|
@ -96,14 +96,21 @@ class MeetingList extends Component<Props> {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { disabled } = this.props;
|
const { _authorization, disabled } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<NavigateSectionList
|
<NavigateSectionList
|
||||||
disabled = { disabled }
|
disabled = { disabled }
|
||||||
onPress = { this._onPress }
|
onPress = { this._onPress }
|
||||||
onRefresh = { this._onRefresh }
|
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() } />
|
sections = { this._toDisplayableList() } />
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -115,12 +122,11 @@ class MeetingList extends Component<Props> {
|
||||||
* of the default one in the {@link NavigateSectionList}.
|
* of the default one in the {@link NavigateSectionList}.
|
||||||
*
|
*
|
||||||
* @private
|
* @private
|
||||||
* @returns {Component}
|
* @returns {Function}
|
||||||
*/
|
*/
|
||||||
_getRenderListEmptyComponent() {
|
_getRenderListEmptyComponent() {
|
||||||
const { _authorization, t } = this.props;
|
const { t } = this.props;
|
||||||
|
|
||||||
if (_authorization === 'denied') {
|
|
||||||
return (
|
return (
|
||||||
<View style = { styles.noPermissionMessageView }>
|
<View style = { styles.noPermissionMessageView }>
|
||||||
<Text style = { styles.noPermissionMessageText }>
|
<Text style = { styles.noPermissionMessageText }>
|
||||||
|
@ -137,9 +143,6 @@ class MeetingList extends Component<Props> {
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return null;
|
|
||||||
}
|
|
||||||
|
|
||||||
_onPress: string => Function;
|
_onPress: string => Function;
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
|
|
@ -45,7 +45,7 @@ CALENDAR_ENABLED
|
||||||
case SET_CALENDAR_AUTHORIZATION:
|
case SET_CALENDAR_AUTHORIZATION:
|
||||||
return {
|
return {
|
||||||
...state,
|
...state,
|
||||||
authorization: action.status
|
authorization: action.authorization
|
||||||
};
|
};
|
||||||
|
|
||||||
case SET_CALENDAR_EVENTS:
|
case SET_CALENDAR_EVENTS:
|
||||||
|
|
|
@ -1,3 +1,5 @@
|
||||||
|
// @flow
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The type of the (redux) action which sets the visibility of
|
* The type of the (redux) action which sets the visibility of
|
||||||
* {@link WelcomePageSideBar}.
|
* {@link WelcomePageSideBar}.
|
||||||
|
@ -8,3 +10,15 @@
|
||||||
* }
|
* }
|
||||||
*/
|
*/
|
||||||
export const SET_SIDEBAR_VISIBLE = Symbol('SET_SIDEBAR_VISIBLE');
|
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
|
// @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}.
|
* Sets the visibility of {@link WelcomePageSideBar}.
|
||||||
|
|
|
@ -9,18 +9,25 @@ import { PagedList } from '../../base/react';
|
||||||
import { MeetingList } from '../../calendar-sync';
|
import { MeetingList } from '../../calendar-sync';
|
||||||
import { RecentList } from '../../recent-list';
|
import { RecentList } from '../../recent-list';
|
||||||
|
|
||||||
|
import { setWelcomePageListDefaultPage } from '../actions';
|
||||||
|
|
||||||
type Props = {
|
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.
|
* Renders the lists disabled.
|
||||||
*/
|
*/
|
||||||
disabled: boolean,
|
disabled: boolean,
|
||||||
|
|
||||||
|
/**
|
||||||
|
* The Redux dispatch function.
|
||||||
|
*/
|
||||||
|
dispatch: Function,
|
||||||
|
|
||||||
/**
|
/**
|
||||||
* The i18n translate function.
|
* The i18n translate function.
|
||||||
*/
|
*/
|
||||||
|
@ -72,6 +79,8 @@ class WelcomePageLists extends Component<Props> {
|
||||||
icon: isAndroid ? 'event_note' : IOS_CALENDAR_ICON,
|
icon: isAndroid ? 'event_note' : IOS_CALENDAR_ICON,
|
||||||
title: t('welcomepage.calendar')
|
title: t('welcomepage.calendar')
|
||||||
} ];
|
} ];
|
||||||
|
|
||||||
|
this._onSelectPage = this._onSelectPage.bind(this);
|
||||||
}
|
}
|
||||||
|
|
||||||
/**
|
/**
|
||||||
|
@ -80,15 +89,35 @@ class WelcomePageLists extends Component<Props> {
|
||||||
* @inheritdoc
|
* @inheritdoc
|
||||||
*/
|
*/
|
||||||
render() {
|
render() {
|
||||||
const { disabled, _hasCalendarEntries } = this.props;
|
const { disabled, _defaultPage } = this.props;
|
||||||
|
|
||||||
|
if (typeof _defaultPage === 'undefined') {
|
||||||
|
return null;
|
||||||
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<PagedList
|
<PagedList
|
||||||
defaultPage = { _hasCalendarEntries ? 1 : 0 }
|
defaultPage = { _defaultPage }
|
||||||
disabled = { disabled }
|
disabled = { disabled }
|
||||||
|
onSelectPage = { this._onSelectPage }
|
||||||
pages = { this.pages } />
|
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.
|
* @param {Object} state - The redux state.
|
||||||
* @protected
|
* @protected
|
||||||
* @returns {{
|
* @returns {{
|
||||||
* _hasCalendarEntries: boolean
|
* _hasRecentListEntries: boolean
|
||||||
* }}
|
* }}
|
||||||
*/
|
*/
|
||||||
function _mapStateToProps(state: Object) {
|
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 {
|
return {
|
||||||
_hasCalendarEntries: Boolean(events && events.length)
|
_defaultPage: defaultPage === 'undefined'
|
||||||
|
? _hasRecentListEntries ? 0 : 1
|
||||||
|
: defaultPage
|
||||||
};
|
};
|
||||||
}
|
}
|
||||||
|
|
||||||
|
|
|
@ -1,12 +1,28 @@
|
||||||
// @flow
|
// @flow
|
||||||
|
|
||||||
import { ReducerRegistry } from '../base/redux';
|
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}.
|
* 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) {
|
switch (action.type) {
|
||||||
case SET_SIDEBAR_VISIBLE:
|
case SET_SIDEBAR_VISIBLE:
|
||||||
return {
|
return {
|
||||||
|
@ -14,6 +30,12 @@ ReducerRegistry.register('features/welcome', (state = {}, action) => {
|
||||||
sideBarVisible: action.visible
|
sideBarVisible: action.visible
|
||||||
};
|
};
|
||||||
|
|
||||||
|
case SET_WELCOME_PAGE_LIST_DEFAULT_PAGE:
|
||||||
|
return {
|
||||||
|
...state,
|
||||||
|
defaultPage: action.pageIndex
|
||||||
|
};
|
||||||
|
|
||||||
default:
|
default:
|
||||||
return state;
|
return state;
|
||||||
}
|
}
|
||||||
|
|
Loading…
Reference in New Issue