Add polls and media attachments to edit comparison modal in web UI (#17727)
This commit is contained in:
		
					parent
					
						
							
								2a56a890da
							
						
					
				
			
			
				commit
				
					
						9f2791eb64
					
				
			
		
					 5 changed files with 154 additions and 49 deletions
				
			
		
							
								
								
									
										116
									
								
								app/javascript/mastodon/components/media_attachments.js
									
										
									
									
									
										Normal file
									
								
							
							
						
						
									
										116
									
								
								app/javascript/mastodon/components/media_attachments.js
									
										
									
									
									
										Normal file
									
								
							|  | @ -0,0 +1,116 @@ | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { MediaGallery, Video, Audio } from 'mastodon/features/ui/util/async-components'; | ||||
| import Bundle from 'mastodon/features/ui/components/bundle'; | ||||
| import noop from 'lodash/noop'; | ||||
| 
 | ||||
| export default class MediaAttachments extends ImmutablePureComponent { | ||||
| 
 | ||||
|   static propTypes = { | ||||
|     status: ImmutablePropTypes.map.isRequired, | ||||
|     height: PropTypes.number, | ||||
|     width: PropTypes.number, | ||||
|   }; | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     height: 110, | ||||
|     width: 239, | ||||
|   }; | ||||
| 
 | ||||
|   updateOnProps = [ | ||||
|     'status', | ||||
|   ]; | ||||
| 
 | ||||
|   renderLoadingMediaGallery = () => { | ||||
|     const { height, width } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='media-gallery' style={{ height, width }} /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderLoadingVideoPlayer = () => { | ||||
|     const { height, width } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='video-player' style={{ height, width }} /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   renderLoadingAudioPlayer = () => { | ||||
|     const { height, width } = this.props; | ||||
| 
 | ||||
|     return ( | ||||
|       <div className='audio-player' style={{ height, width }} /> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|   render () { | ||||
|     const { status, width, height } = this.props; | ||||
|     const mediaAttachments = status.get('media_attachments'); | ||||
| 
 | ||||
|     if (mediaAttachments.size === 0) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     if (mediaAttachments.getIn([0, 'type']) === 'audio') { | ||||
|       const audio = mediaAttachments.get(0); | ||||
| 
 | ||||
|       return ( | ||||
|         <Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} > | ||||
|           {Component => ( | ||||
|             <Component | ||||
|               src={audio.get('url')} | ||||
|               alt={audio.get('description')} | ||||
|               width={width} | ||||
|               height={height} | ||||
|               poster={audio.get('preview_url') || status.getIn(['account', 'avatar_static'])} | ||||
|               backgroundColor={audio.getIn(['meta', 'colors', 'background'])} | ||||
|               foregroundColor={audio.getIn(['meta', 'colors', 'foreground'])} | ||||
|               accentColor={audio.getIn(['meta', 'colors', 'accent'])} | ||||
|               duration={audio.getIn(['meta', 'original', 'duration'], 0)} | ||||
|             /> | ||||
|           )} | ||||
|         </Bundle> | ||||
|       ); | ||||
|     } else if (mediaAttachments.getIn([0, 'type']) === 'video') { | ||||
|       const video = mediaAttachments.get(0); | ||||
| 
 | ||||
|       return ( | ||||
|         <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > | ||||
|           {Component => ( | ||||
|             <Component | ||||
|               preview={video.get('preview_url')} | ||||
|               frameRate={video.getIn(['meta', 'original', 'frame_rate'])} | ||||
|               blurhash={video.get('blurhash')} | ||||
|               src={video.get('url')} | ||||
|               alt={video.get('description')} | ||||
|               width={width} | ||||
|               height={height} | ||||
|               inline | ||||
|               sensitive={status.get('sensitive')} | ||||
|               onOpenVideo={noop} | ||||
|             /> | ||||
|           )} | ||||
|         </Bundle> | ||||
|       ); | ||||
|     } else { | ||||
|       return ( | ||||
|         <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} > | ||||
|           {Component => ( | ||||
|             <Component | ||||
|               media={mediaAttachments} | ||||
|               sensitive={status.get('sensitive')} | ||||
|               defaultWidth={width} | ||||
|               height={height} | ||||
|               onOpenMedia={noop} | ||||
|             /> | ||||
|           )} | ||||
|         </Bundle> | ||||
|       ); | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
| } | ||||
|  | @ -1,14 +1,12 @@ | |||
| import React from 'react'; | ||||
| import PropTypes from 'prop-types'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import noop from 'lodash/noop'; | ||||
| import StatusContent from 'mastodon/components/status_content'; | ||||
| import { MediaGallery, Video } from 'mastodon/features/ui/util/async-components'; | ||||
| import Bundle from 'mastodon/features/ui/components/bundle'; | ||||
| import Avatar from 'mastodon/components/avatar'; | ||||
| import DisplayName from 'mastodon/components/display_name'; | ||||
| import RelativeTimestamp from 'mastodon/components/relative_timestamp'; | ||||
| import Option from './option'; | ||||
| import MediaAttachments from 'mastodon/components/media_attachments'; | ||||
| 
 | ||||
| export default class StatusCheckBox extends React.PureComponent { | ||||
| 
 | ||||
|  | @ -27,51 +25,10 @@ export default class StatusCheckBox extends React.PureComponent { | |||
|   render () { | ||||
|     const { status, checked } = this.props; | ||||
| 
 | ||||
|     let media = null; | ||||
| 
 | ||||
|     if (status.get('reblog')) { | ||||
|       return null; | ||||
|     } | ||||
| 
 | ||||
|     if (status.get('media_attachments').size > 0) { | ||||
|       if (status.get('media_attachments').some(item => item.get('type') === 'unknown')) { | ||||
| 
 | ||||
|       } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { | ||||
|         const video = status.getIn(['media_attachments', 0]); | ||||
| 
 | ||||
|         media = ( | ||||
|           <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > | ||||
|             {Component => ( | ||||
|               <Component | ||||
|                 preview={video.get('preview_url')} | ||||
|                 blurhash={video.get('blurhash')} | ||||
|                 src={video.get('url')} | ||||
|                 alt={video.get('description')} | ||||
|                 width={239} | ||||
|                 height={110} | ||||
|                 inline | ||||
|                 sensitive={status.get('sensitive')} | ||||
|                 onOpenVideo={noop} | ||||
|               /> | ||||
|             )} | ||||
|           </Bundle> | ||||
|         ); | ||||
|       } else { | ||||
|         media = ( | ||||
|           <Bundle fetchComponent={MediaGallery} loading={this.renderLoadingMediaGallery} > | ||||
|             {Component => ( | ||||
|               <Component | ||||
|                 media={status.get('media_attachments')} | ||||
|                 sensitive={status.get('sensitive')} | ||||
|                 height={110} | ||||
|                 onOpenMedia={noop} | ||||
|               /> | ||||
|             )} | ||||
|           </Bundle> | ||||
|         ); | ||||
|       } | ||||
|     } | ||||
| 
 | ||||
|     const labelComponent = ( | ||||
|       <div className='status-check-box__status poll__option__text'> | ||||
|         <div className='detailed-status__display-name'> | ||||
|  | @ -79,12 +36,13 @@ export default class StatusCheckBox extends React.PureComponent { | |||
|             <Avatar account={status.get('account')} size={46} /> | ||||
|           </div> | ||||
| 
 | ||||
|           <div><DisplayName account={status.get('account')} /> · <RelativeTimestamp timestamp={status.get('created_at')} /></div> | ||||
|           <div> | ||||
|             <DisplayName account={status.get('account')} /> · <RelativeTimestamp timestamp={status.get('created_at')} /> | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|         <StatusContent status={status} /> | ||||
| 
 | ||||
|         {media} | ||||
|         <MediaAttachments status={status} /> | ||||
|       </div> | ||||
|     ); | ||||
| 
 | ||||
|  |  | |||
|  | @ -9,6 +9,7 @@ import escapeTextContentForBrowser from 'escape-html'; | |||
| import InlineAccount from 'mastodon/components/inline_account'; | ||||
| import IconButton from 'mastodon/components/icon_button'; | ||||
| import RelativeTimestamp from 'mastodon/components/relative_timestamp'; | ||||
| import MediaAttachments from 'mastodon/components/media_attachments'; | ||||
| 
 | ||||
| const mapStateToProps = (state, { statusId }) => ({ | ||||
|   versions: state.getIn(['history', statusId, 'items']), | ||||
|  | @ -70,6 +71,25 @@ class CompareHistoryModal extends React.PureComponent { | |||
|             )} | ||||
| 
 | ||||
|             <div className='status__content__text status__content__text--visible translate' dangerouslySetInnerHTML={content} /> | ||||
| 
 | ||||
|             {!!currentVersion.get('poll') && ( | ||||
|               <div className='poll'> | ||||
|                 <ul> | ||||
|                   {currentVersion.getIn(['poll', 'options']).map(option => ( | ||||
|                     <li key={option.get('title')}> | ||||
|                       <span className='poll__input disabled' /> | ||||
| 
 | ||||
|                       <span | ||||
|                         className='poll__option__text translate' | ||||
|                         dangerouslySetInnerHTML={{ __html: emojify(escapeTextContentForBrowser(option.get('title')), emojiMap) }} | ||||
|                       /> | ||||
|                     </li> | ||||
|                   ))} | ||||
|                 </ul> | ||||
|               </div> | ||||
|             )} | ||||
| 
 | ||||
|             <MediaAttachments status={currentVersion} /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  |  | |||
|  | @ -1127,7 +1127,7 @@ | |||
|   .media-gallery, | ||||
|   .audio-player, | ||||
|   .video-player { | ||||
|     margin-top: 8px; | ||||
|     margin-top: 15px; | ||||
|     max-width: 250px; | ||||
|   } | ||||
| 
 | ||||
|  | @ -5609,6 +5609,12 @@ a.status-card.compact:hover { | |||
|       margin: 20px 0; | ||||
|     } | ||||
|   } | ||||
| 
 | ||||
|   .media-gallery, | ||||
|   .audio-player, | ||||
|   .video-player { | ||||
|     margin-top: 15px; | ||||
|   } | ||||
| } | ||||
| 
 | ||||
| .loading-bar { | ||||
|  |  | |||
|  | @ -25,7 +25,12 @@ class StatusEdit < ApplicationRecord | |||
| 
 | ||||
|   class PreservedMediaAttachment < ActiveModelSerializers::Model | ||||
|     attributes :media_attachment, :description | ||||
|     delegate :id, :type, :url, :preview_url, :remote_url, :preview_remote_url, :text_url, :meta, :blurhash, to: :media_attachment | ||||
| 
 | ||||
|     delegate :id, :type, :url, :preview_url, :remote_url, | ||||
|              :preview_remote_url, :text_url, :meta, :blurhash, | ||||
|              :not_processed?, :needs_redownload?, :local?, | ||||
|              :file, :thumbnail, :thumbnail_remote_url, | ||||
|              :shortcode, to: :media_attachment | ||||
|   end | ||||
| 
 | ||||
|   rate_limit by: :account, family: :statuses | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue