logo

mastofe

My custom branche(s) on git.pleroma.social/pleroma/mastofe git clone https://hacktivis.me/git/mastofe.git

image_loader.js (3873B)


  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import classNames from 'classnames';
  4. import ZoomableImage from './zoomable_image';
  5. export default class ImageLoader extends React.PureComponent {
  6. static propTypes = {
  7. alt: PropTypes.string,
  8. src: PropTypes.string.isRequired,
  9. previewSrc: PropTypes.string,
  10. width: PropTypes.number,
  11. height: PropTypes.number,
  12. onClick: PropTypes.func,
  13. }
  14. static defaultProps = {
  15. alt: '',
  16. width: null,
  17. height: null,
  18. };
  19. state = {
  20. loading: true,
  21. error: false,
  22. }
  23. removers = [];
  24. canvas = null;
  25. get canvasContext() {
  26. if (!this.canvas) {
  27. return null;
  28. }
  29. this._canvasContext = this._canvasContext || this.canvas.getContext('2d');
  30. return this._canvasContext;
  31. }
  32. componentDidMount () {
  33. this.loadImage(this.props);
  34. }
  35. componentWillReceiveProps (nextProps) {
  36. if (this.props.src !== nextProps.src) {
  37. this.loadImage(nextProps);
  38. }
  39. }
  40. componentWillUnmount () {
  41. this.removeEventListeners();
  42. }
  43. loadImage (props) {
  44. this.removeEventListeners();
  45. this.setState({ loading: true, error: false });
  46. Promise.all([
  47. props.previewSrc && this.loadPreviewCanvas(props),
  48. this.hasSize() && this.loadOriginalImage(props),
  49. ].filter(Boolean))
  50. .then(() => {
  51. this.setState({ loading: false, error: false });
  52. this.clearPreviewCanvas();
  53. })
  54. .catch(() => this.setState({ loading: false, error: true }));
  55. }
  56. loadPreviewCanvas = ({ previewSrc, width, height }) => new Promise((resolve, reject) => {
  57. const image = new Image();
  58. const removeEventListeners = () => {
  59. image.removeEventListener('error', handleError);
  60. image.removeEventListener('load', handleLoad);
  61. };
  62. const handleError = () => {
  63. removeEventListeners();
  64. reject();
  65. };
  66. const handleLoad = () => {
  67. removeEventListeners();
  68. this.canvasContext.drawImage(image, 0, 0, width, height);
  69. resolve();
  70. };
  71. image.addEventListener('error', handleError);
  72. image.addEventListener('load', handleLoad);
  73. image.src = previewSrc;
  74. this.removers.push(removeEventListeners);
  75. })
  76. clearPreviewCanvas () {
  77. const { width, height } = this.canvas;
  78. this.canvasContext.clearRect(0, 0, width, height);
  79. }
  80. loadOriginalImage = ({ src }) => new Promise((resolve, reject) => {
  81. const image = new Image();
  82. const removeEventListeners = () => {
  83. image.removeEventListener('error', handleError);
  84. image.removeEventListener('load', handleLoad);
  85. };
  86. const handleError = () => {
  87. removeEventListeners();
  88. reject();
  89. };
  90. const handleLoad = () => {
  91. removeEventListeners();
  92. resolve();
  93. };
  94. image.addEventListener('error', handleError);
  95. image.addEventListener('load', handleLoad);
  96. image.src = src;
  97. this.removers.push(removeEventListeners);
  98. });
  99. removeEventListeners () {
  100. this.removers.forEach(listeners => listeners());
  101. this.removers = [];
  102. }
  103. hasSize () {
  104. const { width, height } = this.props;
  105. return typeof width === 'number' && typeof height === 'number';
  106. }
  107. setCanvasRef = c => {
  108. this.canvas = c;
  109. }
  110. render () {
  111. const { alt, src, width, height, onClick } = this.props;
  112. const { loading } = this.state;
  113. const className = classNames('image-loader', {
  114. 'image-loader--loading': loading,
  115. 'image-loader--amorphous': !this.hasSize(),
  116. });
  117. return (
  118. <div className={className}>
  119. {loading ? (
  120. <canvas
  121. className='image-loader__preview-canvas'
  122. ref={this.setCanvasRef}
  123. width={width}
  124. height={height}
  125. />
  126. ) : (
  127. <ZoomableImage
  128. alt={alt}
  129. src={src}
  130. onClick={onClick}
  131. />
  132. )}
  133. </div>
  134. );
  135. }
  136. }