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.

99 lines
3.1 KiB

  1. import React, { PureComponent, Fragment } from 'react';
  2. import ReactDOM from 'react-dom';
  3. import PropTypes from 'prop-types';
  4. import { IntlProvider, addLocaleData } from 'react-intl';
  5. import { getLocale } from '../locales';
  6. import MediaGallery from '../components/media_gallery';
  7. import Video from '../features/video';
  8. import Card from '../features/status/components/card';
  9. import Poll from 'mastodon/components/poll';
  10. import ModalRoot from '../components/modal_root';
  11. import { getScrollbarWidth } from '../features/ui/components/modal_root';
  12. import MediaModal from '../features/ui/components/media_modal';
  13. import { List as ImmutableList, fromJS } from 'immutable';
  14. const { localeData, messages } = getLocale();
  15. addLocaleData(localeData);
  16. const MEDIA_COMPONENTS = { MediaGallery, Video, Card, Poll };
  17. export default class MediaContainer extends PureComponent {
  18. static propTypes = {
  19. locale: PropTypes.string.isRequired,
  20. components: PropTypes.object.isRequired,
  21. };
  22. state = {
  23. media: null,
  24. index: null,
  25. time: null,
  26. };
  27. handleOpenMedia = (media, index) => {
  28. document.body.classList.add('with-modals--active');
  29. document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
  30. this.setState({ media, index });
  31. }
  32. handleOpenVideo = (video, time) => {
  33. const media = ImmutableList([video]);
  34. document.body.classList.add('with-modals--active');
  35. document.documentElement.style.marginRight = `${getScrollbarWidth()}px`;
  36. this.setState({ media, time });
  37. }
  38. handleCloseMedia = () => {
  39. document.body.classList.remove('with-modals--active');
  40. document.documentElement.style.marginRight = 0;
  41. this.setState({ media: null, index: null, time: null });
  42. }
  43. render () {
  44. const { locale, components } = this.props;
  45. return (
  46. <IntlProvider locale={locale} messages={messages}>
  47. <Fragment>
  48. {[].map.call(components, (component, i) => {
  49. const componentName = component.getAttribute('data-component');
  50. const Component = MEDIA_COMPONENTS[componentName];
  51. const { media, card, poll, ...props } = JSON.parse(component.getAttribute('data-props'));
  52. Object.assign(props, {
  53. ...(media ? { media: fromJS(media) } : {}),
  54. ...(card ? { card: fromJS(card) } : {}),
  55. ...(poll ? { poll: fromJS(poll) } : {}),
  56. ...(componentName === 'Video' ? {
  57. onOpenVideo: this.handleOpenVideo,
  58. } : {
  59. onOpenMedia: this.handleOpenMedia,
  60. }),
  61. });
  62. return ReactDOM.createPortal(
  63. <Component {...props} key={`media-${i}`} />,
  64. component,
  65. );
  66. })}
  67. <ModalRoot onClose={this.handleCloseMedia}>
  68. {this.state.media && (
  69. <MediaModal
  70. media={this.state.media}
  71. index={this.state.index || 0}
  72. time={this.state.time}
  73. onClose={this.handleCloseMedia}
  74. />
  75. )}
  76. </ModalRoot>
  77. </Fragment>
  78. </IntlProvider>
  79. );
  80. }
  81. }