@momo-kits/avatar 0.0.65-alpha.16 → 0.0.65-alpha.22

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
Files changed (3) hide show
  1. package/Avatar.js +659 -368
  2. package/Avatar.web.js +549 -366
  3. package/package.json +2 -2
package/Avatar.js CHANGED
@@ -1,408 +1,699 @@
1
1
  /* eslint-disable indent */
2
- import {
3
- Colors,
4
- Image,
5
- Radius,
6
- ScaleSize,
7
- Text,
8
- ValueUtil,
9
- } from '@momo-kits/core-v2';
10
2
  import PropTypes from 'prop-types';
11
- import React, {useState} from 'react';
12
- import {StyleSheet, TouchableOpacity, View} from 'react-native';
3
+ import React, { Component } from 'react';
4
+ import {
5
+ View,
6
+ StyleSheet,
7
+ TouchableOpacity,
8
+ Dimensions,
9
+ Platform,
10
+ } from 'react-native';
11
+ import { get, isEqual } from 'lodash';
12
+ import {
13
+ ValueUtil,
14
+ Colors,
15
+ Text,
16
+ IconSource,
17
+ Image,
18
+ ScaleSize,
19
+ } from '@momo-kits/core';
20
+
21
+ export const AvatarQuality = {
22
+ low: 'low',
23
+ medium: 'medium',
24
+ high: 'high',
25
+ };
26
+
27
+ export const AvatarSize = {
28
+ tiny: 'tiny',
29
+ small: 'small',
30
+ medium: 'medium',
31
+ large: 'large',
32
+ giant: 'giant',
33
+ size8: 'size8',
34
+ size24: 'size24',
35
+ size36: 'size36',
36
+ size44: 'size44',
37
+ size48: 'size48',
38
+ size52: 'size52',
39
+ size56: 'size56',
40
+ size64: 'size64',
41
+ size72: 'size72',
42
+ size80: 'size80',
43
+ size88: 'size88',
44
+ size96: 'size96',
45
+ };
46
+
47
+ const COLORS = [
48
+ '#3683f3',
49
+ '#ea63af',
50
+ '#f94889',
51
+ '#82b6da',
52
+ '#2fc093',
53
+ '#7b4a41',
54
+ '#ff97bc',
55
+ '#4130d7',
56
+ '#c3c3c3',
57
+ '#f7ae2f',
58
+ '#b04595',
59
+ '#57b748',
60
+ '#ff8900',
61
+ '#7841bf',
62
+ '#be3f3e',
63
+ '#c51515',
64
+ '#8e6472',
65
+ '#fed27d',
66
+ '#ff6f5d',
67
+ '#05b8ae',
68
+ ];
13
69
 
14
- const AvatarQuality = {
15
- low: 'low',
16
- medium: 'medium',
17
- high: 'high',
70
+ export const SubIconType = {
71
+ momo: 'momo',
72
+ online: 'online',
73
+ key: 'key',
74
+ none: 'none',
18
75
  };
19
76
 
20
77
  const styles = StyleSheet.create({
21
- container: {
22
- justifyContent: 'center',
23
- alignItems: 'center',
24
- },
25
- shortNameView: {
26
- backgroundColor: Colors.pink_09,
27
- justifyContent: 'center',
28
- alignItems: 'center',
29
- borderWidth: 1,
30
- borderColor: Colors.black_04,
31
- },
32
- shortNameText: {
33
- // fontSize: 17,
34
- color: Colors.pink_03,
35
- },
36
- shortNameTextCustom: {
37
- color: '#f6f6f6',
38
- },
39
- absolute: {
40
- position: 'absolute',
41
- top: 0,
42
- left: 0,
43
- right: 0,
44
- bottom: 0,
45
- },
46
- subIcon: {
47
- position: 'absolute',
48
- bottom: 0,
49
- right: 0,
50
- },
51
- topIcon: {
52
- position: 'absolute',
53
- top: 0,
54
- right: 0,
55
- },
56
- subIconImage: {
57
- width: 22,
58
- height: 22,
59
- },
60
- subIconImageOnline: {
61
- backgroundColor: Colors.kiwi_green,
62
- borderColor: 'white',
63
- borderWidth: 1,
64
- },
78
+ container: {
79
+ justifyContent: 'center',
80
+ alignItems: 'center',
81
+ },
82
+ shortNameView: {
83
+ justifyContent: 'center',
84
+ alignItems: 'center',
85
+
86
+ backgroundColor: Colors.very_light_pink_four,
87
+ width: '100%',
88
+ height: '100%',
89
+ },
90
+ shortNameText: {
91
+ // fontSize: 17,
92
+ color: Colors.pink_03,
93
+ },
94
+ shortNameTextCustom: {
95
+ color: '#f6f6f6',
96
+ },
97
+ absolute: {
98
+ position: 'absolute',
99
+ top: 0,
100
+ left: 0,
101
+ right: 0,
102
+ bottom: 0,
103
+ },
104
+ subIcon: {
105
+ position: 'absolute',
106
+ bottom: 0,
107
+ right: 0,
108
+ },
109
+ subIconImage: {
110
+ width: 22,
111
+ height: 22,
112
+ },
113
+ subIconImageOnline: {
114
+ backgroundColor: Colors.kiwi_green,
115
+ borderColor: 'white',
116
+ borderWidth: 1,
117
+ },
65
118
  });
66
119
 
67
- const subIconSize = size => {
68
- switch (size) {
69
- case 'giant':
70
- return {
71
- width: 12,
72
- height: 12,
73
- borderRadius: 6,
74
- };
75
- case 'small':
76
- return {
120
+ const subIconSize = (width) => ({
121
+ tiny: {
77
122
  width: 8,
78
123
  height: 8,
79
124
  borderRadius: 4,
80
- };
81
- case 'medium':
82
- return {
125
+ },
126
+ small: {
127
+ width: 10,
128
+ height: 10,
129
+ borderRadius: 5,
130
+ },
131
+ medium: {
83
132
  width: 12,
84
133
  height: 12,
85
134
  borderRadius: 6,
86
- };
87
- case 'large':
88
- return {
89
- width: 12,
90
- height: 12,
91
- borderRadius: 6,
92
- };
93
- default:
94
- return {
95
- width: 12,
96
- height: 12,
97
- borderRadius: 6,
98
- };
99
- }
100
- };
101
-
102
- const avatarSize = size => {
103
- switch (size) {
104
- case 'tiny':
105
- return {
135
+ },
136
+ large: {
137
+ width: 16,
138
+ height: 16,
139
+ borderRadius: 8,
140
+ },
141
+ size8: {
142
+ width: 8,
143
+ height: 8,
144
+ borderRadius: 4,
145
+ },
146
+ size24: {
147
+ width: 10,
148
+ height: 10,
149
+ borderRadius: 5,
150
+ },
151
+ size36: {
152
+ width: 10,
153
+ height: 10,
154
+ borderRadius: 5,
155
+ },
156
+ size44: {
157
+ width: 14,
158
+ height: 14,
159
+ borderRadius: 7,
160
+ },
161
+ size48: {
162
+ width: 14,
163
+ height: 14,
164
+ borderRadius: 7,
165
+ },
166
+ size52: {
167
+ width: 16,
168
+ height: 16,
169
+ borderRadius: 8,
170
+ },
171
+ size56: {
172
+ width: 16,
173
+ height: 16,
174
+ borderRadius: 8,
175
+ },
176
+ size64: {
177
+ width: 16,
178
+ height: 16,
179
+ borderRadius: 8,
180
+ top: -2,
181
+ left: -2,
182
+ },
183
+ size72: {
184
+ width: 18,
185
+ height: 18,
186
+ borderRadius: 9,
187
+ top: -3,
188
+ left: -3,
189
+ },
190
+ size80: {
191
+ width: 18,
192
+ height: 18,
193
+ borderRadius: 9,
194
+ top: -3,
195
+ left: -3,
196
+ },
197
+ size88: {
198
+ width: 18,
199
+ height: 18,
200
+ borderRadius: 9,
201
+ top: -4,
202
+ left: -4,
203
+ },
204
+ size96: {
205
+ width: 20,
206
+ height: 20,
207
+ borderRadius: 10,
208
+ top: -4,
209
+ left: -4,
210
+ },
211
+ giant: {
212
+ top: -5,
213
+ left: -5,
106
214
  width: 24,
107
215
  height: 24,
108
- };
109
- case 'small': {
110
- return {
216
+ borderRadius: 12,
217
+ },
218
+ });
219
+
220
+ const avatarSize = (width) => ({
221
+ tiny: {
222
+ width: 16,
223
+ height: 16,
224
+ borderRadius: 8,
225
+ },
226
+ small: {
111
227
  width: 32,
112
228
  height: 32,
113
- };
114
- }
115
- case 'medium':
116
- return {
229
+ borderRadius: 16,
230
+ },
231
+ medium: {
117
232
  width: 40,
118
233
  height: 40,
119
- };
120
- case 'large':
121
- return {
234
+ borderRadius: 20,
235
+ },
236
+ large: {
237
+ width: 60,
238
+ height: 60,
239
+ borderRadius: 30,
240
+ },
241
+ giant: {
242
+ width: 120,
243
+ height: 120,
244
+ borderRadius: 60,
245
+ },
246
+ size8: {
247
+ width: 8,
248
+ height: 8,
249
+ borderRadius: 4,
250
+ },
251
+ size24: {
252
+ width: 24,
253
+ height: 24,
254
+ borderRadius: 12,
255
+ },
256
+ size36: {
257
+ width: 36,
258
+ height: 36,
259
+ borderRadius: 18,
260
+ },
261
+ size44: {
262
+ width: 44,
263
+ height: 44,
264
+ borderRadius: 22,
265
+ },
266
+ size48: {
267
+ width: 48,
268
+ height: 48,
269
+ borderRadius: 24,
270
+ },
271
+ size52: {
272
+ width: 52,
273
+ height: 52,
274
+ borderRadius: 26,
275
+ },
276
+ size56: {
122
277
  width: 56,
123
278
  height: 56,
124
- };
125
- case 'giant':
126
- return {
279
+ borderRadius: 28,
280
+ },
281
+ size64: {
282
+ width: 64,
283
+ height: 64,
284
+ borderRadius: 32,
285
+ },
286
+ size72: {
127
287
  width: 72,
128
288
  height: 72,
129
- };
130
- default:
131
- return {
132
- width: 40,
133
- height: 40,
134
- };
135
- }
136
- };
289
+ borderRadius: 36,
290
+ },
291
+ size80: {
292
+ width: 80,
293
+ height: 80,
294
+ borderRadius: 40,
295
+ },
296
+ size88: {
297
+ width: 88,
298
+ height: 88,
299
+ borderRadius: 44,
300
+ },
301
+ size96: {
302
+ width: 96,
303
+ height: 96,
304
+ borderRadius: 48,
305
+ },
306
+ });
137
307
 
138
- const shortName = size => {
139
- switch (size) {
140
- case 'tiny':
141
- return {
142
- fontSize: ScaleSize(10),
143
- lineHeight: 16,
144
- };
145
- case 'small':
146
- return {
147
- fontSize: ScaleSize(15),
148
- lineHeight: 22,
149
- };
150
- case 'medium':
151
- return {
152
- fontSize: ScaleSize(15),
153
- lineHeight: 22,
154
- };
155
- case 'large':
156
- return {
157
- fontSize: ScaleSize(15),
158
- lineHeight: 22,
159
- };
160
- case 'giant':
161
- return {
162
- fontSize: ScaleSize(15),
163
- lineHeight: 22,
164
- };
165
- default:
166
- return {
167
- fontSize: ScaleSize(15),
168
- lineHeight: 22,
169
- };
170
- }
171
- };
308
+ const shortName = (width) => ({
309
+ tiny: {
310
+ fontSize: ScaleSize(5, width),
311
+ },
312
+ small: {
313
+ fontSize: ScaleSize(11, width),
314
+ },
315
+ medium: {
316
+ fontSize: ScaleSize(15, width),
317
+ },
318
+ middle: {
319
+ fontSize: ScaleSize(16, width),
320
+ },
321
+ large: {
322
+ fontSize: ScaleSize(20, width),
323
+ },
324
+ giant: {
325
+ fontSize: ScaleSize(25, width),
326
+ },
327
+ });
172
328
 
173
- const Avatar = ({
174
- resizeMode,
175
- cached,
176
- quality,
177
- size,
178
- shape,
179
- name,
180
- source,
181
- onPress,
182
- style,
183
- subIcon,
184
- topIcon,
185
- loading,
186
- }) => {
187
- const {width, height} = avatarSize(size);
188
- const {fontSize, lineHeight} = shortName(size);
189
- const {
190
- width: iconWidth,
191
- height: iconHeight,
192
- borderRadius: iconRadius,
193
- } = subIconSize(size);
194
-
195
- const [imageSource, setImageSource] = useState(
196
- ValueUtil.getImageSource(source),
197
- );
198
- const getContactShortName = name => {
199
- if (!name) return '';
200
- const shortNameList = name.split(' ', 2);
201
- const alphabet = [];
202
- shortNameList.forEach(n => {
203
- if (n && n.length > 0) {
204
- alphabet.push(n[0].toString().toUpperCase());
205
- }
206
- });
207
- return alphabet.join('');
208
- };
209
- const isUrl = url => {
210
- const expression =
211
- /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/gi;
212
- return expression.test(url);
213
- };
214
-
215
- const isValidImageUrl = source => {
216
- return source && source.uri && isUrl(source.uri);
217
- };
218
-
219
- const mapAvatarQuality = (url, quality) => {
220
- if (!quality || quality === AvatarQuality.medium) {
221
- return url;
329
+ export default class Avatar extends Component {
330
+ constructor(props) {
331
+ super(props);
332
+ this.color = props.custom
333
+ ? this.getAvatarColor(props.name)
334
+ : Colors.pink_10;
335
+ this.state = {
336
+ hideSource: false,
337
+ ownUpdate: false,
338
+ source: props.source,
339
+ dimensions: {},
340
+ };
341
+ this.imageSource = '';
222
342
  }
223
- if (quality === AvatarQuality.low) {
224
- return url.replace('.png', '_s64.png');
343
+
344
+ componentDidMount() {
345
+ const { scaleSize } = this.props;
346
+ if (scaleSize) {
347
+ this.subscription = Dimensions.addEventListener(
348
+ 'change',
349
+ ({ window }) => {
350
+ if (
351
+ window?.height > 0 &&
352
+ window?.width > 0 &&
353
+ Platform.OS === 'android'
354
+ ) {
355
+ this.setState({ dimensions: window });
356
+ }
357
+ },
358
+ );
359
+ }
225
360
  }
226
- if (quality === AvatarQuality.high) {
227
- return url.replace('.png', '_s512.png');
361
+
362
+ componentWillUnmount() {
363
+ const { scaleSize } = this.props;
364
+ if (scaleSize) {
365
+ this.subscription?.remove?.();
366
+ }
228
367
  }
229
368
 
230
- return url;
231
- };
232
-
233
- const onLoadSourceError = source => {
234
- setImageSource(source);
235
- };
236
-
237
- const renderSubIcon = () => {
238
- // const subIconSizeStyle = this.mapSubIconSize(size);
239
- if (subIcon && shape === 'circle' && size !== 'tiny') {
240
- return (
241
- <View
242
- style={[
243
- styles.subIcon,
244
- size === 'large' && {
245
- bottom: 2,
246
- right: 2,
247
- },
248
- size === 'giant' && {
249
- bottom: 4,
250
- right: 4,
251
- },
252
- ]}>
253
- {typeof subIcon === 'string' ? (
254
- <Image
255
- source={subIcon}
256
- style={[
257
- {
258
- width: iconWidth,
259
- height: iconHeight,
260
- borderRadius: iconRadius,
261
- },
262
- ]}
263
- />
264
- ) : (
265
- subIcon()
266
- )}
267
- </View>
268
- );
369
+ static getDerivedStateFromProps(nextProps, prevState) {
370
+ if (prevState.ownUpdate) {
371
+ return {
372
+ ownUpdate: false,
373
+ };
374
+ }
375
+ if (!isEqual(nextProps.source, prevState.source)) {
376
+ return { source: nextProps.source, hideSource: false };
377
+ }
378
+ return null;
269
379
  }
270
- };
271
-
272
- const renderTopIcon = () => {
273
- if (topIcon && shape === 'circle' && size !== 'tiny') {
274
- return (
275
- <View
276
- style={[
277
- styles.topIcon,
278
- size === 'large' && {
279
- top: 2,
280
- right: 2,
281
- },
282
- size === 'giant' && {
283
- top: 4,
284
- right: 4,
285
- },
286
- ]}>
287
- <Image
288
- source={topIcon}
289
- style={[
290
- {
291
- width: iconWidth,
292
- height: iconHeight,
293
- borderRadius: iconRadius,
294
- },
295
- ]}
296
- />
297
- </View>
298
- );
380
+
381
+ getContactShortName = (name) => {
382
+ if (!name) return '';
383
+ const shortNameList = name.split(' ', 2);
384
+ const alphabet = [];
385
+ shortNameList.forEach((n) => {
386
+ if (n && n.length > 0) {
387
+ alphabet.push(n[0].toString().toUpperCase());
388
+ }
389
+ });
390
+ return alphabet.join('');
391
+ };
392
+
393
+ hashUrl(url) {
394
+ let hash = 0;
395
+ let char;
396
+ if (url?.length === 0) return hash;
397
+ for (let i = 0; i < url?.length; i++) {
398
+ char = url?.charCodeAt(i);
399
+ hash = (hash << 5) - hash + char;
400
+ hash &= hash; // Convert to 32bit integer
401
+ }
402
+ return hash;
299
403
  }
300
- };
301
-
302
- const onPressIcon = () => {
303
- if (onPress && typeof onPress === 'function') onPress();
304
- };
305
-
306
- const renderShortName = () => {
307
- if (name) {
308
- const shortedName = getContactShortName(name);
309
- // const shortNameStyle = mapShortNameStyle(size);
310
- return (
311
- <View
312
- style={[
313
- {
314
- width,
315
- height,
316
- borderRadius: shape === 'circle' ? width / 2 : Radius.XS,
317
- },
318
- styles.shortNameView,
319
- ]}>
320
- <Text
321
- weight="regular"
322
- style={{
323
- fontSize,
324
- lineHeight,
325
- color: Colors.pink_03,
326
- }}>
327
- {shortedName}
328
- </Text>
329
- </View>
330
- );
404
+
405
+ renderShortName = (name, avatarStyle, size, color) => {
406
+ if (name) {
407
+ const shortedName = this.getContactShortName(name);
408
+ const shortNameStyle = this.mapShortNameStyle(size);
409
+ return (
410
+ <View
411
+ style={[
412
+ styles.shortNameView,
413
+ avatarStyle,
414
+ { backgroundColor: color },
415
+ ]}>
416
+ <Text.H4
417
+ style={[
418
+ this.props.custom
419
+ ? styles.shortNameTextCustom
420
+ : styles.shortNameText,
421
+ shortNameStyle,
422
+ ]}>
423
+ {shortedName}
424
+ </Text.H4>
425
+ </View>
426
+ );
427
+ }
428
+ return <View />;
429
+ };
430
+
431
+ isUrl(url) {
432
+ const expression =
433
+ /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/gi;
434
+ return expression.test(url);
331
435
  }
332
- return <View />;
333
- };
334
-
335
- if (imageSource?.uri) {
336
- imageSource.priority = 'high';
337
- imageSource.uri = mapAvatarQuality(imageSource.uri, quality);
338
- // if doest not have time query, set default cache time to 1 day
339
- if (!imageSource.uri?.includes('?') && cached) {
340
- const midnight = new Date().setHours(0, 0, 0, 0);
341
- imageSource.uri = `${imageSource.uri}?time=${midnight}`;
436
+
437
+ isValidImageUrl = (source) => {
438
+ const validSource = source && source.uri && this.isUrl(source.uri);
439
+ return validSource;
440
+ };
441
+
442
+ mapAvatarQuality = (url, quality) => {
443
+ if (!quality || quality === AvatarQuality.medium) {
444
+ return url;
445
+ }
446
+ if (quality === AvatarQuality.low) {
447
+ return url.replace('.png', '_s64.png');
448
+ }
449
+ if (quality === AvatarQuality.high) {
450
+ return url.replace('.png', '_s512.png');
451
+ }
452
+
453
+ return url;
454
+ };
455
+
456
+ mapStyleFromSize = (size) => {
457
+ const { dimensions } = this.state;
458
+ const avatarStyle = avatarSize(
459
+ dimensions?.width > 0 ? dimensions?.width : null,
460
+ ).small;
461
+ if (typeof size === 'object') return size;
462
+ if (avatarSize(dimensions?.width > 0 ? dimensions?.width : null)[size])
463
+ return avatarSize(dimensions?.width > 0 ? dimensions?.width : null)[
464
+ size
465
+ ];
466
+ return avatarStyle;
467
+ };
468
+
469
+ mapShortNameStyle = (size) => {
470
+ let shortNameStyle = {};
471
+ const { dimensions } = this.state;
472
+ switch (size) {
473
+ case AvatarSize.tiny: {
474
+ shortNameStyle = shortName(
475
+ dimensions?.width > 0 ? dimensions?.width : null,
476
+ ).tiny;
477
+ break;
478
+ }
479
+ case AvatarSize.small: {
480
+ shortNameStyle = shortName(
481
+ dimensions?.width > 0 ? dimensions?.width : null,
482
+ ).small;
483
+ break;
484
+ }
485
+ case AvatarSize.medium: {
486
+ shortNameStyle = shortName(
487
+ dimensions?.width > 0 ? dimensions?.width : null,
488
+ ).medium;
489
+ break;
490
+ }
491
+ case AvatarSize.size48: {
492
+ shortNameStyle = shortName(dimensions?.width || null).middle;
493
+ break;
494
+ }
495
+ case AvatarSize.large: {
496
+ shortNameStyle = shortName(
497
+ dimensions?.width > 0 ? dimensions?.width : null,
498
+ ).large;
499
+ break;
500
+ }
501
+ case AvatarSize.giant: {
502
+ shortNameStyle = shortName(
503
+ dimensions?.width > 0 ? dimensions?.width : null,
504
+ ).giant;
505
+ break;
506
+ }
507
+ default:
508
+ shortNameStyle = shortName(
509
+ dimensions?.width > 0 ? dimensions?.width : null,
510
+ ).small;
511
+ }
512
+ return shortNameStyle;
513
+ };
514
+
515
+ mapSubIconSize = (size) => {
516
+ const { dimensions } = this.state;
517
+ const subIconStyle = subIconSize(
518
+ dimensions?.width > 0 ? dimensions?.width : null,
519
+ ).small;
520
+ if (subIconSize(dimensions?.width > 0 ? dimensions?.width : null)[size])
521
+ return subIconSize(
522
+ dimensions?.width > 0 ? dimensions?.width : null,
523
+ )[size];
524
+ return subIconStyle;
525
+ };
526
+
527
+ extractSize = (avatarStyle) => {
528
+ const { style } = this.props;
529
+ const borderWidth = get(style, 'borderWidth', 1);
530
+ const containerWidth = Math.round(avatarStyle.width + 2 + borderWidth);
531
+ return {
532
+ width: containerWidth,
533
+ height: containerWidth,
534
+ borderRadius: containerWidth / 2,
535
+ borderWidth,
536
+ borderColor: Colors.black_01,
537
+ };
538
+ };
539
+
540
+ onLoadSourceError = (imageSource) => {
541
+ if (this.imageSource?.uri !== imageSource?.uri) {
542
+ this.imageSource = imageSource;
543
+ this.setState({ hideSource: true, ownUpdate: true });
544
+ }
545
+ };
546
+
547
+ renderSubIcon = () => {
548
+ const { subIcon, size, subIconSource } = this.props;
549
+ if ((!subIcon || subIcon === SubIconType.none) && !subIconSource)
550
+ return <View />;
551
+ const subIconSizeStyle = this.mapSubIconSize(size);
552
+ const iconSource = ValueUtil.getImageSource(subIconSource);
553
+ return (
554
+ <View style={styles.subIcon}>
555
+ {subIcon === SubIconType.momo ? (
556
+ <Image
557
+ style={[styles.subIconImage, subIconSizeStyle]}
558
+ source={IconSource.ic_round_momo_tiny}
559
+ />
560
+ ) : (
561
+ <View />
562
+ )}
563
+ {subIcon === SubIconType.online ? (
564
+ <View
565
+ style={[styles.subIconImageOnline, subIconSizeStyle]}
566
+ />
567
+ ) : (
568
+ <View />
569
+ )}
570
+
571
+ {iconSource && subIconSource && !subIcon ? (
572
+ <Image
573
+ style={[styles.subIconImage, subIconSizeStyle]}
574
+ source={iconSource}
575
+ />
576
+ ) : (
577
+ <View />
578
+ )}
579
+ </View>
580
+ );
581
+ };
582
+
583
+ getAvatarColor(name) {
584
+ const shortName = this.getContactShortName(name);
585
+ if (!shortName) return '#d5d6d6';
586
+ const firstLetter = shortName[0].charCodeAt(0);
587
+ let colorIndex = Number(firstLetter) % 26;
588
+ if (colorIndex > 19) colorIndex = 25 - colorIndex;
589
+ return COLORS[colorIndex];
342
590
  }
343
- }
344
-
345
- const renderImage = () => {
346
- return (
347
- <Image
348
- cached={cached}
349
- resizeMode={resizeMode}
350
- onLoadSourceError={() => onLoadSourceError(source)}
351
- source={source}
352
- loading={loading}
353
- style={[
354
- {
355
- width,
356
- height,
357
- borderRadius: shape === 'circle' ? width / 2 : Radius.XS,
358
- },
359
- ]}
360
- />
361
- );
362
- };
363
-
364
- return (
365
- <TouchableOpacity
366
- activeOpacity={onPress ? 0.5 : 1}
367
- style={style}
368
- onPress={onPressIcon}>
369
- <View
370
- style={{
371
- alignSelf: 'baseline',
372
- }}>
373
- {!!imageSource && isValidImageUrl(imageSource)
374
- ? renderImage()
375
- : renderShortName()}
376
- {renderTopIcon()}
377
- {renderSubIcon()}
378
- </View>
379
- </TouchableOpacity>
380
- );
381
- };
591
+
592
+ onPress = () => {
593
+ const { onPress } = this.props;
594
+ if (onPress && typeof onPress === 'function') onPress();
595
+ };
596
+
597
+ render() {
598
+ const {
599
+ size,
600
+ source,
601
+ name,
602
+ resizeMode,
603
+ style,
604
+ onPress,
605
+ isShowLoading,
606
+ loading,
607
+ cached,
608
+ extraPropsImage,
609
+ quality,
610
+ } = this.props;
611
+ const { hideSource } = this.state;
612
+ const avatarStyle = this.mapStyleFromSize(size);
613
+ const containerSize = this.extractSize(avatarStyle);
614
+ const imageSource = ValueUtil.getImageSource(source);
615
+ const showName =
616
+ typeof imageSource === 'object' &&
617
+ Object.keys(imageSource).length === 0 &&
618
+ typeof imageSource !== 'number';
619
+ // add priority high for avatar
620
+ if (imageSource?.uri) {
621
+ imageSource.priority = 'high';
622
+ imageSource.uri = this.mapAvatarQuality(imageSource.uri, quality);
623
+ // if doest not have time query, set default cache time to 1 day
624
+ if (!imageSource.uri?.includes('?') && cached) {
625
+ const midnight = new Date().setHours(0, 0, 0, 0);
626
+ imageSource.uri = `${imageSource.uri}?time=${midnight}`;
627
+ }
628
+ }
629
+
630
+ const inner = (
631
+ <View style={[styles.container, containerSize, style]}>
632
+ {showName || hideSource ? (
633
+ this.renderShortName(name, avatarStyle, size, this.color)
634
+ ) : (
635
+ <View />
636
+ )}
637
+ {this.isValidImageUrl(imageSource) && !hideSource ? (
638
+ <Image
639
+ cached={cached}
640
+ loading={loading || isShowLoading}
641
+ source={imageSource}
642
+ onError={() => this.onLoadSourceError(imageSource)}
643
+ style={avatarStyle}
644
+ resizeMode={resizeMode}
645
+ {...extraPropsImage}
646
+ />
647
+ ) : (
648
+ <View />
649
+ )}
650
+ {this.renderSubIcon()}
651
+ </View>
652
+ );
653
+ if (onPress) {
654
+ return (
655
+ <TouchableOpacity
656
+ activeOpacity={onPress ? 0.5 : 1}
657
+ onPress={this.onPress}>
658
+ {inner}
659
+ </TouchableOpacity>
660
+ );
661
+ }
662
+ return inner;
663
+ }
664
+ }
382
665
 
383
666
  Avatar.propTypes = {
384
- name: PropTypes.string,
385
- quality: PropTypes.oneOf(['low', 'medium', 'high']),
386
- size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large', 'giant']),
387
- source: PropTypes.oneOfType([
388
- PropTypes.shape({uri: PropTypes.string}),
389
- PropTypes.number,
390
- PropTypes.string,
391
- ]),
392
- subIcon: PropTypes.oneOfType([PropTypes.string, PropTypes.func]),
393
- topIcon: PropTypes.string,
394
-
395
- style: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
396
- onPress: PropTypes.func,
397
- loading: PropTypes.bool,
398
- shape: PropTypes.oneOf(['circle', 'square']),
667
+ name: PropTypes.string,
668
+ resizeMode: PropTypes.string,
669
+ quality: PropTypes.oneOf(['low', 'medium', 'high']),
670
+ size: PropTypes.any,
671
+ source: PropTypes.oneOfType([
672
+ PropTypes.shape({ uri: PropTypes.string }),
673
+ PropTypes.number,
674
+ PropTypes.string,
675
+ ]),
676
+ subIcon: PropTypes.oneOf(['momo', 'online', 'key', 'none']),
677
+ subIconSource: PropTypes.oneOfType([
678
+ PropTypes.shape({ uri: PropTypes.string }),
679
+ PropTypes.number,
680
+ PropTypes.string,
681
+ ]),
682
+ style: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
683
+ onPress: PropTypes.func,
684
+ loading: PropTypes.bool,
685
+ cached: PropTypes.bool,
686
+ scaleSize: PropTypes.bool,
687
+ extraPropsImage: PropTypes.object,
688
+ number: PropTypes.string,
689
+ custom: PropTypes.bool,
399
690
  };
400
691
 
401
692
  Avatar.defaultProps = {
402
- quality: 'medium',
403
- size: 'small',
404
- shape: 'circle',
405
- name: '',
693
+ resizeMode: 'cover',
694
+ cached: true,
695
+ scaleSize: false,
696
+ quality: 'medium',
697
+ size: 'small',
698
+ custom: false,
406
699
  };
407
-
408
- export default Avatar;