[Glitch] Translate CW, poll options and media descriptions
Port 69057467cb to glitch-soc
Co-authored-by: Claire <claire.github-309c@sitedethib.com>
Signed-off-by: Claire <claire.github-309c@sitedethib.com>
			
			
This commit is contained in:
		
				
					committed by
					
						 Claire
						Claire
					
				
			
			
				
	
			
			
			
						parent
						
							93c714417f
						
					
				
				
					commit
					7e25fd9b0c
				
			| @@ -6,7 +6,7 @@ import { unescapeHTML } from 'flavours/glitch/utils/html'; | ||||
|  | ||||
| const domParser = new DOMParser(); | ||||
|  | ||||
| const makeEmojiMap = record => record.emojis.reduce((obj, emoji) => { | ||||
| const makeEmojiMap = emojis => emojis.reduce((obj, emoji) => { | ||||
|   obj[`:${emoji.shortcode}:`] = emoji; | ||||
|   return obj; | ||||
| }, {}); | ||||
| @@ -20,7 +20,7 @@ export function searchTextFromRawStatus (status) { | ||||
| export function normalizeAccount(account) { | ||||
|   account = { ...account }; | ||||
|  | ||||
|   const emojiMap = makeEmojiMap(account); | ||||
|   const emojiMap = makeEmojiMap(account.emojis); | ||||
|   const displayName = account.display_name.trim().length === 0 ? account.username : account.display_name; | ||||
|  | ||||
|   account.display_name_html = emojify(escapeTextContentForBrowser(displayName), emojiMap); | ||||
| @@ -78,7 +78,7 @@ export function normalizeStatus(status, normalOldStatus, settings) { | ||||
|   } else { | ||||
|     const spoilerText   = normalStatus.spoiler_text || ''; | ||||
|     const searchContent = ([spoilerText, status.content].concat((status.poll && status.poll.options) ? status.poll.options.map(option => option.title) : [])).concat(status.media_attachments.map(att => att.description)).join('\n\n').replace(/<br\s*\/?>/g, '\n').replace(/<\/p><p>/g, '\n\n'); | ||||
|     const emojiMap      = makeEmojiMap(normalStatus); | ||||
|     const emojiMap      = makeEmojiMap(normalStatus.emojis); | ||||
|  | ||||
|     normalStatus.search_index = domParser.parseFromString(searchContent, 'text/html').documentElement.textContent; | ||||
|     normalStatus.contentHtml  = emojify(normalStatus.content, emojiMap); | ||||
| @@ -89,22 +89,48 @@ export function normalizeStatus(status, normalOldStatus, settings) { | ||||
|   return normalStatus; | ||||
| } | ||||
|  | ||||
| export function normalizeStatusTranslation(translation, status) { | ||||
|   const emojiMap = makeEmojiMap(status.get('emojis').toJS()); | ||||
|  | ||||
|   const normalTranslation = { | ||||
|     detected_source_language: translation.detected_source_language, | ||||
|     language: translation.language, | ||||
|     provider: translation.provider, | ||||
|     contentHtml: emojify(translation.content, emojiMap), | ||||
|     spoilerHtml: emojify(escapeTextContentForBrowser(translation.spoiler_text), emojiMap), | ||||
|     spoiler_text: translation.spoiler_text, | ||||
|   }; | ||||
|  | ||||
|   return normalTranslation; | ||||
| } | ||||
|  | ||||
| export function normalizePoll(poll) { | ||||
|   const normalPoll = { ...poll }; | ||||
|   const emojiMap = makeEmojiMap(normalPoll); | ||||
|   const emojiMap = makeEmojiMap(poll.emojis); | ||||
|  | ||||
|   normalPoll.options = poll.options.map((option, index) => ({ | ||||
|     ...option, | ||||
|     voted: poll.own_votes && poll.own_votes.includes(index), | ||||
|     title_emojified: emojify(escapeTextContentForBrowser(option.title), emojiMap), | ||||
|     titleHtml: emojify(escapeTextContentForBrowser(option.title), emojiMap), | ||||
|   })); | ||||
|  | ||||
|   return normalPoll; | ||||
| } | ||||
|  | ||||
| export function normalizePollOptionTranslation(translation, poll) { | ||||
|   const emojiMap = makeEmojiMap(poll.get('emojis').toJS()); | ||||
|  | ||||
|   const normalTranslation = { | ||||
|     ...translation, | ||||
|     titleHtml: emojify(escapeTextContentForBrowser(translation.title), emojiMap), | ||||
|   }; | ||||
|  | ||||
|   return normalTranslation; | ||||
| } | ||||
|  | ||||
| export function normalizeAnnouncement(announcement) { | ||||
|   const normalAnnouncement = { ...announcement }; | ||||
|   const emojiMap = makeEmojiMap(normalAnnouncement); | ||||
|   const emojiMap = makeEmojiMap.emojis(normalAnnouncement); | ||||
|  | ||||
|   normalAnnouncement.contentHtml = emojify(normalAnnouncement.content, emojiMap); | ||||
|  | ||||
|   | ||||
| @@ -344,7 +344,8 @@ export const translateStatusFail = (id, error) => ({ | ||||
|   error, | ||||
| }); | ||||
|  | ||||
| export const undoStatusTranslation = id => ({ | ||||
| export const undoStatusTranslation = (id, pollId) => ({ | ||||
|   type: STATUS_TRANSLATE_UNDO, | ||||
|   id, | ||||
|   pollId, | ||||
| }); | ||||
|   | ||||
| @@ -52,8 +52,9 @@ export default class MediaAttachments extends ImmutablePureComponent { | ||||
|   }; | ||||
|  | ||||
|   render () { | ||||
|     const { status, lang, width, height, revealed } = this.props; | ||||
|     const { status, width, height, revealed } = this.props; | ||||
|     const mediaAttachments = status.get('media_attachments'); | ||||
|     const language = status.getIn(['language', 'translation']) || status.get('language') || this.props.lang; | ||||
|  | ||||
|     if (mediaAttachments.size === 0) { | ||||
|       return null; | ||||
| @@ -61,14 +62,15 @@ export default class MediaAttachments extends ImmutablePureComponent { | ||||
|  | ||||
|     if (mediaAttachments.getIn([0, 'type']) === 'audio') { | ||||
|       const audio = mediaAttachments.get(0); | ||||
|       const description = audio.getIn(['translation', 'description']) || audio.get('description'); | ||||
|  | ||||
|       return ( | ||||
|         <Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} > | ||||
|           {Component => ( | ||||
|             <Component | ||||
|               src={audio.get('url')} | ||||
|               alt={audio.get('description')} | ||||
|               lang={lang || status.get('language')} | ||||
|               alt={description} | ||||
|               lang={language} | ||||
|               width={width} | ||||
|               height={height} | ||||
|               poster={audio.get('preview_url') || status.getIn(['account', 'avatar_static'])} | ||||
| @@ -82,6 +84,7 @@ export default class MediaAttachments extends ImmutablePureComponent { | ||||
|       ); | ||||
|     } else if (mediaAttachments.getIn([0, 'type']) === 'video') { | ||||
|       const video = mediaAttachments.get(0); | ||||
|       const description = video.getIn(['translation', 'description']) || video.get('description'); | ||||
|  | ||||
|       return ( | ||||
|         <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > | ||||
| @@ -91,8 +94,8 @@ export default class MediaAttachments extends ImmutablePureComponent { | ||||
|               frameRate={video.getIn(['meta', 'original', 'frame_rate'])} | ||||
|               blurhash={video.get('blurhash')} | ||||
|               src={video.get('url')} | ||||
|               alt={video.get('description')} | ||||
|               lang={lang || status.get('language')} | ||||
|               alt={description} | ||||
|               lang={language} | ||||
|               width={width} | ||||
|               height={height} | ||||
|               inline | ||||
| @@ -109,7 +112,7 @@ export default class MediaAttachments extends ImmutablePureComponent { | ||||
|           {Component => ( | ||||
|             <Component | ||||
|               media={mediaAttachments} | ||||
|               lang={lang || status.get('language')} | ||||
|               lang={language} | ||||
|               sensitive={status.get('sensitive')} | ||||
|               defaultWidth={width} | ||||
|               revealed={revealed} | ||||
|   | ||||
| @@ -124,10 +124,12 @@ class Item extends PureComponent { | ||||
|       badges.push(<span key='alt' className='media-gallery__gifv__label'>ALT</span>); | ||||
|     } | ||||
|  | ||||
|     const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); | ||||
|  | ||||
|     if (attachment.get('type') === 'unknown') { | ||||
|       return ( | ||||
|         <div className={classNames('media-gallery__item', { standalone, 'media-gallery__item--tall': height === 100, 'media-gallery__item--wide': width === 100 })} key={attachment.get('id')}> | ||||
|           <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={attachment.get('description')} lang={lang} target='_blank' rel='noopener noreferrer'> | ||||
|           <a className='media-gallery__item-thumbnail' href={attachment.get('remote_url') || attachment.get('url')} style={{ cursor: 'pointer' }} title={description} lang={lang} target='_blank' rel='noopener noreferrer'> | ||||
|             <Blurhash | ||||
|               hash={attachment.get('blurhash')} | ||||
|               className='media-gallery__preview' | ||||
| @@ -166,8 +168,8 @@ class Item extends PureComponent { | ||||
|             src={previewUrl} | ||||
|             srcSet={srcSet} | ||||
|             sizes={sizes} | ||||
|             alt={attachment.get('description')} | ||||
|             title={attachment.get('description')} | ||||
|             alt={description} | ||||
|             title={description} | ||||
|             lang={lang} | ||||
|             style={{ objectPosition: letterbox ? null : `${x}% ${y}%` }} | ||||
|             onLoad={this.handleImageLoad} | ||||
| @@ -183,8 +185,8 @@ class Item extends PureComponent { | ||||
|         <div className={classNames('media-gallery__gifv', { autoplay: autoPlay })}> | ||||
|           <video | ||||
|             className={`media-gallery__item-gifv-thumbnail${letterbox ? ' letterbox' : ''}`} | ||||
|             aria-label={attachment.get('description')} | ||||
|             title={attachment.get('description')} | ||||
|             aria-label={description} | ||||
|             title={description} | ||||
|             lang={lang} | ||||
|             role='application' | ||||
|             src={attachment.get('url')} | ||||
|   | ||||
| @@ -139,10 +139,12 @@ class Poll extends ImmutablePureComponent { | ||||
|     const active          = !!this.state.selected[`${optionIndex}`]; | ||||
|     const voted           = option.get('voted') || (poll.get('own_votes') && poll.get('own_votes').includes(optionIndex)); | ||||
|  | ||||
|     let titleEmojified = option.get('title_emojified'); | ||||
|     if (!titleEmojified) { | ||||
|     const title = option.getIn(['translation', 'title']) || option.get('title'); | ||||
|     let titleHtml = option.getIn(['translation', 'titleHtml']) || option.get('titleHtml'); | ||||
|  | ||||
|     if (!titleHtml) { | ||||
|       const emojiMap = makeEmojiMap(poll); | ||||
|       titleEmojified = emojify(escapeTextContentForBrowser(option.get('title')), emojiMap); | ||||
|       titleHtml = emojify(escapeTextContentForBrowser(title), emojiMap); | ||||
|     } | ||||
|  | ||||
|     return ( | ||||
| @@ -164,7 +166,7 @@ class Poll extends ImmutablePureComponent { | ||||
|               role={poll.get('multiple') ? 'checkbox' : 'radio'} | ||||
|               onKeyPress={this.handleOptionKeyPress} | ||||
|               aria-checked={active} | ||||
|               aria-label={option.get('title')} | ||||
|               aria-label={title} | ||||
|               lang={lang} | ||||
|               data-index={optionIndex} | ||||
|             /> | ||||
| @@ -183,7 +185,7 @@ class Poll extends ImmutablePureComponent { | ||||
|           <span | ||||
|             className='poll__option__text translate' | ||||
|             lang={lang} | ||||
|             dangerouslySetInnerHTML={{ __html: titleEmojified }} | ||||
|             dangerouslySetInnerHTML={{ __html: titleHtml }} | ||||
|           /> | ||||
|  | ||||
|           {!!voted && <span className='poll__voted'> | ||||
|   | ||||
| @@ -26,12 +26,18 @@ import StatusHeader from './status_header'; | ||||
| import StatusIcons from './status_icons'; | ||||
| import StatusPrepend from './status_prepend'; | ||||
|  | ||||
| const domParser = new DOMParser(); | ||||
|  | ||||
| export const textForScreenReader = (intl, status, rebloggedByText = false, expanded = false) => { | ||||
|   const displayName = status.getIn(['account', 'display_name']); | ||||
|  | ||||
|   const spoilerText = status.getIn(['translation', 'spoiler_text']) || status.get('spoiler_text'); | ||||
|   const contentHtml = status.getIn(['translation', 'contentHtml']) || status.get('contentHtml'); | ||||
|   const contentText = domParser.parseFromString(contentHtml, 'text/html').documentElement.textContent; | ||||
|  | ||||
|   const values = [ | ||||
|     displayName.length === 0 ? status.getIn(['account', 'acct']).split('@')[0] : displayName, | ||||
|     status.get('spoiler_text') && !expanded ? status.get('spoiler_text') : status.get('search_index').slice(status.get('spoiler_text').length), | ||||
|     spoilerText && !expanded ? spoilerText : contentText, | ||||
|     intl.formatDate(status.get('created_at'), { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }), | ||||
|     status.getIn(['account', 'acct']), | ||||
|   ]; | ||||
| @@ -391,12 +397,14 @@ class Status extends ImmutablePureComponent { | ||||
|  | ||||
|   handleOpenVideo = (options) => { | ||||
|     const { status } = this.props; | ||||
|     this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), status.get('language'), options); | ||||
|     const lang = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|     this.props.onOpenVideo(status.get('id'), status.getIn(['media_attachments', 0]), lang, options); | ||||
|   }; | ||||
|  | ||||
|   handleOpenMedia = (media, index) => { | ||||
|     const { status } = this.props; | ||||
|     this.props.onOpenMedia(status.get('id'), media, index, status.get('language')); | ||||
|     const lang = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|     this.props.onOpenMedia(status.get('id'), media, index, lang); | ||||
|   }; | ||||
|  | ||||
|   handleHotkeyOpenMedia = e => { | ||||
| @@ -406,10 +414,11 @@ class Status extends ImmutablePureComponent { | ||||
|     e.preventDefault(); | ||||
|  | ||||
|     if (status.get('media_attachments').size > 0) { | ||||
|       const lang = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|       if (status.getIn(['media_attachments', 0, 'type']) === 'video') { | ||||
|         onOpenVideo(statusId, status.getIn(['media_attachments', 0]), { startTime: 0 }); | ||||
|         onOpenVideo(statusId, status.getIn(['media_attachments', 0]), lang, { startTime: 0 }); | ||||
|       } else { | ||||
|         onOpenMedia(statusId, status.get('media_attachments'), 0); | ||||
|         onOpenMedia(statusId, status.get('media_attachments'), 0, lang); | ||||
|       } | ||||
|     } | ||||
|   }; | ||||
| @@ -625,6 +634,8 @@ class Status extends ImmutablePureComponent { | ||||
|       media.push(<PictureInPicturePlaceholder />); | ||||
|       mediaIcons.push('video-camera'); | ||||
|     } else if (attachments.size > 0) { | ||||
|       const language = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|  | ||||
|       if (muted || attachments.some(item => item.get('type') === 'unknown')) { | ||||
|         media.push( | ||||
|           <AttachmentList | ||||
| @@ -634,14 +645,15 @@ class Status extends ImmutablePureComponent { | ||||
|         ); | ||||
|       } else if (attachments.getIn([0, 'type']) === 'audio') { | ||||
|         const attachment = status.getIn(['media_attachments', 0]); | ||||
|         const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); | ||||
|  | ||||
|         media.push( | ||||
|           <Bundle fetchComponent={Audio} loading={this.renderLoadingAudioPlayer} > | ||||
|             {Component => ( | ||||
|               <Component | ||||
|                 src={attachment.get('url')} | ||||
|                 alt={attachment.get('description')} | ||||
|                 lang={status.get('language')} | ||||
|                 alt={description} | ||||
|                 lang={language} | ||||
|                 poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])} | ||||
|                 backgroundColor={attachment.getIn(['meta', 'colors', 'background'])} | ||||
|                 foregroundColor={attachment.getIn(['meta', 'colors', 'foreground'])} | ||||
| @@ -662,6 +674,7 @@ class Status extends ImmutablePureComponent { | ||||
|         mediaIcons.push('music'); | ||||
|       } else if (attachments.getIn([0, 'type']) === 'video') { | ||||
|         const attachment = status.getIn(['media_attachments', 0]); | ||||
|         const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); | ||||
|  | ||||
|         media.push( | ||||
|           <Bundle fetchComponent={Video} loading={this.renderLoadingVideoPlayer} > | ||||
| @@ -670,8 +683,8 @@ class Status extends ImmutablePureComponent { | ||||
|               frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])} | ||||
|               blurhash={attachment.get('blurhash')} | ||||
|               src={attachment.get('url')} | ||||
|               alt={attachment.get('description')} | ||||
|               lang={status.get('language')} | ||||
|               alt={description} | ||||
|               lang={language} | ||||
|               inline | ||||
|               sensitive={status.get('sensitive')} | ||||
|               letterbox={settings.getIn(['media', 'letterbox'])} | ||||
| @@ -691,7 +704,7 @@ class Status extends ImmutablePureComponent { | ||||
|             {Component => ( | ||||
|               <Component | ||||
|                 media={attachments} | ||||
|                 lang={status.get('language')} | ||||
|                 lang={language} | ||||
|                 sensitive={status.get('sensitive')} | ||||
|                 letterbox={settings.getIn(['media', 'letterbox'])} | ||||
|                 fullwidth={!rootId && settings.getIn(['media', 'fullwidth'])} | ||||
| @@ -724,7 +737,8 @@ class Status extends ImmutablePureComponent { | ||||
|     } | ||||
|  | ||||
|     if (status.get('poll')) { | ||||
|       contentMedia.push(<PollContainer pollId={status.get('poll')} lang={status.get('language')} />); | ||||
|       const language = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|       contentMedia.push(<PollContainer pollId={status.get('poll')} lang={language} />); | ||||
|       contentMediaIcons.push('tasks'); | ||||
|     } | ||||
|  | ||||
|   | ||||
| @@ -327,11 +327,11 @@ class StatusContent extends PureComponent { | ||||
|     const hidden = this.props.onExpandedToggle ? !this.props.expanded : this.state.hidden; | ||||
|     const contentLocale = intl.locale.replace(/[_-].*/, ''); | ||||
|     const targetLanguages = this.props.languages?.get(status.get('language') || 'und'); | ||||
|     const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('contentHtml').length > 0 && targetLanguages?.includes(contentLocale); | ||||
|     const renderTranslate = this.props.onTranslate && this.context.identity.signedIn && ['public', 'unlisted'].includes(status.get('visibility')) && status.get('search_index').trim().length > 0 && targetLanguages?.includes(contentLocale); | ||||
|  | ||||
|     const content = { __html: status.get('translation') ? status.getIn(['translation', 'content']) : status.get('contentHtml') }; | ||||
|     const spoilerContent = { __html: status.get('spoilerHtml') }; | ||||
|     const lang = status.get('translation') ? intl.locale : status.get('language'); | ||||
|     const content = { __html: status.getIn(['translation', 'contentHtml']) || status.get('contentHtml') }; | ||||
|     const spoilerContent = { __html: status.getIn(['translation', 'spoilerHtml']) || status.get('spoilerHtml') }; | ||||
|     const language = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|     const classNames = classnames('status__content', { | ||||
|       'status__content--with-action': parseClick && !disabled, | ||||
|       'status__content--with-spoiler': status.get('spoiler_text').length > 0, | ||||
| @@ -396,7 +396,7 @@ class StatusContent extends PureComponent { | ||||
|           <p | ||||
|             style={{ marginBottom: hidden && status.get('mentions').isEmpty() ? '0px' : null }} | ||||
|           > | ||||
|             <span dangerouslySetInnerHTML={spoilerContent} className='translate' lang={lang} /> | ||||
|             <span dangerouslySetInnerHTML={spoilerContent} className='translate' lang={language} /> | ||||
|             {' '} | ||||
|             <button type='button' className='status__content__spoiler-link' onClick={this.handleSpoilerClick} aria-expanded={!hidden}> | ||||
|               {toggleText} | ||||
| @@ -414,7 +414,7 @@ class StatusContent extends PureComponent { | ||||
|               className='status__content__text translate' | ||||
|               onMouseEnter={this.handleMouseEnter} | ||||
|               onMouseLeave={this.handleMouseLeave} | ||||
|               lang={lang} | ||||
|               lang={language} | ||||
|             /> | ||||
|             {!hidden && translateButton} | ||||
|             {media} | ||||
| @@ -439,7 +439,7 @@ class StatusContent extends PureComponent { | ||||
|             tabIndex={0} | ||||
|             onMouseEnter={this.handleMouseEnter} | ||||
|             onMouseLeave={this.handleMouseLeave} | ||||
|             lang={lang} | ||||
|             lang={language} | ||||
|           /> | ||||
|           {translateButton} | ||||
|           {media} | ||||
| @@ -460,7 +460,7 @@ class StatusContent extends PureComponent { | ||||
|             tabIndex={0} | ||||
|             onMouseEnter={this.handleMouseEnter} | ||||
|             onMouseLeave={this.handleMouseLeave} | ||||
|             lang={lang} | ||||
|             lang={language} | ||||
|           /> | ||||
|           {translateButton} | ||||
|           {media} | ||||
|   | ||||
| @@ -218,7 +218,7 @@ const mapDispatchToProps = (dispatch, { intl, contextType }) => ({ | ||||
|  | ||||
|   onTranslate (status) { | ||||
|     if (status.get('translation')) { | ||||
|       dispatch(undoStatusTranslation(status.get('id'))); | ||||
|       dispatch(undoStatusTranslation(status.get('id'), status.get('poll'))); | ||||
|     } else { | ||||
|       dispatch(translateStatus(status.get('id'))); | ||||
|     } | ||||
|   | ||||
| @@ -158,6 +158,8 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|       outerStyle.height = `${this.state.height}px`; | ||||
|     } | ||||
|  | ||||
|     const language = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|  | ||||
|     if (pictureInPicture.get('inUse')) { | ||||
|       media.push(<PictureInPicturePlaceholder />); | ||||
|       mediaIcons.push('video-camera'); | ||||
| @@ -166,12 +168,13 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|         media.push(<AttachmentList media={status.get('media_attachments')} />); | ||||
|       } else if (status.getIn(['media_attachments', 0, 'type']) === 'audio') { | ||||
|         const attachment = status.getIn(['media_attachments', 0]); | ||||
|         const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); | ||||
|  | ||||
|         media.push( | ||||
|           <Audio | ||||
|             src={attachment.get('url')} | ||||
|             alt={attachment.get('description')} | ||||
|             lang={status.get('language')} | ||||
|             alt={description} | ||||
|             lang={language} | ||||
|             duration={attachment.getIn(['meta', 'original', 'duration'], 0)} | ||||
|             poster={attachment.get('preview_url') || status.getIn(['account', 'avatar_static'])} | ||||
|             backgroundColor={attachment.getIn(['meta', 'colors', 'background'])} | ||||
| @@ -187,14 +190,16 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|         mediaIcons.push('music'); | ||||
|       } else if (status.getIn(['media_attachments', 0, 'type']) === 'video') { | ||||
|         const attachment = status.getIn(['media_attachments', 0]); | ||||
|         const description = attachment.getIn(['translation', 'description']) || attachment.get('description'); | ||||
|  | ||||
|         media.push( | ||||
|           <Video | ||||
|             preview={attachment.get('preview_url')} | ||||
|             frameRate={attachment.getIn(['meta', 'original', 'frame_rate'])} | ||||
|             blurhash={attachment.get('blurhash')} | ||||
|             src={attachment.get('url')} | ||||
|             alt={attachment.get('description')} | ||||
|             lang={status.get('language')} | ||||
|             alt={description} | ||||
|             lang={language} | ||||
|             inline | ||||
|             sensitive={status.get('sensitive')} | ||||
|             letterbox={settings.getIn(['media', 'letterbox'])} | ||||
| @@ -213,7 +218,7 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|             standalone | ||||
|             sensitive={status.get('sensitive')} | ||||
|             media={status.get('media_attachments')} | ||||
|             lang={status.get('language')} | ||||
|             lang={language} | ||||
|             letterbox={settings.getIn(['media', 'letterbox'])} | ||||
|             fullwidth={settings.getIn(['media', 'fullwidth'])} | ||||
|             hidden={!expanded} | ||||
|   | ||||
| @@ -481,7 +481,7 @@ class Status extends ImmutablePureComponent { | ||||
|     const { dispatch } = this.props; | ||||
|  | ||||
|     if (status.get('translation')) { | ||||
|       dispatch(undoStatusTranslation(status.get('id'))); | ||||
|       dispatch(undoStatusTranslation(status.get('id'), status.get('poll'))); | ||||
|     } else { | ||||
|       dispatch(translateStatus(status.get('id'))); | ||||
|     } | ||||
|   | ||||
| @@ -8,7 +8,7 @@ import Audio from 'flavours/glitch/features/audio'; | ||||
| import Footer from 'flavours/glitch/features/picture_in_picture/components/footer'; | ||||
|  | ||||
| const mapStateToProps = (state, { statusId }) => ({ | ||||
|   language: state.getIn(['statuses', statusId, 'language']), | ||||
|   status: state.getIn(['statuses', statusId]), | ||||
|   accountStaticAvatar: state.getIn(['accounts', state.getIn(['statuses', statusId, 'account']), 'avatar_static']), | ||||
| }); | ||||
|  | ||||
| @@ -17,7 +17,7 @@ class AudioModal extends ImmutablePureComponent { | ||||
|   static propTypes = { | ||||
|     media: ImmutablePropTypes.map.isRequired, | ||||
|     statusId: PropTypes.string.isRequired, | ||||
|     language: PropTypes.string, | ||||
|     status: ImmutablePropTypes.map.isRequired, | ||||
|     accountStaticAvatar: PropTypes.string.isRequired, | ||||
|     options: PropTypes.shape({ | ||||
|       autoPlay: PropTypes.bool, | ||||
| @@ -31,15 +31,17 @@ class AudioModal extends ImmutablePureComponent { | ||||
|   }; | ||||
|  | ||||
|   render () { | ||||
|     const { media, language, accountStaticAvatar, statusId, onClose } = this.props; | ||||
|     const { media, status, accountStaticAvatar, onClose } = this.props; | ||||
|     const options = this.props.options || {}; | ||||
|     const language = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|     const description = media.getIn(['translation', 'description']) || media.get('description'); | ||||
|  | ||||
|     return ( | ||||
|       <div className='modal-root__modal audio-modal'> | ||||
|         <div className='audio-modal__container'> | ||||
|           <Audio | ||||
|             src={media.get('url')} | ||||
|             alt={media.get('description')} | ||||
|             alt={description} | ||||
|             lang={language} | ||||
|             duration={media.getIn(['meta', 'original', 'duration'], 0)} | ||||
|             height={150} | ||||
| @@ -52,7 +54,7 @@ class AudioModal extends ImmutablePureComponent { | ||||
|         </div> | ||||
|  | ||||
|         <div className='media-modal__overlay'> | ||||
|           {statusId && <Footer statusId={statusId} withOpenButton onClose={onClose} />} | ||||
|           {status && <Footer statusId={status.get('id')} withOpenButton onClose={onClose} />} | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   | ||||
| @@ -147,6 +147,7 @@ class MediaModal extends ImmutablePureComponent { | ||||
|     const content = media.map((image) => { | ||||
|       const width  = image.getIn(['meta', 'original', 'width']) || null; | ||||
|       const height = image.getIn(['meta', 'original', 'height']) || null; | ||||
|       const description = image.getIn(['translation', 'description']) || image.get('description'); | ||||
|  | ||||
|       if (image.get('type') === 'image') { | ||||
|         return ( | ||||
| @@ -155,7 +156,7 @@ class MediaModal extends ImmutablePureComponent { | ||||
|             src={image.get('url')} | ||||
|             width={width} | ||||
|             height={height} | ||||
|             alt={image.get('description')} | ||||
|             alt={description} | ||||
|             lang={lang} | ||||
|             key={image.get('url')} | ||||
|             onClick={this.toggleNavigation} | ||||
| @@ -178,7 +179,7 @@ class MediaModal extends ImmutablePureComponent { | ||||
|             volume={volume || 1} | ||||
|             onCloseVideo={onClose} | ||||
|             detailed | ||||
|             alt={image.get('description')} | ||||
|             alt={description} | ||||
|             lang={lang} | ||||
|             key={image.get('url')} | ||||
|           /> | ||||
| @@ -190,7 +191,7 @@ class MediaModal extends ImmutablePureComponent { | ||||
|             width={width} | ||||
|             height={height} | ||||
|             key={image.get('url')} | ||||
|             alt={image.get('description')} | ||||
|             alt={description} | ||||
|             lang={lang} | ||||
|             onClick={this.toggleNavigation} | ||||
|           /> | ||||
|   | ||||
| @@ -9,7 +9,7 @@ import Footer from 'flavours/glitch/features/picture_in_picture/components/foote | ||||
| import Video from 'flavours/glitch/features/video'; | ||||
|  | ||||
| const mapStateToProps = (state, { statusId }) => ({ | ||||
|   language: state.getIn(['statuses', statusId, 'language']), | ||||
|   status: state.getIn(['statuses', statusId]), | ||||
| }); | ||||
|  | ||||
| class VideoModal extends ImmutablePureComponent { | ||||
| @@ -17,7 +17,7 @@ class VideoModal extends ImmutablePureComponent { | ||||
|   static propTypes = { | ||||
|     media: ImmutablePropTypes.map.isRequired, | ||||
|     statusId: PropTypes.string, | ||||
|     language: PropTypes.string, | ||||
|     status: ImmutablePropTypes.map, | ||||
|     options: PropTypes.shape({ | ||||
|       startTime: PropTypes.number, | ||||
|       autoPlay: PropTypes.bool, | ||||
| @@ -38,8 +38,10 @@ class VideoModal extends ImmutablePureComponent { | ||||
|   } | ||||
|  | ||||
|   render () { | ||||
|     const { media, statusId, language, onClose } = this.props; | ||||
|     const { media, status, onClose } = this.props; | ||||
|     const options = this.props.options || {}; | ||||
|     const language = status.getIn(['translation', 'language']) || status.get('language'); | ||||
|     const description = media.getIn(['translation', 'description']) || media.get('description'); | ||||
|  | ||||
|     return ( | ||||
|       <div className='modal-root__modal video-modal'> | ||||
| @@ -55,13 +57,13 @@ class VideoModal extends ImmutablePureComponent { | ||||
|             onCloseVideo={onClose} | ||||
|             autoFocus | ||||
|             detailed | ||||
|             alt={media.get('description')} | ||||
|             alt={description} | ||||
|             lang={language} | ||||
|           /> | ||||
|         </div> | ||||
|  | ||||
|         <div className='media-modal__overlay'> | ||||
|           {statusId && <Footer statusId={statusId} withOpenButton onClose={onClose} />} | ||||
|           {status && <Footer statusId={status.get('id')} withOpenButton onClose={onClose} />} | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|   | ||||
| @@ -2,14 +2,43 @@ import { Map as ImmutableMap, fromJS } from 'immutable'; | ||||
|  | ||||
| import { POLLS_IMPORT } from 'flavours/glitch/actions/importer'; | ||||
|  | ||||
| import { normalizePollOptionTranslation } from '../actions/importer/normalizer'; | ||||
| import { STATUS_TRANSLATE_SUCCESS, STATUS_TRANSLATE_UNDO } from '../actions/statuses'; | ||||
|  | ||||
| const importPolls = (state, polls) => state.withMutations(map => polls.forEach(poll => map.set(poll.id, fromJS(poll)))); | ||||
|  | ||||
| const statusTranslateSuccess = (state, pollTranslation) => { | ||||
|   return state.withMutations(map => { | ||||
|     if (pollTranslation) { | ||||
|       const poll = state.get(pollTranslation.id); | ||||
|  | ||||
|       pollTranslation.options.forEach((item, index) => { | ||||
|         map.setIn([pollTranslation.id, 'options', index, 'translation'], fromJS(normalizePollOptionTranslation(item, poll))); | ||||
|       }); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const statusTranslateUndo = (state, id) => { | ||||
|   return state.withMutations(map => { | ||||
|     const options = map.getIn([id, 'options']); | ||||
|  | ||||
|     if (options) { | ||||
|       options.forEach((item, index) => map.deleteIn([id, 'options', index, 'translation'])); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const initialState = ImmutableMap(); | ||||
|  | ||||
| export default function polls(state = initialState, action) { | ||||
|   switch(action.type) { | ||||
|   case POLLS_IMPORT: | ||||
|     return importPolls(state, action.polls); | ||||
|   case STATUS_TRANSLATE_SUCCESS: | ||||
|     return statusTranslateSuccess(state, action.translation.poll); | ||||
|   case STATUS_TRANSLATE_UNDO: | ||||
|     return statusTranslateUndo(state, action.pollId); | ||||
|   default: | ||||
|     return state; | ||||
|   } | ||||
|   | ||||
| @@ -25,6 +25,7 @@ import { | ||||
| } from 'flavours/glitch/actions/timelines'; | ||||
|  | ||||
| import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; | ||||
| import { normalizeStatusTranslation } from '../actions/importer/normalizer'; | ||||
|  | ||||
| const importStatus = (state, status) => state.set(status.id, fromJS(status)); | ||||
|  | ||||
| @@ -39,6 +40,27 @@ const deleteStatus = (state, id, references) => { | ||||
|   return state.delete(id); | ||||
| }; | ||||
|  | ||||
| const statusTranslateSuccess = (state, id, translation) => { | ||||
|   return state.withMutations(map => { | ||||
|     map.setIn([id, 'translation'], fromJS(normalizeStatusTranslation(translation, map.get(id)))); | ||||
|  | ||||
|     const list = map.getIn([id, 'media_attachments']); | ||||
|     if (translation.media_attachments && list) { | ||||
|       translation.media_attachments.forEach(item => { | ||||
|         const index = list.findIndex(i => i.get('id') === item.id); | ||||
|         map.setIn([id, 'media_attachments', index, 'translation'], fromJS({ description: item.description })); | ||||
|       }); | ||||
|     } | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const statusTranslateUndo = (state, id) => { | ||||
|   return state.withMutations(map => { | ||||
|     map.deleteIn([id, 'translation']); | ||||
|     map.getIn([id, 'media_attachments']).forEach((item, index) => map.deleteIn([id, 'media_attachments', index, 'translation'])); | ||||
|   }); | ||||
| }; | ||||
|  | ||||
| const initialState = ImmutableMap(); | ||||
|  | ||||
| export default function statuses(state = initialState, action) { | ||||
| @@ -90,9 +112,9 @@ export default function statuses(state = initialState, action) { | ||||
|   case TIMELINE_DELETE: | ||||
|     return deleteStatus(state, action.id, action.references); | ||||
|   case STATUS_TRANSLATE_SUCCESS: | ||||
|     return state.setIn([action.id, 'translation'], fromJS(action.translation)); | ||||
|     return statusTranslateSuccess(state, action.id, action.translation); | ||||
|   case STATUS_TRANSLATE_UNDO: | ||||
|     return state.deleteIn([action.id, 'translation']); | ||||
|     return statusTranslateUndo(state, action.id); | ||||
|   default: | ||||
|     return state; | ||||
|   } | ||||
|   | ||||
		Reference in New Issue
	
	Block a user