浏览代码

[RN] Add recent-list feature

master
Lyubo Marinov 7 年前
父节点
当前提交
1e0550c746

+ 3
- 3
css/_font.scss 查看文件

@@ -25,12 +25,12 @@
25 25
     -moz-osx-font-smoothing: grayscale;
26 26
 }
27 27
 
28
-.icon-public:before {
29
-  content: "\e80b";
30
-}
31 28
 .icon-event_note:before {
32 29
     content: "\e616";
33 30
 }
31
+.icon-public:before {
32
+    content: "\e80b";
33
+}
34 34
 .icon-timer:before {
35 35
     content: "\e425";
36 36
 }

+ 9
- 11
react/features/base/lib-jitsi-meet/native/Storage.js 查看文件

@@ -90,22 +90,20 @@ export default class Storage {
90 90
 
91 91
     /**
92 92
      * Returns the value associated with a specific key in this storage in an
93
-     * async manner. This method is required for those cases where we need
94
-     * the stored data but we're not sure yet whether the {@code Storage}
95
-     * is already initialised or not - e.g. on app start.
93
+     * async manner. This method is required for those cases where we need the
94
+     * stored data but we're not sure yet whether the {@code Storage} is already
95
+     * initialised or not - e.g. on app start.
96 96
      *
97
-     * @private
98 97
      * @param {string} key - The name of the key to retrieve the value of.
98
+     * @private
99 99
      * @returns {Promise}
100 100
      */
101 101
     _getItemAsync(key) {
102
-        return new Promise(resolve => {
103
-            AsyncStorage.getItem(
104
-                `${String(this._keyPrefix)}${key}`,
105
-                (error, result) => {
106
-                    resolve(result ? result : null);
107
-                });
108
-        });
102
+        return new Promise(
103
+            resolve =>
104
+                AsyncStorage.getItem(
105
+                    `${String(this._keyPrefix)}${key}`,
106
+                    (error, result) => resolve(result ? result : null)));
109 107
     }
110 108
 
111 109
     /**

+ 41
- 44
react/features/recent-list/components/AbstractRecentList.js 查看文件

@@ -3,51 +3,51 @@
3 3
 import { Component } from 'react';
4 4
 import { ListView } from 'react-native';
5 5
 
6
-import { getRecentRooms } from '../functions';
7
-
8 6
 import { appNavigate } from '../../app';
9 7
 
8
+import { getRecentRooms } from '../functions';
9
+
10 10
 /**
11
- * The type of the React {@code Component} state of {@link AbstractRecentList}.
11
+ * The type of the React {@code Component} props of {@link AbstractRecentList}
12 12
  */
13
-type State = {
13
+type Props = {
14 14
 
15 15
     /**
16
-     *  The {@code ListView.DataSource} to be used for the {@code ListView}.
17
-     *  Its content comes from the native implementation of
18
-     *  {@code window.localStorage}.
16
+     * The redux store's {@code dispatch} function.
19 17
      */
20
-    dataSource: Object
21
-}
18
+    dispatch: Dispatch<*>
19
+};
22 20
 
23 21
 /**
24
- * The type of the React {@code Component} props of {@link AbstractRecentList}
22
+ * The type of the React {@code Component} state of {@link AbstractRecentList}.
25 23
  */
26
-type Props = {
24
+type State = {
27 25
 
28 26
     /**
29
-     * Redux store dispatch function.
27
+     * The {@code ListView.DataSource} to be used for the {@code ListView}. Its
28
+     * content comes from the native implementation of
29
+     * {@code window.localStorage}.
30 30
      */
31
-    dispatch: Dispatch<*>,
32
-}
31
+    dataSource: Object
32
+};
33 33
 
34 34
 /**
35
- * Implements a React {@link Component} which represents the list of
36
- * conferences recently joined, similar to how a list of last dialed
37
- * numbers list would do on a mobile
35
+ * Implements a React {@link Component} which represents the list of conferences
36
+ * recently joined, similar to how a list of last dialed numbers list would do
37
+ * on a mobile device.
38 38
  *
39 39
  * @extends Component
40 40
  */
41 41
 export default class AbstractRecentList extends Component<Props, State> {
42 42
 
43 43
     /**
44
-    * The datasource that backs the {@code ListView}
45
-    */
44
+     * The datasource that backs the {@code ListView}.
45
+     */
46 46
     listDataSource = new ListView.DataSource({
47 47
         rowHasChanged: (r1, r2) =>
48 48
             r1.conference !== r2.conference
49 49
                 && r1.dateTimeStamp !== r2.dateTimeStamp
50
-    });;
50
+    });
51 51
 
52 52
     /**
53 53
      * Initializes a new {@code AbstractRecentList} instance.
@@ -67,35 +67,32 @@ export default class AbstractRecentList extends Component<Props, State> {
67 67
      * @inheritdoc
68 68
      */
69 69
     componentWillMount() {
70
-        // this must be done asynchronously because we don't have the storage
71
-        // initiated on app startup immediately.
72
-        getRecentRooms().then(rooms => {
73
-            this.setState({
74
-                dataSource: this.listDataSource.cloneWithRows(rooms)
75
-            });
76
-        });
70
+        // The following must be done asynchronously because we don't have the
71
+        // storage initiated on app startup immediately.
72
+        getRecentRooms()
73
+            .then(rooms =>
74
+                this.setState({
75
+                    dataSource: this.listDataSource.cloneWithRows(rooms)
76
+                }));
77 77
     }
78 78
 
79 79
     /**
80
-    * Creates a bound onPress action for the list item.
81
-    *
82
-    * @param {string} room - The selected room.
83
-    * @returns {Function}
84
-    */
85
-    _onSelect(room) {
86
-        return this._onJoin.bind(this, room);
80
+     * Joins the selected room.
81
+     *
82
+     * @param {string} room - The selected room.
83
+     * @returns {void}
84
+     */
85
+    _onJoin(room) {
86
+        room && this.props.dispatch(appNavigate(room));
87 87
     }
88 88
 
89 89
     /**
90
-    * Joins the selected room.
91
-    *
92
-    * @param {string} room - The selected room.
93
-    * @returns {void}
94
-    */
95
-    _onJoin(room) {
96
-        if (room) {
97
-            this.props.dispatch(appNavigate(room));
98
-        }
90
+     * Creates a bound onPress action for the list item.
91
+     *
92
+     * @param {string} room - The selected room.
93
+     * @returns {Function}
94
+     */
95
+    _onSelect(room) {
96
+        return this._onJoin.bind(this, room);
99 97
     }
100
-
101 98
 }

+ 91
- 92
react/features/recent-list/components/RecentList.native.js 查看文件

@@ -2,11 +2,11 @@ import React from 'react';
2 2
 import { ListView, Text, TouchableHighlight, View } from 'react-native';
3 3
 import { connect } from 'react-redux';
4 4
 
5
+import { Icon } from '../../base/font-icons';
6
+
5 7
 import AbstractRecentList from './AbstractRecentList';
6 8
 import styles, { UNDERLAY_COLOR } from './styles';
7 9
 
8
-import { Icon } from '../../base/font-icons';
9
-
10 10
 /**
11 11
  * The native container rendering the list of the recently joined rooms.
12 12
  *
@@ -15,11 +15,11 @@ import { Icon } from '../../base/font-icons';
15 15
 class RecentList extends AbstractRecentList {
16 16
     /**
17 17
      * Initializes a new {@code RecentList} instance.
18
-     *
19 18
      */
20 19
     constructor() {
21 20
         super();
22 21
 
22
+        // Bind event handlers so they are only bound once per instance.
23 23
         this._getAvatarStyle = this._getAvatarStyle.bind(this);
24 24
         this._onSelect = this._onSelect.bind(this);
25 25
         this._renderConfDuration = this._renderConfDuration.bind(this);
@@ -28,8 +28,8 @@ class RecentList extends AbstractRecentList {
28 28
     }
29 29
 
30 30
     /**
31
-     * Implements React's {@link Component#render()}. Renders a list of
32
-     * recently joined rooms.
31
+     * Implements React's {@link Component#render()}. Renders a list of recently
32
+     * joined rooms.
33 33
      *
34 34
      * @inheritdoc
35 35
      * @returns {ReactElement}
@@ -50,79 +50,32 @@ class RecentList extends AbstractRecentList {
50 50
     }
51 51
 
52 52
     /**
53
-    * Renders the list of recently joined rooms.
54
-    *
55
-    * @private
56
-    * @param {Object} data - The row data to be rendered.
57
-    * @returns {ReactElement}
58
-    */
59
-    _renderRow(data) {
60
-        return (
61
-            <TouchableHighlight
62
-                onPress = { this._onSelect(data.conference) }
63
-                underlayColor = { UNDERLAY_COLOR } >
64
-                <View style = { styles.row } >
65
-                    <View style = { styles.avatarContainer } >
66
-                        <View style = { this._getAvatarStyle(data) } >
67
-                            <Text style = { styles.avatarContent }>
68
-                                { data.initials }
69
-                            </Text>
70
-                        </View>
71
-                    </View>
72
-                    <View style = { styles.detailsContainer } >
73
-                        <Text
74
-                            numberOfLines = { 1 }
75
-                            style = { styles.roomName }>
76
-                            { data.room }
77
-                        </Text>
78
-                        <View style = { styles.infoWithIcon } >
79
-                            <Icon
80
-                                name = 'event_note'
81
-                                style = { styles.inlineIcon } />
82
-                            <Text style = { styles.date }>
83
-                                { data.dateString }
84
-                            </Text>
85
-                        </View>
86
-                        {
87
-                            this._renderConfDuration(data)
88
-                        }
89
-                        {
90
-                            this._renderServerInfo(data)
91
-                        }
92
-                    </View>
93
-                </View>
94
-            </TouchableHighlight>
95
-        );
96
-    }
97
-
98
-    /**
99
-    * Assembles the style array of the avatar based on if the conference
100
-    * was a home or remote server conference (based on current app setting).
101
-    *
102
-    * @private
103
-    * @param {Object} recentListEntry - The recent list entry being rendered.
104
-    * @returns {Array<Object>}
105
-    */
53
+     * Assembles the style array of the avatar based on if the conference was a
54
+     * home or remote server conference (based on current app setting).
55
+     *
56
+     * @param {Object} recentListEntry - The recent list entry being rendered.
57
+     * @private
58
+     * @returns {Array<Object>}
59
+     */
106 60
     _getAvatarStyle(recentListEntry) {
107 61
         const avatarStyles = [ styles.avatar ];
108 62
 
109 63
         if (recentListEntry.baseURL !== this.props._homeServer) {
110 64
             avatarStyles.push(
111
-                this._getColorForServerName(recentListEntry.serverName)
112
-            );
65
+                this._getColorForServerName(recentListEntry.serverName));
113 66
         }
114 67
 
115 68
         return avatarStyles;
116 69
     }
117 70
 
118 71
     /**
119
-    * Returns a style (color) based on the server name, so then the
120
-    * same server will always be rendered with the same avatar color.
121
-    *
122
-    * @private
123
-    * @param {string} serverName - The recent list entry being rendered.
124
-    * @returns {Object}
125
-    */
72
+     * Returns a style (color) based on the server name, so then the same server
73
+     * will always be rendered with the same avatar color.
74
+     *
75
+     * @param {string} serverName - The recent list entry being rendered.
76
+     * @private
77
+     * @returns {Object}
78
+     */
126 79
     _getColorForServerName(serverName) {
127 80
         let nameHash = 0;
128 81
 
@@ -134,22 +87,21 @@ class RecentList extends AbstractRecentList {
134 87
     }
135 88
 
136 89
     /**
137
-    * Renders the server info component based on if the entry was
138
-    * on a different server or not.
139
-    *
140
-    * @private
141
-    * @param {Object} recentListEntry - The recent list entry being rendered.
142
-    * @returns {ReactElement}
143
-    */
144
-    _renderServerInfo(recentListEntry) {
145
-        if (recentListEntry.baseURL !== this.props._homeServer) {
90
+     * Renders the conference duration if available.
91
+     *
92
+     * @param {Object} recentListEntry - The recent list entry being rendered.
93
+     * @private
94
+     * @returns {ReactElement}
95
+     */
96
+    _renderConfDuration({ conferenceDurationString }) {
97
+        if (conferenceDurationString) {
146 98
             return (
147 99
                 <View style = { styles.infoWithIcon } >
148 100
                     <Icon
149
-                        name = 'public'
101
+                        name = 'timer'
150 102
                         style = { styles.inlineIcon } />
151
-                    <Text style = { styles.serverName }>
152
-                        { recentListEntry.serverName }
103
+                    <Text style = { styles.confLength }>
104
+                        { conferenceDurationString }
153 105
                     </Text>
154 106
                 </View>
155 107
             );
@@ -159,21 +111,22 @@ class RecentList extends AbstractRecentList {
159 111
     }
160 112
 
161 113
     /**
162
-    * Renders the conference duration if available.
163
-    *
164
-    * @private
165
-    * @param {Object} recentListEntry - The recent list entry being rendered.
166
-    * @returns {ReactElement}
167
-    */
168
-    _renderConfDuration(recentListEntry) {
169
-        if (recentListEntry.conferenceDurationString) {
114
+     * Renders the server info component based on if the entry was on a
115
+     * different server or not.
116
+     *
117
+     * @param {Object} recentListEntry - The recent list entry being rendered.
118
+     * @private
119
+     * @returns {ReactElement}
120
+     */
121
+    _renderServerInfo(recentListEntry) {
122
+        if (recentListEntry.baseURL !== this.props._homeServer) {
170 123
             return (
171 124
                 <View style = { styles.infoWithIcon } >
172 125
                     <Icon
173
-                        name = 'timer'
126
+                        name = 'public'
174 127
                         style = { styles.inlineIcon } />
175
-                    <Text style = { styles.confLength }>
176
-                        { recentListEntry.conferenceDurationString }
128
+                    <Text style = { styles.serverName }>
129
+                        { recentListEntry.serverName }
177 130
                     </Text>
178 131
                 </View>
179 132
             );
@@ -181,6 +134,52 @@ class RecentList extends AbstractRecentList {
181 134
 
182 135
         return null;
183 136
     }
137
+
138
+    /**
139
+     * Renders the list of recently joined rooms.
140
+     *
141
+     * @param {Object} data - The row data to be rendered.
142
+     * @private
143
+     * @returns {ReactElement}
144
+     */
145
+    _renderRow(data) {
146
+        return (
147
+            <TouchableHighlight
148
+                onPress = { this._onSelect(data.conference) }
149
+                underlayColor = { UNDERLAY_COLOR } >
150
+                <View style = { styles.row } >
151
+                    <View style = { styles.avatarContainer } >
152
+                        <View style = { this._getAvatarStyle(data) } >
153
+                            <Text style = { styles.avatarContent }>
154
+                                { data.initials }
155
+                            </Text>
156
+                        </View>
157
+                    </View>
158
+                    <View style = { styles.detailsContainer } >
159
+                        <Text
160
+                            numberOfLines = { 1 }
161
+                            style = { styles.roomName }>
162
+                            { data.room }
163
+                        </Text>
164
+                        <View style = { styles.infoWithIcon } >
165
+                            <Icon
166
+                                name = 'event_note'
167
+                                style = { styles.inlineIcon } />
168
+                            <Text style = { styles.date }>
169
+                                { data.dateString }
170
+                            </Text>
171
+                        </View>
172
+                        {
173
+                            this._renderConfDuration(data)
174
+                        }
175
+                        {
176
+                            this._renderServerInfo(data)
177
+                        }
178
+                    </View>
179
+                </View>
180
+            </TouchableHighlight>
181
+        );
182
+    }
184 183
 }
185 184
 
186 185
 /**
@@ -195,8 +194,8 @@ class RecentList extends AbstractRecentList {
195 194
 function _mapStateToProps(state) {
196 195
     return {
197 196
         /**
198
-         * The default server name based on which we determine
199
-         * the render method.
197
+         * The default server name based on which we determine the render
198
+         * method.
200 199
          *
201 200
          * @private
202 201
          * @type {string}

+ 46
- 48
react/features/recent-list/components/styles.js 查看文件

@@ -1,58 +1,57 @@
1
-import {
2
-    createStyleSheet,
3
-    BoxModel
4
-} from '../../base/styles';
1
+import { createStyleSheet, BoxModel } from '../../base/styles';
5 2
 
6 3
 const AVATAR_OPACITY = 0.4;
4
+
7 5
 const AVATAR_SIZE = 65;
6
+
8 7
 const OVERLAY_FONT_COLOR = 'rgba(255, 255, 255, 0.6)';
9 8
 
10 9
 export const UNDERLAY_COLOR = 'rgba(255, 255, 255, 0.2)';
11 10
 
12 11
 /**
13
- * The styles of the React {@code Components} of the feature: recent list
12
+ * The styles of the React {@code Component}s of the feature recent-list i.e.
14 13
  * {@code RecentList}.
15 14
  */
16 15
 export default createStyleSheet({
17 16
 
18 17
     /**
19
-    * The style of the actual avatar
20
-    */
18
+     * The style of the actual avatar.
19
+     */
21 20
     avatar: {
22
-        width: AVATAR_SIZE,
23
-        height: AVATAR_SIZE,
24 21
         alignItems: 'center',
25 22
         backgroundColor: `rgba(23, 160, 219, ${AVATAR_OPACITY})`,
23
+        borderRadius: AVATAR_SIZE,
24
+        height: AVATAR_SIZE,
26 25
         justifyContent: 'center',
27
-        borderRadius: AVATAR_SIZE
26
+        width: AVATAR_SIZE
28 27
     },
29 28
 
30 29
     /**
31
-    * The style of the avatar container that makes the avatar rounded.
32
-    */
30
+     * The style of the avatar container that makes the avatar rounded.
31
+     */
33 32
     avatarContainer: {
33
+        alignItems: 'center',
34 34
         flexDirection: 'row',
35 35
         justifyContent: 'space-around',
36
-        alignItems: 'center',
37 36
         paddingTop: 5
38 37
     },
39 38
 
40 39
     /**
41
-    * Simple {@code Text} content of the avatar (the actual initials)
42
-    */
40
+     * Simple {@code Text} content of the avatar (the actual initials).
41
+     */
43 42
     avatarContent: {
43
+        backgroundColor: 'rgba(0, 0, 0, 0)',
44 44
         color: OVERLAY_FONT_COLOR,
45 45
         fontSize: 32,
46 46
         fontWeight: '100',
47
-        backgroundColor: 'rgba(0, 0, 0, 0)',
48 47
         textAlign: 'center'
49 48
     },
50 49
 
51 50
     /**
52
-    * List of styles of the avatar of a remote meeting
53
-    * (not the default server). The number of colors are limited
54
-    * because they should match nicely.
55
-    */
51
+     * List of styles of the avatar of a remote meeting (not the default
52
+     * server). The number of colors are limited because they should match
53
+     * nicely.
54
+     */
56 55
     avatarRemoteServer1: {
57 56
         backgroundColor: `rgba(232, 105, 156, ${AVATAR_OPACITY})`
58 57
     },
@@ -74,79 +73,78 @@ export default createStyleSheet({
74 73
     },
75 74
 
76 75
     /**
77
-    * Style of the conference length (if rendered)
78
-    */
76
+     * The style of the conference length (if rendered).
77
+     */
79 78
     confLength: {
80 79
         color: OVERLAY_FONT_COLOR,
81 80
         fontWeight: 'normal'
82 81
     },
83 82
 
84 83
     /**
85
-    * This is the top level container style of the list
86
-    */
84
+     * The top level container style of the list.
85
+     */
87 86
     container: {
88 87
         flex: 1
89 88
     },
90 89
 
91 90
     /**
92
-    * Second line of the list (date).
93
-    * May be extended with server name later.
94
-    */
91
+     * Second line of the list (date). May be extended with server name later.
92
+     */
95 93
     date: {
96 94
         color: OVERLAY_FONT_COLOR
97 95
     },
98 96
 
99 97
     /**
100
-    * The style of the details container (right side) of the list
101
-    */
98
+     * The style of the details container (right side) of the list.
99
+     */
102 100
     detailsContainer: {
101
+        alignItems: 'flex-start',
103 102
         flex: 1,
104 103
         flexDirection: 'column',
105 104
         justifyContent: 'center',
106
-        marginLeft: 2 * BoxModel.margin,
107
-        alignItems: 'flex-start'
105
+        marginLeft: 2 * BoxModel.margin
108 106
     },
109 107
 
110 108
     /**
111
-    * The container for an info line with an inline icon.
112
-    */
109
+     * The container for an info line with an inline icon.
110
+     */
113 111
     infoWithIcon: {
112
+        alignItems: 'center',
114 113
         flexDirection: 'row',
115
-        justifyContent: 'flex-start',
116
-        alignItems: 'center'
114
+        justifyContent: 'flex-start'
117 115
     },
118 116
 
119 117
     /**
120
-    * Style of an inline icon in an info line.
121
-    */
118
+     * The style of an inline icon in an info line.
119
+     */
122 120
     inlineIcon: {
123 121
         color: OVERLAY_FONT_COLOR,
124 122
         marginRight: 5
125 123
     },
126 124
 
127 125
     /**
128
-    * First line of the list (room name)
129
-    */
126
+     * First line of the list (room name).
127
+     */
130 128
     roomName: {
129
+        color: OVERLAY_FONT_COLOR,
131 130
         fontSize: 18,
132
-        fontWeight: 'bold',
133
-        color: OVERLAY_FONT_COLOR
131
+        fontWeight: 'bold'
134 132
     },
135 133
 
136 134
     /**
137
-    * The style of one single row in the list
138
-    */
135
+     * The style of one single row in the list.
136
+     */
139 137
     row: {
140
-        padding: 8,
141
-        paddingBottom: 0,
138
+        alignItems: 'center',
142 139
         flex: 1,
143 140
         flexDirection: 'row',
144
-        alignItems: 'center'
141
+        padding: 8,
142
+        paddingBottom: 0
145 143
     },
146 144
 
147 145
     /**
148
-    * Style of the server name component (if rendered)
149
-    */
146
+     * The style of the server name component (if rendered).
147
+     */
150 148
     serverName: {
151 149
         color: OVERLAY_FONT_COLOR,
152 150
         fontWeight: 'normal'

+ 4
- 1
react/features/recent-list/constants.js 查看文件

@@ -1,10 +1,13 @@
1 1
 /**
2 2
  * The max size of the list.
3
+ *
4
+ * @type {number}
3 5
  */
4 6
 export const LIST_SIZE = 30;
5 7
 
6 8
 /**
7
- * The name of the {@code localStorage} item where recent rooms are stored.
9
+ * The name of the {@code window.localStorage} item where recent rooms are
10
+ * stored.
8 11
  *
9 12
  * @type {string}
10 13
  */

+ 65
- 80
react/features/recent-list/functions.js 查看文件

@@ -2,131 +2,116 @@
2 2
 
3 3
 import moment from 'moment';
4 4
 
5
-import { RECENT_URL_STORAGE } from './constants';
6
-
7 5
 import { i18next } from '../base/i18n';
8 6
 import { parseURIString } from '../base/util';
9 7
 
8
+import { RECENT_URL_STORAGE } from './constants';
9
+
10 10
 /**
11
-* Retreives the recent room list and generates all the data needed
12
-* to be displayed.
13
-*
14
-* @returns {Promise} The {@code Promise} to be resolved when the list
15
-* is available.
16
-*/
11
+ * Retreives the recent room list and generates all the data needed to be
12
+ * displayed.
13
+ *
14
+ * @returns {Promise} The {@code Promise} to be resolved when the list is
15
+ * available.
16
+ */
17 17
 export function getRecentRooms(): Promise<Array<Object>> {
18
-    return new Promise(resolve => {
19
-        window.localStorage._getItemAsync(RECENT_URL_STORAGE)
20
-            .then(recentUrls => {
21
-                if (recentUrls) {
22
-                    const recentUrlsObj = JSON.parse(recentUrls);
23
-                    const recentRoomDS = [];
18
+    return new Promise((resolve, reject) =>
19
+        window.localStorage._getItemAsync(RECENT_URL_STORAGE).then(
20
+            /* onFulfilled */ recentURLs => {
21
+                const recentRoomDS = [];
24 22
 
25
-                    for (const entry of recentUrlsObj) {
26
-                        const location = parseURIString(entry.conference);
23
+                if (recentURLs) {
24
+                    for (const e of JSON.parse(recentURLs)) {
25
+                        const location = parseURIString(e.conference);
27 26
 
28 27
                         if (location && location.room && location.hostname) {
29 28
                             recentRoomDS.push({
30 29
                                 baseURL:
31 30
                                     `${location.protocol}//${location.host}`,
32
-                                conference: entry.conference,
33
-                                dateTimeStamp: entry.date,
34
-                                conferenceDuration: entry.conferenceDuration,
35
-                                dateString: _getDateString(
36
-                                    entry.date
37
-                                ),
38
-                                conferenceDurationString: _getLengthString(
39
-                                    entry.conferenceDuration
40
-                                ),
31
+                                conference: e.conference,
32
+                                conferenceDuration: e.conferenceDuration,
33
+                                conferenceDurationString:
34
+                                    _getDurationString(e.conferenceDuration),
35
+                                dateString: _getDateString(e.date),
36
+                                dateTimeStamp: e.date,
41 37
                                 initials: _getInitials(location.room),
42 38
                                 room: location.room,
43 39
                                 serverName: location.hostname
44 40
                             });
45 41
                         }
46 42
                     }
47
-
48
-                    resolve(recentRoomDS.reverse());
49
-                } else {
50
-                    resolve([]);
51 43
                 }
52
-            });
53
-    });
44
+
45
+                resolve(recentRoomDS.reverse());
46
+            },
47
+            /* onRejected */ reject)
48
+    );
54 49
 }
55 50
 
56 51
 /**
57
-* Retreives the recent URL list as a list of objects.
58
-*
59
-* @returns {Array} The list of already stored recent URLs.
60
-*/
61
-export function getRecentUrls() {
62
-    let recentUrls = window.localStorage.getItem(RECENT_URL_STORAGE);
63
-
64
-    if (recentUrls) {
65
-        recentUrls = JSON.parse(recentUrls);
66
-    } else {
67
-        recentUrls = [];
68
-    }
69
-
70
-    return recentUrls;
52
+ * Retreives the recent URL list as a list of objects.
53
+ *
54
+ * @returns {Array} The list of already stored recent URLs.
55
+ */
56
+export function getRecentURLs() {
57
+    const recentURLs = window.localStorage.getItem(RECENT_URL_STORAGE);
58
+
59
+    return recentURLs ? JSON.parse(recentURLs) : [];
71 60
 }
72 61
 
73 62
 /**
74
-* Updates the recent URL list.
75
-*
76
-* @param {Array} recentUrls - The new URL list.
77
-* @returns {void}
78
-*/
79
-export function updaterecentUrls(recentUrls: Array<Object>) {
63
+ * Updates the recent URL list.
64
+ *
65
+ * @param {Array} recentURLs - The new URL list.
66
+ * @returns {void}
67
+ */
68
+export function updateRecentURLs(recentURLs: Array<Object>) {
80 69
     window.localStorage.setItem(
81 70
         RECENT_URL_STORAGE,
82
-        JSON.stringify(recentUrls)
71
+        JSON.stringify(recentURLs)
83 72
     );
84 73
 }
85 74
 
86 75
 /**
87
-* Returns a well formatted date string to be displayed in the list.
88
-*
89
-* @private
90
-* @param {number} dateTimeStamp - The UTC timestamp to be converted to String.
91
-* @returns {string}
92
-*/
76
+ * Returns a well formatted date string to be displayed in the list.
77
+ *
78
+ * @param {number} dateTimeStamp - The UTC timestamp to be converted to String.
79
+ * @private
80
+ * @returns {string}
81
+ */
93 82
 function _getDateString(dateTimeStamp: number) {
94 83
     const date = new Date(dateTimeStamp);
84
+    const m = moment(date).locale(i18next.language);
95 85
 
96 86
     if (date.toDateString() === new Date().toDateString()) {
97
-        // the date is today, we use fromNow format
98
-
99
-        return moment(date)
100
-                .locale(i18next.language)
101
-                .fromNow();
87
+        // The date is today, we use fromNow format.
88
+        return m.fromNow();
102 89
     }
103 90
 
104
-    return moment(date)
105
-            .locale(i18next.language)
106
-            .format('lll');
91
+    return m.format('lll');
107 92
 }
108 93
 
109 94
 /**
110
-* Returns a well formatted duration string to be displayed
111
-* as the conference length.
112
-*
113
-* @private
114
-* @param {number} duration - The duration in MS.
115
-* @returns {string}
116
-*/
117
-function _getLengthString(duration: number) {
95
+ * Returns a well formatted duration string to be displayed as the conference
96
+ * length.
97
+ *
98
+ * @param {number} duration - The duration in MS.
99
+ * @private
100
+ * @returns {string}
101
+ */
102
+function _getDurationString(duration: number) {
118 103
     return moment.duration(duration)
119 104
             .locale(i18next.language)
120 105
             .humanize();
121 106
 }
122 107
 
123 108
 /**
124
-* Returns the initials supposed to be used based on the room name.
125
-*
126
-* @private
127
-* @param {string} room - The room name.
128
-* @returns {string}
129
-*/
109
+ * Returns the initials supposed to be used based on the room name.
110
+ *
111
+ * @param {string} room - The room name.
112
+ * @private
113
+ * @returns {string}
114
+ */
130 115
 function _getInitials(room: string) {
131 116
     return room && room.charAt(0) ? room.charAt(0).toUpperCase() : '?';
132 117
 }

+ 44
- 42
react/features/recent-list/middleware.js 查看文件

@@ -1,16 +1,16 @@
1
-/* @flow */
2
-
3
-import { LIST_SIZE } from './constants';
4
-import { getRecentUrls, updaterecentUrls } from './functions';
1
+// @flow
5 2
 
6 3
 import { CONFERENCE_WILL_LEAVE, SET_ROOM } from '../base/conference';
7 4
 import { MiddlewareRegistry } from '../base/redux';
8 5
 
6
+import { LIST_SIZE } from './constants';
7
+import { getRecentURLs, updateRecentURLs } from './functions';
8
+
9 9
 /**
10
- * Middleware that captures joined rooms so then it can be saved to
11
- * {@code localStorage}
10
+ * Middleware that captures joined rooms so they can be saved into
11
+ * {@code window.localStorage}.
12 12
  *
13
- * @param {Store} store - Redux store.
13
+ * @param {Store} store - The redux store.
14 14
  * @returns {Function}
15 15
  */
16 16
 MiddlewareRegistry.register(store => next => action => {
@@ -26,44 +26,44 @@ MiddlewareRegistry.register(store => next => action => {
26 26
 });
27 27
 
28 28
 /**
29
-* Stores the recently joined room in {@code localStorage}.
29
+* Stores the recently joined room into {@code window.localStorage}.
30 30
 *
31 31
 * @param {Store} store - The redux store in which the specified action is being
32 32
 * dispatched.
33
-* @param {Dispatch} next - The redux dispatch function to dispatch the
33
+* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
34 34
 * specified action to the specified store.
35
-* @param {Action} action - The redux action CONFERENCE_JOINED which is being
35
+* @param {Action} action - The redux action {@code SET_ROOM} which is being
36 36
 * dispatched in the specified store.
37
+* @private
37 38
 * @returns {Object} The new state that is the result of the reduction of the
38 39
 * specified action.
39 40
 */
40 41
 function _storeJoinedRoom(store, next, action) {
41 42
     const result = next(action);
43
+
42 44
     const { room } = action;
43 45
 
44 46
     if (room) {
45 47
         const { locationURL } = store.getState()['features/base/connection'];
46
-        const conferenceLink = locationURL.href;
47
-
48
-        // if the current conference is already in the list,
49
-        // we remove it to add it
50
-        // to the top at the end
51
-        const recentUrls = getRecentUrls().filter(
52
-            entry => entry.conference !== conferenceLink
53
-        );
54
-
55
-        // please note, this is a reverse sorted array
56
-        // (newer elements at the end)
57
-        recentUrls.push({
58
-            conference: conferenceLink,
59
-            date: Date.now(),
60
-            conferenceDuration: 0
48
+        const conference = locationURL.href;
49
+
50
+        // If the current conference is already in the list, we remove it to add
51
+        // it to the top at the end.
52
+        const recentURLs
53
+            = getRecentURLs()
54
+                .filter(e => e.conference !== conference);
55
+
56
+        // XXX This is a reverse sorted array (i.e. newer elements at the end).
57
+        recentURLs.push({
58
+            conference,
59
+            conferenceDuration: 0,
60
+            date: Date.now()
61 61
         });
62 62
 
63 63
         // maximising the size
64
-        recentUrls.splice(0, recentUrls.length - LIST_SIZE);
64
+        recentURLs.splice(0, recentURLs.length - LIST_SIZE);
65 65
 
66
-        updaterecentUrls(recentUrls);
66
+        updateRecentURLs(recentURLs);
67 67
     }
68 68
 
69 69
     return result;
@@ -72,33 +72,35 @@ function _storeJoinedRoom(store, next, action) {
72 72
 /**
73 73
 * Updates the conference length when left.
74 74
 *
75
-* @private
76 75
 * @param {Store} store - The redux store in which the specified action is being
77 76
 * dispatched.
78
-* @param {Dispatch} next - The redux dispatch function to dispatch the
77
+* @param {Dispatch} next - The redux {@code dispatch} function to dispatch the
79 78
 * specified action to the specified store.
80
-* @param {Action} action - The redux action CONFERENCE_JOINED which is being
81
-* dispatched in the specified store.
79
+* @param {Action} action - The redux action {@code CONFERENCE_WILL_LEAVE} which
80
+* is being dispatched in the specified store.
81
+* @private
82 82
 * @returns {Object} The new state that is the result of the reduction of the
83 83
 * specified action.
84 84
 */
85
-function _updateConferenceDuration(store, next, action) {
85
+function _updateConferenceDuration({ getState }, next, action) {
86 86
     const result = next(action);
87
-    const { locationURL } = store.getState()['features/base/connection'];
87
+
88
+    const { locationURL } = getState()['features/base/connection'];
88 89
 
89 90
     if (locationURL && locationURL.href) {
90
-        const recentUrls = getRecentUrls();
91
+        const recentURLs = getRecentURLs();
91 92
 
92
-        if (recentUrls.length > 0
93
-            && recentUrls[recentUrls.length - 1].conference
94
-                === locationURL.href) {
95
-            // the last conference start was stored
96
-            // so we need to update the length
93
+        if (recentURLs.length > 0) {
94
+            const mostRecentURL = recentURLs[recentURLs.length - 1];
97 95
 
98
-            recentUrls[recentUrls.length - 1].conferenceDuration
99
-                = Date.now() - recentUrls[recentUrls.length - 1].date;
96
+            if (mostRecentURL.conference === locationURL.href) {
97
+                // The last conference start was stored so we need to update the
98
+                // length.
99
+                mostRecentURL.conferenceDuration
100
+                    = Date.now() - mostRecentURL.date;
100 101
 
101
-            updaterecentUrls(recentUrls);
102
+                updateRecentURLs(recentURLs);
103
+            }
102 104
         }
103 105
     }
104 106
 

+ 4
- 4
react/features/welcome/components/WelcomePage.native.js 查看文件

@@ -2,10 +2,6 @@ import React from 'react';
2 2
 import { TextInput, TouchableHighlight, View } from 'react-native';
3 3
 import { connect } from 'react-redux';
4 4
 
5
-import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
6
-import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
7
-import styles, { PLACEHOLDER_TEXT_COLOR } from './styles';
8
-
9 5
 import { translate } from '../../base/i18n';
10 6
 import { MEDIA_TYPE } from '../../base/media';
11 7
 import { Link, LoadingIndicator, Text } from '../../base/react';
@@ -13,6 +9,10 @@ import { ColorPalette } from '../../base/styles';
13 9
 import { createDesiredLocalTracks } from '../../base/tracks';
14 10
 import { RecentList } from '../../recent-list';
15 11
 
12
+import { AbstractWelcomePage, _mapStateToProps } from './AbstractWelcomePage';
13
+import LocalVideoTrackUnderlay from './LocalVideoTrackUnderlay';
14
+import styles, { PLACEHOLDER_TEXT_COLOR } from './styles';
15
+
16 16
 /**
17 17
  * The URL at which the privacy policy is available to the user.
18 18
  */

+ 2
- 2
react/features/welcome/components/styles.js 查看文件

@@ -5,13 +5,13 @@ import {
5 5
     fixAndroidViewClipping
6 6
 } from '../../base/styles';
7 7
 
8
+export const PLACEHOLDER_TEXT_COLOR = 'rgba(255, 255, 255, 0.3)';
9
+
8 10
 /**
9 11
  * The default color of text on the WelcomePage.
10 12
  */
11 13
 const TEXT_COLOR = ColorPalette.white;
12 14
 
13
-export const PLACEHOLDER_TEXT_COLOR = 'rgba(255, 255, 255, 0.3)';
14
-
15 15
 /**
16 16
  * The styles of the React {@code Components} of the feature welcome including
17 17
  * {@code WelcomePage} and {@code BlankPage}.

正在加载...
取消
保存