浏览代码

[RN] Change default WelcomeScreen tab and persist user choice

master
Bettenbuk Zoltan 7 年前
父节点
当前提交
dcfebf746f

+ 35
- 20
react/features/base/react/components/native/AbstractPagedList.js 查看文件

@@ -23,6 +23,11 @@ type Props = {
23 23
      */
24 24
     dispatch: Function,
25 25
 
26
+    /**
27
+     * Callback to execute on page change.
28
+     */
29
+    onSelectPage: ?Function,
30
+
26 31
     /**
27 32
      * The pages of the PagedList component to be rendered.
28 33
      * 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> {
61 66
     }
62 67
 
63 68
     /**
64
-     * Implements React {@code Component}'s componentWillReceiveProps.
69
+     * Implements React's {@code Component} componentDidMount.
65 70
      *
66 71
      * @inheritdoc
67 72
      */
68
-    componentWillReceiveProps(newProps: Props) {
69
-        const { defaultPage } = newProps;
70
-
71
-        if (defaultPage !== this.props.defaultPage) {
72
-            // Default page changed due to a redux update. This is likely to
73
-            // happen after APP_WILL_MOUNT. So we update the active tab.
74
-            this._platformSpecificPageSelect(defaultPage);
75
-        }
73
+    componentDidMount() {
74
+        this._maybeRefreshActivePage();
76 75
     }
77 76
 
78 77
     /**
@@ -119,6 +118,26 @@ export default class AbstractPagedList extends Component<Props, State> {
119 118
         this._selectPage(pageIndex);
120 119
     }
121 120
 
121
+    _maybeRefreshActivePage: () => void
122
+
123
+    /**
124
+     * Components that this PagedList displays may have a refresh function to
125
+     * refresh its content when displayed (or based on custom logic). This
126
+     * function invokes this logic if it's present.
127
+     *
128
+     * @private
129
+     * @returns {void}
130
+     */
131
+    _maybeRefreshActivePage() {
132
+        const selectedPage = this.props.pages[this.state.pageIndex];
133
+
134
+        if (selectedPage && selectedPage.component) {
135
+            const { refresh } = selectedPage.component;
136
+
137
+            typeof refresh === 'function' && refresh(this.props.dispatch);
138
+        }
139
+    }
140
+
122 141
     _renderPagedList: boolean => React$Node;
123 142
 
124 143
     _selectPage: number => void;
@@ -133,19 +152,15 @@ export default class AbstractPagedList extends Component<Props, State> {
133 152
     _selectPage(pageIndex: number) {
134 153
         const validatedPageIndex = this._validatePageIndex(pageIndex);
135 154
 
136
-        this.setState({
137
-            pageIndex: validatedPageIndex
138
-        });
139
-
140
-        // The page's Component may have a refresh(dispatch) function which we
141
-        // invoke when the page is selected.
142
-        const selectedPage = this.props.pages[validatedPageIndex];
155
+        const { onSelectPage } = this.props;
143 156
 
144
-        if (selectedPage && selectedPage.component) {
145
-            const { refresh } = selectedPage.component;
146
-
147
-            typeof refresh === 'function' && refresh(this.props.dispatch);
157
+        if (typeof onSelectPage === 'function') {
158
+            onSelectPage(validatedPageIndex);
148 159
         }
160
+
161
+        this.setState({
162
+            pageIndex: validatedPageIndex
163
+        }, () => this._maybeRefreshActivePage());
149 164
     }
150 165
 
151 166
     _validatePageIndex: number => number

+ 24
- 21
react/features/calendar-sync/components/MeetingList.native.js 查看文件

@@ -96,14 +96,21 @@ class MeetingList extends Component<Props> {
96 96
      * @inheritdoc
97 97
      */
98 98
     render() {
99
-        const { disabled } = this.props;
99
+        const { _authorization, disabled } = this.props;
100 100
 
101 101
         return (
102 102
             <NavigateSectionList
103 103
                 disabled = { disabled }
104 104
                 onPress = { this._onPress }
105 105
                 onRefresh = { this._onRefresh }
106
-                renderListEmptyComponent = { this._getRenderListEmptyComponent }
106
+
107
+                // If we don't provide a list specific renderListEmptyComponent,
108
+                // then the default empty component of the NavigateSectionList
109
+                // will be rendered, which (atm) is a simple "Pull to refresh"
110
+                // message.
111
+                renderListEmptyComponent
112
+                    = { _authorization === 'denied'
113
+                        ? this._getRenderListEmptyComponent() : undefined }
107 114
                 sections = { this._toDisplayableList() } />
108 115
         );
109 116
     }
@@ -115,29 +122,25 @@ class MeetingList extends Component<Props> {
115 122
      * of the default one in the {@link NavigateSectionList}.
116 123
      *
117 124
      * @private
118
-     * @returns {Component}
125
+     * @returns {Function}
119 126
      */
120 127
     _getRenderListEmptyComponent() {
121
-        const { _authorization, t } = this.props;
128
+        const { t } = this.props;
122 129
 
123
-        if (_authorization === 'denied') {
124
-            return (
125
-                <View style = { styles.noPermissionMessageView }>
126
-                    <Text style = { styles.noPermissionMessageText }>
127
-                        { t('calendarSync.permissionMessage') }
130
+        return (
131
+            <View style = { styles.noPermissionMessageView }>
132
+                <Text style = { styles.noPermissionMessageText }>
133
+                    { t('calendarSync.permissionMessage') }
134
+                </Text>
135
+                <TouchableOpacity
136
+                    onPress = { openSettings }
137
+                    style = { styles.noPermissionMessageButton } >
138
+                    <Text style = { styles.noPermissionMessageButtonText }>
139
+                        { t('calendarSync.permissionButton') }
128 140
                     </Text>
129
-                    <TouchableOpacity
130
-                        onPress = { openSettings }
131
-                        style = { styles.noPermissionMessageButton } >
132
-                        <Text style = { styles.noPermissionMessageButtonText }>
133
-                            { t('calendarSync.permissionButton') }
134
-                        </Text>
135
-                    </TouchableOpacity>
136
-                </View>
137
-            );
138
-        }
139
-
140
-        return null;
141
+                </TouchableOpacity>
142
+            </View>
143
+        );
141 144
     }
142 145
 
143 146
     _onPress: string => Function;

+ 1
- 1
react/features/calendar-sync/reducer.js 查看文件

@@ -45,7 +45,7 @@ CALENDAR_ENABLED
45 45
         case SET_CALENDAR_AUTHORIZATION:
46 46
             return {
47 47
                 ...state,
48
-                authorization: action.status
48
+                authorization: action.authorization
49 49
             };
50 50
 
51 51
         case SET_CALENDAR_EVENTS:

+ 14
- 0
react/features/welcome/actionTypes.js 查看文件

@@ -1,3 +1,5 @@
1
+// @flow
2
+
1 3
 /**
2 4
  * The type of the (redux) action which sets the visibility of
3 5
  * {@link WelcomePageSideBar}.
@@ -8,3 +10,15 @@
8 10
  * }
9 11
  */
10 12
 export const SET_SIDEBAR_VISIBLE = Symbol('SET_SIDEBAR_VISIBLE');
13
+
14
+/**
15
+ * Action to update the default page index of the {@code WelcomePageLists}
16
+ * component.
17
+ *
18
+ * {
19
+ *     type: SET_WELCOME_PAGE_LIST_DEFAULT_PAGE,
20
+ *     pageIndex: number
21
+ * }
22
+ */
23
+export const SET_WELCOME_PAGE_LIST_DEFAULT_PAGE
24
+    = Symbol('SET_WELCOME_PAGE_LIST_DEFAULT_PAGE');

+ 21
- 1
react/features/welcome/actions.js 查看文件

@@ -1,6 +1,26 @@
1 1
 // @flow
2 2
 
3
-import { SET_SIDEBAR_VISIBLE } from './actionTypes';
3
+import {
4
+    SET_SIDEBAR_VISIBLE,
5
+    SET_WELCOME_PAGE_LIST_DEFAULT_PAGE
6
+} from './actionTypes';
7
+
8
+/**
9
+ * Action to update the default page index of the {@code WelcomePageLists}
10
+ * component.
11
+ *
12
+ * @param {number} pageIndex - The index of the selected page.
13
+ * @returns {{
14
+ *     type: SET_WELCOME_PAGE_LIST_DEFAULT_PAGE,
15
+ *     pageIndex: number
16
+ * }}
17
+ */
18
+export function setWelcomePageListDefaultPage(pageIndex: number) {
19
+    return {
20
+        type: SET_WELCOME_PAGE_LIST_DEFAULT_PAGE,
21
+        pageIndex
22
+    };
23
+}
4 24
 
5 25
 /**
6 26
  * Sets the visibility of {@link WelcomePageSideBar}.

+ 40
- 7
react/features/welcome/components/WelcomePageLists.js 查看文件

@@ -9,18 +9,25 @@ import { PagedList } from '../../base/react';
9 9
 import { MeetingList } from '../../calendar-sync';
10 10
 import { RecentList } from '../../recent-list';
11 11
 
12
+import { setWelcomePageListDefaultPage } from '../actions';
13
+
12 14
 type Props = {
13 15
 
14 16
     /**
15
-     * True if the calendar feature has fetched entries, false otherwise
17
+     * The stored default page index.
16 18
      */
17
-    _hasCalendarEntries: boolean,
19
+    _defaultPage: number,
18 20
 
19 21
     /**
20 22
      * Renders the lists disabled.
21 23
      */
22 24
     disabled: boolean,
23 25
 
26
+    /**
27
+     * The Redux dispatch function.
28
+     */
29
+    dispatch: Function,
30
+
24 31
     /**
25 32
      * The i18n translate function.
26 33
      */
@@ -72,6 +79,8 @@ class WelcomePageLists extends Component<Props> {
72 79
             icon: isAndroid ? 'event_note' : IOS_CALENDAR_ICON,
73 80
             title: t('welcomepage.calendar')
74 81
         } ];
82
+
83
+        this._onSelectPage = this._onSelectPage.bind(this);
75 84
     }
76 85
 
77 86
     /**
@@ -80,15 +89,35 @@ class WelcomePageLists extends Component<Props> {
80 89
      * @inheritdoc
81 90
      */
82 91
     render() {
83
-        const { disabled, _hasCalendarEntries } = this.props;
92
+        const { disabled, _defaultPage } = this.props;
93
+
94
+        if (typeof _defaultPage === 'undefined') {
95
+            return null;
96
+        }
84 97
 
85 98
         return (
86 99
             <PagedList
87
-                defaultPage = { _hasCalendarEntries ? 1 : 0 }
100
+                defaultPage = { _defaultPage }
88 101
                 disabled = { disabled }
102
+                onSelectPage = { this._onSelectPage }
89 103
                 pages = { this.pages } />
90 104
         );
91 105
     }
106
+
107
+    _onSelectPage: number => void
108
+
109
+    /**
110
+     * Callback for the {@code PagedList} page select action.
111
+     *
112
+     * @private
113
+     * @param {number} pageIndex - The index of the selected page.
114
+     * @returns {void}
115
+     */
116
+    _onSelectPage(pageIndex) {
117
+        const { dispatch } = this.props;
118
+
119
+        dispatch(setWelcomePageListDefaultPage(pageIndex));
120
+    }
92 121
 }
93 122
 
94 123
 /**
@@ -98,14 +127,18 @@ class WelcomePageLists extends Component<Props> {
98 127
  * @param {Object} state - The redux state.
99 128
  * @protected
100 129
  * @returns {{
101
- *     _hasCalendarEntries: boolean
130
+ *     _hasRecentListEntries: boolean
102 131
  * }}
103 132
  */
104 133
 function _mapStateToProps(state: Object) {
105
-    const { events } = state['features/calendar-sync'];
134
+    const { defaultPage } = state['features/welcome'];
135
+    const recentList = state['features/recent-list'];
136
+    const _hasRecentListEntries = Boolean(recentList && recentList.length);
106 137
 
107 138
     return {
108
-        _hasCalendarEntries: Boolean(events && events.length)
139
+        _defaultPage: defaultPage === 'undefined'
140
+            ? _hasRecentListEntries ? 0 : 1
141
+            : defaultPage
109 142
     };
110 143
 }
111 144
 

+ 24
- 2
react/features/welcome/reducer.js 查看文件

@@ -1,12 +1,28 @@
1 1
 // @flow
2 2
 
3 3
 import { ReducerRegistry } from '../base/redux';
4
-import { SET_SIDEBAR_VISIBLE } from './actionTypes';
4
+import { PersistenceRegistry } from '../base/storage';
5
+import {
6
+    SET_SIDEBAR_VISIBLE,
7
+    SET_WELCOME_PAGE_LIST_DEFAULT_PAGE
8
+} from './actionTypes';
9
+
10
+/**
11
+ * The Redux store name this feature uses.
12
+ */
13
+const STORE_NAME = 'features/welcome';
14
+
15
+/**
16
+ * Sets up the persistence of the feature {@code features/welcome}.
17
+ */
18
+PersistenceRegistry.register(STORE_NAME, {
19
+    defaultPage: true
20
+});
5 21
 
6 22
 /**
7 23
  * Reduces redux actions for the purposes of {@code features/welcome}.
8 24
  */
9
-ReducerRegistry.register('features/welcome', (state = {}, action) => {
25
+ReducerRegistry.register(STORE_NAME, (state = {}, action) => {
10 26
     switch (action.type) {
11 27
     case SET_SIDEBAR_VISIBLE:
12 28
         return {
@@ -14,6 +30,12 @@ ReducerRegistry.register('features/welcome', (state = {}, action) => {
14 30
             sideBarVisible: action.visible
15 31
         };
16 32
 
33
+    case SET_WELCOME_PAGE_LIST_DEFAULT_PAGE:
34
+        return {
35
+            ...state,
36
+            defaultPage: action.pageIndex
37
+        };
38
+
17 39
     default:
18 40
         return state;
19 41
     }

正在加载...
取消
保存