@momo-kits/avatar 0.0.62-beta → 0.0.62-beta.1

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 (4) hide show
  1. package/Avatar.js +312 -422
  2. package/Avatar.web.js +73 -4
  3. package/package.json +14 -14
  4. package/publish.sh +1 -1
package/Avatar.js CHANGED
@@ -1,13 +1,21 @@
1
1
  /* eslint-disable indent */
2
- import PropTypes from 'prop-types';
3
- import React, { Component } from 'react';
4
- import {
5
- View, StyleSheet, TouchableOpacity, Dimensions
6
- } from 'react-native';
7
- import { get, isEqual } from 'lodash';
8
2
  import {
9
- ValueUtil, Colors, Text, IconSource, Image, ScaleSize
10
- } from '@momo-kits/core';
3
+ Colors,
4
+ Image,
5
+ Radius,
6
+ ScaleSize,
7
+ Text,
8
+ ValueUtil,
9
+ } from '@momo-kits/core-v2';
10
+ import PropTypes from 'prop-types';
11
+ import React from 'react';
12
+ import { StyleSheet, TouchableOpacity, View } from 'react-native';
13
+
14
+ export const AvatarQuality = {
15
+ low: 'low',
16
+ medium: 'medium',
17
+ high: 'high',
18
+ };
11
19
 
12
20
  export const AvatarSize = {
13
21
  tiny: 'tiny',
@@ -15,315 +23,191 @@ export const AvatarSize = {
15
23
  medium: 'medium',
16
24
  large: 'large',
17
25
  giant: 'giant',
18
- size8: 'size8',
19
- size24: 'size24',
20
- size36: 'size36',
21
- size44: 'size44',
22
- size48: 'size48',
23
- size52: 'size52',
24
- size56: 'size56',
25
- size64: 'size64',
26
- size72: 'size72',
27
- size80: 'size80',
28
- size88: 'size88',
29
- size96: 'size96'
30
26
  };
31
27
 
32
28
  export const SubIconType = {
33
29
  momo: 'momo',
34
30
  online: 'online',
35
31
  key: 'key',
36
- none: 'none'
32
+ none: 'none',
37
33
  };
38
34
 
39
35
  const styles = StyleSheet.create({
40
36
  container: {
41
37
  justifyContent: 'center',
42
- alignItems: 'center'
38
+ alignItems: 'center',
43
39
  },
44
40
  shortNameView: {
41
+ backgroundColor: Colors.pink_09,
45
42
  justifyContent: 'center',
46
43
  alignItems: 'center',
47
-
48
- backgroundColor: Colors.very_light_pink_four,
49
- width: '100%',
50
- height: '100%'
44
+ borderWidth: 1,
45
+ borderColor: Colors.black_04,
51
46
  },
52
47
  shortNameText: {
53
48
  // fontSize: 17,
54
- color: '#f6f6f6'
49
+ color: Colors.pink_03,
50
+ },
51
+ shortNameTextCustom: {
52
+ color: '#f6f6f6',
55
53
  },
56
54
  absolute: {
57
55
  position: 'absolute',
58
56
  top: 0,
59
57
  left: 0,
60
58
  right: 0,
61
- bottom: 0
59
+ bottom: 0,
62
60
  },
63
61
  subIcon: {
64
62
  position: 'absolute',
65
63
  bottom: 0,
66
- right: 0
64
+ right: 0,
65
+ },
66
+ topIcon: {
67
+ position: 'absolute',
68
+ top: 0,
69
+ right: 0,
67
70
  },
68
71
  subIconImage: {
69
72
  width: 22,
70
- height: 22
73
+ height: 22,
71
74
  },
72
75
  subIconImageOnline: {
73
76
  backgroundColor: Colors.kiwi_green,
74
77
  borderColor: 'white',
75
- borderWidth: 1
76
- }
77
- });
78
-
79
- const subIconSize = (width) => ({
80
- tiny: {
81
- width: 8,
82
- height: 8,
83
- borderRadius: 4
84
- },
85
- small: {
86
- width: 10,
87
- height: 10,
88
- borderRadius: 5
89
- },
90
- medium: {
91
- width: 12,
92
- height: 12,
93
- borderRadius: 6
78
+ borderWidth: 1,
94
79
  },
95
- large: {
96
- width: 16,
97
- height: 16,
98
- borderRadius: 8
99
- },
100
- size8: {
101
- width: 8,
102
- height: 8,
103
- borderRadius: 4
104
- },
105
- size24: {
106
- width: 10,
107
- height: 10,
108
- borderRadius: 5
109
- },
110
- size36: {
111
- width: 10,
112
- height: 10,
113
- borderRadius: 5
114
- },
115
- size44: {
116
- width: 14,
117
- height: 14,
118
- borderRadius: 7
119
- },
120
- size48: {
121
- width: 14,
122
- height: 14,
123
- borderRadius: 7
124
- },
125
- size52: {
126
- width: 16,
127
- height: 16,
128
- borderRadius: 8
129
- },
130
- size56: {
131
- width: 16,
132
- height: 16,
133
- borderRadius: 8
134
- },
135
- size64: {
136
- width: 16,
137
- height: 16,
138
- borderRadius: 8,
139
- top: -2,
140
- left: -2,
141
- },
142
- size72: {
143
- width: 18,
144
- height: 18,
145
- borderRadius: 9,
146
- top: -3,
147
- left: -3,
148
- },
149
- size80: {
150
- width: 18,
151
- height: 18,
152
- borderRadius: 9,
153
- top: -3,
154
- left: -3,
155
- },
156
- size88: {
157
- width: 18,
158
- height: 18,
159
- borderRadius: 9,
160
- top: -4,
161
- left: -4,
162
- },
163
- size96: {
164
- width: 20,
165
- height: 20,
166
- borderRadius: 10,
167
- top: -4,
168
- left: -4,
169
- },
170
- giant: {
171
- top: -5,
172
- left: -5,
173
- width: 24,
174
- height: 24,
175
- borderRadius: 12
176
- }
177
80
  });
178
81
 
179
- const avatarSize = (width) => ({
180
- tiny: {
181
- width: 16,
182
- height: 16,
183
- borderRadius: 8
184
- },
185
- small: {
186
- width: 32,
187
- height: 32,
188
- borderRadius: 16
189
- },
190
- medium: {
191
- width: 40,
192
- height: 40,
193
- borderRadius: 20
194
- },
195
- large: {
196
- width: 60,
197
- height: 60,
198
- borderRadius: 30
199
- },
200
- giant: {
201
- width: 120,
202
- height: 120,
203
- borderRadius: 60
204
- },
205
- size8: {
206
- width: 8,
207
- height: 8,
208
- borderRadius: 4
209
- },
210
- size24: {
211
- width: 24,
212
- height: 24,
213
- borderRadius: 12
214
- },
215
- size36: {
216
- width: 36,
217
- height: 36,
218
- borderRadius: 18
219
- },
220
- size44: {
221
- width: 44,
222
- height: 44,
223
- borderRadius: 22
224
- },
225
- size48: {
226
- width: 48,
227
- height: 48,
228
- borderRadius: 24
229
- },
230
- size52: {
231
- width: 52,
232
- height: 52,
233
- borderRadius: 26
234
- },
235
- size56: {
236
- width: 56,
237
- height: 56,
238
- borderRadius: 28
239
- },
240
- size64: {
241
- width: 64,
242
- height: 64,
243
- borderRadius: 32
244
- },
245
- size72: {
246
- width: 72,
247
- height: 72,
248
- borderRadius: 36
249
- },
250
- size80: {
251
- width: 80,
252
- height: 80,
253
- borderRadius: 40
254
- },
255
- size88: {
256
- width: 88,
257
- height: 88,
258
- borderRadius: 44
259
- },
260
- size96: {
261
- width: 96,
262
- height: 96,
263
- borderRadius: 48
264
- }
265
- });
266
-
267
- const shortName = (width) => ({
268
- tiny: {
269
- fontSize: ScaleSize(5, width)
270
- },
271
- small: {
272
- fontSize: ScaleSize(11, width)
273
- },
274
- medium: {
275
- fontSize: ScaleSize(15, width)
276
- },
277
- large: {
278
- fontSize: ScaleSize(20, width)
279
- },
280
- giant: {
281
- fontSize: ScaleSize(25, width)
282
- }
283
- });
284
-
285
- export default class Avatar extends Component {
286
- constructor(props) {
287
- super(props);
288
- this.state = {
289
- hideSource: false,
290
- ownUpdate: false,
291
- source: props.source,
292
- dimensions: {}
293
- };
294
- }
295
-
296
- componentDidMount() {
297
- const { scaleSize } = this.props;
298
- if (scaleSize) {
299
- this.subscription = Dimensions.addEventListener(
300
- 'change',
301
- ({ window }) => {
302
- this.setState({ dimensions: window });
303
- }
304
- );
305
- }
82
+ const subIconSize = (size) => {
83
+ switch (size) {
84
+ case 'giant':
85
+ return {
86
+ width: 12,
87
+ height: 12,
88
+ borderRadius: 6,
89
+ };
90
+ case 'small':
91
+ return {
92
+ width: 8,
93
+ height: 8,
94
+ borderRadius: 4,
95
+ };
96
+ case 'medium':
97
+ return {
98
+ width: 12,
99
+ height: 12,
100
+ borderRadius: 6,
101
+ };
102
+ case 'large':
103
+ return {
104
+ width: 12,
105
+ height: 12,
106
+ borderRadius: 6,
107
+ };
108
+ default:
109
+ return {
110
+ width: 12,
111
+ height: 12,
112
+ borderRadius: 6,
113
+ };
306
114
  }
115
+ };
307
116
 
308
- componentWillUnmount() {
309
- const { scaleSize } = this.props;
310
- if (scaleSize) {
311
- this.subscription?.remove?.();
117
+ const avatarSize = (size) => {
118
+ switch (size) {
119
+ case 'tiny':
120
+ return {
121
+ width: 16,
122
+ height: 16,
123
+ };
124
+ case 'small': {
125
+ return {
126
+ width: 32,
127
+ height: 32,
128
+ };
312
129
  }
130
+ case 'medium':
131
+ return {
132
+ width: 40,
133
+ height: 40,
134
+ };
135
+ case 'large':
136
+ return {
137
+ width: 56,
138
+ height: 56,
139
+ };
140
+ case 'giant':
141
+ return {
142
+ width: 72,
143
+ height: 72,
144
+ };
145
+ default:
146
+ return {
147
+ width: 40,
148
+ height: 40,
149
+ };
313
150
  }
151
+ };
314
152
 
315
- static getDerivedStateFromProps(nextProps, prevState) {
316
- if (prevState.ownUpdate) {
153
+ const shortName = (size) => {
154
+ switch (size) {
155
+ case 'tiny':
317
156
  return {
318
- ownUpdate: false,
157
+ fontSize: ScaleSize(8),
158
+ lineHeight: 16,
159
+ };
160
+ case 'small':
161
+ return {
162
+ fontSize: ScaleSize(14),
163
+ lineHeight: 20,
164
+ };
165
+ case 'medium':
166
+ return {
167
+ fontSize: ScaleSize(18),
168
+ lineHeight: 22,
169
+ };
170
+ case 'large':
171
+ return {
172
+ fontSize: ScaleSize(25),
173
+ };
174
+ case 'giant':
175
+ return {
176
+ fontSize: ScaleSize(32),
177
+ };
178
+ default:
179
+ return {
180
+ fontSize: ScaleSize(18),
181
+ lineHeight: 22,
319
182
  };
320
- } if (!isEqual(nextProps.source, prevState.source)) {
321
- return { source: nextProps.source, hideSource: false };
322
- }
323
- return null;
324
183
  }
184
+ };
185
+
186
+ const Avatar = ({
187
+ resizeMode,
188
+ cached,
189
+ quality,
190
+ size,
191
+ shape,
192
+ name,
193
+ source,
194
+ onPress,
195
+ style,
196
+ subIcon,
197
+ topIcon,
198
+ loading,
199
+ }) => {
200
+ const { width, height } = avatarSize(size);
201
+ const { fontSize, lineHeight } = shortName(size);
202
+ const {
203
+ width: iconWidth,
204
+ height: iconHeight,
205
+ borderRadius: iconRadius,
206
+ } = subIconSize(size);
207
+
208
+ const imageSource = ValueUtil.getImageSource(source);
325
209
 
326
- getContactShortName = (name) => {
210
+ const getContactShortName = (name) => {
327
211
  if (!name) return '';
328
212
  const shortNameList = name.split(' ', 2);
329
213
  const alphabet = [];
@@ -333,185 +217,191 @@ export default class Avatar extends Component {
333
217
  }
334
218
  });
335
219
  return alphabet.join('');
336
- }
337
-
338
- renderShortName = (name, avatarStyle, size) => {
339
- if (name) {
340
- const shortedName = this.getContactShortName(name);
341
- const shortNameStyle = this.mapShortNameStyle(size);
342
- return (
343
- <View style={[styles.shortNameView, avatarStyle]}>
344
- <Text.H4 style={[styles.shortNameText, shortNameStyle]}>{shortedName}</Text.H4>
345
- </View>
346
- );
347
- }
348
- return <View />;
349
- }
350
-
351
- isUrl(url) {
352
- const expression = /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/gi;
220
+ };
221
+ const isUrl = (url) => {
222
+ const expression =
223
+ /(ftp|http|https):\/\/(\w+:{0,1}\w*@)?(\S+)(:[0-9]+)?(\/|\/([\w#!:.?+=&%@!\-/]))?/gi;
353
224
  return expression.test(url);
354
- }
225
+ };
355
226
 
356
- isValidImageUrl = (source) => {
357
- const validSource = source && (((source.uri && this.isUrl(source.uri)) || (typeof source === 'number')));
227
+ const isValidImageUrl = (source) => {
228
+ const validSource = source && source.uri && isUrl(source.uri);
358
229
  return validSource;
359
- }
230
+ };
360
231
 
361
- mapStyleFromSize = (size) => {
362
- const { dimensions } = this.state;
363
- const avatarStyle = avatarSize(dimensions?.width || null).small;
364
- if (typeof size === 'object') return size;
365
- if (avatarSize(dimensions?.width || null)[size]) return avatarSize(dimensions?.width || null)[size];
366
- return avatarStyle;
367
- }
368
-
369
- mapShortNameStyle = (size) => {
370
- let shortNameStyle = {};
371
- const { dimensions } = this.state;
372
- switch (size) {
373
- case AvatarSize.tiny: {
374
- shortNameStyle = shortName(dimensions?.width || null).tiny;
375
- break;
376
- }
377
- case AvatarSize.small: {
378
- shortNameStyle = shortName(dimensions?.width || null).small;
379
- break;
380
- }
381
- case AvatarSize.medium: {
382
- shortNameStyle = shortName(dimensions?.width || null).medium;
383
- break;
384
- }
385
- case AvatarSize.large: {
386
- shortNameStyle = shortName(dimensions?.width || null).large;
387
- break;
388
- }
389
- case AvatarSize.giant: {
390
- shortNameStyle = shortName(dimensions?.width || null).giant;
391
- break;
392
- }
393
- default:
394
- shortNameStyle = shortName(dimensions?.width || null).small;
232
+ const mapAvatarQuality = (url, quality) => {
233
+ if (!quality || quality === AvatarQuality.medium) {
234
+ return url;
235
+ }
236
+ if (quality === AvatarQuality.low) {
237
+ return url.replace('.png', '_s64.png');
238
+ }
239
+ if (quality === AvatarQuality.high) {
240
+ return url.replace('.png', '_s512.png');
395
241
  }
396
- return shortNameStyle;
397
- }
398
-
399
- mapSubIconSize = (size) => {
400
- const { dimensions } = this.state;
401
- const subIconStyle = subIconSize(dimensions?.width || null).small;
402
- if (subIconSize(dimensions?.width || null)[size]) return subIconSize(dimensions?.width || null)[size];
403
- return subIconStyle;
404
- }
405
242
 
406
- extractSize = (avatarStyle) => {
407
- const { style } = this.props;
408
- const borderWidth = get(style, 'borderWidth', 1);
409
- const containerWidth = Math.round(avatarStyle.width + 2 + borderWidth);
410
- return {
411
- width: containerWidth,
412
- height: containerWidth,
413
- borderRadius: containerWidth / 2,
414
- borderWidth,
415
- borderColor: Colors.black_01
416
- };
417
- }
243
+ return url;
244
+ };
418
245
 
419
- onLoadSourceError = () => {
420
- this.setState({ hideSource: true, ownUpdate: true });
421
- }
246
+ const onLoadSourceError = (imageSource) => {
247
+ if (this.imageSource?.uri !== imageSource?.uri) {
248
+ this.imageSource = imageSource;
249
+ this.setState({ hideSource: true, ownUpdate: true });
250
+ }
251
+ };
422
252
 
423
- renderSubIcon = () => {
424
- const { subIcon, size, subIconSource } = this.props;
425
- if ((!subIcon || subIcon === SubIconType.none) && !subIconSource) return <View />;
426
- const subIconSizeStyle = this.mapSubIconSize(size);
427
- const iconSource = ValueUtil.getImageSource(subIconSource);
428
- return (
429
- <View style={styles.subIcon}>
430
- {subIcon === SubIconType.momo
431
- ? (
432
- <Image
433
- style={[styles.subIconImage, subIconSizeStyle]}
434
- source={IconSource.ic_round_momo_tiny}
435
- />
436
- )
437
- : <View />}
438
- {subIcon === SubIconType.online
439
- ? <View style={[styles.subIconImageOnline, subIconSizeStyle]} /> : <View />}
253
+ const renderSubIcon = () => {
254
+ // const subIconSizeStyle = this.mapSubIconSize(size);
255
+ if (subIcon && shape === 'circle' && size !== 'tiny') {
256
+ return (
257
+ <View style={styles.subIcon}>
258
+ <Image
259
+ source={subIcon}
260
+ style={[
261
+ {
262
+ width: iconWidth,
263
+ height: iconHeight,
264
+ borderRadius: iconRadius,
265
+ bottom: iconWidth / 2 - 2,
266
+ right: iconWidth / 2 - 2,
267
+ },
268
+ ]}
269
+ />
270
+ </View>
271
+ );
272
+ }
273
+ };
440
274
 
441
- {iconSource && subIconSource && !subIcon ? (
275
+ const renderTopIcon = () => {
276
+ if (topIcon && shape === 'circle' && size !== 'tiny') {
277
+ return (
278
+ <View style={styles.topIcon}>
442
279
  <Image
443
- style={[styles.subIconImage, subIconSizeStyle]}
444
- source={iconSource}
280
+ source={topIcon}
281
+ style={[
282
+ {
283
+ width: iconWidth,
284
+ height: iconHeight,
285
+ borderRadius: iconRadius,
286
+ top: iconWidth / 2 - 2,
287
+ right: iconWidth / 2 - 2,
288
+ },
289
+ ]}
445
290
  />
446
- )
447
- : <View />}
448
- </View>
449
- );
450
- }
291
+ </View>
292
+ );
293
+ }
294
+ };
451
295
 
452
- onPress = () => {
453
- const { onPress } = this.props;
296
+ const onPressIcon = () => {
454
297
  if (onPress && typeof onPress === 'function') onPress();
455
- }
298
+ };
456
299
 
457
- render() {
458
- const {
459
- size, source, name, resizeMode, style, onPress, isShowLoading, loading, cached, extraPropsImage
460
- } = this.props;
461
- const { hideSource } = this.state;
462
- const avatarStyle = this.mapStyleFromSize(size);
463
- const containerSize = this.extractSize(avatarStyle);
464
- const imageSource = ValueUtil.getImageSource(source);
465
- const showName = typeof imageSource === 'object' && Object.keys(imageSource).length === 0 && typeof imageSource !== 'number';
466
- const inner = (
467
- <View style={[styles.container, containerSize, style]}>
468
- {(showName || hideSource) ? this.renderShortName(name, avatarStyle, size) : <View />}
469
- {this.isValidImageUrl(imageSource) && !hideSource
470
- ? (
471
- <Image
472
- cached={cached}
473
- loading={loading || isShowLoading}
474
- source={imageSource}
475
- onError={this.onLoadSourceError}
476
- style={avatarStyle}
477
- resizeMode={resizeMode}
478
- {...extraPropsImage}
479
- />
480
- ) : <View />}
481
- {this.renderSubIcon()}
482
- </View>
483
- );
484
- if (onPress) {
300
+ const renderShortName = () => {
301
+ if (name) {
302
+ const shortedName = getContactShortName(name);
303
+ // const shortNameStyle = mapShortNameStyle(size);
485
304
  return (
486
- <TouchableOpacity activeOpacity={onPress ? 0.5 : 1} onPress={this.onPress}>
487
- {inner}
488
- </TouchableOpacity>
305
+ <View
306
+ style={[
307
+ {
308
+ width,
309
+ height,
310
+ borderRadius:
311
+ shape === 'circle' ? width / 2 : Radius.XS,
312
+ },
313
+ styles.shortNameView,
314
+ ]}>
315
+ <Text
316
+ style={{ fontSize, lineHeight, color: Colors.pink_03 }}>
317
+ {shortedName}
318
+ </Text>
319
+ </View>
489
320
  );
490
321
  }
491
- return inner;
322
+ return <View />;
323
+ };
324
+
325
+ if (imageSource?.uri) {
326
+ imageSource.priority = 'high';
327
+ imageSource.uri = mapAvatarQuality(imageSource.uri, quality);
328
+ // if doest not have time query, set default cache time to 1 day
329
+ if (!imageSource.uri?.includes('?') && cached) {
330
+ const midnight = new Date().setHours(0, 0, 0, 0);
331
+ imageSource.uri = `${imageSource.uri}?time=${midnight}`;
332
+ }
492
333
  }
493
- }
334
+
335
+ const renderImage = () => {
336
+ return (
337
+ <Image
338
+ cached={cached}
339
+ resizeMode={resizeMode}
340
+ onLoadSourceError={() => onLoadSourceError(source)}
341
+ source={source}
342
+ loading={loading}
343
+ style={[
344
+ {
345
+ width,
346
+ height,
347
+ borderRadius:
348
+ shape === 'circle' ? width / 2 : Radius.XS,
349
+ },
350
+ ]}
351
+ />
352
+ );
353
+ };
354
+
355
+ return (
356
+ <TouchableOpacity
357
+ activeOpacity={!!onPress ? 0.5 : 1}
358
+ style={[style]}
359
+ onPress={onPressIcon}>
360
+ <View
361
+ style={{
362
+ alignSelf: 'baseline',
363
+ }}>
364
+ {!!imageSource && isValidImageUrl(imageSource)
365
+ ? renderImage()
366
+ : renderShortName()}
367
+ {renderTopIcon()}
368
+ {renderSubIcon()}
369
+ </View>
370
+ </TouchableOpacity>
371
+ );
372
+ };
494
373
 
495
374
  Avatar.propTypes = {
496
375
  name: PropTypes.string,
497
- resizeMode: PropTypes.string,
498
- size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large', 'giant', 'size8', 'size24', 'size36', 'size44', 'size48', 'size52', 'size56', 'size64', 'size72', 'size80', 'size88', 'size96',
499
- PropTypes.shape({
500
- width: PropTypes.number, height: PropTypes.number, borderRadius: PropTypes.number
501
- })]),
502
- source: PropTypes.oneOfType([PropTypes.shape({ uri: PropTypes.string }), PropTypes.number, PropTypes.string]),
503
- subIcon: PropTypes.oneOf(['momo', 'online', 'key', 'none']),
504
- subIconSource: PropTypes.oneOfType([PropTypes.shape({ uri: PropTypes.string }), PropTypes.number, PropTypes.string]),
376
+ quality: PropTypes.oneOf(['low', 'medium', 'high']),
377
+ size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large', 'giant']),
378
+ source: PropTypes.oneOfType([
379
+ PropTypes.shape({ uri: PropTypes.string }),
380
+ PropTypes.number,
381
+ PropTypes.string,
382
+ ]),
383
+ subIcon: PropTypes.string,
384
+ topIcon: PropTypes.string,
385
+ subIconSource: PropTypes.oneOfType([
386
+ PropTypes.shape({ uri: PropTypes.string }),
387
+ PropTypes.number,
388
+ PropTypes.string,
389
+ ]),
505
390
  style: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
506
391
  onPress: PropTypes.func,
507
392
  loading: PropTypes.bool,
508
- cached: PropTypes.bool,
509
- scaleSize: PropTypes.bool,
510
- extraPropsImage: PropTypes.object
393
+ extraPropsImage: PropTypes.object,
394
+ shape: PropTypes.oneOf(['circle', 'square']),
511
395
  };
512
396
 
513
397
  Avatar.defaultProps = {
514
398
  resizeMode: 'cover',
515
399
  cached: true,
516
- scaleSize: false
400
+ scaleSize: false,
401
+ quality: 'medium',
402
+ size: 'small',
403
+ shape: 'circle',
404
+ name: '',
517
405
  };
406
+
407
+ export default Avatar;
package/Avatar.web.js CHANGED
@@ -11,6 +11,35 @@ import { Icons as IconSource } from '../../core/icons';
11
11
  import Text from '../../core/components/typography';
12
12
  import { RFValueHorizontal as ScaleSize } from '../../core/components/typography/reponsiveSize';
13
13
 
14
+ export const AvatarQuality = {
15
+ low: 'low',
16
+ medium: 'medium',
17
+ high: 'high',
18
+ };
19
+
20
+ const COLORS = [
21
+ '#3683f3',
22
+ '#ea63af',
23
+ '#f94889',
24
+ '#82b6da',
25
+ '#2fc093',
26
+ '#7b4a41',
27
+ '#ff97bc',
28
+ '#4130d7',
29
+ '#c3c3c3',
30
+ '#f7ae2f',
31
+ '#b04595',
32
+ '#57b748',
33
+ '#ff8900',
34
+ '#7841bf',
35
+ '#be3f3e',
36
+ '#c51515',
37
+ '#8e6472',
38
+ '#fed27d',
39
+ '#ff6f5d',
40
+ '#05b8ae',
41
+ ];
42
+
14
43
  export const AvatarSize = {
15
44
  tiny: 'tiny',
16
45
  small: 'small',
@@ -287,6 +316,7 @@ const shortName = (width) => ({
287
316
  export default class Avatar extends Component {
288
317
  constructor(props) {
289
318
  super(props);
319
+ this.color = this.getAvatarColor(props.name);
290
320
  this.state = {
291
321
  hideSource: false,
292
322
  ownUpdate: false,
@@ -301,7 +331,9 @@ export default class Avatar extends Component {
301
331
  this.subscription = Dimensions.addEventListener(
302
332
  'change',
303
333
  ({ window }) => {
304
- this.setState({ dimensions: window });
334
+ if(window?.height > 0 && window?.width > 0) {
335
+ this.setState({ dimensions: window });
336
+ }
305
337
  }
306
338
  );
307
339
  }
@@ -356,10 +388,24 @@ export default class Avatar extends Component {
356
388
  }
357
389
 
358
390
  isValidImageUrl = (source) => {
359
- const validSource = source && (((source.uri && this.isUrl(source.uri)) || (typeof source === 'number')));
391
+ const validSource = source && source.uri && this.isUrl(source.uri);
360
392
  return validSource;
361
393
  }
362
394
 
395
+ mapAvatarQuality = (url, quality) => {
396
+ if (!quality || quality === AvatarQuality.medium) {
397
+ return url;
398
+ }
399
+ if (quality === AvatarQuality.low) {
400
+ return url.replace('.png', '_s64.png');
401
+ }
402
+ if (quality === AvatarQuality.high) {
403
+ return url.replace('.png', '_s512.png');
404
+ }
405
+
406
+ return url;
407
+ }
408
+
363
409
  mapStyleFromSize = (size) => {
364
410
  const { dimensions } = this.state;
365
411
  const avatarStyle = avatarSize(dimensions?.width || null).small;
@@ -418,6 +464,16 @@ export default class Avatar extends Component {
418
464
  };
419
465
  }
420
466
 
467
+ getAvatarColor(name) {
468
+ const shortName = this.getContactShortName(name)
469
+ if (!shortName) return "#d5d6d6"
470
+ const firstLetter = shortName[0].charCodeAt(0)
471
+ let colorIndex = Number(firstLetter) % 26
472
+ if (colorIndex > 19) colorIndex = 25 - colorIndex
473
+ return COLORS[colorIndex]
474
+ }
475
+
476
+
421
477
  onLoadSourceError = () => {
422
478
  this.setState({ hideSource: true, ownUpdate: true });
423
479
  }
@@ -458,13 +514,24 @@ export default class Avatar extends Component {
458
514
 
459
515
  render() {
460
516
  const {
461
- size, source, name, resizeMode, style, onPress, isShowLoading, loading, cached, extraPropsImage
517
+ size, source, name, resizeMode, style, onPress, isShowLoading, loading, cached, extraPropsImage, quality
462
518
  } = this.props;
463
519
  const { hideSource } = this.state;
464
520
  const avatarStyle = this.mapStyleFromSize(size);
465
521
  const containerSize = this.extractSize(avatarStyle);
466
522
  const imageSource = ValueUtil.getImageSource(source);
467
523
  const showName = typeof imageSource === 'object' && Object.keys(imageSource).length === 0 && typeof imageSource !== 'number';
524
+ // add priority high for avatar
525
+ if (imageSource?.uri) {
526
+ imageSource.priority = 'high';
527
+ imageSource.uri = this.mapAvatarQuality(imageSource.uri, quality);
528
+ // if doest not have time query, set default cache time to 1 day
529
+ if (!imageSource.uri?.includes('?') && cached) {
530
+ const midnight = new Date().setHours(0, 0, 0, 0);
531
+ imageSource.uri = `${imageSource.uri}?time=${midnight}`;
532
+ }
533
+ }
534
+
468
535
  const inner = (
469
536
  <View style={[styles.container, containerSize, style]}>
470
537
  {(showName || hideSource) ? this.renderShortName(name, avatarStyle, size) : <View />}
@@ -497,6 +564,7 @@ export default class Avatar extends Component {
497
564
  Avatar.propTypes = {
498
565
  name: PropTypes.string,
499
566
  resizeMode: PropTypes.string,
567
+ quality: PropTypes.oneOf(['low', 'medium', 'high']),
500
568
  size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large', 'giant', 'size8', 'size24', 'size36', 'size44', 'size48', 'size52', 'size56', 'size64', 'size72', 'size80', 'size88', 'size96',
501
569
  PropTypes.shape({
502
570
  width: PropTypes.number, height: PropTypes.number, borderRadius: PropTypes.number
@@ -515,5 +583,6 @@ Avatar.propTypes = {
515
583
  Avatar.defaultProps = {
516
584
  resizeMode: 'cover',
517
585
  cached: true,
518
- scaleSize: false
586
+ scaleSize: false,
587
+ quality: 'medium',
519
588
  };
package/package.json CHANGED
@@ -1,16 +1,16 @@
1
1
  {
2
- "name": "@momo-kits/avatar",
3
- "version": "0.0.62-beta",
4
- "private": false,
5
- "main": "index.js",
6
- "dependencies": {},
7
- "peerDependencies": {
8
- "react-native": ">=0.55",
9
- "prop-types": "^15.7.2",
10
- "react": "16.9.0",
11
- "@momo-kits/core": ">=0.0.5-beta",
12
- "lodash": "^4.17.15"
13
- },
14
- "devDependencies": {},
15
- "license": "MoMo"
2
+ "name": "@momo-kits/avatar",
3
+ "version": "0.0.62-beta.1",
4
+ "private": false,
5
+ "main": "index.js",
6
+ "dependencies": {},
7
+ "peerDependencies": {
8
+ "react-native": ">=0.55",
9
+ "prop-types": "^15.7.2",
10
+ "react": "16.9.0",
11
+ "@momo-kits/core-v2": ">=0.0.5-beta",
12
+ "lodash": "^4.17.15"
13
+ },
14
+ "devDependencies": {},
15
+ "license": "MoMo"
16
16
  }
package/publish.sh CHANGED
@@ -21,7 +21,7 @@ rsync -r --verbose --exclude '*.mdx' --exclude '*Demo.js' --exclude 'props-type
21
21
  #npm login
22
22
  #publish dist to npm
23
23
  cd dist
24
- npm publish --access=public
24
+ npm publish --tag beta --access=public
25
25
  cd ..
26
26
  rm -rf dist
27
27