|
@@ -106,6 +106,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
106
|
106
|
this.state = this.defaultState;
|
107
|
107
|
|
108
|
108
|
this._keyExtractor = this._keyExtractor.bind(this);
|
|
109
|
+ this._renderInvitedItem = this._renderInvitedItem.bind(this);
|
109
|
110
|
this._renderItem = this._renderItem.bind(this);
|
110
|
111
|
this._renderSeparator = this._renderSeparator.bind(this);
|
111
|
112
|
this._onClearField = this._onClearField.bind(this);
|
|
@@ -138,7 +139,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
138
|
139
|
_addPeopleEnabled,
|
139
|
140
|
_dialOutEnabled
|
140
|
141
|
} = this.props;
|
141
|
|
- const { inviteItems } = this.state;
|
|
142
|
+ const { inviteItems, selectableItems } = this.state;
|
142
|
143
|
|
143
|
144
|
let placeholderKey = 'searchPlaceholder';
|
144
|
145
|
|
|
@@ -187,14 +188,23 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
187
|
188
|
value = { this.state.fieldValue } />
|
188
|
189
|
{ this._renderAndroidClearButton() }
|
189
|
190
|
</View>
|
190
|
|
- <FlatList
|
191
|
|
- ItemSeparatorComponent = { this._renderSeparator }
|
192
|
|
- data = { this.state.selectableItems }
|
193
|
|
- extraData = { inviteItems }
|
194
|
|
- keyExtractor = { this._keyExtractor }
|
195
|
|
- keyboardShouldPersistTaps = 'always'
|
196
|
|
- renderItem = { this._renderItem }
|
197
|
|
- style = { styles.resultList } />
|
|
191
|
+ { Boolean(inviteItems.length) && <View style = { styles.invitedList }>
|
|
192
|
+ <FlatList
|
|
193
|
+ data = { inviteItems }
|
|
194
|
+ horizontal = { true }
|
|
195
|
+ keyExtractor = { this._keyExtractor }
|
|
196
|
+ keyboardShouldPersistTaps = 'always'
|
|
197
|
+ renderItem = { this._renderInvitedItem } />
|
|
198
|
+ </View> }
|
|
199
|
+ <View style = { styles.resultList }>
|
|
200
|
+ <FlatList
|
|
201
|
+ ItemSeparatorComponent = { this._renderSeparator }
|
|
202
|
+ data = { selectableItems }
|
|
203
|
+ extraData = { inviteItems }
|
|
204
|
+ keyExtractor = { this._keyExtractor }
|
|
205
|
+ keyboardShouldPersistTaps = 'always'
|
|
206
|
+ renderItem = { this._renderItem } />
|
|
207
|
+ </View>
|
198
|
208
|
</SafeAreaView>
|
199
|
209
|
</KeyboardAvoidingView>
|
200
|
210
|
</SlidingView>
|
|
@@ -210,6 +220,33 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
210
|
220
|
this.setState(this.defaultState);
|
211
|
221
|
}
|
212
|
222
|
|
|
223
|
+ /**
|
|
224
|
+ * Returns an object capable of being rendered by an {@code AvatarListItem}.
|
|
225
|
+ *
|
|
226
|
+ * @param {Object} flatListItem - An item of the data array of the {@code FlatList}.
|
|
227
|
+ * @returns {?Object}
|
|
228
|
+ */
|
|
229
|
+ _getRenderableItem(flatListItem) {
|
|
230
|
+ const { item } = flatListItem;
|
|
231
|
+
|
|
232
|
+ switch (item.type) {
|
|
233
|
+ case 'phone':
|
|
234
|
+ return {
|
|
235
|
+ avatar: 'icon://phone',
|
|
236
|
+ key: item.number,
|
|
237
|
+ title: item.number
|
|
238
|
+ };
|
|
239
|
+ case 'user':
|
|
240
|
+ return {
|
|
241
|
+ avatar: item.avatar,
|
|
242
|
+ key: item.id || item.user_id,
|
|
243
|
+ title: item.name
|
|
244
|
+ };
|
|
245
|
+ default:
|
|
246
|
+ return null;
|
|
247
|
+ }
|
|
248
|
+ }
|
|
249
|
+
|
213
|
250
|
_invite: Array<Object> => Promise<Array<Object>>
|
214
|
251
|
|
215
|
252
|
_isAddDisabled: () => boolean;
|
|
@@ -303,7 +340,7 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
303
|
340
|
});
|
304
|
341
|
} else {
|
305
|
342
|
// Item is not selected yet, need to add to the list.
|
306
|
|
- const items: Array<*> = inviteItems.concat(item);
|
|
343
|
+ const items: Array<Object> = inviteItems.concat(item);
|
307
|
344
|
|
308
|
345
|
this.setState({
|
309
|
346
|
inviteItems: _.sortBy(items, [ 'name', 'number' ])
|
|
@@ -344,27 +381,8 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
344
|
381
|
*/
|
345
|
382
|
_performSearch(query) {
|
346
|
383
|
this._query(query).then(results => {
|
347
|
|
- const { inviteItems } = this.state;
|
348
|
|
-
|
349
|
|
- let selectableItems = results.filter(result => {
|
350
|
|
- switch (result.type) {
|
351
|
|
- case 'phone':
|
352
|
|
- return result.allowed && result.number
|
353
|
|
- && !inviteItems.find(
|
354
|
|
- _.matchesProperty('number', result.number));
|
355
|
|
- case 'user':
|
356
|
|
- return !inviteItems.find(
|
357
|
|
- (result.user_id && _.matchesProperty('id', result.id))
|
358
|
|
- || (result.user_id && _.matchesProperty('user_id', result.user_id)));
|
359
|
|
- default:
|
360
|
|
- return false;
|
361
|
|
- }
|
362
|
|
- });
|
363
|
|
-
|
364
|
|
- selectableItems = _.sortBy(selectableItems, [ 'name', 'number' ]);
|
365
|
|
-
|
366
|
384
|
this.setState({
|
367
|
|
- selectableItems: this.state.inviteItems.concat(selectableItems)
|
|
385
|
+ selectableItems: _.sortBy(results, [ 'name', 'number' ])
|
368
|
386
|
});
|
369
|
387
|
})
|
370
|
388
|
.finally(() => {
|
|
@@ -378,8 +396,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
378
|
396
|
|
379
|
397
|
_query: (string) => Promise<Array<Object>>;
|
380
|
398
|
|
381
|
|
- _renderItem: Object => ?React$Element<*>
|
382
|
|
-
|
383
|
399
|
/**
|
384
|
400
|
* Renders a button to clear the text field on Android.
|
385
|
401
|
*
|
|
@@ -405,8 +421,46 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
405
|
421
|
);
|
406
|
422
|
}
|
407
|
423
|
|
|
424
|
+ _renderInvitedItem: Object => ?React$Element<*>
|
|
425
|
+
|
|
426
|
+ /**
|
|
427
|
+ * Renders a single item in the invited {@code FlatList}.
|
|
428
|
+ *
|
|
429
|
+ * @param {Object} flatListItem - An item of the data array of the
|
|
430
|
+ * {@code FlatList}.
|
|
431
|
+ * @param {number} index - The index of the currently rendered item.
|
|
432
|
+ * @returns {?React$Element<*>}
|
|
433
|
+ */
|
|
434
|
+ _renderInvitedItem(flatListItem, index) {
|
|
435
|
+ const { item } = flatListItem;
|
|
436
|
+ const renderableItem = this._getRenderableItem(flatListItem);
|
|
437
|
+
|
|
438
|
+ return (
|
|
439
|
+ <TouchableOpacity onPress = { this._onPressItem(item) } >
|
|
440
|
+ <View
|
|
441
|
+ pointerEvents = 'box-only'
|
|
442
|
+ style = { styles.itemWrapper }>
|
|
443
|
+ <AvatarListItem
|
|
444
|
+ avatarOnly = { true }
|
|
445
|
+ avatarSize = { AVATAR_SIZE }
|
|
446
|
+ avatarStyle = { styles.avatar }
|
|
447
|
+ avatarTextStyle = { styles.avatarText }
|
|
448
|
+ item = { renderableItem }
|
|
449
|
+ key = { index }
|
|
450
|
+ linesStyle = { styles.itemLinesStyle }
|
|
451
|
+ titleStyle = { styles.itemText } />
|
|
452
|
+ <Icon
|
|
453
|
+ name = 'cancel'
|
|
454
|
+ style = { styles.unselectIcon } />
|
|
455
|
+ </View>
|
|
456
|
+ </TouchableOpacity>
|
|
457
|
+ );
|
|
458
|
+ }
|
|
459
|
+
|
|
460
|
+ _renderItem: Object => ?React$Element<*>
|
|
461
|
+
|
408
|
462
|
/**
|
409
|
|
- * Renders a single item in the {@code FlatList}.
|
|
463
|
+ * Renders a single item in the search result {@code FlatList}.
|
410
|
464
|
*
|
411
|
465
|
* @param {Object} flatListItem - An item of the data array of the
|
412
|
466
|
* {@code FlatList}.
|
|
@@ -417,28 +471,20 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
417
|
471
|
const { item } = flatListItem;
|
418
|
472
|
const { inviteItems } = this.state;
|
419
|
473
|
let selected = false;
|
420
|
|
- let renderableItem;
|
|
474
|
+ const renderableItem = this._getRenderableItem(flatListItem);
|
|
475
|
+
|
|
476
|
+ if (!renderableItem) {
|
|
477
|
+ return null;
|
|
478
|
+ }
|
421
|
479
|
|
422
|
480
|
switch (item.type) {
|
423
|
481
|
case 'phone':
|
424
|
|
- selected
|
425
|
|
- = inviteItems.find(_.matchesProperty('number', item.number));
|
426
|
|
- renderableItem = {
|
427
|
|
- avatar: 'icon://phone',
|
428
|
|
- key: item.number,
|
429
|
|
- title: item.number
|
430
|
|
- };
|
|
482
|
+ selected = inviteItems.find(_.matchesProperty('number', item.number));
|
431
|
483
|
break;
|
432
|
484
|
case 'user':
|
433
|
|
- selected
|
434
|
|
- = item.id
|
435
|
|
- ? inviteItems.find(_.matchesProperty('id', item.id))
|
436
|
|
- : inviteItems.find(_.matchesProperty('user_id', item.user_id));
|
437
|
|
- renderableItem = {
|
438
|
|
- avatar: item.avatar,
|
439
|
|
- key: item.id || item.user_id,
|
440
|
|
- title: item.name
|
441
|
|
- };
|
|
485
|
+ selected = item.id
|
|
486
|
+ ? inviteItems.find(_.matchesProperty('id', item.id))
|
|
487
|
+ : inviteItems.find(_.matchesProperty('user_id', item.user_id));
|
442
|
488
|
break;
|
443
|
489
|
default:
|
444
|
490
|
return null;
|
|
@@ -449,11 +495,6 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
449
|
495
|
<View
|
450
|
496
|
pointerEvents = 'box-only'
|
451
|
497
|
style = { styles.itemWrapper }>
|
452
|
|
- <Icon
|
453
|
|
- name = { selected
|
454
|
|
- ? 'radio_button_checked'
|
455
|
|
- : 'radio_button_unchecked' }
|
456
|
|
- style = { styles.radioButton } />
|
457
|
498
|
<AvatarListItem
|
458
|
499
|
avatarSize = { AVATAR_SIZE }
|
459
|
500
|
avatarStyle = { styles.avatar }
|
|
@@ -462,6 +503,9 @@ class AddPeopleDialog extends AbstractAddPeopleDialog<Props, State> {
|
462
|
503
|
key = { index }
|
463
|
504
|
linesStyle = { styles.itemLinesStyle }
|
464
|
505
|
titleStyle = { styles.itemText } />
|
|
506
|
+ { selected && <Icon
|
|
507
|
+ name = 'check'
|
|
508
|
+ style = { styles.selectedIcon } /> }
|
465
|
509
|
</View>
|
466
|
510
|
</TouchableOpacity>
|
467
|
511
|
);
|