Change design of boost modal in web UI (#31555)
This commit is contained in:
		
					parent
					
						
							
								d820c0883d
							
						
					
				
			
			
				commit
				
					
						29b9642b31
					
				
			
		
					 3 changed files with 135 additions and 98 deletions
				
			
		|  | @ -1,28 +1,17 @@ | |||
| import type { MouseEventHandler } from 'react'; | ||||
| import { useCallback, useState } from 'react'; | ||||
| 
 | ||||
| import { defineMessages, FormattedMessage, useIntl } from 'react-intl'; | ||||
| 
 | ||||
| import classNames from 'classnames'; | ||||
| import { useHistory } from 'react-router'; | ||||
| 
 | ||||
| import type Immutable from 'immutable'; | ||||
| 
 | ||||
| import RepeatIcon from '@/material-icons/400-24px/repeat.svg?react'; | ||||
| import AttachmentList from 'mastodon/components/attachment_list'; | ||||
| import { Button } from 'mastodon/components/button'; | ||||
| import { Icon } from 'mastodon/components/icon'; | ||||
| import { VisibilityIcon } from 'mastodon/components/visibility_icon'; | ||||
| import PrivacyDropdown from 'mastodon/features/compose/components/privacy_dropdown'; | ||||
| import type { Account } from 'mastodon/models/account'; | ||||
| import { EmbeddedStatus } from 'mastodon/features/notifications_v2/components/embedded_status'; | ||||
| import type { Status, StatusVisibility } from 'mastodon/models/status'; | ||||
| import { useAppSelector } from 'mastodon/store'; | ||||
| 
 | ||||
| import { Avatar } from '../../../components/avatar'; | ||||
| import { Button } from '../../../components/button'; | ||||
| import { DisplayName } from '../../../components/display_name'; | ||||
| import { RelativeTimestamp } from '../../../components/relative_timestamp'; | ||||
| import StatusContent from '../../../components/status_content'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   cancel_reblog: { | ||||
|     id: 'status.cancel_reblog_private', | ||||
|  | @ -37,18 +26,17 @@ export const BoostModal: React.FC<{ | |||
|   onReblog: (status: Status, privacy: StatusVisibility) => void; | ||||
| }> = ({ status, onReblog, onClose }) => { | ||||
|   const intl = useIntl(); | ||||
|   const history = useHistory(); | ||||
| 
 | ||||
|   const default_privacy = useAppSelector( | ||||
|   const defaultPrivacy = useAppSelector( | ||||
|     // eslint-disable-next-line @typescript-eslint/no-unsafe-call, @typescript-eslint/no-unsafe-member-access
 | ||||
|     (state) => state.compose.get('default_privacy') as StatusVisibility, | ||||
|   ); | ||||
| 
 | ||||
|   const account = status.get('account') as Account; | ||||
|   const statusId = status.get('id') as string; | ||||
|   const statusVisibility = status.get('visibility') as StatusVisibility; | ||||
| 
 | ||||
|   const [privacy, setPrivacy] = useState<StatusVisibility>( | ||||
|     statusVisibility === 'private' ? 'private' : default_privacy, | ||||
|     statusVisibility === 'private' ? 'private' : defaultPrivacy, | ||||
|   ); | ||||
| 
 | ||||
|   const onPrivacyChange = useCallback((value: StatusVisibility) => { | ||||
|  | @ -60,20 +48,9 @@ export const BoostModal: React.FC<{ | |||
|     onClose(); | ||||
|   }, [onClose, onReblog, status, privacy]); | ||||
| 
 | ||||
|   const handleAccountClick = useCallback<MouseEventHandler>( | ||||
|     (e) => { | ||||
|       if (e.button === 0 && !(e.ctrlKey || e.metaKey)) { | ||||
|         e.preventDefault(); | ||||
|         onClose(); | ||||
|         history.push(`/@${account.acct}`); | ||||
|       } | ||||
|     }, | ||||
|     [history, onClose, account], | ||||
|   ); | ||||
| 
 | ||||
|   const buttonText = status.get('reblogged') | ||||
|     ? messages.cancel_reblog | ||||
|     : messages.reblog; | ||||
|   const handleCancel = useCallback(() => { | ||||
|     onClose(); | ||||
|   }, [onClose]); | ||||
| 
 | ||||
|   const findContainer = useCallback( | ||||
|     () => document.getElementsByClassName('modal-root__container')[0], | ||||
|  | @ -81,81 +58,78 @@ export const BoostModal: React.FC<{ | |||
|   ); | ||||
| 
 | ||||
|   return ( | ||||
|     <div className='modal-root__modal boost-modal'> | ||||
|       <div className='boost-modal__container'> | ||||
|         <div | ||||
|           className={classNames( | ||||
|             'status', | ||||
|             `status-${statusVisibility}`, | ||||
|             'light', | ||||
|           )} | ||||
|         > | ||||
|           <div className='status__info'> | ||||
|             <a | ||||
|               href={`/@${account.acct}/${status.get('id') as string}`} | ||||
|               className='status__relative-time' | ||||
|               target='_blank' | ||||
|               rel='noopener noreferrer' | ||||
|             > | ||||
|               <span className='status__visibility-icon'> | ||||
|                 <VisibilityIcon visibility={statusVisibility} /> | ||||
|               </span> | ||||
|               <RelativeTimestamp | ||||
|                 timestamp={status.get('created_at') as string} | ||||
|               /> | ||||
|             </a> | ||||
| 
 | ||||
|             <a | ||||
|               onClick={handleAccountClick} | ||||
|               href={`/@${account.acct}`} | ||||
|               className='status__display-name' | ||||
|             > | ||||
|               <div className='status__avatar'> | ||||
|                 <Avatar account={account} size={48} /> | ||||
|               </div> | ||||
| 
 | ||||
|               <DisplayName account={account} /> | ||||
|             </a> | ||||
|     <div className='modal-root__modal safety-action-modal'> | ||||
|       <div className='safety-action-modal__top'> | ||||
|         <div className='safety-action-modal__header'> | ||||
|           <div className='safety-action-modal__header__icon'> | ||||
|             <Icon icon={RepeatIcon} id='retweet' /> | ||||
|           </div> | ||||
| 
 | ||||
|           {/* @ts-expect-error Expected until StatusContent is typed */} | ||||
|           <StatusContent status={status} /> | ||||
|           <div> | ||||
|             <h1> | ||||
|               {status.get('reblogged') ? ( | ||||
|                 <FormattedMessage | ||||
|                   id='boost_modal.undo_reblog' | ||||
|                   defaultMessage='Unboost post?' | ||||
|                 /> | ||||
|               ) : ( | ||||
|                 <FormattedMessage | ||||
|                   id='boost_modal.reblog' | ||||
|                   defaultMessage='Boost post?' | ||||
|                 /> | ||||
|               )} | ||||
|             </h1> | ||||
|             <div> | ||||
|               <FormattedMessage | ||||
|                 id='boost_modal.combo' | ||||
|                 defaultMessage='You can press {combo} to skip this next time' | ||||
|                 values={{ | ||||
|                   combo: ( | ||||
|                     <span className='hotkey-combination'> | ||||
|                       <kbd>Shift</kbd>+<Icon id='retweet' icon={RepeatIcon} /> | ||||
|                     </span> | ||||
|                   ), | ||||
|                 }} | ||||
|               /> | ||||
|             </div> | ||||
|           </div> | ||||
|         </div> | ||||
| 
 | ||||
|           {(status.get('media_attachments') as Immutable.List<unknown>).size > | ||||
|             0 && ( | ||||
|             <AttachmentList compact media={status.get('media_attachments')} /> | ||||
|           )} | ||||
|         <div className='safety-action-modal__status'> | ||||
|           <EmbeddedStatus statusId={statusId} /> | ||||
|         </div> | ||||
|       </div> | ||||
| 
 | ||||
|       <div className='boost-modal__action-bar'> | ||||
|         <div> | ||||
|           <FormattedMessage | ||||
|             id='boost_modal.combo' | ||||
|             defaultMessage='You can press {combo} to skip this next time' | ||||
|             values={{ | ||||
|               combo: ( | ||||
|                 <span> | ||||
|                   Shift + <Icon id='retweet' icon={RepeatIcon} /> | ||||
|                 </span> | ||||
|               ), | ||||
|             }} | ||||
|       <div className={classNames('safety-action-modal__bottom')}> | ||||
|         <div className='safety-action-modal__actions'> | ||||
|           {!status.get('reblogged') && ( | ||||
|             <PrivacyDropdown | ||||
|               noDirect | ||||
|               value={privacy} | ||||
|               container={findContainer} | ||||
|               onChange={onPrivacyChange} | ||||
|               disabled={statusVisibility === 'private'} | ||||
|             /> | ||||
|           )} | ||||
| 
 | ||||
|           <div className='spacer' /> | ||||
| 
 | ||||
|           <button onClick={handleCancel} className='link-button'> | ||||
|             <FormattedMessage | ||||
|               id='confirmation_modal.cancel' | ||||
|               defaultMessage='Cancel' | ||||
|             /> | ||||
|           </button> | ||||
| 
 | ||||
|           <Button | ||||
|             onClick={handleReblog} | ||||
|             text={intl.formatMessage( | ||||
|               status.get('reblogged') | ||||
|                 ? messages.cancel_reblog | ||||
|                 : messages.reblog, | ||||
|             )} | ||||
|           /> | ||||
|         </div> | ||||
|         {statusVisibility !== 'private' && !status.get('reblogged') && ( | ||||
|           <PrivacyDropdown | ||||
|             noDirect | ||||
|             value={privacy} | ||||
|             container={findContainer} | ||||
|             onChange={onPrivacyChange} | ||||
|           /> | ||||
|         )} | ||||
|         <Button | ||||
|           text={intl.formatMessage(buttonText)} | ||||
|           onClick={handleReblog} | ||||
|           // eslint-disable-next-line jsx-a11y/no-autofocus
 | ||||
|           autoFocus | ||||
|         /> | ||||
|       </div> | ||||
|     </div> | ||||
|   ); | ||||
|  |  | |||
		Loading…
	
	Add table
		Add a link
		
	
		Reference in a new issue