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.

151 lines
5.7 KiB

6 years ago
6 years ago
  1. import React from 'react';
  2. import PropTypes from 'prop-types';
  3. import ImmutablePropTypes from 'react-immutable-proptypes';
  4. import Avatar from 'flavours/glitch/components/avatar';
  5. import DisplayName from 'flavours/glitch/components/display_name';
  6. import StatusContent from 'flavours/glitch/components/status_content';
  7. import MediaGallery from 'flavours/glitch/components/media_gallery';
  8. import AttachmentList from 'flavours/glitch/components/attachment_list';
  9. import { Link } from 'react-router-dom';
  10. import { FormattedDate, FormattedNumber } from 'react-intl';
  11. import Card from './card';
  12. import ImmutablePureComponent from 'react-immutable-pure-component';
  13. import Video from 'flavours/glitch/features/video';
  14. import VisibilityIcon from 'flavours/glitch/components/status_visibility_icon';
  15. export default class DetailedStatus extends ImmutablePureComponent {
  16. static contextTypes = {
  17. router: PropTypes.object,
  18. };
  19. static propTypes = {
  20. status: ImmutablePropTypes.map.isRequired,
  21. settings: ImmutablePropTypes.map.isRequired,
  22. onOpenMedia: PropTypes.func.isRequired,
  23. onOpenVideo: PropTypes.func.isRequired,
  24. onToggleHidden: PropTypes.func.isRequired,
  25. expanded: PropTypes.bool,
  26. };
  27. handleAccountClick = (e) => {
  28. if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey)) {
  29. e.preventDefault();
  30. this.context.router.history.push(`/accounts/${this.props.status.getIn(['account', 'id'])}`);
  31. }
  32. e.stopPropagation();
  33. }
  34. parseClick = (e, destination) => {
  35. if (e.button === 0 && !(e.ctrlKey || e.altKey || e.metaKey)) {
  36. e.preventDefault();
  37. this.context.router.history.push(destination);
  38. }
  39. e.stopPropagation();
  40. }
  41. handleOpenVideo = (media, startTime) => {
  42. this.props.onOpenVideo(media, startTime);
  43. }
  44. render () {
  45. const status = this.props.status.get('reblog') ? this.props.status.get('reblog') : this.props.status;
  46. const { expanded, onToggleHidden, settings } = this.props;
  47. let media = '';
  48. let mediaIcon = null;
  49. let applicationLink = '';
  50. let reblogLink = '';
  51. let reblogIcon = 'retweet';
  52. if (status.get('media_attachments').size > 0) {
  53. if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) {
  54. media = <AttachmentList media={status.get('media_attachments')} />;
  55. } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') {
  56. const video = status.getIn(['media_attachments', 0]);
  57. media = (
  58. <Video
  59. preview={video.get('preview_url')}
  60. src={video.get('url')}
  61. alt={video.get('description')}
  62. inline
  63. sensitive={status.get('sensitive')}
  64. letterbox={settings.getIn(['media', 'letterbox'])}
  65. fullwidth={settings.getIn(['media', 'fullwidth'])}
  66. preventPlayback={!expanded}
  67. onOpenVideo={this.handleOpenVideo}
  68. autoplay
  69. />
  70. );
  71. mediaIcon = 'video-camera';
  72. } else {
  73. media = (
  74. <MediaGallery
  75. standalone
  76. sensitive={status.get('sensitive')}
  77. media={status.get('media_attachments')}
  78. letterbox={settings.getIn(['media', 'letterbox'])}
  79. fullwidth={settings.getIn(['media', 'fullwidth'])}
  80. hidden={!expanded}
  81. onOpenMedia={this.props.onOpenMedia}
  82. />
  83. );
  84. mediaIcon = 'picture-o';
  85. }
  86. } else media = <Card onOpenMedia={this.props.onOpenMedia} card={status.get('card', null)} />;
  87. if (status.get('application')) {
  88. applicationLink = <span> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener'>{status.getIn(['application', 'name'])}</a></span>;
  89. }
  90. if (status.get('visibility') === 'direct') {
  91. reblogIcon = 'envelope';
  92. } else if (status.get('visibility') === 'private') {
  93. reblogIcon = 'lock';
  94. }
  95. if (status.get('visibility') === 'private') {
  96. reblogLink = <i className={`fa fa-${reblogIcon}`} />;
  97. } else {
  98. reblogLink = (<Link to={`/statuses/${status.get('id')}/reblogs`} className='detailed-status__link'>
  99. <i className={`fa fa-${reblogIcon}`} />
  100. <span className='detailed-status__reblogs'>
  101. <FormattedNumber value={status.get('reblogs_count')} />
  102. </span>
  103. </Link>);
  104. }
  105. return (
  106. <div className='detailed-status' data-status-by={status.getIn(['account', 'acct'])}>
  107. <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='detailed-status__display-name'>
  108. <div className='detailed-status__display-avatar'><Avatar account={status.get('account')} size={48} /></div>
  109. <DisplayName account={status.get('account')} />
  110. </a>
  111. <StatusContent
  112. status={status}
  113. media={media}
  114. mediaIcon={mediaIcon}
  115. expanded={expanded}
  116. collapsed={false}
  117. onExpandedToggle={onToggleHidden}
  118. parseClick={this.parseClick}
  119. />
  120. <div className='detailed-status__meta'>
  121. <a className='detailed-status__datetime' href={status.get('url')} target='_blank' rel='noopener'>
  122. <FormattedDate value={new Date(status.get('created_at'))} hour12={false} year='numeric' month='short' day='2-digit' hour='2-digit' minute='2-digit' />
  123. </a>{applicationLink} · {reblogLink} · <Link to={`/statuses/${status.get('id')}/favourites`} className='detailed-status__link'>
  124. <i className='fa fa-star' />
  125. <span className='detailed-status__favorites'>
  126. <FormattedNumber value={status.get('favourites_count')} />
  127. </span>
  128. </Link> · <VisibilityIcon visibility={status.get('visibility')} />
  129. </div>
  130. </div>
  131. );
  132. }
  133. }