瀏覽代碼

[RN] Add recent-list feature

master
Lyubo Marinov 7 年之前
父節點
當前提交
1e0550c746

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

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

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

90
 
90
 
91
     /**
91
     /**
92
      * Returns the value associated with a specific key in this storage in an
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
      * @param {string} key - The name of the key to retrieve the value of.
97
      * @param {string} key - The name of the key to retrieve the value of.
98
+     * @private
99
      * @returns {Promise}
99
      * @returns {Promise}
100
      */
100
      */
101
     _getItemAsync(key) {
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
 import { Component } from 'react';
3
 import { Component } from 'react';
4
 import { ListView } from 'react-native';
4
 import { ListView } from 'react-native';
5
 
5
 
6
-import { getRecentRooms } from '../functions';
7
-
8
 import { appNavigate } from '../../app';
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
  * @extends Component
39
  * @extends Component
40
  */
40
  */
41
 export default class AbstractRecentList extends Component<Props, State> {
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
     listDataSource = new ListView.DataSource({
46
     listDataSource = new ListView.DataSource({
47
         rowHasChanged: (r1, r2) =>
47
         rowHasChanged: (r1, r2) =>
48
             r1.conference !== r2.conference
48
             r1.conference !== r2.conference
49
                 && r1.dateTimeStamp !== r2.dateTimeStamp
49
                 && r1.dateTimeStamp !== r2.dateTimeStamp
50
-    });;
50
+    });
51
 
51
 
52
     /**
52
     /**
53
      * Initializes a new {@code AbstractRecentList} instance.
53
      * Initializes a new {@code AbstractRecentList} instance.
67
      * @inheritdoc
67
      * @inheritdoc
68
      */
68
      */
69
     componentWillMount() {
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
 import { ListView, Text, TouchableHighlight, View } from 'react-native';
2
 import { ListView, Text, TouchableHighlight, View } from 'react-native';
3
 import { connect } from 'react-redux';
3
 import { connect } from 'react-redux';
4
 
4
 
5
+import { Icon } from '../../base/font-icons';
6
+
5
 import AbstractRecentList from './AbstractRecentList';
7
 import AbstractRecentList from './AbstractRecentList';
6
 import styles, { UNDERLAY_COLOR } from './styles';
8
 import styles, { UNDERLAY_COLOR } from './styles';
7
 
9
 
8
-import { Icon } from '../../base/font-icons';
9
-
10
 /**
10
 /**
11
  * The native container rendering the list of the recently joined rooms.
11
  * The native container rendering the list of the recently joined rooms.
12
  *
12
  *
15
 class RecentList extends AbstractRecentList {
15
 class RecentList extends AbstractRecentList {
16
     /**
16
     /**
17
      * Initializes a new {@code RecentList} instance.
17
      * Initializes a new {@code RecentList} instance.
18
-     *
19
      */
18
      */
20
     constructor() {
19
     constructor() {
21
         super();
20
         super();
22
 
21
 
22
+        // Bind event handlers so they are only bound once per instance.
23
         this._getAvatarStyle = this._getAvatarStyle.bind(this);
23
         this._getAvatarStyle = this._getAvatarStyle.bind(this);
24
         this._onSelect = this._onSelect.bind(this);
24
         this._onSelect = this._onSelect.bind(this);
25
         this._renderConfDuration = this._renderConfDuration.bind(this);
25
         this._renderConfDuration = this._renderConfDuration.bind(this);
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
      * @inheritdoc
34
      * @inheritdoc
35
      * @returns {ReactElement}
35
      * @returns {ReactElement}
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
     _getAvatarStyle(recentListEntry) {
60
     _getAvatarStyle(recentListEntry) {
107
         const avatarStyles = [ styles.avatar ];
61
         const avatarStyles = [ styles.avatar ];
108
 
62
 
109
         if (recentListEntry.baseURL !== this.props._homeServer) {
63
         if (recentListEntry.baseURL !== this.props._homeServer) {
110
             avatarStyles.push(
64
             avatarStyles.push(
111
-                this._getColorForServerName(recentListEntry.serverName)
112
-            );
65
+                this._getColorForServerName(recentListEntry.serverName));
113
         }
66
         }
114
 
67
 
115
         return avatarStyles;
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
     _getColorForServerName(serverName) {
79
     _getColorForServerName(serverName) {
127
         let nameHash = 0;
80
         let nameHash = 0;
128
 
81
 
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
             return (
98
             return (
147
                 <View style = { styles.infoWithIcon } >
99
                 <View style = { styles.infoWithIcon } >
148
                     <Icon
100
                     <Icon
149
-                        name = 'public'
101
+                        name = 'timer'
150
                         style = { styles.inlineIcon } />
102
                         style = { styles.inlineIcon } />
151
-                    <Text style = { styles.serverName }>
152
-                        { recentListEntry.serverName }
103
+                    <Text style = { styles.confLength }>
104
+                        { conferenceDurationString }
153
                     </Text>
105
                     </Text>
154
                 </View>
106
                 </View>
155
             );
107
             );
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
             return (
123
             return (
171
                 <View style = { styles.infoWithIcon } >
124
                 <View style = { styles.infoWithIcon } >
172
                     <Icon
125
                     <Icon
173
-                        name = 'timer'
126
+                        name = 'public'
174
                         style = { styles.inlineIcon } />
127
                         style = { styles.inlineIcon } />
175
-                    <Text style = { styles.confLength }>
176
-                        { recentListEntry.conferenceDurationString }
128
+                    <Text style = { styles.serverName }>
129
+                        { recentListEntry.serverName }
177
                     </Text>
130
                     </Text>
178
                 </View>
131
                 </View>
179
             );
132
             );
181
 
134
 
182
         return null;
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
 function _mapStateToProps(state) {
194
 function _mapStateToProps(state) {
196
     return {
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
          * @private
200
          * @private
202
          * @type {string}
201
          * @type {string}

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

1
-import {
2
-    createStyleSheet,
3
-    BoxModel
4
-} from '../../base/styles';
1
+import { createStyleSheet, BoxModel } from '../../base/styles';
5
 
2
 
6
 const AVATAR_OPACITY = 0.4;
3
 const AVATAR_OPACITY = 0.4;
4
+
7
 const AVATAR_SIZE = 65;
5
 const AVATAR_SIZE = 65;
6
+
8
 const OVERLAY_FONT_COLOR = 'rgba(255, 255, 255, 0.6)';
7
 const OVERLAY_FONT_COLOR = 'rgba(255, 255, 255, 0.6)';
9
 
8
 
10
 export const UNDERLAY_COLOR = 'rgba(255, 255, 255, 0.2)';
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
  * {@code RecentList}.
13
  * {@code RecentList}.
15
  */
14
  */
16
 export default createStyleSheet({
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
     avatar: {
20
     avatar: {
22
-        width: AVATAR_SIZE,
23
-        height: AVATAR_SIZE,
24
         alignItems: 'center',
21
         alignItems: 'center',
25
         backgroundColor: `rgba(23, 160, 219, ${AVATAR_OPACITY})`,
22
         backgroundColor: `rgba(23, 160, 219, ${AVATAR_OPACITY})`,
23
+        borderRadius: AVATAR_SIZE,
24
+        height: AVATAR_SIZE,
26
         justifyContent: 'center',
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
     avatarContainer: {
32
     avatarContainer: {
33
+        alignItems: 'center',
34
         flexDirection: 'row',
34
         flexDirection: 'row',
35
         justifyContent: 'space-around',
35
         justifyContent: 'space-around',
36
-        alignItems: 'center',
37
         paddingTop: 5
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
     avatarContent: {
42
     avatarContent: {
43
+        backgroundColor: 'rgba(0, 0, 0, 0)',
44
         color: OVERLAY_FONT_COLOR,
44
         color: OVERLAY_FONT_COLOR,
45
         fontSize: 32,
45
         fontSize: 32,
46
         fontWeight: '100',
46
         fontWeight: '100',
47
-        backgroundColor: 'rgba(0, 0, 0, 0)',
48
         textAlign: 'center'
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
     avatarRemoteServer1: {
55
     avatarRemoteServer1: {
57
         backgroundColor: `rgba(232, 105, 156, ${AVATAR_OPACITY})`
56
         backgroundColor: `rgba(232, 105, 156, ${AVATAR_OPACITY})`
58
     },
57
     },
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
     confLength: {
78
     confLength: {
80
         color: OVERLAY_FONT_COLOR,
79
         color: OVERLAY_FONT_COLOR,
81
         fontWeight: 'normal'
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
     container: {
86
     container: {
88
         flex: 1
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
     date: {
93
     date: {
96
         color: OVERLAY_FONT_COLOR
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
     detailsContainer: {
100
     detailsContainer: {
101
+        alignItems: 'flex-start',
103
         flex: 1,
102
         flex: 1,
104
         flexDirection: 'column',
103
         flexDirection: 'column',
105
         justifyContent: 'center',
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
     infoWithIcon: {
111
     infoWithIcon: {
112
+        alignItems: 'center',
114
         flexDirection: 'row',
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
     inlineIcon: {
120
     inlineIcon: {
123
         color: OVERLAY_FONT_COLOR,
121
         color: OVERLAY_FONT_COLOR,
124
         marginRight: 5
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
     roomName: {
128
     roomName: {
129
+        color: OVERLAY_FONT_COLOR,
131
         fontSize: 18,
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
     row: {
137
     row: {
140
-        padding: 8,
141
-        paddingBottom: 0,
138
+        alignItems: 'center',
142
         flex: 1,
139
         flex: 1,
143
         flexDirection: 'row',
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
     serverName: {
148
     serverName: {
151
         color: OVERLAY_FONT_COLOR,
149
         color: OVERLAY_FONT_COLOR,
152
         fontWeight: 'normal'
150
         fontWeight: 'normal'

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

1
 /**
1
 /**
2
  * The max size of the list.
2
  * The max size of the list.
3
+ *
4
+ * @type {number}
3
  */
5
  */
4
 export const LIST_SIZE = 30;
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
  * @type {string}
12
  * @type {string}
10
  */
13
  */

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

2
 
2
 
3
 import moment from 'moment';
3
 import moment from 'moment';
4
 
4
 
5
-import { RECENT_URL_STORAGE } from './constants';
6
-
7
 import { i18next } from '../base/i18n';
5
 import { i18next } from '../base/i18n';
8
 import { parseURIString } from '../base/util';
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
 export function getRecentRooms(): Promise<Array<Object>> {
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
                         if (location && location.room && location.hostname) {
27
                         if (location && location.room && location.hostname) {
29
                             recentRoomDS.push({
28
                             recentRoomDS.push({
30
                                 baseURL:
29
                                 baseURL:
31
                                     `${location.protocol}//${location.host}`,
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
                                 initials: _getInitials(location.room),
37
                                 initials: _getInitials(location.room),
42
                                 room: location.room,
38
                                 room: location.room,
43
                                 serverName: location.hostname
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
     window.localStorage.setItem(
69
     window.localStorage.setItem(
81
         RECENT_URL_STORAGE,
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
 function _getDateString(dateTimeStamp: number) {
82
 function _getDateString(dateTimeStamp: number) {
94
     const date = new Date(dateTimeStamp);
83
     const date = new Date(dateTimeStamp);
84
+    const m = moment(date).locale(i18next.language);
95
 
85
 
96
     if (date.toDateString() === new Date().toDateString()) {
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
     return moment.duration(duration)
103
     return moment.duration(duration)
119
             .locale(i18next.language)
104
             .locale(i18next.language)
120
             .humanize();
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
 function _getInitials(room: string) {
115
 function _getInitials(room: string) {
131
     return room && room.charAt(0) ? room.charAt(0).toUpperCase() : '?';
116
     return room && room.charAt(0) ? room.charAt(0).toUpperCase() : '?';
132
 }
117
 }

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

1
-/* @flow */
2
-
3
-import { LIST_SIZE } from './constants';
4
-import { getRecentUrls, updaterecentUrls } from './functions';
1
+// @flow
5
 
2
 
6
 import { CONFERENCE_WILL_LEAVE, SET_ROOM } from '../base/conference';
3
 import { CONFERENCE_WILL_LEAVE, SET_ROOM } from '../base/conference';
7
 import { MiddlewareRegistry } from '../base/redux';
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
  * @returns {Function}
14
  * @returns {Function}
15
  */
15
  */
16
 MiddlewareRegistry.register(store => next => action => {
16
 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
 * @param {Store} store - The redux store in which the specified action is being
31
 * @param {Store} store - The redux store in which the specified action is being
32
 * dispatched.
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
 * specified action to the specified store.
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
 * dispatched in the specified store.
36
 * dispatched in the specified store.
37
+* @private
37
 * @returns {Object} The new state that is the result of the reduction of the
38
 * @returns {Object} The new state that is the result of the reduction of the
38
 * specified action.
39
 * specified action.
39
 */
40
 */
40
 function _storeJoinedRoom(store, next, action) {
41
 function _storeJoinedRoom(store, next, action) {
41
     const result = next(action);
42
     const result = next(action);
43
+
42
     const { room } = action;
44
     const { room } = action;
43
 
45
 
44
     if (room) {
46
     if (room) {
45
         const { locationURL } = store.getState()['features/base/connection'];
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
         // maximising the size
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
     return result;
69
     return result;
72
 /**
72
 /**
73
 * Updates the conference length when left.
73
 * Updates the conference length when left.
74
 *
74
 *
75
-* @private
76
 * @param {Store} store - The redux store in which the specified action is being
75
 * @param {Store} store - The redux store in which the specified action is being
77
 * dispatched.
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
 * specified action to the specified store.
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
 * @returns {Object} The new state that is the result of the reduction of the
82
 * @returns {Object} The new state that is the result of the reduction of the
83
 * specified action.
83
 * specified action.
84
 */
84
 */
85
-function _updateConferenceDuration(store, next, action) {
85
+function _updateConferenceDuration({ getState }, next, action) {
86
     const result = next(action);
86
     const result = next(action);
87
-    const { locationURL } = store.getState()['features/base/connection'];
87
+
88
+    const { locationURL } = getState()['features/base/connection'];
88
 
89
 
89
     if (locationURL && locationURL.href) {
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
 import { TextInput, TouchableHighlight, View } from 'react-native';
2
 import { TextInput, TouchableHighlight, View } from 'react-native';
3
 import { connect } from 'react-redux';
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
 import { translate } from '../../base/i18n';
5
 import { translate } from '../../base/i18n';
10
 import { MEDIA_TYPE } from '../../base/media';
6
 import { MEDIA_TYPE } from '../../base/media';
11
 import { Link, LoadingIndicator, Text } from '../../base/react';
7
 import { Link, LoadingIndicator, Text } from '../../base/react';
13
 import { createDesiredLocalTracks } from '../../base/tracks';
9
 import { createDesiredLocalTracks } from '../../base/tracks';
14
 import { RecentList } from '../../recent-list';
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
  * The URL at which the privacy policy is available to the user.
17
  * The URL at which the privacy policy is available to the user.
18
  */
18
  */

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

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

Loading…
取消
儲存