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.

UploadImageButton.tsx 4.0KB

123456789101112131415161718192021222324252627282930313233343536373839404142434445464748495051525354555657585960616263646566676869707172737475767778798081828384858687888990919293949596979899100101102103104105106107108109110111112113114115116117118119120121122123124125126127128129130131132133134135136137138139140141142143144145146147148149150
  1. /* eslint-disable lines-around-comment */
  2. import { Theme } from '@mui/material';
  3. import React, { useCallback, useRef } from 'react';
  4. import { WithTranslation } from 'react-i18next';
  5. import { makeStyles } from 'tss-react/mui';
  6. import { v4 as uuidv4 } from 'uuid';
  7. import { translate } from '../../base/i18n/functions';
  8. import Icon from '../../base/icons/components/Icon';
  9. import { IconPlusCircle } from '../../base/icons/svg';
  10. import { type Image, VIRTUAL_BACKGROUND_TYPE } from '../constants';
  11. // @ts-ignore
  12. import { resizeImage } from '../functions';
  13. // @ts-ignore
  14. import logger from '../logger';
  15. interface Props extends WithTranslation {
  16. /**
  17. * Callback used to set the 'loading' state of the parent component.
  18. */
  19. setLoading: Function;
  20. /**
  21. * Callback used to set the options.
  22. */
  23. setOptions: Function;
  24. /**
  25. * Callback used to set the storedImages array.
  26. */
  27. setStoredImages: Function;
  28. /**
  29. * If a label should be displayed alongside the button.
  30. */
  31. showLabel: boolean;
  32. /**
  33. * A list of images locally stored.
  34. */
  35. storedImages: Array<Image>;
  36. }
  37. // @ts-ignore
  38. const useStyles = makeStyles()((theme: Theme) => {
  39. return {
  40. addBackground: {
  41. marginRight: theme.spacing(2)
  42. },
  43. button: {
  44. display: 'none'
  45. },
  46. label: {
  47. fontSize: '14px',
  48. fontWeight: '600',
  49. lineHeight: '20px',
  50. marginLeft: '-10px',
  51. marginTop: theme.spacing(3),
  52. marginBottom: theme.spacing(2),
  53. color: '#669aec',
  54. display: 'inline-flex',
  55. cursor: 'pointer'
  56. }
  57. };
  58. });
  59. /**
  60. * Component used to upload an image.
  61. *
  62. * @param {Object} Props - The props of the component.
  63. * @returns {React$Node}
  64. */
  65. function UploadImageButton({
  66. setLoading,
  67. setOptions,
  68. setStoredImages,
  69. showLabel,
  70. storedImages,
  71. t
  72. }: Props) {
  73. const { classes } = useStyles();
  74. const uploadImageButton = useRef<HTMLInputElement>(null);
  75. const uploadImageKeyPress = useCallback(e => {
  76. if (uploadImageButton.current && (e.key === ' ' || e.key === 'Enter')) {
  77. e.preventDefault();
  78. uploadImageButton.current.click();
  79. }
  80. }, [ uploadImageButton.current ]);
  81. const uploadImage = useCallback(async e => {
  82. const reader = new FileReader();
  83. const imageFile = e.target.files;
  84. reader.readAsDataURL(imageFile[0]);
  85. reader.onload = async () => {
  86. const url = await resizeImage(reader.result);
  87. const uuId = uuidv4();
  88. setStoredImages([
  89. ...storedImages,
  90. {
  91. id: uuId,
  92. src: url
  93. }
  94. ]);
  95. setOptions({
  96. backgroundType: VIRTUAL_BACKGROUND_TYPE.IMAGE,
  97. enabled: true,
  98. url,
  99. selectedThumbnail: uuId
  100. });
  101. };
  102. logger.info('New virtual background image uploaded!');
  103. reader.onerror = () => {
  104. setLoading(false);
  105. logger.error('Failed to upload virtual image!');
  106. };
  107. }, [ storedImages ]);
  108. return (
  109. <>
  110. {showLabel && <label
  111. aria-label = { t('virtualBackground.uploadImage') }
  112. className = { classes.label }
  113. htmlFor = 'file-upload'
  114. onKeyPress = { uploadImageKeyPress }
  115. tabIndex = { 0 } >
  116. <Icon
  117. className = { classes.addBackground }
  118. size = { 20 }
  119. src = { IconPlusCircle } />
  120. {t('virtualBackground.addBackground')}
  121. </label>}
  122. <input
  123. accept = 'image/*'
  124. className = { classes.button }
  125. id = 'file-upload'
  126. onChange = { uploadImage }
  127. ref = { uploadImageButton }
  128. type = 'file' />
  129. </>
  130. );
  131. }
  132. export default translate(UploadImageButton);