From dcfebf746f6bb81ced5a338dd067efad44093b48 Mon Sep 17 00:00:00 2001 From: Bettenbuk Zoltan Date: Tue, 5 Jun 2018 14:34:40 +0200 Subject: [PATCH] [RN] Change default WelcomeScreen tab and persist user choice --- .../components/native/AbstractPagedList.js | 55 ++++++++++++------- .../components/MeetingList.native.js | 45 ++++++++------- react/features/calendar-sync/reducer.js | 2 +- react/features/welcome/actionTypes.js | 14 +++++ react/features/welcome/actions.js | 22 +++++++- .../welcome/components/WelcomePageLists.js | 47 +++++++++++++--- react/features/welcome/reducer.js | 26 ++++++++- 7 files changed, 159 insertions(+), 52 deletions(-) diff --git a/react/features/base/react/components/native/AbstractPagedList.js b/react/features/base/react/components/native/AbstractPagedList.js index 49545d6ee..0e31df853 100644 --- a/react/features/base/react/components/native/AbstractPagedList.js +++ b/react/features/base/react/components/native/AbstractPagedList.js @@ -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 { } /** - * 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 { 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 { _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 diff --git a/react/features/calendar-sync/components/MeetingList.native.js b/react/features/calendar-sync/components/MeetingList.native.js index fb67b9a5d..ea1be7222 100644 --- a/react/features/calendar-sync/components/MeetingList.native.js +++ b/react/features/calendar-sync/components/MeetingList.native.js @@ -96,14 +96,21 @@ class MeetingList extends Component { * @inheritdoc */ render() { - const { disabled } = this.props; + const { _authorization, disabled } = this.props; return ( ); } @@ -115,29 +122,25 @@ class MeetingList extends Component { * 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 ( - - - { t('calendarSync.permissionMessage') } + return ( + + + { t('calendarSync.permissionMessage') } + + + + { t('calendarSync.permissionButton') } - - - { t('calendarSync.permissionButton') } - - - - ); - } - - return null; + + + ); } _onPress: string => Function; diff --git a/react/features/calendar-sync/reducer.js b/react/features/calendar-sync/reducer.js index 7a302dd31..50ca82707 100644 --- a/react/features/calendar-sync/reducer.js +++ b/react/features/calendar-sync/reducer.js @@ -45,7 +45,7 @@ CALENDAR_ENABLED case SET_CALENDAR_AUTHORIZATION: return { ...state, - authorization: action.status + authorization: action.authorization }; case SET_CALENDAR_EVENTS: diff --git a/react/features/welcome/actionTypes.js b/react/features/welcome/actionTypes.js index be0ea0b6c..f8bb6ecc0 100644 --- a/react/features/welcome/actionTypes.js +++ b/react/features/welcome/actionTypes.js @@ -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'); diff --git a/react/features/welcome/actions.js b/react/features/welcome/actions.js index f6983d8b3..f29cd1148 100644 --- a/react/features/welcome/actions.js +++ b/react/features/welcome/actions.js @@ -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}. diff --git a/react/features/welcome/components/WelcomePageLists.js b/react/features/welcome/components/WelcomePageLists.js index 028fa873e..fd30f4a56 100644 --- a/react/features/welcome/components/WelcomePageLists.js +++ b/react/features/welcome/components/WelcomePageLists.js @@ -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 { 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 { * @inheritdoc */ render() { - const { disabled, _hasCalendarEntries } = this.props; + const { disabled, _defaultPage } = this.props; + + if (typeof _defaultPage === 'undefined') { + return null; + } return ( ); } + + _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 { * @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 }; } diff --git a/react/features/welcome/reducer.js b/react/features/welcome/reducer.js index 87f4c50a2..72a31a4fb 100644 --- a/react/features/welcome/reducer.js +++ b/react/features/welcome/reducer.js @@ -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; }