@momo-kits/avatar 0.0.2-beta → 0.0.4

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 +91 -27
  2. package/Avatar.web.js +98 -69
  3. package/package.json +15 -16
package/Avatar.js CHANGED
@@ -2,13 +2,19 @@
2
2
  import PropTypes from 'prop-types';
3
3
  import React, { Component } from 'react';
4
4
  import {
5
- View, StyleSheet, TouchableOpacity
5
+ View, StyleSheet, TouchableOpacity, Dimensions
6
6
  } from 'react-native';
7
7
  import { get, isEqual } from 'lodash';
8
8
  import {
9
- ValueUtil, Colors, Text, IconSource, Image
9
+ ValueUtil, Colors, Text, IconSource, Image, ScaleSize
10
10
  } from '@momo-kits/core';
11
11
 
12
+ export const AvatarQuality = {
13
+ low: 'low',
14
+ medium: 'medium',
15
+ high: 'high',
16
+ };
17
+
12
18
  export const AvatarSize = {
13
19
  tiny: 'tiny',
14
20
  small: 'small',
@@ -76,7 +82,7 @@ const styles = StyleSheet.create({
76
82
  }
77
83
  });
78
84
 
79
- const subIconSize = StyleSheet.create({
85
+ const subIconSize = (width) => ({
80
86
  tiny: {
81
87
  width: 8,
82
88
  height: 8,
@@ -176,7 +182,7 @@ const subIconSize = StyleSheet.create({
176
182
  }
177
183
  });
178
184
 
179
- const avatarSize = StyleSheet.create({
185
+ const avatarSize = (width) => ({
180
186
  tiny: {
181
187
  width: 16,
182
188
  height: 16,
@@ -264,21 +270,21 @@ const avatarSize = StyleSheet.create({
264
270
  }
265
271
  });
266
272
 
267
- const shortName = StyleSheet.create({
273
+ const shortName = (width) => ({
268
274
  tiny: {
269
- fontSize: 5
275
+ fontSize: ScaleSize(5, width)
270
276
  },
271
277
  small: {
272
- fontSize: 11
278
+ fontSize: ScaleSize(11, width)
273
279
  },
274
280
  medium: {
275
- fontSize: 15
281
+ fontSize: ScaleSize(15, width)
276
282
  },
277
283
  large: {
278
- fontSize: 20
284
+ fontSize: ScaleSize(20, width)
279
285
  },
280
286
  giant: {
281
- fontSize: 25
287
+ fontSize: ScaleSize(25, width)
282
288
  }
283
289
  });
284
290
 
@@ -288,8 +294,31 @@ export default class Avatar extends Component {
288
294
  this.state = {
289
295
  hideSource: false,
290
296
  ownUpdate: false,
291
- source: props.source
297
+ source: props.source,
298
+ dimensions: {}
292
299
  };
300
+ this.imageSource = '';
301
+ }
302
+
303
+ componentDidMount() {
304
+ const { scaleSize } = this.props;
305
+ if (scaleSize) {
306
+ this.subscription = Dimensions.addEventListener(
307
+ 'change',
308
+ ({ window }) => {
309
+ if(window?.height > 0 && window?.width > 0) {
310
+ this.setState({ dimensions: window });
311
+ }
312
+ }
313
+ );
314
+ }
315
+ }
316
+
317
+ componentWillUnmount() {
318
+ const { scaleSize } = this.props;
319
+ if (scaleSize) {
320
+ this.subscription?.remove?.();
321
+ }
293
322
  }
294
323
 
295
324
  static getDerivedStateFromProps(nextProps, prevState) {
@@ -334,49 +363,66 @@ export default class Avatar extends Component {
334
363
  }
335
364
 
336
365
  isValidImageUrl = (source) => {
337
- const validSource = source && (((source.uri && this.isUrl(source.uri)) || (typeof source === 'number')));
366
+ const validSource = source && source.uri && this.isUrl(source.uri);
338
367
  return validSource;
339
368
  }
340
369
 
370
+ mapAvatarQuality = (url, quality) => {
371
+ if (!quality || quality === AvatarQuality.medium) {
372
+ return url;
373
+ }
374
+ if (quality === AvatarQuality.low) {
375
+ return url.replace('.png', '_s64.png');
376
+ }
377
+ if (quality === AvatarQuality.high) {
378
+ return url.replace('.png', '_s512.png');
379
+ }
380
+
381
+ return url;
382
+ }
383
+
341
384
  mapStyleFromSize = (size) => {
342
- const avatarStyle = avatarSize.small;
385
+ const { dimensions } = this.state;
386
+ const avatarStyle = avatarSize(dimensions?.width > 0 ? dimensions?.width : null).small;
343
387
  if (typeof size === 'object') return size;
344
- if (avatarSize[size]) return avatarSize[size];
388
+ if (avatarSize(dimensions?.width > 0 ? dimensions?.width : null)[size]) return avatarSize(dimensions?.width > 0 ? dimensions?.width : null)[size];
345
389
  return avatarStyle;
346
390
  }
347
391
 
348
392
  mapShortNameStyle = (size) => {
349
393
  let shortNameStyle = {};
394
+ const { dimensions } = this.state;
350
395
  switch (size) {
351
396
  case AvatarSize.tiny: {
352
- shortNameStyle = shortName.tiny;
397
+ shortNameStyle = shortName(dimensions?.width > 0 ? dimensions?.width : null).tiny;
353
398
  break;
354
399
  }
355
400
  case AvatarSize.small: {
356
- shortNameStyle = shortName.small;
401
+ shortNameStyle = shortName(dimensions?.width > 0 ? dimensions?.width : null).small;
357
402
  break;
358
403
  }
359
404
  case AvatarSize.medium: {
360
- shortNameStyle = shortName.medium;
405
+ shortNameStyle = shortName(dimensions?.width > 0 ? dimensions?.width : null).medium;
361
406
  break;
362
407
  }
363
408
  case AvatarSize.large: {
364
- shortNameStyle = shortName.large;
409
+ shortNameStyle = shortName(dimensions?.width > 0 ? dimensions?.width : null).large;
365
410
  break;
366
411
  }
367
412
  case AvatarSize.giant: {
368
- shortNameStyle = shortName.giant;
413
+ shortNameStyle = shortName(dimensions?.width > 0 ? dimensions?.width : null).giant;
369
414
  break;
370
415
  }
371
416
  default:
372
- shortNameStyle = shortName.small;
417
+ shortNameStyle = shortName(dimensions?.width > 0 ? dimensions?.width : null).small;
373
418
  }
374
419
  return shortNameStyle;
375
420
  }
376
421
 
377
422
  mapSubIconSize = (size) => {
378
- const subIconStyle = subIconSize.small;
379
- if (subIconSize[size]) return subIconSize[size];
423
+ const { dimensions } = this.state;
424
+ const subIconStyle = subIconSize(dimensions?.width > 0 ? dimensions?.width : null).small;
425
+ if (subIconSize(dimensions?.width > 0 ? dimensions?.width : null)[size]) return subIconSize(dimensions?.width > 0 ? dimensions?.width : null)[size];
380
426
  return subIconStyle;
381
427
  }
382
428
 
@@ -393,8 +439,11 @@ export default class Avatar extends Component {
393
439
  };
394
440
  }
395
441
 
396
- onLoadSourceError = () => {
397
- this.setState({ hideSource: true, ownUpdate: true });
442
+ onLoadSourceError = (imageSource) => {
443
+ if(this.imageSource?.uri !== imageSource?.uri){
444
+ this.imageSource = imageSource;
445
+ this.setState({ hideSource: true, ownUpdate: true });
446
+ }
398
447
  }
399
448
 
400
449
  renderSubIcon = () => {
@@ -433,13 +482,24 @@ export default class Avatar extends Component {
433
482
 
434
483
  render() {
435
484
  const {
436
- size, source, name, resizeMode, style, onPress, isShowLoading, loading, cached, extraPropsImage
485
+ size, source, name, resizeMode, style, onPress, isShowLoading, loading, cached, extraPropsImage, quality
437
486
  } = this.props;
438
487
  const { hideSource } = this.state;
439
488
  const avatarStyle = this.mapStyleFromSize(size);
440
489
  const containerSize = this.extractSize(avatarStyle);
441
490
  const imageSource = ValueUtil.getImageSource(source);
442
491
  const showName = typeof imageSource === 'object' && Object.keys(imageSource).length === 0 && typeof imageSource !== 'number';
492
+ // add priority high for avatar
493
+ if (imageSource?.uri) {
494
+ imageSource.priority = 'high';
495
+ imageSource.uri = this.mapAvatarQuality(imageSource.uri, quality);
496
+ // if doest not have time query, set default cache time to 1 day
497
+ if (!imageSource.uri?.includes('?') && cached) {
498
+ const midnight = new Date().setHours(0, 0, 0, 0);
499
+ imageSource.uri = `${imageSource.uri}?time=${midnight}`;
500
+ }
501
+ }
502
+
443
503
  const inner = (
444
504
  <View style={[styles.container, containerSize, style]}>
445
505
  {(showName || hideSource) ? this.renderShortName(name, avatarStyle, size) : <View />}
@@ -449,7 +509,7 @@ export default class Avatar extends Component {
449
509
  cached={cached}
450
510
  loading={loading || isShowLoading}
451
511
  source={imageSource}
452
- onError={this.onLoadSourceError}
512
+ onError={() => this.onLoadSourceError(imageSource)}
453
513
  style={avatarStyle}
454
514
  resizeMode={resizeMode}
455
515
  {...extraPropsImage}
@@ -472,6 +532,7 @@ export default class Avatar extends Component {
472
532
  Avatar.propTypes = {
473
533
  name: PropTypes.string,
474
534
  resizeMode: PropTypes.string,
535
+ quality: PropTypes.oneOf(['low', 'medium', 'high']),
475
536
  size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large', 'giant', 'size8', 'size24', 'size36', 'size44', 'size48', 'size52', 'size56', 'size64', 'size72', 'size80', 'size88', 'size96',
476
537
  PropTypes.shape({
477
538
  width: PropTypes.number, height: PropTypes.number, borderRadius: PropTypes.number
@@ -483,10 +544,13 @@ Avatar.propTypes = {
483
544
  onPress: PropTypes.func,
484
545
  loading: PropTypes.bool,
485
546
  cached: PropTypes.bool,
547
+ scaleSize: PropTypes.bool,
486
548
  extraPropsImage: PropTypes.object
487
549
  };
488
550
 
489
551
  Avatar.defaultProps = {
490
552
  resizeMode: 'cover',
491
- cached: true
553
+ cached: true,
554
+ scaleSize: false,
555
+ quality: 'medium',
492
556
  };
package/Avatar.web.js CHANGED
@@ -2,14 +2,20 @@
2
2
  import PropTypes from 'prop-types';
3
3
  import React, { Component } from 'react';
4
4
  import {
5
- View, StyleSheet, TouchableOpacity, Image
5
+ View, StyleSheet, TouchableOpacity, Image, Dimensions
6
6
  } from 'react-native';
7
7
  import { get, isEqual } from 'lodash';
8
- import ValueUtils from '../../utils/ValueUtil';
9
- import Colors from '../../colors';
10
- import { Icons } from '../../icons';
11
- import Text from '../typography';
12
- import { ColorCore } from '../../colors/colorCore';
8
+ import ValueUtil from '../../core/utils/ValueUtil';
9
+ import Colors from '../../core/colors';
10
+ import { Icons as IconSource } from '../../core/icons';
11
+ import Text from '../../core/components/typography';
12
+ import { RFValueHorizontal as ScaleSize } from '../../core/components/typography/reponsiveSize';
13
+
14
+ export const AvatarQuality = {
15
+ low: 'low',
16
+ medium: 'medium',
17
+ high: 'high',
18
+ };
13
19
 
14
20
  export const AvatarSize = {
15
21
  tiny: 'tiny',
@@ -78,7 +84,7 @@ const styles = StyleSheet.create({
78
84
  }
79
85
  });
80
86
 
81
- const subIconSize = StyleSheet.create({
87
+ const subIconSize = (width) => ({
82
88
  tiny: {
83
89
  width: 8,
84
90
  height: 8,
@@ -178,7 +184,7 @@ const subIconSize = StyleSheet.create({
178
184
  }
179
185
  });
180
186
 
181
- const avatarSize = StyleSheet.create({
187
+ const avatarSize = (width) => ({
182
188
  tiny: {
183
189
  width: 16,
184
190
  height: 16,
@@ -266,21 +272,21 @@ const avatarSize = StyleSheet.create({
266
272
  }
267
273
  });
268
274
 
269
- const shortName = StyleSheet.create({
275
+ const shortName = (width) => ({
270
276
  tiny: {
271
- fontSize: 5
277
+ fontSize: ScaleSize(5, width)
272
278
  },
273
279
  small: {
274
- fontSize: 11
280
+ fontSize: ScaleSize(11, width)
275
281
  },
276
282
  medium: {
277
- fontSize: 15
283
+ fontSize: ScaleSize(15, width)
278
284
  },
279
285
  large: {
280
- fontSize: 20
286
+ fontSize: ScaleSize(20, width)
281
287
  },
282
288
  giant: {
283
- fontSize: 25
289
+ fontSize: ScaleSize(25, width)
284
290
  }
285
291
  });
286
292
 
@@ -290,10 +296,32 @@ export default class Avatar extends Component {
290
296
  this.state = {
291
297
  hideSource: false,
292
298
  ownUpdate: false,
293
- source: props.source
299
+ source: props.source,
300
+ dimensions: {}
294
301
  };
295
302
  }
296
303
 
304
+ componentDidMount() {
305
+ const { scaleSize } = this.props;
306
+ if (scaleSize) {
307
+ this.subscription = Dimensions.addEventListener(
308
+ 'change',
309
+ ({ window }) => {
310
+ if(window?.height > 0 && window?.width > 0) {
311
+ this.setState({ dimensions: window });
312
+ }
313
+ }
314
+ );
315
+ }
316
+ }
317
+
318
+ componentWillUnmount() {
319
+ const { scaleSize } = this.props;
320
+ if (scaleSize) {
321
+ this.subscription?.remove?.();
322
+ }
323
+ }
324
+
297
325
  static getDerivedStateFromProps(nextProps, prevState) {
298
326
  if (prevState.ownUpdate) {
299
327
  return {
@@ -336,49 +364,66 @@ export default class Avatar extends Component {
336
364
  }
337
365
 
338
366
  isValidImageUrl = (source) => {
339
- const validSource = source && (((source.uri && this.isUrl(source.uri)) || (typeof source === 'number')));
367
+ const validSource = source && source.uri && this.isUrl(source.uri);
340
368
  return validSource;
341
369
  }
342
370
 
371
+ mapAvatarQuality = (url, quality) => {
372
+ if (!quality || quality === AvatarQuality.medium) {
373
+ return url;
374
+ }
375
+ if (quality === AvatarQuality.low) {
376
+ return url.replace('.png', '_s64.png');
377
+ }
378
+ if (quality === AvatarQuality.high) {
379
+ return url.replace('.png', '_s512.png');
380
+ }
381
+
382
+ return url;
383
+ }
384
+
343
385
  mapStyleFromSize = (size) => {
344
- const avatarStyle = avatarSize.small;
386
+ const { dimensions } = this.state;
387
+ const avatarStyle = avatarSize(dimensions?.width || null).small;
345
388
  if (typeof size === 'object') return size;
346
- if (avatarSize[size]) return avatarSize[size];
389
+ if (avatarSize(dimensions?.width || null)[size]) return avatarSize(dimensions?.width || null)[size];
347
390
  return avatarStyle;
348
391
  }
349
392
 
350
393
  mapShortNameStyle = (size) => {
351
394
  let shortNameStyle = {};
395
+ const { dimensions } = this.state;
352
396
  switch (size) {
353
397
  case AvatarSize.tiny: {
354
- shortNameStyle = shortName.tiny;
398
+ shortNameStyle = shortName(dimensions?.width || null).tiny;
355
399
  break;
356
400
  }
357
401
  case AvatarSize.small: {
358
- shortNameStyle = shortName.small;
402
+ shortNameStyle = shortName(dimensions?.width || null).small;
359
403
  break;
360
404
  }
361
405
  case AvatarSize.medium: {
362
- shortNameStyle = shortName.medium;
406
+ shortNameStyle = shortName(dimensions?.width || null).medium;
363
407
  break;
364
408
  }
365
409
  case AvatarSize.large: {
366
- shortNameStyle = shortName.large;
410
+ shortNameStyle = shortName(dimensions?.width || null).large;
367
411
  break;
368
412
  }
369
413
  case AvatarSize.giant: {
370
- shortNameStyle = shortName.giant;
414
+ shortNameStyle = shortName(dimensions?.width || null).giant;
371
415
  break;
372
416
  }
373
417
  default:
374
- shortNameStyle = shortName.small;
418
+ shortNameStyle = shortName(dimensions?.width || null).small;
375
419
  }
376
420
  return shortNameStyle;
377
421
  }
378
422
 
379
423
  mapSubIconSize = (size) => {
380
- const subIconStyle = subIconSize.small;
381
- if (subIconSize[size]) return subIconSize[size];
424
+ const { dimensions } = this.state;
425
+ const subIconStyle = subIconSize(dimensions?.width || null).small;
426
+ if (subIconSize(dimensions?.width || null)[size]) return subIconSize(dimensions?.width || null)[size];
382
427
  return subIconStyle;
383
428
  }
384
429
 
@@ -391,7 +436,7 @@ export default class Avatar extends Component {
391
436
  height: containerWidth,
392
437
  borderRadius: containerWidth / 2,
393
438
  borderWidth,
394
- borderColor: ColorCore.black_01
439
+ borderColor: Colors.black_01
395
440
  };
396
441
  }
397
442
 
@@ -403,14 +448,14 @@ export default class Avatar extends Component {
403
448
  const { subIcon, size, subIconSource } = this.props;
404
449
  if ((!subIcon || subIcon === SubIconType.none) && !subIconSource) return <View />;
405
450
  const subIconSizeStyle = this.mapSubIconSize(size);
406
- const iconSource = ValueUtils.getImageSource(subIconSource);
451
+ const iconSource = ValueUtil.getImageSource(subIconSource);
407
452
  return (
408
453
  <View style={styles.subIcon}>
409
454
  {subIcon === SubIconType.momo
410
455
  ? (
411
456
  <Image
412
457
  style={[styles.subIconImage, subIconSizeStyle]}
413
- source={Icons.ic_round_momo_tiny}
458
+ source={IconSource.ic_round_momo_tiny}
414
459
  />
415
460
  )
416
461
  : <View />}
@@ -435,16 +480,27 @@ export default class Avatar extends Component {
435
480
 
436
481
  render() {
437
482
  const {
438
- size, source, name, resizeMode, style, onPress, isShowLoading, loading, cached, extraPropsImage
483
+ size, source, name, resizeMode, style, onPress, isShowLoading, loading, cached, extraPropsImage, quality
439
484
  } = this.props;
440
485
  const { hideSource } = this.state;
441
486
  const avatarStyle = this.mapStyleFromSize(size);
442
487
  const containerSize = this.extractSize(avatarStyle);
443
- const imageSource = ValueUtils.getImageSource(source);
488
+ const imageSource = ValueUtil.getImageSource(source);
444
489
  const showName = typeof imageSource === 'object' && Object.keys(imageSource).length === 0 && typeof imageSource !== 'number';
490
+ // add priority high for avatar
491
+ if (imageSource?.uri) {
492
+ imageSource.priority = 'high';
493
+ imageSource.uri = this.mapAvatarQuality(imageSource.uri, quality);
494
+ // if doest not have time query, set default cache time to 1 day
495
+ if (!imageSource.uri?.includes('?') && cached) {
496
+ const midnight = new Date().setHours(0, 0, 0, 0);
497
+ imageSource.uri = `${imageSource.uri}?time=${midnight}`;
498
+ }
499
+ }
500
+
445
501
  const inner = (
446
- <View style={[styles.container, avatarStyle, style]}>
447
- {(showName || hideSource) ? this.renderShortName(name, avatarStyle, size) : null}
502
+ <View style={[styles.container, containerSize, style]}>
503
+ {(showName || hideSource) ? this.renderShortName(name, avatarStyle, size) : <View />}
448
504
  {this.isValidImageUrl(imageSource) && !hideSource
449
505
  ? (
450
506
  <Image
@@ -472,54 +528,27 @@ export default class Avatar extends Component {
472
528
  }
473
529
 
474
530
  Avatar.propTypes = {
475
- /**
476
- * The default name when image load fail.
477
- */
478
531
  name: PropTypes.string,
479
- /**
480
- * How resize should the avatar be?
481
- */
482
532
  resizeMode: PropTypes.string,
483
- /**
484
- * How large should the avatar be?
485
- */
486
- size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large', 'giant', 'size8', 'size24', 'size36', 'size44', 'size48', 'size52', 'size56', 'size64', 'size72', 'size80', 'size88', 'size96']),
487
- /**
488
- * What source Avatar to use?
489
- */
533
+ quality: PropTypes.oneOf(['low', 'medium', 'high']),
534
+ size: PropTypes.oneOf(['tiny', 'small', 'medium', 'large', 'giant', 'size8', 'size24', 'size36', 'size44', 'size48', 'size52', 'size56', 'size64', 'size72', 'size80', 'size88', 'size96',
535
+ PropTypes.shape({
536
+ width: PropTypes.number, height: PropTypes.number, borderRadius: PropTypes.number
537
+ })]),
490
538
  source: PropTypes.oneOfType([PropTypes.shape({ uri: PropTypes.string }), PropTypes.number, PropTypes.string]),
491
- /**
492
- * What subIcon Avatar to use?
493
- */
494
539
  subIcon: PropTypes.oneOf(['momo', 'online', 'key', 'none']),
495
- /**
496
- * What custom subIcon Avatar to use?
497
- */
498
540
  subIconSource: PropTypes.oneOfType([PropTypes.shape({ uri: PropTypes.string }), PropTypes.number, PropTypes.string]),
499
- /**
500
- * What custom style Avatar to use?
501
- */
502
541
  style: PropTypes.oneOfType([PropTypes.array, PropTypes.object]),
503
- /**
504
- * Optional click handler.
505
- */
506
542
  onPress: PropTypes.func,
507
- /**
508
- * Optional loading handler.
509
- */
510
543
  loading: PropTypes.bool,
511
- /**
512
- * Optional cached handler.
513
- */
514
544
  cached: PropTypes.bool,
515
- /**
516
- * Optional extra prop of Image.
517
- */
545
+ scaleSize: PropTypes.bool,
518
546
  extraPropsImage: PropTypes.object
519
547
  };
520
548
 
521
549
  Avatar.defaultProps = {
522
550
  resizeMode: 'cover',
523
- size: 'medium',
524
- cached: true
551
+ cached: true,
552
+ scaleSize: false,
553
+ quality: 'medium',
525
554
  };
package/package.json CHANGED
@@ -1,17 +1,16 @@
1
1
  {
2
- "name": "@momo-kits/avatar",
3
- "version": "0.0.2-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
- "lodash": "^4.17.15",
12
- "@momo-kits/core": ">=0.0.5-beta"
13
- },
14
- "devDependencies": {},
15
- "license": "MoMo"
16
- }
17
-
2
+ "name": "@momo-kits/avatar",
3
+ "version": "0.0.4",
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"
16
+ }