You can not select more than 25 topics Topics must start with a letter or number, can include dashes ('-') and can be up to 35 characters long.

AbstractAvatar.js 4.7KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150151152153154155156157158159160161162163164165166167168169170171172173174175176177
  1. // @flow
  2. import { PureComponent } from 'react';
  3. import { getParticipantById } from '../../participants';
  4. import { getAvatarColor, getInitials } from '../functions';
  5. export type Props = {
  6. /**
  7. * The string we base the initials on (this is generated from a list of precendences).
  8. */
  9. _initialsBase: ?string,
  10. /**
  11. * An URL that we validated that it can be loaded.
  12. */
  13. _loadableAvatarUrl: ?string,
  14. /**
  15. * A string to override the initials to generate a color of. This is handy if you don't want to make
  16. * the background color match the string that the initials are generated from.
  17. */
  18. colorBase?: string,
  19. /**
  20. * Display name of the entity to render an avatar for (if any). This is handy when we need
  21. * an avatar for a non-participasnt entity (e.g. a recent list item).
  22. */
  23. displayName?: string,
  24. /**
  25. * The ID of the participant to render an avatar for (if it's a participant avatar).
  26. */
  27. participantId?: string,
  28. /**
  29. * The size of the avatar.
  30. */
  31. size: number,
  32. /**
  33. * URI of the avatar, if any.
  34. */
  35. uri: ?string,
  36. }
  37. type State = {
  38. avatarFailed: boolean
  39. }
  40. export const DEFAULT_SIZE = 65;
  41. /**
  42. * Implements an abstract class to render avatars in the app.
  43. */
  44. export default class AbstractAvatar<P: Props> extends PureComponent<P, State> {
  45. /**
  46. * Instantiates a new {@code Component}.
  47. *
  48. * @inheritdoc
  49. */
  50. constructor(props: P) {
  51. super(props);
  52. this.state = {
  53. avatarFailed: false
  54. };
  55. this._onAvatarLoadError = this._onAvatarLoadError.bind(this);
  56. }
  57. /**
  58. * Implements {@code Component#componentDidUpdate}.
  59. *
  60. * @inheritdoc
  61. */
  62. componentDidUpdate(prevProps: P) {
  63. if (prevProps.uri !== this.props.uri) {
  64. // URI changed, so we need to try to fetch it again.
  65. // Eslint doesn't like this statement, but based on the React doc, it's safe if it's
  66. // wrapped in a condition: https://reactjs.org/docs/react-component.html#componentdidupdate
  67. // eslint-disable-next-line react/no-did-update-set-state
  68. this.setState({
  69. avatarFailed: false
  70. });
  71. }
  72. }
  73. /**
  74. * Implements {@code Componenr#render}.
  75. *
  76. * @inheritdoc
  77. */
  78. render() {
  79. const {
  80. _initialsBase,
  81. _loadableAvatarUrl,
  82. colorBase,
  83. uri
  84. } = this.props;
  85. const { avatarFailed } = this.state;
  86. // _loadableAvatarUrl is validated that it can be loaded, but uri (if present) is not, so
  87. // we still need to do a check for that. And an explicitly provided URI is higher priority than
  88. // an avatar URL anyhow.
  89. if ((uri && !avatarFailed) || _loadableAvatarUrl) {
  90. return this._renderURLAvatar((!avatarFailed && uri) || _loadableAvatarUrl);
  91. }
  92. const _initials = getInitials(_initialsBase);
  93. if (_initials) {
  94. return this._renderInitialsAvatar(_initials, getAvatarColor(colorBase || _initialsBase));
  95. }
  96. return this._renderDefaultAvatar();
  97. }
  98. _onAvatarLoadError: () => void;
  99. /**
  100. * Callback to handle the error while loading of the avatar URI.
  101. *
  102. * @returns {void}
  103. */
  104. _onAvatarLoadError() {
  105. this.setState({
  106. avatarFailed: true
  107. });
  108. }
  109. /**
  110. * Function to render the actual, platform specific default avatar component.
  111. *
  112. * @returns {React$Element<*>}
  113. */
  114. _renderDefaultAvatar: () => React$Element<*>
  115. /**
  116. * Function to render the actual, platform specific initials-based avatar component.
  117. *
  118. * @param {string} initials - The initials to use.
  119. * @param {string} color - The color to use.
  120. * @returns {React$Element<*>}
  121. */
  122. _renderInitialsAvatar: (string, string) => React$Element<*>
  123. /**
  124. * Function to render the actual, platform specific URL-based avatar component.
  125. *
  126. * @param {string} uri - The URI of the avatar.
  127. * @returns {React$Element<*>}
  128. */
  129. _renderURLAvatar: ?string => React$Element<*>
  130. }
  131. /**
  132. * Maps part of the Redux state to the props of this component.
  133. *
  134. * @param {Object} state - The Redux state.
  135. * @param {Props} ownProps - The own props of the component.
  136. * @returns {Props}
  137. */
  138. export function _mapStateToProps(state: Object, ownProps: Props) {
  139. const { displayName, participantId } = ownProps;
  140. const _participant = participantId && getParticipantById(state, participantId);
  141. const _initialsBase = (_participant && (_participant.name || _participant.email)) || displayName;
  142. return {
  143. _initialsBase,
  144. _loadableAvatarUrl: _participant && _participant.loadableAvatarUrl
  145. };
  146. }