Change icons in web UI (#27385)
Co-authored-by: Renaud Chaput <renchap@gmail.com>
This commit is contained in:
		
							parent
							
								
									b1885387b6
								
							
						
					
					
						commit
						134de736dc
					
				
							
								
								
									
										3
									
								
								app/javascript/__mocks__/svg.js
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										3
									
								
								app/javascript/__mocks__/svg.js
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,3 @@ | |||||||
|  | // eslint-disable-next-line import/no-anonymous-default-export
 | ||||||
|  | export default 'SvgrURL'; | ||||||
|  | export const ReactComponent = 'div'; | ||||||
| @ -18,7 +18,6 @@ import { Avatar } from './avatar'; | |||||||
| import { Button } from './button'; | import { Button } from './button'; | ||||||
| import { FollowersCounter } from './counters'; | import { FollowersCounter } from './counters'; | ||||||
| import { DisplayName } from './display_name'; | import { DisplayName } from './display_name'; | ||||||
| import { IconButton } from './icon_button'; |  | ||||||
| import { RelativeTimestamp } from './relative_timestamp'; | import { RelativeTimestamp } from './relative_timestamp'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
| @ -45,10 +44,7 @@ class Account extends ImmutablePureComponent { | |||||||
|     intl: PropTypes.object.isRequired, |     intl: PropTypes.object.isRequired, | ||||||
|     hidden: PropTypes.bool, |     hidden: PropTypes.bool, | ||||||
|     minimal: PropTypes.bool, |     minimal: PropTypes.bool, | ||||||
|     actionIcon: PropTypes.string, |  | ||||||
|     actionTitle: PropTypes.string, |  | ||||||
|     defaultAction: PropTypes.string, |     defaultAction: PropTypes.string, | ||||||
|     onActionClick: PropTypes.func, |  | ||||||
|     withBio: PropTypes.bool, |     withBio: PropTypes.bool, | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
| @ -76,12 +72,8 @@ class Account extends ImmutablePureComponent { | |||||||
|     this.props.onMuteNotifications(this.props.account, false); |     this.props.onMuteNotifications(this.props.account, false); | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   handleAction = () => { |  | ||||||
|     this.props.onActionClick(this.props.account); |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   render () { |   render () { | ||||||
|     const { account, intl, hidden, withBio, onActionClick, actionIcon, actionTitle, defaultAction, size, minimal } = this.props; |     const { account, intl, hidden, withBio, defaultAction, size, minimal } = this.props; | ||||||
| 
 | 
 | ||||||
|     if (!account) { |     if (!account) { | ||||||
|       return <EmptyAccount size={size} minimal={minimal} />; |       return <EmptyAccount size={size} minimal={minimal} />; | ||||||
| @ -98,9 +90,7 @@ class Account extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|     let buttons; |     let buttons; | ||||||
| 
 | 
 | ||||||
|     if (actionIcon && onActionClick) { |     if (account.get('id') !== me && account.get('relationship', null) !== null) { | ||||||
|       buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />; |  | ||||||
|     } else if (!actionIcon && account.get('id') !== me && account.get('relationship', null) !== null) { |  | ||||||
|       const following = account.getIn(['relationship', 'following']); |       const following = account.getIn(['relationship', 'following']); | ||||||
|       const requested = account.getIn(['relationship', 'requested']); |       const requested = account.getIn(['relationship', 'requested']); | ||||||
|       const blocking  = account.getIn(['relationship', 'blocking']); |       const blocking  = account.getIn(['relationship', 'blocking']); | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ import classNames from 'classnames'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| const filename = url => url.split('/').pop().split('#')[0].split('?')[0]; | const filename = url => url.split('/').pop().split('#')[0].split('?')[0]; | ||||||
| @ -25,7 +27,7 @@ export default class AttachmentList extends ImmutablePureComponent { | |||||||
|       <div className={classNames('attachment-list', { compact })}> |       <div className={classNames('attachment-list', { compact })}> | ||||||
|         {!compact && ( |         {!compact && ( | ||||||
|           <div className='attachment-list__icon'> |           <div className='attachment-list__icon'> | ||||||
|             <Icon id='link' /> |             <Icon id='link' icon={LinkIcon} /> | ||||||
|           </div> |           </div> | ||||||
|         )} |         )} | ||||||
| 
 | 
 | ||||||
| @ -36,7 +38,7 @@ export default class AttachmentList extends ImmutablePureComponent { | |||||||
|             return ( |             return ( | ||||||
|               <li key={attachment.get('id')}> |               <li key={attachment.get('id')}> | ||||||
|                 <a href={displayUrl} target='_blank' rel='noopener noreferrer'> |                 <a href={displayUrl} target='_blank' rel='noopener noreferrer'> | ||||||
|                   {compact && <Icon id='link' />} |                   {compact && <Icon id='link' icon={LinkIcon} />} | ||||||
|                   {compact && ' ' } |                   {compact && ' ' } | ||||||
|                   {displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />} |                   {displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />} | ||||||
|                 </a> |                 </a> | ||||||
|  | |||||||
| @ -2,9 +2,9 @@ import PropTypes from 'prop-types'; | |||||||
| 
 | 
 | ||||||
| import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
| import { ReactComponent as GroupsIcon } from '@material-design-icons/svg/outlined/group.svg'; | import { ReactComponent as GroupsIcon } from '@material-symbols/svg-600/outlined/group.svg'; | ||||||
| import { ReactComponent as PersonIcon } from '@material-design-icons/svg/outlined/person.svg'; | import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg'; | ||||||
| import { ReactComponent as SmartToyIcon } from '@material-design-icons/svg/outlined/smart_toy.svg'; | import { ReactComponent as SmartToyIcon } from '@material-symbols/svg-600/outlined/smart_toy.svg'; | ||||||
| 
 | 
 | ||||||
| 
 | 
 | ||||||
| export const Badge = ({ icon, label, domain }) => ( | export const Badge = ({ icon, label, domain }) => ( | ||||||
|  | |||||||
| @ -1,13 +0,0 @@ | |||||||
| export const Check: React.FC = () => ( |  | ||||||
|   <svg |  | ||||||
|     xmlns='http://www.w3.org/2000/svg' |  | ||||||
|     viewBox='0 0 20 20' |  | ||||||
|     fill='currentColor' |  | ||||||
|   > |  | ||||||
|     <path |  | ||||||
|       fillRule='evenodd' |  | ||||||
|       d='M16.704 4.153a.75.75 0 01.143 1.052l-8 10.5a.75.75 0 01-1.127.075l-4.5-4.5a.75.75 0 011.06-1.06l3.894 3.893 7.48-9.817a.75.75 0 011.05-.143z' |  | ||||||
|       clipRule='evenodd' |  | ||||||
|     /> |  | ||||||
|   </svg> |  | ||||||
| ); |  | ||||||
| @ -6,6 +6,8 @@ import { FormattedMessage } from 'react-intl'; | |||||||
| 
 | 
 | ||||||
| import { withRouter } from 'react-router-dom'; | import { withRouter } from 'react-router-dom'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
| @ -34,7 +36,7 @@ class ColumnBackButton extends PureComponent { | |||||||
| 
 | 
 | ||||||
|     const component = ( |     const component = ( | ||||||
|       <button onClick={this.handleClick} className='column-back-button'> |       <button onClick={this.handleClick} className='column-back-button'> | ||||||
|         <Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> |         <Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' /> | ||||||
|         <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> |         <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> | ||||||
|       </button> |       </button> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -1,5 +1,7 @@ | |||||||
| import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| import ColumnBackButton from './column_back_button'; | import ColumnBackButton from './column_back_button'; | ||||||
| @ -10,7 +12,7 @@ export default class ColumnBackButtonSlim extends ColumnBackButton { | |||||||
|     return ( |     return ( | ||||||
|       <div className='column-back-button--slim'> |       <div className='column-back-button--slim'> | ||||||
|         <div role='button' tabIndex={0} onClick={this.handleClick} className='column-back-button column-back-button--slim-button'> |         <div role='button' tabIndex={0} onClick={this.handleClick} className='column-back-button column-back-button--slim-button'> | ||||||
|           <Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> |           <Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' /> | ||||||
|           <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> |           <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  | |||||||
| @ -7,6 +7,13 @@ import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; | |||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| import { withRouter } from 'react-router-dom'; | import { withRouter } from 'react-router-dom'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg'; | ||||||
|  | import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg'; | ||||||
|  | import { ReactComponent as ChevronLeftIcon } from '@material-symbols/svg-600/outlined/chevron_left.svg'; | ||||||
|  | import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg'; | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | import { ReactComponent as TuneIcon } from '@material-symbols/svg-600/outlined/tune.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
| @ -27,6 +34,7 @@ class ColumnHeader extends PureComponent { | |||||||
|     intl: PropTypes.object.isRequired, |     intl: PropTypes.object.isRequired, | ||||||
|     title: PropTypes.node, |     title: PropTypes.node, | ||||||
|     icon: PropTypes.string, |     icon: PropTypes.string, | ||||||
|  |     iconComponent: PropTypes.func, | ||||||
|     active: PropTypes.bool, |     active: PropTypes.bool, | ||||||
|     multiColumn: PropTypes.bool, |     multiColumn: PropTypes.bool, | ||||||
|     extraButton: PropTypes.node, |     extraButton: PropTypes.node, | ||||||
| @ -87,7 +95,7 @@ class ColumnHeader extends PureComponent { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues, history } = this.props; |     const { title, icon, iconComponent, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues, history } = this.props; | ||||||
|     const { collapsed, animating } = this.state; |     const { collapsed, animating } = this.state; | ||||||
| 
 | 
 | ||||||
|     const wrapperClassName = classNames('column-header__wrapper', { |     const wrapperClassName = classNames('column-header__wrapper', { | ||||||
| @ -118,22 +126,22 @@ class ColumnHeader extends PureComponent { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (multiColumn && pinned) { |     if (multiColumn && pinned) { | ||||||
|       pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>; |       pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='times' icon={CloseIcon} /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>; | ||||||
| 
 | 
 | ||||||
|       moveButtons = ( |       moveButtons = ( | ||||||
|         <div key='move-buttons' className='column-header__setting-arrows'> |         <div key='move-buttons' className='column-header__setting-arrows'> | ||||||
|           <button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='icon-button column-header__setting-btn' onClick={this.handleMoveLeft}><Icon id='chevron-left' /></button> |           <button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='icon-button column-header__setting-btn' onClick={this.handleMoveLeft}><Icon id='chevron-left' icon={ChevronLeftIcon} /></button> | ||||||
|           <button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='icon-button column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' /></button> |           <button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='icon-button column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' icon={ChevronRightIcon} /></button> | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
|     } else if (multiColumn && this.props.onPin) { |     } else if (multiColumn && this.props.onPin) { | ||||||
|       pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>; |       pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' icon={AddIcon} /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (!pinned && ((multiColumn && history.location?.state?.fromMastodon) || showBackButton)) { |     if (!pinned && ((multiColumn && history.location?.state?.fromMastodon) || showBackButton)) { | ||||||
|       backButton = ( |       backButton = ( | ||||||
|         <button onClick={this.handleBackClick} className='column-header__back-button'> |         <button onClick={this.handleBackClick} className='column-header__back-button'> | ||||||
|           <Icon id='chevron-left' className='column-back-button__icon' fixedWidth /> |           <Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' /> | ||||||
|           <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> |           <FormattedMessage id='column_back_button.label' defaultMessage='Back' /> | ||||||
|         </button> |         </button> | ||||||
|       ); |       ); | ||||||
| @ -157,21 +165,21 @@ class ColumnHeader extends PureComponent { | |||||||
|           onClick={this.handleToggleClick} |           onClick={this.handleToggleClick} | ||||||
|         > |         > | ||||||
|           <i className='icon-with-badge'> |           <i className='icon-with-badge'> | ||||||
|             <Icon id='sliders' /> |             <Icon id='sliders' icon={TuneIcon} /> | ||||||
|             {collapseIssues && <i className='icon-with-badge__issue-badge' />} |             {collapseIssues && <i className='icon-with-badge__issue-badge' />} | ||||||
|           </i> |           </i> | ||||||
|         </button> |         </button> | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const hasTitle = icon && title; |     const hasTitle = (icon || iconComponent) && title; | ||||||
| 
 | 
 | ||||||
|     const component = ( |     const component = ( | ||||||
|       <div className={wrapperClassName}> |       <div className={wrapperClassName}> | ||||||
|         <h1 className={buttonClassName}> |         <h1 className={buttonClassName}> | ||||||
|           {hasTitle && ( |           {hasTitle && ( | ||||||
|             <button onClick={this.handleTitleClick}> |             <button onClick={this.handleTitleClick}> | ||||||
|               <Icon id={icon} fixedWidth className='column-header__icon' /> |               <Icon id={icon} icon={iconComponent} className='column-header__icon' /> | ||||||
|               {title} |               {title} | ||||||
|             </button> |             </button> | ||||||
|           )} |           )} | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ import { useCallback, useState } from 'react'; | |||||||
| 
 | 
 | ||||||
| import { defineMessages, useIntl } from 'react-intl'; | import { defineMessages, useIntl } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import { bannerSettings } from 'mastodon/settings'; | import { bannerSettings } from 'mastodon/settings'; | ||||||
| 
 | 
 | ||||||
| import { IconButton } from './icon_button'; | import { IconButton } from './icon_button'; | ||||||
| @ -36,6 +38,7 @@ export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({ | |||||||
|       <div className='dismissable-banner__action'> |       <div className='dismissable-banner__action'> | ||||||
|         <IconButton |         <IconButton | ||||||
|           icon='times' |           icon='times' | ||||||
|  |           iconComponent={CloseIcon} | ||||||
|           title={intl.formatMessage(messages.dismiss)} |           title={intl.formatMessage(messages.dismiss)} | ||||||
|           onClick={handleDismiss} |           onClick={handleDismiss} | ||||||
|         /> |         /> | ||||||
|  | |||||||
| @ -2,6 +2,8 @@ import { useCallback } from 'react'; | |||||||
| 
 | 
 | ||||||
| import { defineMessages, useIntl } from 'react-intl'; | import { defineMessages, useIntl } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg'; | ||||||
|  | 
 | ||||||
| import { IconButton } from './icon_button'; | import { IconButton } from './icon_button'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
| @ -34,6 +36,7 @@ export const Domain: React.FC<Props> = ({ domain, onUnblockDomain }) => { | |||||||
|           <IconButton |           <IconButton | ||||||
|             active |             active | ||||||
|             icon='unlock' |             icon='unlock' | ||||||
|  |             iconComponent={LockOpenIcon} | ||||||
|             title={intl.formatMessage(messages.unblockDomain, { domain })} |             title={intl.formatMessage(messages.unblockDomain, { domain })} | ||||||
|             onClick={handleDomainUnblock} |             onClick={handleDomainUnblock} | ||||||
|           /> |           /> | ||||||
|  | |||||||
| @ -6,6 +6,7 @@ import { withRouter } from 'react-router-dom'; | |||||||
| 
 | 
 | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
| import { supportsPassiveEvents } from 'detect-passive-events'; | import { supportsPassiveEvents } from 'detect-passive-events'; | ||||||
| import Overlay from 'react-overlays/Overlay'; | import Overlay from 'react-overlays/Overlay'; | ||||||
| 
 | 
 | ||||||
| @ -163,6 +164,7 @@ class Dropdown extends PureComponent { | |||||||
|   static propTypes = { |   static propTypes = { | ||||||
|     children: PropTypes.node, |     children: PropTypes.node, | ||||||
|     icon: PropTypes.string, |     icon: PropTypes.string, | ||||||
|  |     iconComponent: PropTypes.func, | ||||||
|     items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired, |     items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired, | ||||||
|     loading: PropTypes.bool, |     loading: PropTypes.bool, | ||||||
|     size: PropTypes.number, |     size: PropTypes.number, | ||||||
| @ -255,7 +257,7 @@ class Dropdown extends PureComponent { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   findTarget = () => { |   findTarget = () => { | ||||||
|     return this.target; |     return this.target?.buttonRef?.current; | ||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   componentWillUnmount = () => { |   componentWillUnmount = () => { | ||||||
| @ -271,6 +273,7 @@ class Dropdown extends PureComponent { | |||||||
|   render () { |   render () { | ||||||
|     const { |     const { | ||||||
|       icon, |       icon, | ||||||
|  |       iconComponent, | ||||||
|       items, |       items, | ||||||
|       size, |       size, | ||||||
|       title, |       title, | ||||||
| @ -291,9 +294,11 @@ class Dropdown extends PureComponent { | |||||||
|       onMouseDown: this.handleMouseDown, |       onMouseDown: this.handleMouseDown, | ||||||
|       onKeyDown: this.handleButtonKeyDown, |       onKeyDown: this.handleButtonKeyDown, | ||||||
|       onKeyPress: this.handleKeyPress, |       onKeyPress: this.handleKeyPress, | ||||||
|  |       ref: this.setTargetRef, | ||||||
|     }) : ( |     }) : ( | ||||||
|       <IconButton |       <IconButton | ||||||
|         icon={!open ? icon : 'close'} |         icon={!open ? icon : 'close'} | ||||||
|  |         iconComponent={!open ? iconComponent : CloseIcon} | ||||||
|         title={title} |         title={title} | ||||||
|         active={open} |         active={open} | ||||||
|         disabled={disabled} |         disabled={disabled} | ||||||
| @ -302,14 +307,14 @@ class Dropdown extends PureComponent { | |||||||
|         onMouseDown={this.handleMouseDown} |         onMouseDown={this.handleMouseDown} | ||||||
|         onKeyDown={this.handleButtonKeyDown} |         onKeyDown={this.handleButtonKeyDown} | ||||||
|         onKeyPress={this.handleKeyPress} |         onKeyPress={this.handleKeyPress} | ||||||
|  |         ref={this.setTargetRef} | ||||||
|       /> |       /> | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <> |       <> | ||||||
|         <span ref={this.setTargetRef}> |         {button} | ||||||
|           {button} | 
 | ||||||
|         </span> |  | ||||||
|         <Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}> |         <Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}> | ||||||
|           {({ props, arrowProps, placement }) => ( |           {({ props, arrowProps, placement }) => ( | ||||||
|             <div {...props}> |             <div {...props}> | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ import { FormattedMessage, injectIntl } from 'react-intl'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as ArrowDropDownIcon } from '@material-symbols/svg-600/outlined/arrow_drop_down.svg'; | ||||||
|  | 
 | ||||||
| import { openModal } from 'mastodon/actions/modal'; | import { openModal } from 'mastodon/actions/modal'; | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import InlineAccount from 'mastodon/components/inline_account'; | import InlineAccount from 'mastodon/components/inline_account'; | ||||||
| @ -66,7 +68,7 @@ class EditedTimestamp extends PureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}> |       <DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}> | ||||||
|         <button className='dropdown-menu__text-button'> |         <button className='dropdown-menu__text-button'> | ||||||
|           <FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { hour12: false, month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} /> <Icon id='caret-down' /> |           <FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { hour12: false, month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} /> <Icon id='caret-down' icon={ArrowDropDownIcon} /> | ||||||
|         </button> |         </button> | ||||||
|       </DropdownMenu> |       </DropdownMenu> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -1,20 +1,50 @@ | |||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| 
 | 
 | ||||||
| interface Props extends React.HTMLAttributes<HTMLImageElement> { | import { ReactComponent as CheckBoxOutlineBlankIcon } from '@material-symbols/svg-600/outlined/check_box_outline_blank.svg'; | ||||||
|   id: string; | 
 | ||||||
|   className?: string; | interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> { | ||||||
|   fixedWidth?: boolean; |   title?: string; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export type IconProp = React.FC<SVGPropsWithTitle>; | ||||||
|  | 
 | ||||||
|  | interface Props extends React.SVGProps<SVGSVGElement> { | ||||||
|   children?: never; |   children?: never; | ||||||
|  |   id: string; | ||||||
|  |   icon: IconProp; | ||||||
|  |   title?: string; | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export const Icon: React.FC<Props> = ({ | export const Icon: React.FC<Props> = ({ | ||||||
|   id, |   id, | ||||||
|  |   icon: IconComponent, | ||||||
|   className, |   className, | ||||||
|   fixedWidth, |   title: titleProp, | ||||||
|   ...other |   ...other | ||||||
| }) => ( | }) => { | ||||||
|   <i |   // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | ||||||
|     className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })} |   if (!IconComponent) { | ||||||
|     {...other} |     if (process.env.NODE_ENV !== 'production') { | ||||||
|   /> |       throw new Error(`<Icon id="${id}"> is missing an "icon" prop.`); | ||||||
| ); |     } | ||||||
|  | 
 | ||||||
|  |     IconComponent = CheckBoxOutlineBlankIcon; | ||||||
|  |   } | ||||||
|  | 
 | ||||||
|  |   const ariaHidden = titleProp ? undefined : true; | ||||||
|  |   const role = !ariaHidden ? 'img' : undefined; | ||||||
|  | 
 | ||||||
|  |   // Set the title to an empty string to remove the built-in SVG one if any
 | ||||||
|  |   // eslint-disable-next-line @typescript-eslint/prefer-nullish-coalescing
 | ||||||
|  |   const title = titleProp || ''; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <IconComponent | ||||||
|  |       className={classNames('icon', `icon-${id}`, className)} | ||||||
|  |       title={title} | ||||||
|  |       aria-hidden={ariaHidden} | ||||||
|  |       role={role} | ||||||
|  |       {...other} | ||||||
|  |     /> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
|  | |||||||
| @ -1,19 +1,20 @@ | |||||||
| import { PureComponent } from 'react'; | import { PureComponent, createRef } from 'react'; | ||||||
| 
 | 
 | ||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| 
 | 
 | ||||||
| import { AnimatedNumber } from './animated_number'; | import { AnimatedNumber } from './animated_number'; | ||||||
|  | import type { IconProp } from './icon'; | ||||||
| import { Icon } from './icon'; | import { Icon } from './icon'; | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   className?: string; |   className?: string; | ||||||
|   title: string; |   title: string; | ||||||
|   icon: string; |   icon: string; | ||||||
|  |   iconComponent: IconProp; | ||||||
|   onClick?: React.MouseEventHandler<HTMLButtonElement>; |   onClick?: React.MouseEventHandler<HTMLButtonElement>; | ||||||
|   onMouseDown?: React.MouseEventHandler<HTMLButtonElement>; |   onMouseDown?: React.MouseEventHandler<HTMLButtonElement>; | ||||||
|   onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>; |   onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>; | ||||||
|   onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>; |   onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>; | ||||||
|   size: number; |  | ||||||
|   active: boolean; |   active: boolean; | ||||||
|   expanded?: boolean; |   expanded?: boolean; | ||||||
|   style?: React.CSSProperties; |   style?: React.CSSProperties; | ||||||
| @ -32,8 +33,9 @@ interface States { | |||||||
|   deactivate: boolean; |   deactivate: boolean; | ||||||
| } | } | ||||||
| export class IconButton extends PureComponent<Props, States> { | export class IconButton extends PureComponent<Props, States> { | ||||||
|  |   buttonRef = createRef<HTMLButtonElement>(); | ||||||
|  | 
 | ||||||
|   static defaultProps = { |   static defaultProps = { | ||||||
|     size: 18, |  | ||||||
|     active: false, |     active: false, | ||||||
|     disabled: false, |     disabled: false, | ||||||
|     animate: false, |     animate: false, | ||||||
| @ -85,10 +87,6 @@ export class IconButton extends PureComponent<Props, States> { | |||||||
| 
 | 
 | ||||||
|   render() { |   render() { | ||||||
|     const style = { |     const style = { | ||||||
|       fontSize: `${this.props.size}px`, |  | ||||||
|       width: `${this.props.size * 1.28571429}px`, |  | ||||||
|       height: `${this.props.size * 1.28571429}px`, |  | ||||||
|       lineHeight: `${this.props.size}px`, |  | ||||||
|       ...this.props.style, |       ...this.props.style, | ||||||
|       ...(this.props.active ? this.props.activeStyle : {}), |       ...(this.props.active ? this.props.activeStyle : {}), | ||||||
|     }; |     }; | ||||||
| @ -99,6 +97,7 @@ export class IconButton extends PureComponent<Props, States> { | |||||||
|       disabled, |       disabled, | ||||||
|       expanded, |       expanded, | ||||||
|       icon, |       icon, | ||||||
|  |       iconComponent, | ||||||
|       inverted, |       inverted, | ||||||
|       overlay, |       overlay, | ||||||
|       tabIndex, |       tabIndex, | ||||||
| @ -120,13 +119,9 @@ export class IconButton extends PureComponent<Props, States> { | |||||||
|       'icon-button--with-counter': typeof counter !== 'undefined', |       'icon-button--with-counter': typeof counter !== 'undefined', | ||||||
|     }); |     }); | ||||||
| 
 | 
 | ||||||
|     if (typeof counter !== 'undefined') { |  | ||||||
|       style.width = 'auto'; |  | ||||||
|     } |  | ||||||
| 
 |  | ||||||
|     let contents = ( |     let contents = ( | ||||||
|       <> |       <> | ||||||
|         <Icon id={icon} fixedWidth aria-hidden='true' />{' '} |         <Icon id={icon} icon={iconComponent} aria-hidden='true' />{' '} | ||||||
|         {typeof counter !== 'undefined' && ( |         {typeof counter !== 'undefined' && ( | ||||||
|           <span className='icon-button__counter'> |           <span className='icon-button__counter'> | ||||||
|             <AnimatedNumber value={counter} /> |             <AnimatedNumber value={counter} /> | ||||||
| @ -158,6 +153,7 @@ export class IconButton extends PureComponent<Props, States> { | |||||||
|         style={style} |         style={style} | ||||||
|         tabIndex={tabIndex} |         tabIndex={tabIndex} | ||||||
|         disabled={disabled} |         disabled={disabled} | ||||||
|  |         ref={this.buttonRef} | ||||||
|       > |       > | ||||||
|         {contents} |         {contents} | ||||||
|       </button> |       </button> | ||||||
|  | |||||||
| @ -1,21 +1,24 @@ | |||||||
|  | import type { IconProp } from './icon'; | ||||||
| import { Icon } from './icon'; | import { Icon } from './icon'; | ||||||
| 
 | 
 | ||||||
| const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num); | const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num); | ||||||
| 
 | 
 | ||||||
| interface Props { | interface Props { | ||||||
|   id: string; |   id: string; | ||||||
|  |   icon: IconProp; | ||||||
|   count: number; |   count: number; | ||||||
|   issueBadge: boolean; |   issueBadge: boolean; | ||||||
|   className: string; |   className: string; | ||||||
| } | } | ||||||
| export const IconWithBadge: React.FC<Props> = ({ | export const IconWithBadge: React.FC<Props> = ({ | ||||||
|   id, |   id, | ||||||
|  |   icon, | ||||||
|   count, |   count, | ||||||
|   issueBadge, |   issueBadge, | ||||||
|   className, |   className, | ||||||
| }) => ( | }) => ( | ||||||
|   <i className='icon-with-badge'> |   <i className='icon-with-badge'> | ||||||
|     <Icon id={id} fixedWidth className={className} /> |     <Icon id={id} icon={icon} className={className} /> | ||||||
|     {count > 0 && ( |     {count > 0 && ( | ||||||
|       <i className='icon-with-badge__badge'>{formatNumber(count)}</i> |       <i className='icon-with-badge__badge'>{formatNumber(count)}</i> | ||||||
|     )} |     )} | ||||||
|  | |||||||
| @ -2,6 +2,8 @@ import { useCallback } from 'react'; | |||||||
| 
 | 
 | ||||||
| import { useIntl, defineMessages } from 'react-intl'; | import { useIntl, defineMessages } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg'; | ||||||
|  | 
 | ||||||
| import { Icon } from 'mastodon/components/icon'; | import { Icon } from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
| @ -28,7 +30,7 @@ export const LoadGap: React.FC<Props> = ({ disabled, maxId, onClick }) => { | |||||||
|       onClick={handleClick} |       onClick={handleClick} | ||||||
|       aria-label={intl.formatMessage(messages.load_more)} |       aria-label={intl.formatMessage(messages.load_more)} | ||||||
|     > |     > | ||||||
|       <Icon id='ellipsis-h' /> |       <Icon id='ellipsis-h' icon={MoreHorizIcon} /> | ||||||
|     </button> |     </button> | ||||||
|   ); |   ); | ||||||
| }; | }; | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import classNames from 'classnames'; | |||||||
| import { is } from 'immutable'; | import { is } from 'immutable'; | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { Blurhash } from 'mastodon/components/blurhash'; | import { Blurhash } from 'mastodon/components/blurhash'; | ||||||
| @ -323,7 +324,7 @@ class MediaGallery extends PureComponent { | |||||||
|         </button> |         </button> | ||||||
|       ); |       ); | ||||||
|     } else if (visible) { |     } else if (visible) { | ||||||
|       spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} ariaHidden />; |       spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' iconComponent={VisibilityOffIcon} overlay onClick={this.handleOpen} ariaHidden />; | ||||||
|     } else { |     } else { | ||||||
|       spoilerButton = ( |       spoilerButton = ( | ||||||
|         <button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'> |         <button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'> | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ import { FormattedMessage } from 'react-intl'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CancelPresentationIcon } from '@material-symbols/svg-600/outlined/cancel_presentation.svg'; | ||||||
|  | 
 | ||||||
| import { removePictureInPicture } from 'mastodon/actions/picture_in_picture'; | import { removePictureInPicture } from 'mastodon/actions/picture_in_picture'; | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| @ -25,7 +27,7 @@ class PictureInPicturePlaceholder extends PureComponent { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className='picture-in-picture-placeholder' style={{ aspectRatio }} role='button' tabIndex={0} onClick={this.handleClick}> |       <div className='picture-in-picture-placeholder' style={{ aspectRatio }} role='button' tabIndex={0} onClick={this.handleClick}> | ||||||
|         <Icon id='window-restore' /> |         <Icon id='window-restore' icon={CancelPresentationIcon} /> | ||||||
|         <FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' /> |         <FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' /> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import classNames from 'classnames'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||||
| import escapeTextContentForBrowser from 'escape-html'; | import escapeTextContentForBrowser from 'escape-html'; | ||||||
| import spring from 'react-motion/lib/spring'; | import spring from 'react-motion/lib/spring'; | ||||||
| 
 | 
 | ||||||
| @ -192,7 +193,7 @@ class Poll extends ImmutablePureComponent { | |||||||
|           /> |           /> | ||||||
| 
 | 
 | ||||||
|           {!!voted && <span className='poll__voted'> |           {!!voted && <span className='poll__voted'> | ||||||
|             <Icon id='check' className='poll__voted__mark' title={intl.formatMessage(messages.voted)} /> |             <Icon id='check' icon={CheckIcon} className='poll__voted__mark' title={intl.formatMessage(messages.voted)} /> | ||||||
|           </span>} |           </span>} | ||||||
|         </label> |         </label> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -7,6 +7,10 @@ import classNames from 'classnames'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg'; | ||||||
|  | import { ReactComponent as PushPinIcon } from '@material-symbols/svg-600/outlined/push_pin.svg'; | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg'; | ||||||
| import { HotKeys } from 'react-hotkeys'; | import { HotKeys } from 'react-hotkeys'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -27,6 +31,7 @@ import { getHashtagBarForStatus } from './hashtag_bar'; | |||||||
| import { RelativeTimestamp } from './relative_timestamp'; | import { RelativeTimestamp } from './relative_timestamp'; | ||||||
| import StatusActionBar from './status_action_bar'; | import StatusActionBar from './status_action_bar'; | ||||||
| import StatusContent from './status_content'; | import StatusContent from './status_content'; | ||||||
|  | import { VisibilityIcon } from './visibility_icon'; | ||||||
| 
 | 
 | ||||||
| const domParser = new DOMParser(); | const domParser = new DOMParser(); | ||||||
| 
 | 
 | ||||||
| @ -405,7 +410,7 @@ class Status extends ImmutablePureComponent { | |||||||
|     if (featured) { |     if (featured) { | ||||||
|       prepend = ( |       prepend = ( | ||||||
|         <div className='status__prepend'> |         <div className='status__prepend'> | ||||||
|           <div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' className='status__prepend-icon' fixedWidth /></div> |           <div className='status__prepend-icon-wrapper'><Icon id='thumb-tack' icon={PushPinIcon} className='status__prepend-icon' /></div> | ||||||
|           <FormattedMessage id='status.pinned' defaultMessage='Pinned post' /> |           <FormattedMessage id='status.pinned' defaultMessage='Pinned post' /> | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
| @ -414,7 +419,7 @@ class Status extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|       prepend = ( |       prepend = ( | ||||||
|         <div className='status__prepend'> |         <div className='status__prepend'> | ||||||
|           <div className='status__prepend-icon-wrapper'><Icon id='retweet' className='status__prepend-icon' fixedWidth /></div> |           <div className='status__prepend-icon-wrapper'><Icon id='retweet' icon={RepeatIcon} className='status__prepend-icon' /></div> | ||||||
|           <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handlePrependAccountClick} data-id={status.getIn(['account', 'id'])} href={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} /> |           <FormattedMessage id='status.reblogged_by' defaultMessage='{name} boosted' values={{ name: <a onClick={this.handlePrependAccountClick} data-id={status.getIn(['account', 'id'])} href={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} /> | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
| @ -426,7 +431,7 @@ class Status extends ImmutablePureComponent { | |||||||
|     } else if (status.get('visibility') === 'direct') { |     } else if (status.get('visibility') === 'direct') { | ||||||
|       prepend = ( |       prepend = ( | ||||||
|         <div className='status__prepend'> |         <div className='status__prepend'> | ||||||
|           <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div> |           <div className='status__prepend-icon-wrapper'><Icon id='at' icon={AlternateEmailIcon} className='status__prepend-icon' /></div> | ||||||
|           <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' /> |           <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' /> | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
| @ -435,7 +440,7 @@ class Status extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|       prepend = ( |       prepend = ( | ||||||
|         <div className='status__prepend'> |         <div className='status__prepend'> | ||||||
|           <div className='status__prepend-icon-wrapper'><Icon id='reply' className='status__prepend-icon' fixedWidth /></div> |           <div className='status__prepend-icon-wrapper'><Icon id='reply' icon={ReplyIcon} className='status__prepend-icon' /></div> | ||||||
|           <FormattedMessage id='status.replied_to' defaultMessage='Replied to {name}' values={{ name: <a onClick={this.handlePrependAccountClick} data-id={status.getIn(['account', 'id'])} href={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} /> |           <FormattedMessage id='status.replied_to' defaultMessage='Replied to {name}' values={{ name: <a onClick={this.handlePrependAccountClick} data-id={status.getIn(['account', 'id'])} href={`/@${status.getIn(['account', 'acct'])}`} className='status__display-name muted'><bdi><strong dangerouslySetInnerHTML={display_name_html} /></bdi></a> }} /> | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
| @ -534,15 +539,6 @@ class Status extends ImmutablePureComponent { | |||||||
|       statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />; |       statusAvatar = <AvatarOverlay account={status.get('account')} friend={account} />; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const visibilityIconInfo = { |  | ||||||
|       'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) }, |  | ||||||
|       'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) }, |  | ||||||
|       'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) }, |  | ||||||
|       'direct': { icon: 'at', text: intl.formatMessage(messages.direct_short) }, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const visibilityIcon = visibilityIconInfo[status.get('visibility')]; |  | ||||||
| 
 |  | ||||||
|     const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status); |     const {statusContentProps, hashtagBar} = getHashtagBarForStatus(status); | ||||||
|     const expanded = !status.get('hidden') || status.get('spoiler_text').length === 0; |     const expanded = !status.get('hidden') || status.get('spoiler_text').length === 0; | ||||||
| 
 | 
 | ||||||
| @ -557,7 +553,7 @@ class Status extends ImmutablePureComponent { | |||||||
|             {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */} |             {/* eslint-disable-next-line jsx-a11y/no-static-element-interactions */} | ||||||
|             <div onClick={this.handleClick} className='status__info'> |             <div onClick={this.handleClick} className='status__info'> | ||||||
|               <a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'> |               <a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'> | ||||||
|                 <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span> |                 <span className='status__visibility-icon'><VisibilityIcon visibility={status.get('visibility')} /></span> | ||||||
|                 <RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>} |                 <RelativeTimestamp timestamp={status.get('created_at')} />{status.get('edited_at') && <abbr title={intl.formatMessage(messages.edited, { date: intl.formatDate(status.get('edited_at'), { hour12: false, year: 'numeric', month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) })}> *</abbr>} | ||||||
|               </a> |               </a> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,6 +9,16 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as BookmarkIcon } from '@material-symbols/svg-600/outlined/bookmark-fill.svg'; | ||||||
|  | import { ReactComponent as BookmarkBorderIcon } from '@material-symbols/svg-600/outlined/bookmark.svg'; | ||||||
|  | import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg'; | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg'; | ||||||
|  | import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg'; | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg'; | ||||||
|  | import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg'; | ||||||
|  | import { ReactComponent as VisibilityIcon } from '@material-symbols/svg-600/outlined/visibility.svg'; | ||||||
|  | 
 | ||||||
| import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; | import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; | ||||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
| @ -336,12 +346,15 @@ class StatusActionBar extends ImmutablePureComponent { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let replyIcon; |     let replyIcon; | ||||||
|  |     let replyIconComponent; | ||||||
|     let replyTitle; |     let replyTitle; | ||||||
|     if (status.get('in_reply_to_id', null) === null) { |     if (status.get('in_reply_to_id', null) === null) { | ||||||
|       replyIcon = 'reply'; |       replyIcon = 'reply'; | ||||||
|  |       replyIconComponent = ReplyIcon; | ||||||
|       replyTitle = intl.formatMessage(messages.reply); |       replyTitle = intl.formatMessage(messages.reply); | ||||||
|     } else { |     } else { | ||||||
|       replyIcon = 'reply-all'; |       replyIcon = 'reply-all'; | ||||||
|  |       replyIconComponent = ReplyAllIcon; | ||||||
|       replyTitle = intl.formatMessage(messages.replyAll); |       replyTitle = intl.formatMessage(messages.replyAll); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -359,29 +372,29 @@ class StatusActionBar extends ImmutablePureComponent { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const filterButton = this.props.onFilter && ( |     const filterButton = this.props.onFilter && ( | ||||||
|       <IconButton className='status__action-bar__button' title={intl.formatMessage(messages.hide)} icon='eye' onClick={this.handleHideClick} /> |       <IconButton className='status__action-bar__button' title={intl.formatMessage(messages.hide)} icon='eye' iconComponent={VisibilityIcon} onClick={this.handleHideClick} /> | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  |     const isReply = status.get('in_reply_to_account_id') === status.getIn(['account', 'id']); | ||||||
|  | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className='status__action-bar'> |       <div className='status__action-bar'> | ||||||
|         <IconButton className='status__action-bar__button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} /> |         <IconButton className='status__action-bar__button' title={replyTitle} icon={isReply ? 'reply' : replyIcon} iconComponent={isReply ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} /> | ||||||
|         <IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} /> |         <IconButton className={classNames('status__action-bar__button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} /> | ||||||
|         <IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} /> |         <IconButton className='status__action-bar__button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} /> | ||||||
|         <IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /> |         <IconButton className='status__action-bar__button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /> | ||||||
| 
 | 
 | ||||||
|         {filterButton} |         {filterButton} | ||||||
| 
 | 
 | ||||||
|         <div className='status__action-bar__dropdown'> |         <DropdownMenuContainer | ||||||
|           <DropdownMenuContainer |           scrollKey={scrollKey} | ||||||
|             scrollKey={scrollKey} |           status={status} | ||||||
|             status={status} |           items={menu} | ||||||
|             items={menu} |           icon='ellipsis-h' | ||||||
|             icon='ellipsis-h' |           iconComponent={MoreHorizIcon} | ||||||
|             size={18} |           direction='right' | ||||||
|             direction='right' |           title={intl.formatMessage(messages.more)} | ||||||
|             title={intl.formatMessage(messages.more)} |         /> | ||||||
|           /> |  | ||||||
|         </div> |  | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ import { Link, withRouter } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import PollContainer from 'mastodon/containers/poll_container'; | import PollContainer from 'mastodon/containers/poll_container'; | ||||||
| import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state'; | import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state'; | ||||||
| @ -257,7 +259,7 @@ class StatusContent extends PureComponent { | |||||||
| 
 | 
 | ||||||
|     const readMoreButton = renderReadMore && ( |     const readMoreButton = renderReadMore && ( | ||||||
|       <button className='status__content__read-more-button' onClick={this.props.onClick} key='read-more'> |       <button className='status__content__read-more-button' onClick={this.props.onClick} key='read-more'> | ||||||
|         <FormattedMessage id='status.read_more' defaultMessage='Read more' /><Icon id='angle-right' fixedWidth /> |         <FormattedMessage id='status.read_more' defaultMessage='Read more' /><Icon id='angle-right' icon={ChevronRightIcon} /> | ||||||
|       </button> |       </button> | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -1,3 +1,5 @@ | |||||||
|  | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||||
|  | 
 | ||||||
| import { Icon } from './icon'; | import { Icon } from './icon'; | ||||||
| 
 | 
 | ||||||
| const domParser = new DOMParser(); | const domParser = new DOMParser(); | ||||||
| @ -21,7 +23,7 @@ interface Props { | |||||||
| } | } | ||||||
| export const VerifiedBadge: React.FC<Props> = ({ link }) => ( | export const VerifiedBadge: React.FC<Props> = ({ link }) => ( | ||||||
|   <span className='verified-badge'> |   <span className='verified-badge'> | ||||||
|     <Icon id='check' className='verified-badge__mark' /> |     <Icon id='check' icon={CheckIcon} className='verified-badge__mark' /> | ||||||
|     <span dangerouslySetInnerHTML={stripRelMe(link)} /> |     <span dangerouslySetInnerHTML={stripRelMe(link)} /> | ||||||
|   </span> |   </span> | ||||||
| ); | ); | ||||||
|  | |||||||
							
								
								
									
										62
									
								
								app/javascript/mastodon/components/visibility_icon.tsx
									
									
									
									
									
										Normal file
									
								
							
							
						
						
									
										62
									
								
								app/javascript/mastodon/components/visibility_icon.tsx
									
									
									
									
									
										Normal file
									
								
							| @ -0,0 +1,62 @@ | |||||||
|  | import { defineMessages, useIntl } from 'react-intl'; | ||||||
|  | 
 | ||||||
|  | import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg'; | ||||||
|  | import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg'; | ||||||
|  | import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg'; | ||||||
|  | import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg'; | ||||||
|  | 
 | ||||||
|  | import { Icon } from './icon'; | ||||||
|  | 
 | ||||||
|  | type Visibility = 'public' | 'unlisted' | 'private' | 'direct'; | ||||||
|  | 
 | ||||||
|  | const messages = defineMessages({ | ||||||
|  |   public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, | ||||||
|  |   unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, | ||||||
|  |   private_short: { | ||||||
|  |     id: 'privacy.private.short', | ||||||
|  |     defaultMessage: 'Followers only', | ||||||
|  |   }, | ||||||
|  |   direct_short: { | ||||||
|  |     id: 'privacy.direct.short', | ||||||
|  |     defaultMessage: 'Mentioned people only', | ||||||
|  |   }, | ||||||
|  | }); | ||||||
|  | 
 | ||||||
|  | export const VisibilityIcon: React.FC<{ visibility: Visibility }> = ({ | ||||||
|  |   visibility, | ||||||
|  | }) => { | ||||||
|  |   const intl = useIntl(); | ||||||
|  | 
 | ||||||
|  |   const visibilityIconInfo = { | ||||||
|  |     public: { | ||||||
|  |       icon: 'globe', | ||||||
|  |       iconComponent: PublicIcon, | ||||||
|  |       text: intl.formatMessage(messages.public_short), | ||||||
|  |     }, | ||||||
|  |     unlisted: { | ||||||
|  |       icon: 'unlock', | ||||||
|  |       iconComponent: LockOpenIcon, | ||||||
|  |       text: intl.formatMessage(messages.unlisted_short), | ||||||
|  |     }, | ||||||
|  |     private: { | ||||||
|  |       icon: 'lock', | ||||||
|  |       iconComponent: LockIcon, | ||||||
|  |       text: intl.formatMessage(messages.private_short), | ||||||
|  |     }, | ||||||
|  |     direct: { | ||||||
|  |       icon: 'at', | ||||||
|  |       iconComponent: AlternateEmailIcon, | ||||||
|  |       text: intl.formatMessage(messages.direct_short), | ||||||
|  |     }, | ||||||
|  |   }; | ||||||
|  | 
 | ||||||
|  |   const visibilityIcon = visibilityIconInfo[visibility]; | ||||||
|  | 
 | ||||||
|  |   return ( | ||||||
|  |     <Icon | ||||||
|  |       id={visibilityIcon.icon} | ||||||
|  |       icon={visibilityIcon.iconComponent} | ||||||
|  |       title={visibilityIcon.text} | ||||||
|  |     /> | ||||||
|  |   ); | ||||||
|  | }; | ||||||
| @ -10,6 +10,9 @@ import { List as ImmutableList } from 'immutable'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg'; | ||||||
|  | import { ReactComponent as ExpandMoreIcon } from '@material-symbols/svg-600/outlined/expand_more.svg'; | ||||||
|  | 
 | ||||||
| import { fetchServer, fetchExtendedDescription, fetchDomainBlocks  } from 'mastodon/actions/server'; | import { fetchServer, fetchExtendedDescription, fetchDomainBlocks  } from 'mastodon/actions/server'; | ||||||
| import Column from 'mastodon/components/column'; | import Column from 'mastodon/components/column'; | ||||||
| import { Icon  }  from 'mastodon/components/icon'; | import { Icon  }  from 'mastodon/components/icon'; | ||||||
| @ -73,7 +76,7 @@ class Section extends PureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className={classNames('about__section', { active: !collapsed })}> |       <div className={classNames('about__section', { active: !collapsed })}> | ||||||
|         <div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}> |         <div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}> | ||||||
|           <Icon id={collapsed ? 'chevron-right' : 'chevron-down'} fixedWidth /> {title} |           <Icon id={collapsed ? 'chevron-right' : 'chevron-down'} icon={collapsed ? ChevronRightIcon : ExpandMoreIcon} /> {title} | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         {!collapsed && ( |         {!collapsed && ( | ||||||
|  | |||||||
| @ -3,6 +3,9 @@ import { FormattedMessage } from 'react-intl'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| export default class FollowRequestNote extends ImmutablePureComponent { | export default class FollowRequestNote extends ImmutablePureComponent { | ||||||
| @ -22,12 +25,12 @@ export default class FollowRequestNote extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|         <div className='follow-request-banner__action'> |         <div className='follow-request-banner__action'> | ||||||
|           <button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}> |           <button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}> | ||||||
|             <Icon id='check' fixedWidth /> |             <Icon id='check' icon={CheckIcon} /> | ||||||
|             <FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' /> |             <FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' /> | ||||||
|           </button> |           </button> | ||||||
| 
 | 
 | ||||||
|           <button type='button' className='button button-tertiary button--destructive' onClick={onReject}> |           <button type='button' className='button button-tertiary button--destructive' onClick={onReject}> | ||||||
|             <Icon id='times' fixedWidth /> |             <Icon id='times' icon={CloseIcon} /> | ||||||
|             <FormattedMessage id='follow_request.reject' defaultMessage='Reject' /> |             <FormattedMessage id='follow_request.reject' defaultMessage='Reject' /> | ||||||
|           </button> |           </button> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -9,6 +9,12 @@ import { NavLink, withRouter } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||||
|  | import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg'; | ||||||
|  | import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg'; | ||||||
|  | import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications-fill.svg'; | ||||||
|  | import { ReactComponent as NotificationsActiveIcon } from '@material-symbols/svg-600/outlined/notifications_active.svg'; | ||||||
|  | 
 | ||||||
| import { Avatar } from 'mastodon/components/avatar'; | import { Avatar } from 'mastodon/components/avatar'; | ||||||
| import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge'; | import { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge'; | ||||||
| import { Button } from 'mastodon/components/button'; | import { Button } from 'mastodon/components/button'; | ||||||
| @ -258,7 +264,7 @@ class Header extends ImmutablePureComponent { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) { |     if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) { | ||||||
|       bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />; |       bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} iconComponent={account.getIn(['relationship', 'notifying']) ? NotificationsIcon : NotificationsActiveIcon}  size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (me !== account.get('id')) { |     if (me !== account.get('id')) { | ||||||
| @ -280,7 +286,7 @@ class Header extends ImmutablePureComponent { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (account.get('locked')) { |     if (account.get('locked')) { | ||||||
|       lockedIcon = <Icon id='lock' title={intl.formatMessage(messages.account_locked)} />; |       lockedIcon = <Icon id='lock' icon={LockIcon} title={intl.formatMessage(messages.account_locked)} />; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     if (signedIn && account.get('id') !== me) { |     if (signedIn && account.get('id') !== me) { | ||||||
| @ -410,7 +416,7 @@ class Header extends ImmutablePureComponent { | |||||||
|                   </> |                   </> | ||||||
|                 )} |                 )} | ||||||
| 
 | 
 | ||||||
|                 <DropdownMenuContainer disabled={menu.length === 0} items={menu} icon='ellipsis-v' size={24} direction='right' /> |                 <DropdownMenuContainer disabled={menu.length === 0} items={menu} icon='ellipsis-v' iconComponent={MoreHorizIcon} size={24} direction='right' /> | ||||||
|               </div> |               </div> | ||||||
|             )} |             )} | ||||||
|           </div> |           </div> | ||||||
| @ -448,7 +454,7 @@ class Header extends ImmutablePureComponent { | |||||||
|                       <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' /> |                       <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' /> | ||||||
| 
 | 
 | ||||||
|                       <dd className='translate' title={pair.get('value_plain')}> |                       <dd className='translate' title={pair.get('value_plain')}> | ||||||
|                         {pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} /> |                         {pair.get('verified_at') && <span title={intl.formatMessage(messages.linkVerifiedOn, { date: intl.formatDate(pair.get('verified_at'), dateFormatOptions) })}><Icon id='check' icon={CheckIcon} className='verified__mark' /></span>} <span dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }} /> | ||||||
|                       </dd> |                       </dd> | ||||||
|                     </dl> |                     </dl> | ||||||
|                   ))} |                   ))} | ||||||
|  | |||||||
| @ -5,6 +5,10 @@ import classNames from 'classnames'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AudiotrackIcon } from '@material-symbols/svg-600/outlined/music_note.svg'; | ||||||
|  | import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow.svg'; | ||||||
|  | import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg'; | ||||||
|  | 
 | ||||||
| import { Blurhash } from 'mastodon/components/blurhash'; | import { Blurhash } from 'mastodon/components/blurhash'; | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state'; | import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state'; | ||||||
| @ -69,7 +73,7 @@ export default class MediaItem extends ImmutablePureComponent { | |||||||
|     if (!visible) { |     if (!visible) { | ||||||
|       icon = ( |       icon = ( | ||||||
|         <span className='account-gallery__item__icons'> |         <span className='account-gallery__item__icons'> | ||||||
|           <Icon id='eye-slash' /> |           <Icon id='eye-slash' icon={VisibilityOffIcon} /> | ||||||
|         </span> |         </span> | ||||||
|       ); |       ); | ||||||
|     } else { |     } else { | ||||||
| @ -84,9 +88,9 @@ export default class MediaItem extends ImmutablePureComponent { | |||||||
|         ); |         ); | ||||||
| 
 | 
 | ||||||
|         if (attachment.get('type') === 'audio') { |         if (attachment.get('type') === 'audio') { | ||||||
|           label = <Icon id='music' />; |           label = <Icon id='music' icon={AudiotrackIcon} />; | ||||||
|         } else { |         } else { | ||||||
|           label = <Icon id='play' />; |           label = <Icon id='play' icon={PlayArrowIcon} />; | ||||||
|         } |         } | ||||||
|       } else if (attachment.get('type') === 'image') { |       } else if (attachment.get('type') === 'image') { | ||||||
|         const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; |         const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; | ||||||
|  | |||||||
| @ -7,6 +7,12 @@ import classNames from 'classnames'; | |||||||
| 
 | 
 | ||||||
| import { is } from 'immutable'; | import { is } from 'immutable'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as DownloadIcon } from '@material-symbols/svg-600/outlined/download.svg'; | ||||||
|  | import { ReactComponent as PauseIcon } from '@material-symbols/svg-600/outlined/pause.svg'; | ||||||
|  | import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow.svg'; | ||||||
|  | import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg'; | ||||||
|  | import { ReactComponent as VolumeOffIcon } from '@material-symbols/svg-600/outlined/volume_off.svg'; | ||||||
|  | import { ReactComponent as VolumeUpIcon } from '@material-symbols/svg-600/outlined/volume_up.svg'; | ||||||
| import { throttle, debounce } from 'lodash'; | import { throttle, debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -554,8 +560,8 @@ class Audio extends PureComponent { | |||||||
|         <div className='video-player__controls active'> |         <div className='video-player__controls active'> | ||||||
|           <div className='video-player__buttons-bar'> |           <div className='video-player__buttons-bar'> | ||||||
|             <div className='video-player__buttons left'> |             <div className='video-player__buttons left'> | ||||||
|               <button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button> |               <button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} icon={paused ? PlayArrowIcon : PauseIcon} /></button> | ||||||
|               <button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button> |               <button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} icon={muted ? VolumeOffIcon : VolumeUpIcon} /></button> | ||||||
| 
 | 
 | ||||||
|               <div className={classNames('video-player__volume', { active: this.state.hovered })} ref={this.setVolumeRef} onMouseDown={this.handleVolumeMouseDown}> |               <div className={classNames('video-player__volume', { active: this.state.hovered })} ref={this.setVolumeRef} onMouseDown={this.handleVolumeMouseDown}> | ||||||
|                 <div className='video-player__volume__current' style={{ width: `${muted ? 0 : volume * 100}%`, backgroundColor: this._getAccentColor() }} /> |                 <div className='video-player__volume__current' style={{ width: `${muted ? 0 : volume * 100}%`, backgroundColor: this._getAccentColor() }} /> | ||||||
| @ -575,9 +581,9 @@ class Audio extends PureComponent { | |||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|             <div className='video-player__buttons right'> |             <div className='video-player__buttons right'> | ||||||
|               {!editable && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>} |               {!editable && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' icon={VisibilityOffIcon} /></button>} | ||||||
|               <a title={intl.formatMessage(messages.download)} aria-label={intl.formatMessage(messages.download)} className='video-player__download__icon player-button' href={this.props.src} download> |               <a title={intl.formatMessage(messages.download)} aria-label={intl.formatMessage(messages.download)} className='video-player__download__icon player-button' href={this.props.src} download> | ||||||
|                 <Icon id={'download'} fixedWidth /> |                 <Icon id={'download'} icon={DownloadIcon} /> | ||||||
|               </a> |               </a> | ||||||
|             </div> |             </div> | ||||||
|           </div> |           </div> | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'mastodon/actions/bookmarks'; | import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'mastodon/actions/bookmarks'; | ||||||
| @ -79,7 +80,8 @@ class Bookmarks extends ImmutablePureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='bookmark' |           icon='bookmarks' | ||||||
|  |           iconComponent={BookmarksIcon} | ||||||
|           title={intl.formatMessage(messages.heading)} |           title={intl.formatMessage(messages.heading)} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|           onMove={this.handleMove} |           onMove={this.handleMove} | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ import { Helmet } from 'react-helmet'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg'; | ||||||
|  | 
 | ||||||
| import { DismissableBanner } from 'mastodon/components/dismissable_banner'; | import { DismissableBanner } from 'mastodon/components/dismissable_banner'; | ||||||
| import { domain } from 'mastodon/initial_state'; | import { domain } from 'mastodon/initial_state'; | ||||||
| 
 | 
 | ||||||
| @ -128,6 +130,7 @@ class CommunityTimeline extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='users' |           icon='users' | ||||||
|  |           iconComponent={PeopleIcon} | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|           title={intl.formatMessage(messages.title)} |           title={intl.formatMessage(messages.title)} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl'; | |||||||
| 
 | 
 | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg'; | ||||||
|  | 
 | ||||||
| import DropdownMenuContainer from '../../../containers/dropdown_menu_container'; | import DropdownMenuContainer from '../../../containers/dropdown_menu_container'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
| @ -60,7 +62,7 @@ class ActionBar extends PureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className='compose__action-bar'> |       <div className='compose__action-bar'> | ||||||
|         <div className='compose__action-bar-dropdown'> |         <div className='compose__action-bar-dropdown'> | ||||||
|           <DropdownMenuContainer items={menu} icon='bars' size={18} direction='right' /> |           <DropdownMenuContainer items={menu} icon='bars' iconComponent={MenuIcon} size={24} direction='right' /> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -7,6 +7,7 @@ import classNames from 'classnames'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg'; | ||||||
| import { length } from 'stringz'; | import { length } from 'stringz'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -229,7 +230,7 @@ class ComposeForm extends ImmutablePureComponent { | |||||||
|     if (this.props.isEditing) { |     if (this.props.isEditing) { | ||||||
|       publishText = intl.formatMessage(messages.saveChanges); |       publishText = intl.formatMessage(messages.saveChanges); | ||||||
|     } else if (this.props.privacy === 'private' || this.props.privacy === 'direct') { |     } else if (this.props.privacy === 'private' || this.props.privacy === 'direct') { | ||||||
|       publishText = <span className='compose-form__publish-private'><Icon id='lock' /> {intl.formatMessage(messages.publish)}</span>; |       publishText = <><Icon id='lock' icon={LockIcon} /> {intl.formatMessage(messages.publish)}</>; | ||||||
|     } else { |     } else { | ||||||
|       publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish); |       publishText = this.props.privacy !== 'unlisted' ? intl.formatMessage(messages.publishLoud, { publish: intl.formatMessage(messages.publish) }) : intl.formatMessage(messages.publish); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -3,8 +3,11 @@ import { PureComponent } from 'react'; | |||||||
| 
 | 
 | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; | import { defineMessages, injectIntl } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg'; | ||||||
|  | 
 | ||||||
| import { IconButton } from '../../../components/icon_button'; | import { IconButton } from '../../../components/icon_button'; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
|   add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add a poll' }, |   add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add a poll' }, | ||||||
|   remove_poll: { id: 'poll_button.remove_poll', defaultMessage: 'Remove poll' }, |   remove_poll: { id: 'poll_button.remove_poll', defaultMessage: 'Remove poll' }, | ||||||
| @ -40,6 +43,7 @@ class PollButton extends PureComponent { | |||||||
|       <div className='compose-form__poll-button'> |       <div className='compose-form__poll-button'> | ||||||
|         <IconButton |         <IconButton | ||||||
|           icon='tasks' |           icon='tasks' | ||||||
|  |           iconComponent={InsertChartIcon} | ||||||
|           title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)} |           title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)} | ||||||
|           disabled={disabled} |           disabled={disabled} | ||||||
|           onClick={this.handleClick} |           onClick={this.handleClick} | ||||||
|  | |||||||
| @ -8,6 +8,9 @@ import classNames from 'classnames'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg'; | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import AutosuggestInput from 'mastodon/components/autosuggest_input'; | import AutosuggestInput from 'mastodon/components/autosuggest_input'; | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import { IconButton } from 'mastodon/components/icon_button'; | import { IconButton } from 'mastodon/components/icon_button'; | ||||||
| @ -108,7 +111,7 @@ class OptionIntl extends PureComponent { | |||||||
|         </label> |         </label> | ||||||
| 
 | 
 | ||||||
|         <div className='poll__cancel'> |         <div className='poll__cancel'> | ||||||
|           <IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' onClick={this.handleOptionRemove} /> |           <IconButton disabled={index <= 1} title={intl.formatMessage(messages.remove_option)} icon='times' iconComponent={CloseIcon} onClick={this.handleOptionRemove} /> | ||||||
|         </div> |         </div> | ||||||
|       </li> |       </li> | ||||||
|     ); |     ); | ||||||
| @ -164,7 +167,7 @@ class PollForm extends ImmutablePureComponent { | |||||||
|         </ul> |         </ul> | ||||||
| 
 | 
 | ||||||
|         <div className='poll__footer'> |         <div className='poll__footer'> | ||||||
|           <button type='button' disabled={options.size >= 4} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' /> <FormattedMessage {...messages.add_option} /></button> |           <button type='button' disabled={options.size >= 4} className='button button-secondary' onClick={this.handleAddOption}><Icon id='plus' icon={AddIcon} /> <FormattedMessage {...messages.add_option} /></button> | ||||||
| 
 | 
 | ||||||
|           {/* eslint-disable-next-line jsx-a11y/no-onchange */} |           {/* eslint-disable-next-line jsx-a11y/no-onchange */} | ||||||
|           <select value={expiresIn} onChange={this.handleSelectDuration}> |           <select value={expiresIn} onChange={this.handleSelectDuration}> | ||||||
|  | |||||||
| @ -5,9 +5,15 @@ import { injectIntl, defineMessages } from 'react-intl'; | |||||||
| 
 | 
 | ||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
|  | import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg'; | ||||||
|  | import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg'; | ||||||
|  | import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg'; | ||||||
|  | import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg'; | ||||||
| import { supportsPassiveEvents } from 'detect-passive-events'; | import { supportsPassiveEvents } from 'detect-passive-events'; | ||||||
| import Overlay from 'react-overlays/Overlay'; | import Overlay from 'react-overlays/Overlay'; | ||||||
| 
 | 
 | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| import { IconButton } from '../../../components/icon_button'; | import { IconButton } from '../../../components/icon_button'; | ||||||
| @ -123,7 +129,7 @@ class PrivacyDropdownMenu extends PureComponent { | |||||||
|         {items.map(item => ( |         {items.map(item => ( | ||||||
|           <div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}> |           <div role='option' tabIndex={0} key={item.value} data-index={item.value} onKeyDown={this.handleKeyDown} onClick={this.handleClick} className={classNames('privacy-dropdown__option', { active: item.value === value })} aria-selected={item.value === value} ref={item.value === value ? this.setFocusRef : null}> | ||||||
|             <div className='privacy-dropdown__option__icon'> |             <div className='privacy-dropdown__option__icon'> | ||||||
|               <Icon id={item.icon} fixedWidth /> |               <Icon id={item.icon} icon={item.iconComponent} /> | ||||||
|             </div> |             </div> | ||||||
| 
 | 
 | ||||||
|             <div className='privacy-dropdown__option__content'> |             <div className='privacy-dropdown__option__content'> | ||||||
| @ -222,14 +228,14 @@ class PrivacyDropdown extends PureComponent { | |||||||
|     const { intl: { formatMessage } } = this.props; |     const { intl: { formatMessage } } = this.props; | ||||||
| 
 | 
 | ||||||
|     this.options = [ |     this.options = [ | ||||||
|       { icon: 'globe', value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) }, |       { icon: 'globe', iconComponent: PublicIcon, value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) }, | ||||||
|       { icon: 'unlock', value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) }, |       { icon: 'unlock', iconComponent: LockOpenIcon,  value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) }, | ||||||
|       { icon: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, |       { icon: 'lock', iconComponent: LockIcon, value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, | ||||||
|     ]; |     ]; | ||||||
| 
 | 
 | ||||||
|     if (!this.props.noDirect) { |     if (!this.props.noDirect) { | ||||||
|       this.options.push( |       this.options.push( | ||||||
|         { icon: 'at', value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) }, |         { icon: 'at', iconComponent: AlternateEmailIcon, value: 'direct', text: formatMessage(messages.direct_short), meta: formatMessage(messages.direct_long) }, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|   } |   } | ||||||
| @ -253,25 +259,24 @@ class PrivacyDropdown extends PureComponent { | |||||||
|     const valueOption = this.options.find(item => item.value === value); |     const valueOption = this.options.find(item => item.value === value); | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className={classNames('privacy-dropdown', placement, { active: open })} onKeyDown={this.handleKeyDown}> |       <div ref={this.setTargetRef} onKeyDown={this.handleKeyDown}> | ||||||
|         <div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === (placement === 'bottom' ? 0 : (this.options.length - 1)) })} ref={this.setTargetRef}> |         <IconButton | ||||||
|           <IconButton |           className='privacy-dropdown__value-icon' | ||||||
|             className='privacy-dropdown__value-icon' |           icon={valueOption.icon} | ||||||
|             icon={valueOption.icon} |           iconComponent={valueOption.iconComponent} | ||||||
|             title={intl.formatMessage(messages.change_privacy)} |           title={intl.formatMessage(messages.change_privacy)} | ||||||
|             size={18} |           size={18} | ||||||
|             expanded={open} |           expanded={open} | ||||||
|             active={open} |           active={open} | ||||||
|             inverted |           inverted | ||||||
|             onClick={this.handleToggle} |           onClick={this.handleToggle} | ||||||
|             onMouseDown={this.handleMouseDown} |           onMouseDown={this.handleMouseDown} | ||||||
|             onKeyDown={this.handleButtonKeyDown} |           onKeyDown={this.handleButtonKeyDown} | ||||||
|             style={{ height: null, lineHeight: '27px' }} |           style={{ height: null, lineHeight: '27px' }} | ||||||
|             disabled={disabled} |           disabled={disabled} | ||||||
|           /> |         /> | ||||||
|         </div> |  | ||||||
| 
 | 
 | ||||||
|         <Overlay show={open} placement={'bottom'} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}> |         <Overlay show={open} placement={placement} flip target={this.findTarget} container={container} popperConfig={{ strategy: 'fixed', onFirstUpdate: this.handleOverlayEnter }}> | ||||||
|           {({ props, placement }) => ( |           {({ props, placement }) => ( | ||||||
|             <div {...props}> |             <div {...props}> | ||||||
|               <div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}> |               <div className={`dropdown-animation privacy-dropdown__dropdown ${placement}`}> | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import AttachmentList from 'mastodon/components/attachment_list'; | import AttachmentList from 'mastodon/components/attachment_list'; | ||||||
| import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router'; | import { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
| @ -48,7 +50,7 @@ class ReplyIndicator extends ImmutablePureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className='reply-indicator'> |       <div className='reply-indicator'> | ||||||
|         <div className='reply-indicator__header'> |         <div className='reply-indicator__header'> | ||||||
|           <div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} inverted /></div> |           <div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' iconComponent={CloseIcon} onClick={this.handleClick} inverted /></div> | ||||||
| 
 | 
 | ||||||
|           <a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='reply-indicator__display-name'> |           <a href={`/@${status.getIn(['account', 'acct'])}`} onClick={this.handleAccountClick} className='reply-indicator__display-name'> | ||||||
|             <div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div> |             <div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div> | ||||||
|  | |||||||
| @ -8,6 +8,10 @@ import { withRouter } from 'react-router-dom'; | |||||||
| 
 | 
 | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CancelIcon } from '@material-symbols/svg-600/outlined/cancel-fill.svg'; | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import { domain, searchEnabled } from 'mastodon/initial_state'; | import { domain, searchEnabled } from 'mastodon/initial_state'; | ||||||
| import { HASHTAG_REGEX } from 'mastodon/utils/hashtags'; | import { HASHTAG_REGEX } from 'mastodon/utils/hashtags'; | ||||||
| @ -332,8 +336,8 @@ class Search extends PureComponent { | |||||||
|         /> |         /> | ||||||
| 
 | 
 | ||||||
|         <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}> |         <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}> | ||||||
|           <Icon id='search' className={hasValue ? '' : 'active'} /> |           <Icon id='search' icon={SearchIcon} className={hasValue ? '' : 'active'} /> | ||||||
|           <Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} /> |           <Icon id='times-circle' icon={CancelIcon} className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} /> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div className='search__popout'> |         <div className='search__popout'> | ||||||
| @ -345,7 +349,7 @@ class Search extends PureComponent { | |||||||
|                 {recent.size > 0 ? this._getOptions().map(({ label, action, forget }, i) => ( |                 {recent.size > 0 ? this._getOptions().map(({ label, action, forget }, i) => ( | ||||||
|                   <button key={label} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}> |                   <button key={label} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}> | ||||||
|                     <span>{label}</span> |                     <span>{label}</span> | ||||||
|                     <button className='icon-button' onMouseDown={forget}><Icon id='times' /></button> |                     <button className='icon-button' onMouseDown={forget}><Icon id='times' icon={CloseIcon} /></button> | ||||||
|                   </button> |                   </button> | ||||||
|                 )) : ( |                 )) : ( | ||||||
|                   <div className='search__popout__menu__message'> |                   <div className='search__popout__menu__message'> | ||||||
|  | |||||||
| @ -5,6 +5,11 @@ import { FormattedMessage } from 'react-intl'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as FindInPageIcon } from '@material-symbols/svg-600/outlined/find_in_page.svg'; | ||||||
|  | import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg'; | ||||||
|  | import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg'; | ||||||
|  | import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import { LoadMore } from 'mastodon/components/load_more'; | import { LoadMore } from 'mastodon/components/load_more'; | ||||||
| import { SearchSection } from 'mastodon/features/explore/components/search_section'; | import { SearchSection } from 'mastodon/features/explore/components/search_section'; | ||||||
| @ -44,7 +49,7 @@ class SearchResults extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|     if (results.get('accounts') && results.get('accounts').size > 0) { |     if (results.get('accounts') && results.get('accounts').size > 0) { | ||||||
|       accounts = ( |       accounts = ( | ||||||
|         <SearchSection title={<><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}> |         <SearchSection title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}> | ||||||
|           {withoutLastResult(results.get('accounts')).map(accountId => <AccountContainer key={accountId} id={accountId} />)} |           {withoutLastResult(results.get('accounts')).map(accountId => <AccountContainer key={accountId} id={accountId} />)} | ||||||
|           {(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreAccounts} />} |           {(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreAccounts} />} | ||||||
|         </SearchSection> |         </SearchSection> | ||||||
| @ -53,7 +58,7 @@ class SearchResults extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|     if (results.get('hashtags') && results.get('hashtags').size > 0) { |     if (results.get('hashtags') && results.get('hashtags').size > 0) { | ||||||
|       hashtags = ( |       hashtags = ( | ||||||
|         <SearchSection title={<><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>}> |         <SearchSection title={<><Icon id='hashtag' icon={TagIcon} /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>}> | ||||||
|           {withoutLastResult(results.get('hashtags')).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} |           {withoutLastResult(results.get('hashtags')).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} | ||||||
|           {(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreHashtags} />} |           {(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreHashtags} />} | ||||||
|         </SearchSection> |         </SearchSection> | ||||||
| @ -62,7 +67,7 @@ class SearchResults extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|     if (results.get('statuses') && results.get('statuses').size > 0) { |     if (results.get('statuses') && results.get('statuses').size > 0) { | ||||||
|       statuses = ( |       statuses = ( | ||||||
|         <SearchSection title={<><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>}> |         <SearchSection title={<><Icon id='quote-right' icon={FindInPageIcon} /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>}> | ||||||
|           {withoutLastResult(results.get('statuses')).map(statusId => <StatusContainer key={statusId} id={statusId} />)} |           {withoutLastResult(results.get('statuses')).map(statusId => <StatusContainer key={statusId} id={statusId} />)} | ||||||
|           {(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreStatuses} />} |           {(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreStatuses} />} | ||||||
|         </SearchSection> |         </SearchSection> | ||||||
| @ -73,7 +78,7 @@ class SearchResults extends ImmutablePureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className='search-results'> |       <div className='search-results'> | ||||||
|         <div className='search-results__header'> |         <div className='search-results__header'> | ||||||
|           <Icon id='search' fixedWidth /> |           <Icon id='search' icon={SearchIcon} /> | ||||||
|           <FormattedMessage id='explore.search_results' defaultMessage='Search results' /> |           <FormattedMessage id='explore.search_results' defaultMessage='Search results' /> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,6 +5,9 @@ import { FormattedMessage } from 'react-intl'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg'; | ||||||
|  | import { ReactComponent as InfoIcon } from '@material-symbols/svg-600/outlined/info.svg'; | ||||||
| import spring from 'react-motion/lib/spring'; | import spring from 'react-motion/lib/spring'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -47,13 +50,13 @@ export default class Upload extends ImmutablePureComponent { | |||||||
|           {({ scale }) => ( |           {({ scale }) => ( | ||||||
|             <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}> |             <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}> | ||||||
|               <div className='compose-form__upload__actions'> |               <div className='compose-form__upload__actions'> | ||||||
|                 <button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button> |                 <button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' icon={CloseIcon} /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button> | ||||||
|                 <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button> |                 <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button> | ||||||
|               </div> |               </div> | ||||||
| 
 | 
 | ||||||
|               {(media.get('description') || '').length === 0 && ( |               {(media.get('description') || '').length === 0 && ( | ||||||
|                 <div className='compose-form__upload__warning'> |                 <div className='compose-form__upload__warning'> | ||||||
|                   <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button> |                   <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button> | ||||||
|                 </div> |                 </div> | ||||||
|               )} |               )} | ||||||
|             </div> |             </div> | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AddPhotoAlternateIcon } from '@material-symbols/svg-600/outlined/add_photo_alternate.svg'; | ||||||
|  | 
 | ||||||
| import { IconButton } from '../../../components/icon_button'; | import { IconButton } from '../../../components/icon_button'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
| @ -62,7 +64,7 @@ class UploadButton extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className='compose-form__upload-button'> |       <div className='compose-form__upload-button'> | ||||||
|         <IconButton icon='paperclip' title={message} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} /> |         <IconButton icon='paperclip' iconComponent={AddPhotoAlternateIcon} title={message} disabled={disabled} onClick={this.handleClick} className='compose-form__upload-button-icon' size={18} inverted style={iconStyle} /> | ||||||
|         <label> |         <label> | ||||||
|           <span style={{ display: 'none' }}>{message}</span> |           <span style={{ display: 'none' }}>{message}</span> | ||||||
|           <input |           <input | ||||||
|  | |||||||
| @ -3,6 +3,7 @@ import { PureComponent } from 'react'; | |||||||
| 
 | 
 | ||||||
| import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as UploadFileIcon } from '@material-symbols/svg-600/outlined/upload_file.svg'; | ||||||
| import spring from 'react-motion/lib/spring'; | import spring from 'react-motion/lib/spring'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -35,7 +36,7 @@ export default class UploadProgress extends PureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className='upload-progress'> |       <div className='upload-progress'> | ||||||
|         <div className='upload-progress__icon'> |         <div className='upload-progress__icon'> | ||||||
|           <Icon id='upload' /> |           <Icon id='upload' icon={UploadFileIcon} /> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div className='upload-progress__message'> |         <div className='upload-progress__message'> | ||||||
|  | |||||||
| @ -9,6 +9,13 @@ import { Link } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg'; | ||||||
|  | import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg'; | ||||||
|  | import { ReactComponent as LogoutIcon } from '@material-symbols/svg-600/outlined/logout.svg'; | ||||||
|  | import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg'; | ||||||
|  | import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications-fill.svg'; | ||||||
|  | import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg'; | ||||||
|  | import { ReactComponent as SettingsIcon } from '@material-symbols/svg-600/outlined/settings-fill.svg'; | ||||||
| import spring from 'react-motion/lib/spring'; | import spring from 'react-motion/lib/spring'; | ||||||
| 
 | 
 | ||||||
| import { openModal } from 'mastodon/actions/modal'; | import { openModal } from 'mastodon/actions/modal'; | ||||||
| @ -101,21 +108,21 @@ class Compose extends PureComponent { | |||||||
|       return ( |       return ( | ||||||
|         <div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}> |         <div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}> | ||||||
|           <nav className='drawer__header'> |           <nav className='drawer__header'> | ||||||
|             <Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}><Icon id='bars' fixedWidth /></Link> |             <Link to='/getting-started' className='drawer__tab' title={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}><Icon id='bars' icon={MenuIcon} /></Link> | ||||||
|             {!columns.some(column => column.get('id') === 'HOME') && ( |             {!columns.some(column => column.get('id') === 'HOME') && ( | ||||||
|               <Link to='/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}><Icon id='home' fixedWidth /></Link> |               <Link to='/home' className='drawer__tab' title={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}><Icon id='home' icon={HomeIcon} /></Link> | ||||||
|             )} |             )} | ||||||
|             {!columns.some(column => column.get('id') === 'NOTIFICATIONS') && ( |             {!columns.some(column => column.get('id') === 'NOTIFICATIONS') && ( | ||||||
|               <Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}><Icon id='bell' fixedWidth /></Link> |               <Link to='/notifications' className='drawer__tab' title={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}><Icon id='bell' icon={NotificationsIcon} /></Link> | ||||||
|             )} |             )} | ||||||
|             {!columns.some(column => column.get('id') === 'COMMUNITY') && ( |             {!columns.some(column => column.get('id') === 'COMMUNITY') && ( | ||||||
|               <Link to='/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}><Icon id='users' fixedWidth /></Link> |               <Link to='/public/local' className='drawer__tab' title={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}><Icon id='users' icon={PeopleIcon} /></Link> | ||||||
|             )} |             )} | ||||||
|             {!columns.some(column => column.get('id') === 'PUBLIC') && ( |             {!columns.some(column => column.get('id') === 'PUBLIC') && ( | ||||||
|               <Link to='/public' className='drawer__tab' title={intl.formatMessage(messages.public)} aria-label={intl.formatMessage(messages.public)}><Icon id='globe' fixedWidth /></Link> |               <Link to='/public' className='drawer__tab' title={intl.formatMessage(messages.public)} aria-label={intl.formatMessage(messages.public)}><Icon id='globe' icon={PublicIcon} /></Link> | ||||||
|             )} |             )} | ||||||
|             <a href='/settings/preferences' className='drawer__tab' title={intl.formatMessage(messages.preferences)} aria-label={intl.formatMessage(messages.preferences)}><Icon id='cog' fixedWidth /></a> |             <a href='/settings/preferences' className='drawer__tab' title={intl.formatMessage(messages.preferences)} aria-label={intl.formatMessage(messages.preferences)}><Icon id='cog' icon={SettingsIcon} /></a> | ||||||
|             <a href='/auth/sign_out' className='drawer__tab' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)} onClick={this.handleLogoutClick}><Icon id='sign-out' fixedWidth /></a> |             <a href='/auth/sign_out' className='drawer__tab' title={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)} onClick={this.handleLogoutClick}><Icon id='sign-out' icon={LogoutIcon} /></a> | ||||||
|           </nav> |           </nav> | ||||||
| 
 | 
 | ||||||
|           {multiColumn && <SearchContainer /> } |           {multiColumn && <SearchContainer /> } | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ import { Link, withRouter } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg'; | ||||||
|  | import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg'; | ||||||
| import { HotKeys } from 'react-hotkeys'; | import { HotKeys } from 'react-hotkeys'; | ||||||
| 
 | 
 | ||||||
| import AttachmentList from 'mastodon/components/attachment_list'; | import AttachmentList from 'mastodon/components/attachment_list'; | ||||||
| @ -178,7 +180,7 @@ class Conversation extends ImmutablePureComponent { | |||||||
|             )} |             )} | ||||||
| 
 | 
 | ||||||
|             <div className='status__action-bar'> |             <div className='status__action-bar'> | ||||||
|               <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' onClick={this.handleReply} /> |               <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' iconComponent={ReplyIcon} onClick={this.handleReply} /> | ||||||
| 
 | 
 | ||||||
|               <div className='status__action-bar-dropdown'> |               <div className='status__action-bar-dropdown'> | ||||||
|                 <DropdownMenuContainer |                 <DropdownMenuContainer | ||||||
| @ -186,6 +188,7 @@ class Conversation extends ImmutablePureComponent { | |||||||
|                   status={lastStatus} |                   status={lastStatus} | ||||||
|                   items={menu} |                   items={menu} | ||||||
|                   icon='ellipsis-h' |                   icon='ellipsis-h' | ||||||
|  |                   iconComponent={MoreHorizIcon} | ||||||
|                   size={18} |                   size={18} | ||||||
|                   direction='right' |                   direction='right' | ||||||
|                   title={intl.formatMessage(messages.more)} |                   title={intl.formatMessage(messages.more)} | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ import { Helmet } from 'react-helmet'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg'; | ||||||
|  | 
 | ||||||
| import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | ||||||
| import { mountConversations, unmountConversations, expandConversations } from 'mastodon/actions/conversations'; | import { mountConversations, unmountConversations, expandConversations } from 'mastodon/actions/conversations'; | ||||||
| import { connectDirectStream } from 'mastodon/actions/streaming'; | import { connectDirectStream } from 'mastodon/actions/streaming'; | ||||||
| @ -81,6 +83,7 @@ class DirectTimeline extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='at' |           icon='at' | ||||||
|  |           iconComponent={AlternateEmailIcon} | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|           title={intl.formatMessage(messages.title)} |           title={intl.formatMessage(messages.title)} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ import { List as ImmutableList } from 'immutable'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg'; | ||||||
|  | 
 | ||||||
| import { addColumn, removeColumn, moveColumn, changeColumnParams } from 'mastodon/actions/columns'; | import { addColumn, removeColumn, moveColumn, changeColumnParams } from 'mastodon/actions/columns'; | ||||||
| import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory'; | import { fetchDirectory, expandDirectory } from 'mastodon/actions/directory'; | ||||||
| import Column from 'mastodon/components/column'; | import Column from 'mastodon/components/column'; | ||||||
| @ -156,6 +158,7 @@ class Directory extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='address-book-o' |           icon='address-book-o' | ||||||
|  |           iconComponent={PeopleIcon} | ||||||
|           title={intl.formatMessage(messages.title)} |           title={intl.formatMessage(messages.title)} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|           onMove={this.handleMove} |           onMove={this.handleMove} | ||||||
|  | |||||||
| @ -60,7 +60,7 @@ class Blocks extends ImmutablePureComponent { | |||||||
|     const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no blocked domains yet.' />; |     const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no blocked domains yet.' />; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column bindToDocument={!multiColumn} icon='minus-circle' heading={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
| 
 | 
 | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|  | |||||||
| @ -8,6 +8,9 @@ import { NavLink, Switch, Route } from 'react-router-dom'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg'; | ||||||
|  | import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg'; | ||||||
|  | 
 | ||||||
| import Column from 'mastodon/components/column'; | import Column from 'mastodon/components/column'; | ||||||
| import ColumnHeader from 'mastodon/components/column_header'; | import ColumnHeader from 'mastodon/components/column_header'; | ||||||
| import Search from 'mastodon/features/compose/containers/search_container'; | import Search from 'mastodon/features/compose/containers/search_container'; | ||||||
| @ -57,6 +60,7 @@ class Explore extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon={isSearching ? 'search' : 'hashtag'} |           icon={isSearching ? 'search' : 'hashtag'} | ||||||
|  |           iconComponent={isSearching ? SearchIcon : TagIcon} | ||||||
|           title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)} |           title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)} | ||||||
|           onClick={this.handleHeaderClick} |           onClick={this.handleHeaderClick} | ||||||
|           multiColumn={multiColumn} |           multiColumn={multiColumn} | ||||||
|  | |||||||
| @ -9,6 +9,10 @@ import { List as ImmutableList } from 'immutable'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as FindInPageIcon } from '@material-symbols/svg-600/outlined/find_in_page.svg'; | ||||||
|  | import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg'; | ||||||
|  | import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg'; | ||||||
|  | 
 | ||||||
| import { submitSearch, expandSearch } from 'mastodon/actions/search'; | import { submitSearch, expandSearch } from 'mastodon/actions/search'; | ||||||
| import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag'; | import { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag'; | ||||||
| import { Icon } from 'mastodon/components/icon'; | import { Icon } from 'mastodon/components/icon'; | ||||||
| @ -165,19 +169,19 @@ class Results extends PureComponent { | |||||||
|       filteredResults = (accounts.size + hashtags.size + statuses.size) > 0 ? ( |       filteredResults = (accounts.size + hashtags.size + statuses.size) > 0 ? ( | ||||||
|         <> |         <> | ||||||
|           {accounts.size > 0 && ( |           {accounts.size > 0 && ( | ||||||
|             <SearchSection key='accounts' title={<><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>} onClickMore={this.handleLoadMoreAccounts}> |             <SearchSection key='accounts' title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>} onClickMore={this.handleLoadMoreAccounts}> | ||||||
|               {accounts.take(INITIAL_DISPLAY).map(id => <Account key={id} id={id} />)} |               {accounts.take(INITIAL_DISPLAY).map(id => <Account key={id} id={id} />)} | ||||||
|             </SearchSection> |             </SearchSection> | ||||||
|           )} |           )} | ||||||
| 
 | 
 | ||||||
|           {hashtags.size > 0 && ( |           {hashtags.size > 0 && ( | ||||||
|             <SearchSection key='hashtags' title={<><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>} onClickMore={this.handleLoadMoreHashtags}> |             <SearchSection key='hashtags' title={<><Icon id='hashtag' icon={TagIcon} /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>} onClickMore={this.handleLoadMoreHashtags}> | ||||||
|               {hashtags.take(INITIAL_DISPLAY).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} |               {hashtags.take(INITIAL_DISPLAY).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} | ||||||
|             </SearchSection> |             </SearchSection> | ||||||
|           )} |           )} | ||||||
| 
 | 
 | ||||||
|           {statuses.size > 0 && ( |           {statuses.size > 0 && ( | ||||||
|             <SearchSection key='statuses' title={<><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>} onClickMore={this.handleLoadMoreStatuses}> |             <SearchSection key='statuses' title={<><Icon id='quote-right' icon={FindInPageIcon} /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>} onClickMore={this.handleLoadMoreStatuses}> | ||||||
|               {statuses.take(INITIAL_DISPLAY).map(id => <Status key={id} id={id} />)} |               {statuses.take(INITIAL_DISPLAY).map(id => <Status key={id} id={id} />)} | ||||||
|             </SearchSection> |             </SearchSection> | ||||||
|           )} |           )} | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | ||||||
| @ -80,6 +81,7 @@ class Favourites extends ImmutablePureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='star' |           icon='star' | ||||||
|  |           iconComponent={StarIcon} | ||||||
|           title={intl.formatMessage(messages.heading)} |           title={intl.formatMessage(messages.heading)} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|           onMove={this.handleMove} |           onMove={this.handleMove} | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as RefreshIcon } from '@material-symbols/svg-600/outlined/refresh.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions'; | import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions'; | ||||||
| @ -73,7 +74,7 @@ class Favourites extends ImmutablePureComponent { | |||||||
|           showBackButton |           showBackButton | ||||||
|           multiColumn={multiColumn} |           multiColumn={multiColumn} | ||||||
|           extraButton={( |           extraButton={( | ||||||
|             <button type='button' className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button> |             <button type='button' className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' icon={RefreshIcon} /></button> | ||||||
|           )} |           )} | ||||||
|         /> |         /> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -5,6 +5,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg'; | ||||||
| import fuzzysort from 'fuzzysort'; | import fuzzysort from 'fuzzysort'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -78,7 +79,7 @@ class SelectFilter extends PureComponent { | |||||||
|   renderCreateNew (name) { |   renderCreateNew (name) { | ||||||
|     return ( |     return ( | ||||||
|       <div key='add-new-filter' role='button' tabIndex={0} className='language-dropdown__dropdown__results__item' onClick={this.handleNewFilterClick} onKeyDown={this.handleKeyDown}> |       <div key='add-new-filter' role='button' tabIndex={0} className='language-dropdown__dropdown__results__item' onClick={this.handleNewFilterClick} onKeyDown={this.handleKeyDown}> | ||||||
|         <Icon id='plus' fixedWidth /> <FormattedMessage id='filter_modal.select_filter.prompt_new' defaultMessage='New category: {name}' values={{ name }} /> |         <Icon id='plus' icon={AddIcon} /> <FormattedMessage id='filter_modal.select_filter.prompt_new' defaultMessage='New category: {name}' values={{ name }} /> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; | |||||||
| import { Helmet } from 'react-helmet'; | import { Helmet } from 'react-helmet'; | ||||||
| import { NavLink } from 'react-router-dom'; | import { NavLink } from 'react-router-dom'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg'; | ||||||
|  | 
 | ||||||
| import { addColumn } from 'mastodon/actions/columns'; | import { addColumn } from 'mastodon/actions/columns'; | ||||||
| import { changeSetting } from 'mastodon/actions/settings'; | import { changeSetting } from 'mastodon/actions/settings'; | ||||||
| import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming'; | import { connectPublicStream, connectCommunityStream } from 'mastodon/actions/streaming'; | ||||||
| @ -160,6 +162,7 @@ const Firehose = ({ feedType, multiColumn }) => { | |||||||
|     <Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}> |     <Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}> | ||||||
|       <ColumnHeader |       <ColumnHeader | ||||||
|         icon='globe' |         icon='globe' | ||||||
|  |         iconComponent={PublicIcon} | ||||||
|         active={hasUnread} |         active={hasUnread} | ||||||
|         title={intl.formatMessage(messages.title)} |         title={intl.formatMessage(messages.title)} | ||||||
|         onPin={handlePin} |         onPin={handlePin} | ||||||
|  | |||||||
| @ -7,6 +7,9 @@ import { Link } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import { Avatar } from '../../../components/avatar'; | import { Avatar } from '../../../components/avatar'; | ||||||
| import { DisplayName } from '../../../components/display_name'; | import { DisplayName } from '../../../components/display_name'; | ||||||
| import { IconButton } from '../../../components/icon_button'; | import { IconButton } from '../../../components/icon_button'; | ||||||
| @ -41,8 +44,8 @@ class AccountAuthorize extends ImmutablePureComponent { | |||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div className='account--panel'> |         <div className='account--panel'> | ||||||
|           <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div> |           <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' iconComponent={CheckIcon} onClick={onAuthorize} /></div> | ||||||
|           <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div> |           <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' iconComponent={CloseIcon} onClick={onReject} /></div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { expandFollowedHashtags, fetchFollowedHashtags } from 'mastodon/actions/tags'; | import { expandFollowedHashtags, fetchFollowedHashtags } from 'mastodon/actions/tags'; | ||||||
| @ -55,6 +56,7 @@ class FollowedTags extends ImmutablePureComponent { | |||||||
|       <Column bindToDocument={!multiColumn}> |       <Column bindToDocument={!multiColumn}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='hashtag' |           icon='hashtag' | ||||||
|  |           iconComponent={TagIcon} | ||||||
|           title={intl.formatMessage(messages.heading)} |           title={intl.formatMessage(messages.heading)} | ||||||
|           showBackButton |           showBackButton | ||||||
|           multiColumn={multiColumn} |           multiColumn={multiColumn} | ||||||
|  | |||||||
| @ -9,6 +9,9 @@ import { withRouter } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg'; | ||||||
|  | import { ReactComponent as ChevronLeftIcon } from '@material-symbols/svg-600/outlined/chevron_left.svg'; | ||||||
|  | import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg'; | ||||||
| import TransitionMotion from 'react-motion/lib/TransitionMotion'; | import TransitionMotion from 'react-motion/lib/TransitionMotion'; | ||||||
| import spring from 'react-motion/lib/spring'; | import spring from 'react-motion/lib/spring'; | ||||||
| import ReactSwipeableViews from 'react-swipeable-views'; | import ReactSwipeableViews from 'react-swipeable-views'; | ||||||
| @ -294,7 +297,7 @@ class ReactionsBar extends ImmutablePureComponent { | |||||||
|               /> |               /> | ||||||
|             ))} |             ))} | ||||||
| 
 | 
 | ||||||
|             {visibleReactions.size < 8 && <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={<Icon id='plus' />} />} |             {visibleReactions.size < 8 && <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={<Icon id='plus' icon={AddIcon} />} />} | ||||||
|           </div> |           </div> | ||||||
|         )} |         )} | ||||||
|       </TransitionMotion> |       </TransitionMotion> | ||||||
| @ -440,9 +443,9 @@ class Announcements extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|           {announcements.size > 1 && ( |           {announcements.size > 1 && ( | ||||||
|             <div className='announcements__pagination'> |             <div className='announcements__pagination'> | ||||||
|               <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.previous)} icon='chevron-left' onClick={this.handlePrevClick} size={13} /> |               <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.previous)} icon='chevron-left' iconComponent={ChevronLeftIcon} onClick={this.handlePrevClick} size={13} /> | ||||||
|               <span>{index + 1} / {announcements.size}</span> |               <span>{index + 1} / {announcements.size}</span> | ||||||
|               <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.next)} icon='chevron-right' onClick={this.handleNextClick} size={13} /> |               <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.next)} icon='chevron-right' iconComponent={ChevronRightIcon} onClick={this.handleNextClick} size={13} /> | ||||||
|             </div> |             </div> | ||||||
|           )} |           )} | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -9,6 +9,18 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg'; | ||||||
|  | import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg'; | ||||||
|  | import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg'; | ||||||
|  | import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg'; | ||||||
|  | import { ReactComponent as ListAltIcon } from '@material-symbols/svg-600/outlined/list_alt.svg'; | ||||||
|  | import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg'; | ||||||
|  | import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg'; | ||||||
|  | import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg'; | ||||||
|  | import { ReactComponent as SettingsIcon } from '@material-symbols/svg-600/outlined/settings-fill.svg'; | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star.svg'; | ||||||
|  | import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg'; | ||||||
|  | 
 | ||||||
| import { fetchFollowRequests } from 'mastodon/actions/accounts'; | import { fetchFollowRequests } from 'mastodon/actions/accounts'; | ||||||
| import Column from 'mastodon/components/column'; | import Column from 'mastodon/components/column'; | ||||||
| import ColumnHeader from 'mastodon/components/column_header'; | import ColumnHeader from 'mastodon/components/column_header'; | ||||||
| @ -101,38 +113,38 @@ class GettingStarted extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|     if (showTrends) { |     if (showTrends) { | ||||||
|       navItems.push( |       navItems.push( | ||||||
|         <ColumnLink key='explore' icon='hashtag' text={intl.formatMessage(messages.explore)} to='/explore' />, |         <ColumnLink key='explore' icon='hashtag' iconComponent={TagIcon} text={intl.formatMessage(messages.explore)} to='/explore' />, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     navItems.push( |     navItems.push( | ||||||
|       <ColumnLink key='community_timeline' icon='users' text={intl.formatMessage(messages.community_timeline)} to='/public/local' />, |       <ColumnLink key='community_timeline' icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.community_timeline)} to='/public/local' />, | ||||||
|       <ColumnLink key='public_timeline' icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/public' />, |       <ColumnLink key='public_timeline' icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.public_timeline)} to='/public' />, | ||||||
|     ); |     ); | ||||||
| 
 | 
 | ||||||
|     if (signedIn) { |     if (signedIn) { | ||||||
|       navItems.push( |       navItems.push( | ||||||
|         <ColumnSubheading key='header-personal' text={intl.formatMessage(messages.personal)} />, |         <ColumnSubheading key='header-personal' text={intl.formatMessage(messages.personal)} />, | ||||||
|         <ColumnLink key='home' icon='home' text={intl.formatMessage(messages.home_timeline)} to='/home' />, |         <ColumnLink key='home' icon='home' iconComponent={HomeIcon} text={intl.formatMessage(messages.home_timeline)} to='/home' />, | ||||||
|         <ColumnLink key='direct' icon='at' text={intl.formatMessage(messages.direct)} to='/conversations' />, |         <ColumnLink key='direct' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} to='/conversations' />, | ||||||
|         <ColumnLink key='bookmark' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />, |         <ColumnLink key='bookmark' icon='bookmarks' iconComponent={BookmarksIcon} text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />, | ||||||
|         <ColumnLink key='favourites' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />, |         <ColumnLink key='favourites' icon='star' iconComponent={StarIcon} text={intl.formatMessage(messages.favourites)} to='/favourites' />, | ||||||
|         <ColumnLink key='lists' icon='list-ul' text={intl.formatMessage(messages.lists)} to='/lists' />, |         <ColumnLink key='lists' icon='list-ul' iconComponent={ListAltIcon} text={intl.formatMessage(messages.lists)} to='/lists' />, | ||||||
|       ); |       ); | ||||||
| 
 | 
 | ||||||
|       if (myAccount.get('locked') || unreadFollowRequests > 0) { |       if (myAccount.get('locked') || unreadFollowRequests > 0) { | ||||||
|         navItems.push(<ColumnLink key='follow_requests' icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />); |         navItems.push(<ColumnLink key='follow_requests' icon='user-plus' iconComponent={PersonAddIcon} text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />); | ||||||
|       } |       } | ||||||
| 
 | 
 | ||||||
|       navItems.push( |       navItems.push( | ||||||
|         <ColumnSubheading key='header-settings' text={intl.formatMessage(messages.settings_subheading)} />, |         <ColumnSubheading key='header-settings' text={intl.formatMessage(messages.settings_subheading)} />, | ||||||
|         <ColumnLink key='preferences' icon='gears' text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />, |         <ColumnLink key='preferences' icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} href='/settings/preferences' />, | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column> |       <Column> | ||||||
|         {(signedIn && !multiColumn) ? <NavigationContainer /> : <ColumnHeader title={intl.formatMessage(messages.menu)} icon='bars' multiColumn={multiColumn} />} |         {(signedIn && !multiColumn) ? <NavigationContainer /> : <ColumnHeader title={intl.formatMessage(messages.menu)} icon='bars' iconComponent={MenuIcon} multiColumn={multiColumn} />} | ||||||
| 
 | 
 | ||||||
|         <div className='getting-started scrollable scrollable--flex'> |         <div className='getting-started scrollable scrollable--flex'> | ||||||
|           <div className='getting-started__wrapper'> |           <div className='getting-started__wrapper'> | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import { Helmet } from 'react-helmet'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg'; | ||||||
| import { isEqual } from 'lodash'; | import { isEqual } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | ||||||
| @ -190,6 +191,7 @@ class HashtagTimeline extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='hashtag' |           icon='hashtag' | ||||||
|  |           iconComponent={TagIcon} | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|           title={this.title()} |           title={this.title()} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|  | |||||||
| @ -10,6 +10,9 @@ import { List as ImmutableList } from 'immutable'; | |||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { createSelector } from 'reselect'; | import { createSelector } from 'reselect'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CampaignIcon } from '@material-symbols/svg-600/outlined/campaign.svg'; | ||||||
|  | import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg'; | ||||||
|  | 
 | ||||||
| import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/announcements'; | import { fetchAnnouncements, toggleShowAnnouncements } from 'mastodon/actions/announcements'; | ||||||
| import { IconWithBadge } from 'mastodon/components/icon_with_badge'; | import { IconWithBadge } from 'mastodon/components/icon_with_badge'; | ||||||
| import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; | import { NotSignedInIndicator } from 'mastodon/components/not_signed_in_indicator'; | ||||||
| @ -181,7 +184,7 @@ class HomeTimeline extends PureComponent { | |||||||
|           aria-label={intl.formatMessage(showAnnouncements ? messages.hide_announcements : messages.show_announcements)} |           aria-label={intl.formatMessage(showAnnouncements ? messages.hide_announcements : messages.show_announcements)} | ||||||
|           onClick={this.handleToggleAnnouncementsClick} |           onClick={this.handleToggleAnnouncementsClick} | ||||||
|         > |         > | ||||||
|           <IconWithBadge id='bullhorn' count={unreadAnnouncements} /> |           <IconWithBadge id='bullhorn' icon={CampaignIcon} count={unreadAnnouncements} /> | ||||||
|         </button> |         </button> | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| @ -198,6 +201,7 @@ class HomeTimeline extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='home' |           icon='home' | ||||||
|  |           iconComponent={HomeIcon} | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|           title={intl.formatMessage(messages.title)} |           title={intl.formatMessage(messages.title)} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|  | |||||||
| @ -7,6 +7,10 @@ import classNames from 'classnames'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg'; | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg'; | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star.svg'; | ||||||
| import { throttle, escapeRegExp } from 'lodash'; | import { throttle, escapeRegExp } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { openModal, closeModal } from 'mastodon/actions/modal'; | import { openModal, closeModal } from 'mastodon/actions/modal'; | ||||||
| @ -354,22 +358,22 @@ class InteractionModal extends React.PureComponent { | |||||||
| 
 | 
 | ||||||
|     switch(type) { |     switch(type) { | ||||||
|     case 'reply': |     case 'reply': | ||||||
|       icon = <Icon id='reply' />; |       icon = <Icon id='reply' icon={ReplyIcon} />; | ||||||
|       title = <FormattedMessage id='interaction_modal.title.reply' defaultMessage="Reply to {name}'s post" values={{ name }} />; |       title = <FormattedMessage id='interaction_modal.title.reply' defaultMessage="Reply to {name}'s post" values={{ name }} />; | ||||||
|       actionDescription = <FormattedMessage id='interaction_modal.description.reply' defaultMessage='With an account on Mastodon, you can respond to this post.' />; |       actionDescription = <FormattedMessage id='interaction_modal.description.reply' defaultMessage='With an account on Mastodon, you can respond to this post.' />; | ||||||
|       break; |       break; | ||||||
|     case 'reblog': |     case 'reblog': | ||||||
|       icon = <Icon id='retweet' />; |       icon = <Icon id='retweet' icon={RepeatIcon} />; | ||||||
|       title = <FormattedMessage id='interaction_modal.title.reblog' defaultMessage="Boost {name}'s post" values={{ name }} />; |       title = <FormattedMessage id='interaction_modal.title.reblog' defaultMessage="Boost {name}'s post" values={{ name }} />; | ||||||
|       actionDescription = <FormattedMessage id='interaction_modal.description.reblog' defaultMessage='With an account on Mastodon, you can boost this post to share it with your own followers.' />; |       actionDescription = <FormattedMessage id='interaction_modal.description.reblog' defaultMessage='With an account on Mastodon, you can boost this post to share it with your own followers.' />; | ||||||
|       break; |       break; | ||||||
|     case 'favourite': |     case 'favourite': | ||||||
|       icon = <Icon id='star' />; |       icon = <Icon id='star' icon={StarIcon} />; | ||||||
|       title = <FormattedMessage id='interaction_modal.title.favourite' defaultMessage="Favorite {name}'s post" values={{ name }} />; |       title = <FormattedMessage id='interaction_modal.title.favourite' defaultMessage="Favorite {name}'s post" values={{ name }} />; | ||||||
|       actionDescription = <FormattedMessage id='interaction_modal.description.favourite' defaultMessage='With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.' />; |       actionDescription = <FormattedMessage id='interaction_modal.description.favourite' defaultMessage='With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.' />; | ||||||
|       break; |       break; | ||||||
|     case 'follow': |     case 'follow': | ||||||
|       icon = <Icon id='user-plus' />; |       icon = <Icon id='user-plus' icon={PersonAddIcon} />; | ||||||
|       title = <FormattedMessage id='interaction_modal.title.follow' defaultMessage='Follow {name}' values={{ name }} />; |       title = <FormattedMessage id='interaction_modal.title.follow' defaultMessage='Follow {name}' values={{ name }} />; | ||||||
|       actionDescription = <FormattedMessage id='interaction_modal.description.follow' defaultMessage='With an account on Mastodon, you can follow {name} to receive their posts in your home feed.' values={{ name }} />; |       actionDescription = <FormattedMessage id='interaction_modal.description.follow' defaultMessage='With an account on Mastodon, you can follow {name} to receive their posts in your home feed.' values={{ name }} />; | ||||||
|       break; |       break; | ||||||
|  | |||||||
| @ -6,6 +6,8 @@ import { Helmet } from 'react-helmet'; | |||||||
| 
 | 
 | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as InfoIcon } from '@material-symbols/svg-600/outlined/info.svg'; | ||||||
|  | 
 | ||||||
| import Column from 'mastodon/components/column'; | import Column from 'mastodon/components/column'; | ||||||
| import ColumnHeader from 'mastodon/components/column_header'; | import ColumnHeader from 'mastodon/components/column_header'; | ||||||
| 
 | 
 | ||||||
| @ -27,7 +29,8 @@ class KeyboardShortcuts extends ImmutablePureComponent { | |||||||
|       <Column> |       <Column> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           title={intl.formatMessage(messages.heading)} |           title={intl.formatMessage(messages.heading)} | ||||||
|           icon='question' |           icon='info-circle' | ||||||
|  |           iconComponent={InfoIcon} | ||||||
|           multiColumn={multiColumn} |           multiColumn={multiColumn} | ||||||
|         /> |         /> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,6 +6,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg'; | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | import { ReactComponent as ListAltIcon } from '@material-symbols/svg-600/outlined/list_alt.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| import { removeFromListAdder, addToListAdder } from '../../../actions/lists'; | import { removeFromListAdder, addToListAdder } from '../../../actions/lists'; | ||||||
| @ -46,16 +50,16 @@ class List extends ImmutablePureComponent { | |||||||
|     let button; |     let button; | ||||||
| 
 | 
 | ||||||
|     if (added) { |     if (added) { | ||||||
|       button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />; |       button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={onRemove} />; | ||||||
|     } else { |     } else { | ||||||
|       button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />; |       button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={onAdd} />; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className='list'> |       <div className='list'> | ||||||
|         <div className='list__wrapper'> |         <div className='list__wrapper'> | ||||||
|           <div className='list__display-name'> |           <div className='list__display-name'> | ||||||
|             <Icon id='list-ul' className='column-link__icon' fixedWidth /> |             <Icon id='list-ul' icon={ListAltIcon} className='column-link__icon' /> | ||||||
|             {list.get('title')} |             {list.get('title')} | ||||||
|           </div> |           </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -6,6 +6,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg'; | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import { removeFromListEditor, addToListEditor } from '../../../actions/lists'; | import { removeFromListEditor, addToListEditor } from '../../../actions/lists'; | ||||||
| import { Avatar } from '../../../components/avatar'; | import { Avatar } from '../../../components/avatar'; | ||||||
| import { DisplayName } from '../../../components/display_name'; | import { DisplayName } from '../../../components/display_name'; | ||||||
| @ -53,9 +56,9 @@ class Account extends ImmutablePureComponent { | |||||||
|     let button; |     let button; | ||||||
| 
 | 
 | ||||||
|     if (added) { |     if (added) { | ||||||
|       button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />; |       button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={onRemove} />; | ||||||
|     } else { |     } else { | ||||||
|       button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />; |       button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={onAdd} />; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|  | |||||||
| @ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||||
|  | 
 | ||||||
| import { changeListEditorTitle, submitListEditor } from '../../../actions/lists'; | import { changeListEditorTitle, submitListEditor } from '../../../actions/lists'; | ||||||
| import { IconButton } from '../../../components/icon_button'; | import { IconButton } from '../../../components/icon_button'; | ||||||
| 
 | 
 | ||||||
| @ -61,6 +63,7 @@ class ListForm extends PureComponent { | |||||||
|         <IconButton |         <IconButton | ||||||
|           disabled={disabled} |           disabled={disabled} | ||||||
|           icon='check' |           icon='check' | ||||||
|  |           iconComponent={CheckIcon} | ||||||
|           title={title} |           title={title} | ||||||
|           onClick={this.handleClick} |           onClick={this.handleClick} | ||||||
|         /> |         /> | ||||||
|  | |||||||
| @ -7,6 +7,9 @@ import classNames from 'classnames'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CancelIcon } from '@material-symbols/svg-600/outlined/cancel.svg'; | ||||||
|  | import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; | import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; | ||||||
| @ -69,8 +72,8 @@ class Search extends PureComponent { | |||||||
|         </label> |         </label> | ||||||
| 
 | 
 | ||||||
|         <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}> |         <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}> | ||||||
|           <Icon id='search' className={classNames({ active: !hasValue })} /> |           <Icon id='search' icon={SearchIcon} className={classNames({ active: !hasValue })} /> | ||||||
|           <Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} /> |           <Icon id='times-circle' icon={CancelIcon} aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} /> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -9,6 +9,9 @@ import { withRouter } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg'; | ||||||
|  | import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg'; | ||||||
|  | import { ReactComponent as ListAltIcon } from '@material-symbols/svg-600/outlined/list_alt.svg'; | ||||||
| import Toggle from 'react-toggle'; | import Toggle from 'react-toggle'; | ||||||
| 
 | 
 | ||||||
| import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | ||||||
| @ -181,6 +184,7 @@ class ListTimeline extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={title}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={title}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='list-ul' |           icon='list-ul' | ||||||
|  |           iconComponent={ListAltIcon} | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|           title={title} |           title={title} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
| @ -191,11 +195,11 @@ class ListTimeline extends PureComponent { | |||||||
|         > |         > | ||||||
|           <div className='column-settings__row column-header__links'> |           <div className='column-settings__row column-header__links'> | ||||||
|             <button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}> |             <button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}> | ||||||
|               <Icon id='pencil' /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' /> |               <Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' /> | ||||||
|             </button> |             </button> | ||||||
| 
 | 
 | ||||||
|             <button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}> |             <button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}> | ||||||
|               <Icon id='trash' /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' /> |               <Icon id='trash' icon={DeleteIcon} /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' /> | ||||||
|             </button> |             </button> | ||||||
|           </div> |           </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; | |||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { createSelector } from 'reselect'; | import { createSelector } from 'reselect'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as ListAltIcon } from '@material-symbols/svg-600/outlined/list_alt.svg'; | ||||||
|  | 
 | ||||||
| import { fetchLists } from 'mastodon/actions/lists'; | import { fetchLists } from 'mastodon/actions/lists'; | ||||||
| import Column from 'mastodon/components/column'; | import Column from 'mastodon/components/column'; | ||||||
| import ColumnHeader from 'mastodon/components/column_header'; | import ColumnHeader from 'mastodon/components/column_header'; | ||||||
| @ -65,7 +67,7 @@ class Lists extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} label={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' multiColumn={multiColumn} /> |         <ColumnHeader title={intl.formatMessage(messages.heading)} icon='list-ul' iconComponent={ListAltIcon} multiColumn={multiColumn} /> | ||||||
| 
 | 
 | ||||||
|         <NewListForm /> |         <NewListForm /> | ||||||
| 
 | 
 | ||||||
| @ -76,7 +78,7 @@ class Lists extends ImmutablePureComponent { | |||||||
|           bindToDocument={!multiColumn} |           bindToDocument={!multiColumn} | ||||||
|         > |         > | ||||||
|           {lists.map(list => |           {lists.map(list => | ||||||
|             <ColumnLink key={list.get('id')} to={`/lists/${list.get('id')}`} icon='list-ul' text={list.get('title')} />, |             <ColumnLink key={list.get('id')} to={`/lists/${list.get('id')}`} icon='list-ul' iconComponent={ListAltIcon} text={list.get('title')} />, | ||||||
|           )} |           )} | ||||||
|         </ScrollableList> |         </ScrollableList> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as VolumeOffIcon } from '@material-symbols/svg-600/outlined/volume_off.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { fetchMutes, expandMutes } from '../../actions/mutes'; | import { fetchMutes, expandMutes } from '../../actions/mutes'; | ||||||
| @ -61,7 +62,7 @@ class Mutes extends ImmutablePureComponent { | |||||||
|     const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />; |     const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column bindToDocument={!multiColumn} icon='volume-off' heading={intl.formatMessage(messages.heading)}> |       <Column bindToDocument={!multiColumn} icon='volume-off' iconComponent={VolumeOffIcon} heading={intl.formatMessage(messages.heading)}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
|         <ScrollableList |         <ScrollableList | ||||||
|           scrollKey='mutes' |           scrollKey='mutes' | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ import { PureComponent } from 'react'; | |||||||
| 
 | 
 | ||||||
| import { FormattedMessage } from 'react-intl'; | import { FormattedMessage } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as DeleteForeverIcon } from '@material-symbols/svg-600/outlined/delete_forever.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| export default class ClearColumnButton extends PureComponent { | export default class ClearColumnButton extends PureComponent { | ||||||
| @ -13,7 +15,7 @@ export default class ClearColumnButton extends PureComponent { | |||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     return ( |     return ( | ||||||
|       <button className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.props.onClick}><Icon id='eraser' /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button> |       <button className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.props.onClick}><Icon id='eraser' icon={DeleteForeverIcon} /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,6 +3,13 @@ import { PureComponent } from 'react'; | |||||||
| 
 | 
 | ||||||
| import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg'; | ||||||
|  | import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg'; | ||||||
|  | import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg'; | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg'; | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| const tooltips = defineMessages({ | const tooltips = defineMessages({ | ||||||
| @ -66,42 +73,42 @@ class FilterBar extends PureComponent { | |||||||
|           onClick={this.onClick('mention')} |           onClick={this.onClick('mention')} | ||||||
|           title={intl.formatMessage(tooltips.mentions)} |           title={intl.formatMessage(tooltips.mentions)} | ||||||
|         > |         > | ||||||
|           <Icon id='reply-all' fixedWidth /> |           <Icon id='reply-all' icon={ReplyAllIcon} /> | ||||||
|         </button> |         </button> | ||||||
|         <button |         <button | ||||||
|           className={selectedFilter === 'favourite' ? 'active' : ''} |           className={selectedFilter === 'favourite' ? 'active' : ''} | ||||||
|           onClick={this.onClick('favourite')} |           onClick={this.onClick('favourite')} | ||||||
|           title={intl.formatMessage(tooltips.favourites)} |           title={intl.formatMessage(tooltips.favourites)} | ||||||
|         > |         > | ||||||
|           <Icon id='star' fixedWidth /> |           <Icon id='star' icon={StarIcon} /> | ||||||
|         </button> |         </button> | ||||||
|         <button |         <button | ||||||
|           className={selectedFilter === 'reblog' ? 'active' : ''} |           className={selectedFilter === 'reblog' ? 'active' : ''} | ||||||
|           onClick={this.onClick('reblog')} |           onClick={this.onClick('reblog')} | ||||||
|           title={intl.formatMessage(tooltips.boosts)} |           title={intl.formatMessage(tooltips.boosts)} | ||||||
|         > |         > | ||||||
|           <Icon id='retweet' fixedWidth /> |           <Icon id='retweet' icon={RepeatIcon} /> | ||||||
|         </button> |         </button> | ||||||
|         <button |         <button | ||||||
|           className={selectedFilter === 'poll' ? 'active' : ''} |           className={selectedFilter === 'poll' ? 'active' : ''} | ||||||
|           onClick={this.onClick('poll')} |           onClick={this.onClick('poll')} | ||||||
|           title={intl.formatMessage(tooltips.polls)} |           title={intl.formatMessage(tooltips.polls)} | ||||||
|         > |         > | ||||||
|           <Icon id='tasks' fixedWidth /> |           <Icon id='tasks' icon={InsertChartIcon} /> | ||||||
|         </button> |         </button> | ||||||
|         <button |         <button | ||||||
|           className={selectedFilter === 'status' ? 'active' : ''} |           className={selectedFilter === 'status' ? 'active' : ''} | ||||||
|           onClick={this.onClick('status')} |           onClick={this.onClick('status')} | ||||||
|           title={intl.formatMessage(tooltips.statuses)} |           title={intl.formatMessage(tooltips.statuses)} | ||||||
|         > |         > | ||||||
|           <Icon id='home' fixedWidth /> |           <Icon id='home' icon={HomeIcon} /> | ||||||
|         </button> |         </button> | ||||||
|         <button |         <button | ||||||
|           className={selectedFilter === 'follow' ? 'active' : ''} |           className={selectedFilter === 'follow' ? 'active' : ''} | ||||||
|           onClick={this.onClick('follow')} |           onClick={this.onClick('follow')} | ||||||
|           title={intl.formatMessage(tooltips.follows)} |           title={intl.formatMessage(tooltips.follows)} | ||||||
|         > |         > | ||||||
|           <Icon id='user-plus' fixedWidth /> |           <Icon id='user-plus' icon={PersonAddIcon} /> | ||||||
|         </button> |         </button> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -7,6 +7,9 @@ import { Link } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import { Avatar } from 'mastodon/components/avatar'; | import { Avatar } from 'mastodon/components/avatar'; | ||||||
| import { DisplayName } from 'mastodon/components/display_name'; | import { DisplayName } from 'mastodon/components/display_name'; | ||||||
| import { IconButton } from 'mastodon/components/icon_button'; | import { IconButton } from 'mastodon/components/icon_button'; | ||||||
| @ -50,8 +53,8 @@ class FollowRequest extends ImmutablePureComponent { | |||||||
|           </Link> |           </Link> | ||||||
| 
 | 
 | ||||||
|           <div className='account__relationship'> |           <div className='account__relationship'> | ||||||
|             <IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /> |             <IconButton title={intl.formatMessage(messages.authorize)} icon='check' iconComponent={CheckIcon} onClick={onAuthorize} /> | ||||||
|             <IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /> |             <IconButton title={intl.formatMessage(messages.reject)} icon='times' iconComponent={CloseIcon} onClick={onReject} /> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|  | |||||||
| @ -8,6 +8,14 @@ import { Link, withRouter } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg'; | ||||||
|  | import { ReactComponent as FlagIcon } from '@material-symbols/svg-600/outlined/flag-fill.svg'; | ||||||
|  | import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg'; | ||||||
|  | import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg'; | ||||||
|  | import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person-fill.svg'; | ||||||
|  | import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add-fill.svg'; | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg'; | ||||||
| import { HotKeys } from 'react-hotkeys'; | import { HotKeys } from 'react-hotkeys'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -128,9 +136,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-follow focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.follow, { name: account.get('acct') }), notification.get('created_at'))}> |         <div className={classNames('notification notification-follow focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.follow, { name: account.get('acct') }), notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='user-plus' icon={PersonAddIcon} /> | ||||||
|               <Icon id='user-plus' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               <FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} /> |               <FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} /> | ||||||
| @ -150,9 +156,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow_request', defaultMessage: '{name} has requested to follow you' }, { name: account.get('acct') }), notification.get('created_at'))}> |         <div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage({ id: 'notification.follow_request', defaultMessage: '{name} has requested to follow you' }, { name: account.get('acct') }), notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='user' icon={PersonIcon} /> | ||||||
|               <Icon id='user' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               <FormattedMessage id='notification.follow_request' defaultMessage='{name} has requested to follow you' values={{ name: link }} /> |               <FormattedMessage id='notification.follow_request' defaultMessage='{name} has requested to follow you' values={{ name: link }} /> | ||||||
| @ -190,9 +194,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-favourite focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.favourite, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}> |         <div className={classNames('notification notification-favourite focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.favourite, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='star' icon={StarIcon} className='star-icon' /> | ||||||
|               <Icon id='star' className='star-icon' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               <FormattedMessage id='notification.favourite' defaultMessage='{name} favorited your status' values={{ name: link }} /> |               <FormattedMessage id='notification.favourite' defaultMessage='{name} favorited your status' values={{ name: link }} /> | ||||||
| @ -222,9 +224,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-reblog focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}> |         <div className={classNames('notification notification-reblog focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.reblog, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='retweet' icon={RepeatIcon} /> | ||||||
|               <Icon id='retweet' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               <FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={{ name: link }} /> |               <FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={{ name: link }} /> | ||||||
| @ -258,9 +258,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-status focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.status, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}> |         <div className={classNames('notification notification-status focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.status, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='home' icon={HomeIcon} /> | ||||||
|               <Icon id='home' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               <FormattedMessage id='notification.status' defaultMessage='{name} just posted' values={{ name: link }} /> |               <FormattedMessage id='notification.status' defaultMessage='{name} just posted' values={{ name: link }} /> | ||||||
| @ -295,9 +293,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-update focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.update, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}> |         <div className={classNames('notification notification-update focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.update, { name: notification.getIn(['account', 'acct']) }), notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='pencil' icon={EditIcon} /> | ||||||
|               <Icon id='pencil' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               <FormattedMessage id='notification.update' defaultMessage='{name} edited a post' values={{ name: link }} /> |               <FormattedMessage id='notification.update' defaultMessage='{name} edited a post' values={{ name: link }} /> | ||||||
| @ -334,9 +330,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-poll focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, message, notification.get('created_at'))}> |         <div className={classNames('notification notification-poll focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, message, notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='tasks' icon={InsertChartIcon} /> | ||||||
|               <Icon id='tasks' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               {ownPoll ? ( |               {ownPoll ? ( | ||||||
| @ -371,9 +365,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminSignUp, { name: account.get('acct') }), notification.get('created_at'))}> |         <div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminSignUp, { name: account.get('acct') }), notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='user-plus' icon={PersonAddIcon} /> | ||||||
|               <Icon id='user-plus' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               <FormattedMessage id='notification.admin.sign_up' defaultMessage='{name} signed up' values={{ name: link }} /> |               <FormattedMessage id='notification.admin.sign_up' defaultMessage='{name} signed up' values={{ name: link }} /> | ||||||
| @ -401,9 +393,7 @@ class Notification extends ImmutablePureComponent { | |||||||
|       <HotKeys handlers={this.getHandlers()}> |       <HotKeys handlers={this.getHandlers()}> | ||||||
|         <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminReport, { name: account.get('acct'), target: notification.getIn(['report', 'target_account', 'acct']) }), notification.get('created_at'))}> |         <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex={0} aria-label={notificationForScreenReader(intl, intl.formatMessage(messages.adminReport, { name: account.get('acct'), target: notification.getIn(['report', 'target_account', 'acct']) }), notification.get('created_at'))}> | ||||||
|           <div className='notification__message'> |           <div className='notification__message'> | ||||||
|             <div className='notification__favourite-icon-wrapper'> |             <Icon id='flag' icon={FlagIcon} /> | ||||||
|               <Icon id='flag' fixedWidth /> |  | ||||||
|             </div> |  | ||||||
| 
 | 
 | ||||||
|             <span title={notification.get('created_at')}> |             <span title={notification.get('created_at')}> | ||||||
|               <FormattedMessage id='notification.admin.report' defaultMessage='{name} reported {target}' values={{ name: link, target: targetLink }} /> |               <FormattedMessage id='notification.admin.report' defaultMessage='{name} reported {target}' values={{ name: link, target: targetLink }} /> | ||||||
|  | |||||||
| @ -5,6 +5,9 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | import { ReactComponent as TuneIcon } from '@material-symbols/svg-600/outlined/tune.svg'; | ||||||
|  | 
 | ||||||
| import { requestBrowserPermission } from 'mastodon/actions/notifications'; | import { requestBrowserPermission } from 'mastodon/actions/notifications'; | ||||||
| import { changeSetting } from 'mastodon/actions/settings'; | import { changeSetting } from 'mastodon/actions/settings'; | ||||||
| import { Button } from 'mastodon/components/button'; | import { Button } from 'mastodon/components/button'; | ||||||
| @ -36,11 +39,11 @@ class NotificationsPermissionBanner extends PureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className='notifications-permission-banner'> |       <div className='notifications-permission-banner'> | ||||||
|         <div className='notifications-permission-banner__close'> |         <div className='notifications-permission-banner__close'> | ||||||
|           <IconButton icon='times' onClick={this.handleClose} title={intl.formatMessage(messages.close)} /> |           <IconButton icon='times' iconComponent={CloseIcon} onClick={this.handleClose} title={intl.formatMessage(messages.close)} /> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <h2><FormattedMessage id='notifications_permission_banner.title' defaultMessage='Never miss a thing' /></h2> |         <h2><FormattedMessage id='notifications_permission_banner.title' defaultMessage='Never miss a thing' /></h2> | ||||||
|         <p><FormattedMessage id='notifications_permission_banner.how_to_control' defaultMessage="To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled." values={{ icon: <Icon id='sliders' /> }} /></p> |         <p><FormattedMessage id='notifications_permission_banner.how_to_control' defaultMessage="To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled." values={{ icon: <Icon id='sliders' icon={TuneIcon} /> }} /></p> | ||||||
|         <Button onClick={this.handleClick}><FormattedMessage id='notifications_permission_banner.enable' defaultMessage='Enable desktop notifications' /></Button> |         <Button onClick={this.handleClick}><FormattedMessage id='notifications_permission_banner.enable' defaultMessage='Enable desktop notifications' /></Button> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -10,6 +10,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { createSelector } from 'reselect'; | import { createSelector } from 'reselect'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as DoneAllIcon } from '@material-symbols/svg-600/outlined/done_all.svg'; | ||||||
|  | import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications-fill.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { compareId } from 'mastodon/compare_id'; | import { compareId } from 'mastodon/compare_id'; | ||||||
| @ -260,7 +262,7 @@ class Notifications extends PureComponent { | |||||||
|           onClick={this.handleMarkAsRead} |           onClick={this.handleMarkAsRead} | ||||||
|           className='column-header__button' |           className='column-header__button' | ||||||
|         > |         > | ||||||
|           <Icon id='check' /> |           <Icon id='done-all' icon={DoneAllIcon} /> | ||||||
|         </button> |         </button> | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
| @ -269,6 +271,7 @@ class Notifications extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setColumnRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setColumnRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='bell' |           icon='bell' | ||||||
|  |           iconComponent={NotificationsIcon} | ||||||
|           active={isUnread} |           active={isUnread} | ||||||
|           title={intl.formatMessage(messages.title)} |           title={intl.formatMessage(messages.title)} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|  | |||||||
| @ -1,7 +0,0 @@ | |||||||
| const ArrowSmallRight = () => ( |  | ||||||
|   <svg xmlns='http://www.w3.org/2000/svg' viewBox='0 0 20 20' fill='currentColor'> |  | ||||||
|     <path fillRule='evenodd' d='M5 10a.75.75 0 01.75-.75h6.638L10.23 7.29a.75.75 0 111.04-1.08l3.5 3.25a.75.75 0 010 1.08l-3.5 3.25a.75.75 0 11-1.04-1.08l2.158-1.96H5.75A.75.75 0 015 10z' clipRule='evenodd' /> |  | ||||||
|   </svg> |  | ||||||
| ); |  | ||||||
| 
 |  | ||||||
| export default ArrowSmallRight; |  | ||||||
| @ -3,7 +3,9 @@ import { Fragment } from 'react'; | |||||||
| 
 | 
 | ||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| 
 | 
 | ||||||
| import { Check } from 'mastodon/components/check'; | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg'; | ||||||
|  | 
 | ||||||
|  | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| const ProgressIndicator = ({ steps, completed }) => ( | const ProgressIndicator = ({ steps, completed }) => ( | ||||||
|   <div className='onboarding__progress-indicator'> |   <div className='onboarding__progress-indicator'> | ||||||
| @ -12,7 +14,7 @@ const ProgressIndicator = ({ steps, completed }) => ( | |||||||
|         {i > 0 && <div className={classNames('onboarding__progress-indicator__line', { active: completed > i })} />} |         {i > 0 && <div className={classNames('onboarding__progress-indicator__line', { active: completed > i })} />} | ||||||
| 
 | 
 | ||||||
|         <div className={classNames('onboarding__progress-indicator__step', { active: completed > i })}> |         <div className={classNames('onboarding__progress-indicator__step', { active: completed > i })}> | ||||||
|           {completed > i && <Check />} |           {completed > i && <Icon icon={CheckIcon} />} | ||||||
|         </div> |         </div> | ||||||
|       </Fragment> |       </Fragment> | ||||||
|     ))} |     ))} | ||||||
|  | |||||||
| @ -1,15 +1,15 @@ | |||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| 
 | 
 | ||||||
| import { Check } from 'mastodon/components/check'; | import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg'; | ||||||
|  | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg'; | ||||||
|  | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| import ArrowSmallRight from './arrow_small_right'; | const Step = ({ label, description, icon, iconComponent, completed, onClick, href }) => { | ||||||
| 
 |  | ||||||
| const Step = ({ label, description, icon, completed, onClick, href }) => { |  | ||||||
|   const content = ( |   const content = ( | ||||||
|     <> |     <> | ||||||
|       <div className='onboarding__steps__item__icon'> |       <div className='onboarding__steps__item__icon'> | ||||||
|         <Icon id={icon} /> |         <Icon id={icon} icon={iconComponent} /> | ||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <div className='onboarding__steps__item__description'> |       <div className='onboarding__steps__item__description'> | ||||||
| @ -18,7 +18,7 @@ const Step = ({ label, description, icon, completed, onClick, href }) => { | |||||||
|       </div> |       </div> | ||||||
| 
 | 
 | ||||||
|       <div className={completed ? 'onboarding__steps__item__progress' : 'onboarding__steps__item__go'}> |       <div className={completed ? 'onboarding__steps__item__progress' : 'onboarding__steps__item__go'}> | ||||||
|         {completed ? <Check /> : <ArrowSmallRight />} |         {completed ? <Icon icon={CheckIcon} /> : <Icon icon={ArrowRightAltIcon} />} | ||||||
|       </div> |       </div> | ||||||
|     </> |     </> | ||||||
|   ); |   ); | ||||||
| @ -42,6 +42,7 @@ Step.propTypes = { | |||||||
|   label: PropTypes.node, |   label: PropTypes.node, | ||||||
|   description: PropTypes.node, |   description: PropTypes.node, | ||||||
|   icon: PropTypes.string, |   icon: PropTypes.string, | ||||||
|  |   iconComponent: PropTypes.func, | ||||||
|   completed: PropTypes.bool, |   completed: PropTypes.bool, | ||||||
|   href: PropTypes.string, |   href: PropTypes.string, | ||||||
|   onClick: PropTypes.func, |   onClick: PropTypes.func, | ||||||
|  | |||||||
| @ -9,19 +9,24 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AccountCircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg'; | ||||||
|  | import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg'; | ||||||
|  | import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg'; | ||||||
|  | import { ReactComponent as EditNoteIcon } from '@material-symbols/svg-600/outlined/edit_note.svg'; | ||||||
|  | import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import illustration from 'mastodon/../images/elephant_ui_conversation.svg'; | import illustration from 'mastodon/../images/elephant_ui_conversation.svg'; | ||||||
| import { fetchAccount } from 'mastodon/actions/accounts'; | import { fetchAccount } from 'mastodon/actions/accounts'; | ||||||
| import { focusCompose } from 'mastodon/actions/compose'; | import { focusCompose } from 'mastodon/actions/compose'; | ||||||
| import { closeOnboarding } from 'mastodon/actions/onboarding'; | import { closeOnboarding } from 'mastodon/actions/onboarding'; | ||||||
|  | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import Column from 'mastodon/features/ui/components/column'; | import Column from 'mastodon/features/ui/components/column'; | ||||||
| import { me } from 'mastodon/initial_state'; | import { me } from 'mastodon/initial_state'; | ||||||
| import { makeGetAccount } from 'mastodon/selectors'; | import { makeGetAccount } from 'mastodon/selectors'; | ||||||
| import { assetHost } from 'mastodon/utils/config'; | import { assetHost } from 'mastodon/utils/config'; | ||||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
| import ArrowSmallRight from './components/arrow_small_right'; |  | ||||||
| import Step from './components/step'; | import Step from './components/step'; | ||||||
| import Follows from './follows'; | import Follows from './follows'; | ||||||
| import Share from './share'; | import Share from './share'; | ||||||
| @ -115,10 +120,10 @@ class Onboarding extends ImmutablePureComponent { | |||||||
|           </div> |           </div> | ||||||
| 
 | 
 | ||||||
|           <div className='onboarding__steps'> |           <div className='onboarding__steps'> | ||||||
|             <Step onClick={this.handleProfileClick} href='/settings/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} /> |             <Step onClick={this.handleProfileClick} href='/settings/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' iconComponent={AccountCircleIcon} label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} /> | ||||||
|             <Step onClick={this.handleFollowClick} completed={(account.get('following_count') * 1) >= 7} icon='user-plus' label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} /> |             <Step onClick={this.handleFollowClick} completed={(account.get('following_count') * 1) >= 7} icon='user-plus' iconComponent={PersonAddIcon} label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} /> | ||||||
|             <Step onClick={this.handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' values={{ emoji: <img className='emojione' alt='🐘' src={`${assetHost}/emoji/1f418.svg`} /> }} />} /> |             <Step onClick={this.handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' iconComponent={EditNoteIcon} label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' values={{ emoji: <img className='emojione' alt='🐘' src={`${assetHost}/emoji/1f418.svg`} /> }} />} /> | ||||||
|             <Step onClick={this.handleShareClick} completed={shareClicked} icon='copy' label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} /> |             <Step onClick={this.handleShareClick} completed={shareClicked} icon='copy' iconComponent={ContentCopyIcon} label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} /> | ||||||
|           </div> |           </div> | ||||||
| 
 | 
 | ||||||
|           <p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p> |           <p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p> | ||||||
| @ -126,12 +131,12 @@ class Onboarding extends ImmutablePureComponent { | |||||||
|           <div className='onboarding__links'> |           <div className='onboarding__links'> | ||||||
|             <Link to='/explore' className='onboarding__link'> |             <Link to='/explore' className='onboarding__link'> | ||||||
|               <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> |               <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> | ||||||
|               <ArrowSmallRight /> |               <Icon icon={ArrowRightAltIcon} /> | ||||||
|             </Link> |             </Link> | ||||||
| 
 | 
 | ||||||
|             <Link to='/home' className='onboarding__link'> |             <Link to='/home' className='onboarding__link'> | ||||||
|               <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> |               <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> | ||||||
|               <ArrowSmallRight /> |               <Icon icon={ArrowRightAltIcon} /> | ||||||
|             </Link> |             </Link> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
|  | |||||||
| @ -9,6 +9,8 @@ import { Link } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg'; | ||||||
|  | import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg'; | ||||||
| import SwipeableViews from 'react-swipeable-views'; | import SwipeableViews from 'react-swipeable-views'; | ||||||
| 
 | 
 | ||||||
| import Column from 'mastodon/components/column'; | import Column from 'mastodon/components/column'; | ||||||
| @ -16,8 +18,6 @@ import ColumnBackButton from 'mastodon/components/column_back_button'; | |||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import { me, domain } from 'mastodon/initial_state'; | import { me, domain } from 'mastodon/initial_state'; | ||||||
| 
 | 
 | ||||||
| import ArrowSmallRight from './components/arrow_small_right'; |  | ||||||
| 
 |  | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
|   shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' }, |   shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' }, | ||||||
| }); | }); | ||||||
| @ -79,7 +79,7 @@ class CopyPasteText extends PureComponent { | |||||||
|         <textarea readOnly value={value} ref={this.setRef} onClick={this.handleInputClick} onFocus={this.handleFocus} onBlur={this.handleBlur} /> |         <textarea readOnly value={value} ref={this.setRef} onClick={this.handleInputClick} onFocus={this.handleFocus} onBlur={this.handleBlur} /> | ||||||
| 
 | 
 | ||||||
|         <button className='button' onClick={this.handleButtonClick}> |         <button className='button' onClick={this.handleButtonClick}> | ||||||
|           <Icon id='copy' /> {copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : <FormattedMessage id='copypaste.copy_to_clipboard' defaultMessage='Copy to clipboard' />} |           <Icon id='copy' icon={ContentCopyIcon} /> {copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : <FormattedMessage id='copypaste.copy_to_clipboard' defaultMessage='Copy to clipboard' />} | ||||||
|         </button> |         </button> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
| @ -178,12 +178,12 @@ class Share extends PureComponent { | |||||||
|           <div className='onboarding__links'> |           <div className='onboarding__links'> | ||||||
|             <Link to='/home' className='onboarding__link'> |             <Link to='/home' className='onboarding__link'> | ||||||
|               <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> |               <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> | ||||||
|               <ArrowSmallRight /> |               <Icon icon={ArrowRightAltIcon} /> | ||||||
|             </Link> |             </Link> | ||||||
| 
 | 
 | ||||||
|             <Link to='/explore' className='onboarding__link'> |             <Link to='/explore' className='onboarding__link'> | ||||||
|               <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> |               <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> | ||||||
|               <ArrowSmallRight /> |               <Icon icon={ArrowRightAltIcon} /> | ||||||
|             </Link> |             </Link> | ||||||
|           </div> |           </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -9,6 +9,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as OpenInNewIcon } from '@material-symbols/svg-600/outlined/open_in_new.svg'; | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg'; | ||||||
|  | import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg'; | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star.svg'; | ||||||
|  | 
 | ||||||
| import { initBoostModal } from 'mastodon/actions/boosts'; | import { initBoostModal } from 'mastodon/actions/boosts'; | ||||||
| import { replyCompose } from 'mastodon/actions/compose'; | import { replyCompose } from 'mastodon/actions/compose'; | ||||||
| import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions'; | import { reblog, favourite, unreblog, unfavourite } from 'mastodon/actions/interactions'; | ||||||
| @ -169,13 +175,15 @@ class Footer extends ImmutablePureComponent { | |||||||
|     const publicStatus  = ['public', 'unlisted'].includes(status.get('visibility')); |     const publicStatus  = ['public', 'unlisted'].includes(status.get('visibility')); | ||||||
|     const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; |     const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; | ||||||
| 
 | 
 | ||||||
|     let replyIcon, replyTitle; |     let replyIcon, replyIconComponent, replyTitle; | ||||||
| 
 | 
 | ||||||
|     if (status.get('in_reply_to_id', null) === null) { |     if (status.get('in_reply_to_id', null) === null) { | ||||||
|       replyIcon = 'reply'; |       replyIcon = 'reply'; | ||||||
|  |       replyIconComponent = RepeatIcon; | ||||||
|       replyTitle = intl.formatMessage(messages.reply); |       replyTitle = intl.formatMessage(messages.reply); | ||||||
|     } else { |     } else { | ||||||
|       replyIcon = 'reply-all'; |       replyIcon = 'reply-all'; | ||||||
|  |       replyIconComponent = ReplyAllIcon; | ||||||
|       replyTitle = intl.formatMessage(messages.replyAll); |       replyTitle = intl.formatMessage(messages.replyAll); | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
| @ -193,10 +201,10 @@ class Footer extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className='picture-in-picture__footer'> |       <div className='picture-in-picture__footer'> | ||||||
|         <IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} counter={status.get('replies_count')} /> |         <IconButton className='status__action-bar-button' title={replyTitle} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent} onClick={this.handleReplyClick} counter={status.get('replies_count')} /> | ||||||
|         <IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate}  active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={status.get('reblogs_count')} /> |         <IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate}  active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} /> | ||||||
|         <IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} /> |         <IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={StarIcon} onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} /> | ||||||
|         {withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' onClick={this.handleOpenClick} href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} />} |         {withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} />} | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import { Avatar } from 'mastodon/components/avatar'; | import { Avatar } from 'mastodon/components/avatar'; | ||||||
| import { DisplayName } from 'mastodon/components/display_name'; | import { DisplayName } from 'mastodon/components/display_name'; | ||||||
| import { IconButton } from 'mastodon/components/icon_button'; | import { IconButton } from 'mastodon/components/icon_button'; | ||||||
| @ -40,7 +42,7 @@ class Header extends ImmutablePureComponent { | |||||||
|           <DisplayName account={account} /> |           <DisplayName account={account} /> | ||||||
|         </Link> |         </Link> | ||||||
| 
 | 
 | ||||||
|         <IconButton icon='times' onClick={onClose} title={intl.formatMessage(messages.close)} /> |         <IconButton icon='times' iconComponent={CloseIcon} onClick={onClose} title={intl.formatMessage(messages.close)} /> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|   } |   } | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as PushPinIcon } from '@material-symbols/svg-600/outlined/push_pin.svg'; | ||||||
|  | 
 | ||||||
| import { getStatusList } from 'mastodon/selectors'; | import { getStatusList } from 'mastodon/selectors'; | ||||||
| 
 | 
 | ||||||
| import { fetchPinnedStatuses } from '../../actions/pin_statuses'; | import { fetchPinnedStatuses } from '../../actions/pin_statuses'; | ||||||
| @ -50,7 +52,7 @@ class PinnedStatuses extends ImmutablePureComponent { | |||||||
|     const { intl, statusIds, hasMore, multiColumn } = this.props; |     const { intl, statusIds, hasMore, multiColumn } = this.props; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <Column bindToDocument={!multiColumn} icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}> |       <Column bindToDocument={!multiColumn} icon='thumb-tack' iconComponent={PushPinIcon} heading={intl.formatMessage(messages.heading)} ref={this.setRef}> | ||||||
|         <ColumnBackButtonSlim /> |         <ColumnBackButtonSlim /> | ||||||
|         <StatusList |         <StatusList | ||||||
|           statusIds={statusIds} |           statusIds={statusIds} | ||||||
|  | |||||||
| @ -7,6 +7,8 @@ import { Helmet } from 'react-helmet'; | |||||||
| 
 | 
 | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg'; | ||||||
|  | 
 | ||||||
| import { DismissableBanner } from 'mastodon/components/dismissable_banner'; | import { DismissableBanner } from 'mastodon/components/dismissable_banner'; | ||||||
| import { domain } from 'mastodon/initial_state'; | import { domain } from 'mastodon/initial_state'; | ||||||
| 
 | 
 | ||||||
| @ -131,6 +133,7 @@ class PublicTimeline extends PureComponent { | |||||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> |       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||||
|         <ColumnHeader |         <ColumnHeader | ||||||
|           icon='globe' |           icon='globe' | ||||||
|  |           iconComponent={PublicIcon} | ||||||
|           active={hasUnread} |           active={hasUnread} | ||||||
|           title={intl.formatMessage(messages.title)} |           title={intl.formatMessage(messages.title)} | ||||||
|           onPin={this.handlePin} |           onPin={this.handlePin} | ||||||
|  | |||||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as RefreshIcon } from '@material-symbols/svg-600/outlined/refresh.svg'; | ||||||
| import { debounce } from 'lodash'; | import { debounce } from 'lodash'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -74,7 +75,7 @@ class Reblogs extends ImmutablePureComponent { | |||||||
|           showBackButton |           showBackButton | ||||||
|           multiColumn={multiColumn} |           multiColumn={multiColumn} | ||||||
|           extraButton={( |           extraButton={( | ||||||
|             <button type='button' className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button> |             <button type='button' className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' icon={RefreshIcon} /></button> | ||||||
|           )} |           )} | ||||||
|         /> |         /> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -3,7 +3,9 @@ import { PureComponent } from 'react'; | |||||||
| 
 | 
 | ||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| 
 | 
 | ||||||
| import { Check } from 'mastodon/components/check'; | import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg'; | ||||||
|  | 
 | ||||||
|  | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| export default class Option extends PureComponent { | export default class Option extends PureComponent { | ||||||
| 
 | 
 | ||||||
| @ -47,7 +49,7 @@ export default class Option extends PureComponent { | |||||||
|           onKeyPress={this.handleKeyPress} |           onKeyPress={this.handleKeyPress} | ||||||
|           aria-checked={checked} |           aria-checked={checked} | ||||||
|           aria-label={label} |           aria-label={label} | ||||||
|         >{checked && <Check />}</span> |         >{checked && <Icon icon={CheckIcon} />}</span> | ||||||
| 
 | 
 | ||||||
|         {labelComponent ? labelComponent : ( |         {labelComponent ? labelComponent : ( | ||||||
|           <span className='poll__option__text'> |           <span className='poll__option__text'> | ||||||
|  | |||||||
| @ -1,26 +1,17 @@ | |||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| import { PureComponent } from 'react'; | import { PureComponent } from 'react'; | ||||||
| 
 | 
 | ||||||
| import { injectIntl, defineMessages } from 'react-intl'; |  | ||||||
| 
 |  | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| 
 | 
 | ||||||
| import { Avatar } from 'mastodon/components/avatar'; | import { Avatar } from 'mastodon/components/avatar'; | ||||||
| import { DisplayName } from 'mastodon/components/display_name'; | import { DisplayName } from 'mastodon/components/display_name'; | ||||||
| import { Icon }  from 'mastodon/components/icon'; |  | ||||||
| import MediaAttachments from 'mastodon/components/media_attachments'; | import MediaAttachments from 'mastodon/components/media_attachments'; | ||||||
| import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; | import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; | ||||||
| import StatusContent from 'mastodon/components/status_content'; | import StatusContent from 'mastodon/components/status_content'; | ||||||
|  | import { VisibilityIcon } from 'mastodon/components/visibility_icon'; | ||||||
| 
 | 
 | ||||||
| import Option from './option'; | import Option from './option'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ |  | ||||||
|   public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, |  | ||||||
|   unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, |  | ||||||
|   private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' }, |  | ||||||
|   direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' }, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| class StatusCheckBox extends PureComponent { | class StatusCheckBox extends PureComponent { | ||||||
| 
 | 
 | ||||||
|   static propTypes = { |   static propTypes = { | ||||||
| @ -37,21 +28,12 @@ class StatusCheckBox extends PureComponent { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { status, checked, intl } = this.props; |     const { status, checked } = this.props; | ||||||
| 
 | 
 | ||||||
|     if (status.get('reblog')) { |     if (status.get('reblog')) { | ||||||
|       return null; |       return null; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const visibilityIconInfo = { |  | ||||||
|       'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) }, |  | ||||||
|       'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) }, |  | ||||||
|       'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) }, |  | ||||||
|       'direct': { icon: 'at', text: intl.formatMessage(messages.direct_short) }, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const visibilityIcon = visibilityIconInfo[status.get('visibility')]; |  | ||||||
| 
 |  | ||||||
|     const labelComponent = ( |     const labelComponent = ( | ||||||
|       <div className='status-check-box__status poll__option__text'> |       <div className='status-check-box__status poll__option__text'> | ||||||
|         <div className='detailed-status__display-name'> |         <div className='detailed-status__display-name'> | ||||||
| @ -60,7 +42,7 @@ class StatusCheckBox extends PureComponent { | |||||||
|           </div> |           </div> | ||||||
| 
 | 
 | ||||||
|           <div> |           <div> | ||||||
|             <DisplayName account={status.get('account')} /> · <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span> <RelativeTimestamp timestamp={status.get('created_at')} /> |             <DisplayName account={status.get('account')} /> · <span className='status__visibility-icon'><VisibilityIcon visibility={status.get('visibility')} /></span> <RelativeTimestamp timestamp={status.get('created_at')} /> | ||||||
|           </div> |           </div> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
| @ -84,4 +66,4 @@ class StatusCheckBox extends PureComponent { | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default injectIntl(StatusCheckBox); | export default StatusCheckBox; | ||||||
|  | |||||||
| @ -9,6 +9,15 @@ import { withRouter } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as BookmarkIcon } from '@material-symbols/svg-600/outlined/bookmark-fill.svg'; | ||||||
|  | import { ReactComponent as BookmarkBorderIcon } from '@material-symbols/svg-600/outlined/bookmark.svg'; | ||||||
|  | import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg'; | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg'; | ||||||
|  | import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg'; | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg'; | ||||||
|  | import { ReactComponent as StarBorderIcon } from '@material-symbols/svg-600/outlined/star.svg'; | ||||||
|  | 
 | ||||||
| import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; | import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'mastodon/permissions'; | ||||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
| @ -270,10 +279,13 @@ class ActionBar extends PureComponent { | |||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     let replyIcon; |     let replyIcon; | ||||||
|  |     let replyIconComponent; | ||||||
|     if (status.get('in_reply_to_id', null) === null) { |     if (status.get('in_reply_to_id', null) === null) { | ||||||
|       replyIcon = 'reply'; |       replyIcon = 'reply'; | ||||||
|  |       replyIconComponent = ReplyIcon; | ||||||
|     } else { |     } else { | ||||||
|       replyIcon = 'reply-all'; |       replyIcon = 'reply-all'; | ||||||
|  |       replyIconComponent = ReplyAllIcon; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; |     const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; | ||||||
| @ -291,13 +303,13 @@ class ActionBar extends PureComponent { | |||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <div className='detailed-status__action-bar'> |       <div className='detailed-status__action-bar'> | ||||||
|         <div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} onClick={this.handleReplyClick} /></div> |         <div className='detailed-status__button'><IconButton title={intl.formatMessage(messages.reply)} icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} iconComponent={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? ReplyIcon : replyIconComponent}  onClick={this.handleReplyClick} /></div> | ||||||
|         <div className='detailed-status__button'><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} /></div> |         <div className='detailed-status__button'><IconButton className={classNames({ reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} /></div> | ||||||
|         <div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} /></div> |         <div className='detailed-status__button'><IconButton className='star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} /></div> | ||||||
|         <div className='detailed-status__button'><IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /></div> |         <div className='detailed-status__button'><IconButton className='bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /></div> | ||||||
| 
 | 
 | ||||||
|         <div className='detailed-status__action-bar-dropdown'> |         <div className='detailed-status__action-bar-dropdown'> | ||||||
|           <DropdownMenuContainer size={18} icon='ellipsis-h' status={status} items={menu} direction='left' title={intl.formatMessage(messages.more)} /> |           <DropdownMenuContainer icon='ellipsis-h' iconComponent={MoreHorizIcon} status={status} items={menu} direction='left' title={intl.formatMessage(messages.more)} /> | ||||||
|         </div> |         </div> | ||||||
|       </div> |       </div> | ||||||
|     ); |     ); | ||||||
|  | |||||||
| @ -10,6 +10,10 @@ import classNames from 'classnames'; | |||||||
| import Immutable from 'immutable'; | import Immutable from 'immutable'; | ||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as DescriptionIcon } from '@material-symbols/svg-600/outlined/description.svg'; | ||||||
|  | import { ReactComponent as OpenInNewIcon } from '@material-symbols/svg-600/outlined/open_in_new.svg'; | ||||||
|  | import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow.svg'; | ||||||
|  | 
 | ||||||
| import { Blurhash } from 'mastodon/components/blurhash'; | import { Blurhash } from 'mastodon/components/blurhash'; | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; | import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; | ||||||
| @ -197,8 +201,8 @@ export default class Card extends PureComponent { | |||||||
|             {revealed ? ( |             {revealed ? ( | ||||||
|               <div className='status-card__actions' onClick={this.handleEmbedClick} role='none'> |               <div className='status-card__actions' onClick={this.handleEmbedClick} role='none'> | ||||||
|                 <div> |                 <div> | ||||||
|                   <button type='button' onClick={this.handleEmbedClick}><Icon id='play' /></button> |                   <button type='button' onClick={this.handleEmbedClick}><Icon id='play' icon={PlayArrowIcon} /></button> | ||||||
|                   <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a> |                   <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' icon={OpenInNewIcon} /></a> | ||||||
|                 </div> |                 </div> | ||||||
|               </div> |               </div> | ||||||
|             ) : spoilerButton} |             ) : spoilerButton} | ||||||
| @ -222,7 +226,7 @@ export default class Card extends PureComponent { | |||||||
|     } else { |     } else { | ||||||
|       embed = ( |       embed = ( | ||||||
|         <div className='status-card__image'> |         <div className='status-card__image'> | ||||||
|           <Icon id='file-text' /> |           <Icon id='file-text' icon={DescriptionIcon} /> | ||||||
|         </div> |         </div> | ||||||
|       ); |       ); | ||||||
|     } |     } | ||||||
|  | |||||||
| @ -1,6 +1,6 @@ | |||||||
| import PropTypes from 'prop-types'; | import PropTypes from 'prop-types'; | ||||||
| 
 | 
 | ||||||
| import { injectIntl, defineMessages, FormattedDate, FormattedMessage } from 'react-intl'; | import { FormattedDate, FormattedMessage } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
| import classNames from 'classnames'; | import classNames from 'classnames'; | ||||||
| import { Link, withRouter } from 'react-router-dom'; | import { Link, withRouter } from 'react-router-dom'; | ||||||
| @ -8,11 +8,16 @@ import { Link, withRouter } from 'react-router-dom'; | |||||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as AlternateEmailIcon } from '@material-symbols/svg-600/outlined/alternate_email.svg'; | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg'; | ||||||
|  | 
 | ||||||
| import { AnimatedNumber } from 'mastodon/components/animated_number'; | import { AnimatedNumber } from 'mastodon/components/animated_number'; | ||||||
| import EditedTimestamp from 'mastodon/components/edited_timestamp'; | import EditedTimestamp from 'mastodon/components/edited_timestamp'; | ||||||
| import { getHashtagBarForStatus } from 'mastodon/components/hashtag_bar'; | import { getHashtagBarForStatus } from 'mastodon/components/hashtag_bar'; | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder'; | import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder'; | ||||||
|  | import { VisibilityIcon } from 'mastodon/components/visibility_icon'; | ||||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
| import { Avatar } from '../../../components/avatar'; | import { Avatar } from '../../../components/avatar'; | ||||||
| @ -25,13 +30,6 @@ import Video from '../../video'; | |||||||
| 
 | 
 | ||||||
| import Card from './card'; | import Card from './card'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ |  | ||||||
|   public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, |  | ||||||
|   unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, |  | ||||||
|   private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' }, |  | ||||||
|   direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' }, |  | ||||||
| }); |  | ||||||
| 
 |  | ||||||
| class DetailedStatus extends ImmutablePureComponent { | class DetailedStatus extends ImmutablePureComponent { | ||||||
| 
 | 
 | ||||||
|   static propTypes = { |   static propTypes = { | ||||||
| @ -137,7 +135,7 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
|   render () { |   render () { | ||||||
|     const status = this._properStatus(); |     const status = this._properStatus(); | ||||||
|     const outerStyle = { boxSizing: 'border-box' }; |     const outerStyle = { boxSizing: 'border-box' }; | ||||||
|     const { intl, compact, pictureInPicture } = this.props; |     const { compact, pictureInPicture } = this.props; | ||||||
| 
 | 
 | ||||||
|     if (!status) { |     if (!status) { | ||||||
|       return null; |       return null; | ||||||
| @ -146,7 +144,8 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
|     let media           = ''; |     let media           = ''; | ||||||
|     let applicationLink = ''; |     let applicationLink = ''; | ||||||
|     let reblogLink = ''; |     let reblogLink = ''; | ||||||
|     let reblogIcon = 'retweet'; |     const reblogIcon = 'retweet'; | ||||||
|  |     const reblogIconComponent = RepeatIcon; | ||||||
|     let favouriteLink = ''; |     let favouriteLink = ''; | ||||||
|     let edited = ''; |     let edited = ''; | ||||||
| 
 | 
 | ||||||
| @ -223,15 +222,7 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
|       applicationLink = <> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></>; |       applicationLink = <> · <a className='detailed-status__application' href={status.getIn(['application', 'website'])} target='_blank' rel='noopener noreferrer'>{status.getIn(['application', 'name'])}</a></>; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const visibilityIconInfo = { |     const visibilityLink = <> · <VisibilityIcon visibility={status.get('visibility')} /></>; | ||||||
|       'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) }, |  | ||||||
|       'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) }, |  | ||||||
|       'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) }, |  | ||||||
|       'direct': { icon: 'at', text: intl.formatMessage(messages.direct_short) }, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const visibilityIcon = visibilityIconInfo[status.get('visibility')]; |  | ||||||
|     const visibilityLink = <> · <Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></>; |  | ||||||
| 
 | 
 | ||||||
|     if (['private', 'direct'].includes(status.get('visibility'))) { |     if (['private', 'direct'].includes(status.get('visibility'))) { | ||||||
|       reblogLink = ''; |       reblogLink = ''; | ||||||
| @ -240,7 +231,7 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
|         <> |         <> | ||||||
|           {' · '} |           {' · '} | ||||||
|           <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'> |           <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/reblogs`} className='detailed-status__link'> | ||||||
|             <Icon id={reblogIcon} /> |             <Icon id={reblogIcon} icon={reblogIconComponent} /> | ||||||
|             <span className='detailed-status__reblogs'> |             <span className='detailed-status__reblogs'> | ||||||
|               <AnimatedNumber value={status.get('reblogs_count')} /> |               <AnimatedNumber value={status.get('reblogs_count')} /> | ||||||
|             </span> |             </span> | ||||||
| @ -252,7 +243,7 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
|         <> |         <> | ||||||
|           {' · '} |           {' · '} | ||||||
|           <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}> |           <a href={`/interact/${status.get('id')}?type=reblog`} className='detailed-status__link' onClick={this.handleModalLink}> | ||||||
|             <Icon id={reblogIcon} /> |             <Icon id={reblogIcon} icon={reblogIconComponent} /> | ||||||
|             <span className='detailed-status__reblogs'> |             <span className='detailed-status__reblogs'> | ||||||
|               <AnimatedNumber value={status.get('reblogs_count')} /> |               <AnimatedNumber value={status.get('reblogs_count')} /> | ||||||
|             </span> |             </span> | ||||||
| @ -264,7 +255,7 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
|     if (this.props.history) { |     if (this.props.history) { | ||||||
|       favouriteLink = ( |       favouriteLink = ( | ||||||
|         <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/favourites`} className='detailed-status__link'> |         <Link to={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}/favourites`} className='detailed-status__link'> | ||||||
|           <Icon id='star' /> |           <Icon id='star' icon={StarIcon} /> | ||||||
|           <span className='detailed-status__favorites'> |           <span className='detailed-status__favorites'> | ||||||
|             <AnimatedNumber value={status.get('favourites_count')} /> |             <AnimatedNumber value={status.get('favourites_count')} /> | ||||||
|           </span> |           </span> | ||||||
| @ -273,7 +264,7 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
|     } else { |     } else { | ||||||
|       favouriteLink = ( |       favouriteLink = ( | ||||||
|         <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}> |         <a href={`/interact/${status.get('id')}?type=favourite`} className='detailed-status__link' onClick={this.handleModalLink}> | ||||||
|           <Icon id='star' /> |           <Icon id='star' icon={StarIcon} /> | ||||||
|           <span className='detailed-status__favorites'> |           <span className='detailed-status__favorites'> | ||||||
|             <AnimatedNumber value={status.get('favourites_count')} /> |             <AnimatedNumber value={status.get('favourites_count')} /> | ||||||
|           </span> |           </span> | ||||||
| @ -298,7 +289,7 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
|         <div ref={this.setRef} className={classNames('detailed-status', { compact })}> |         <div ref={this.setRef} className={classNames('detailed-status', { compact })}> | ||||||
|           {status.get('visibility') === 'direct' && ( |           {status.get('visibility') === 'direct' && ( | ||||||
|             <div className='status__prepend'> |             <div className='status__prepend'> | ||||||
|               <div className='status__prepend-icon-wrapper'><Icon id='at' className='status__prepend-icon' fixedWidth /></div> |               <div className='status__prepend-icon-wrapper'><Icon id='at' icon={AlternateEmailIcon} className='status__prepend-icon' /></div> | ||||||
|               <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' /> |               <FormattedMessage id='status.direct_indicator' defaultMessage='Private mention' /> | ||||||
|             </div> |             </div> | ||||||
|           )} |           )} | ||||||
| @ -331,4 +322,4 @@ class DetailedStatus extends ImmutablePureComponent { | |||||||
| 
 | 
 | ||||||
| } | } | ||||||
| 
 | 
 | ||||||
| export default withRouter(injectIntl(DetailedStatus)); | export default withRouter(DetailedStatus); | ||||||
|  | |||||||
| @ -12,6 +12,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; | |||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { createSelector } from 'reselect'; | import { createSelector } from 'reselect'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as VisibilityIcon } from '@material-symbols/svg-600/outlined/visibility.svg'; | ||||||
|  | import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg'; | ||||||
| import { HotKeys } from 'react-hotkeys'; | import { HotKeys } from 'react-hotkeys'; | ||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| @ -685,7 +687,7 @@ class Status extends ImmutablePureComponent { | |||||||
|           showBackButton |           showBackButton | ||||||
|           multiColumn={multiColumn} |           multiColumn={multiColumn} | ||||||
|           extraButton={( |           extraButton={( | ||||||
|             <button type='button' className='column-header__button' title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} onClick={this.handleToggleAll}><Icon id={status.get('hidden') ? 'eye-slash' : 'eye'} /></button> |             <button type='button' className='column-header__button' title={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} aria-label={intl.formatMessage(status.get('hidden') ? messages.revealAll : messages.hideAll)} onClick={this.handleToggleAll}><Icon id={status.get('hidden') ? 'eye-slash' : 'eye'} icon={status.get('hidden') ? VisibilityOffIcon : VisibilityIcon} /></button> | ||||||
|           )} |           )} | ||||||
|         /> |         /> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -8,6 +8,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; | |||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| import { createSelector } from 'reselect'; | import { createSelector } from 'reselect'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||||
|  | 
 | ||||||
| import { followAccount } from 'mastodon/actions/accounts'; | import { followAccount } from 'mastodon/actions/accounts'; | ||||||
| import { Button } from 'mastodon/components/button'; | import { Button } from 'mastodon/components/button'; | ||||||
| import { IconButton } from 'mastodon/components/icon_button'; | import { IconButton } from 'mastodon/components/icon_button'; | ||||||
| @ -101,7 +103,7 @@ class SubscribedLanguagesModal extends ImmutablePureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className='modal-root__modal report-dialog-modal'> |       <div className='modal-root__modal report-dialog-modal'> | ||||||
|         <div className='report-modal__target'> |         <div className='report-modal__target'> | ||||||
|           <IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' onClick={onClose} size={20} /> |           <IconButton className='report-modal__close' title={intl.formatMessage(messages.close)} icon='times' iconComponent={CloseIcon} onClick={onClose} size={20} /> | ||||||
|           <FormattedMessage id='subscribed_languages.target' defaultMessage='Change subscribed languages for {target}' values={{ target: <strong>{acct}</strong> }} /> |           <FormattedMessage id='subscribed_languages.target' defaultMessage='Change subscribed languages for {target}' values={{ target: <strong>{acct}</strong> }} /> | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -20,12 +20,12 @@ export default class ActionsModal extends ImmutablePureComponent { | |||||||
|       return <li key={`sep-${i}`} className='dropdown-menu__separator' />; |       return <li key={`sep-${i}`} className='dropdown-menu__separator' />; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     const { icon = null, text, meta = null, active = false, href = '#' } = action; |     const { icon = null, iconComponent = null, text, meta = null, active = false, href = '#' } = action; | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|       <li key={`${text}-${i}`}> |       <li key={`${text}-${i}`}> | ||||||
|         <a href={href} target='_blank' rel='noopener noreferrer' onClick={this.props.onClick} data-index={i} className={classNames({ active })}> |         <a href={href} target='_blank' rel='noopener noreferrer' onClick={this.props.onClick} data-index={i} className={classNames({ active })}> | ||||||
|           {icon && <IconButton title={text} icon={icon} role='presentation' tabIndex={-1} inverted />} |           {icon && <IconButton title={text} icon={icon} iconComponent={iconComponent} role='presentation' tabIndex={-1} inverted />} | ||||||
|           <div> |           <div> | ||||||
|             <div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div> |             <div className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div> | ||||||
|             <div>{meta}</div> |             <div>{meta}</div> | ||||||
|  | |||||||
| @ -9,9 +9,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | |||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||||
| import { connect } from 'react-redux'; | import { connect } from 'react-redux'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||||
|  | 
 | ||||||
| import { changeBoostPrivacy } from 'mastodon/actions/boosts'; | import { changeBoostPrivacy } from 'mastodon/actions/boosts'; | ||||||
| import AttachmentList from 'mastodon/components/attachment_list'; | import AttachmentList from 'mastodon/components/attachment_list'; | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
|  | import { VisibilityIcon } from 'mastodon/components/visibility_icon'; | ||||||
| import PrivacyDropdown from 'mastodon/features/compose/components/privacy_dropdown'; | import PrivacyDropdown from 'mastodon/features/compose/components/privacy_dropdown'; | ||||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||||
| 
 | 
 | ||||||
| @ -24,10 +27,6 @@ import StatusContent from '../../../components/status_content'; | |||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
|   cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' }, |   cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' }, | ||||||
|   reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, |   reblog: { id: 'status.reblog', defaultMessage: 'Boost' }, | ||||||
|   public_short: { id: 'privacy.public.short', defaultMessage: 'Public' }, |  | ||||||
|   unlisted_short: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' }, |  | ||||||
|   private_short: { id: 'privacy.private.short', defaultMessage: 'Followers only' }, |  | ||||||
|   direct_short: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' }, |  | ||||||
| }); | }); | ||||||
| 
 | 
 | ||||||
| const mapStateToProps = state => { | const mapStateToProps = state => { | ||||||
| @ -76,22 +75,13 @@ class BoostModal extends ImmutablePureComponent { | |||||||
|     const { status, privacy, intl } = this.props; |     const { status, privacy, intl } = this.props; | ||||||
|     const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog; |     const buttonText = status.get('reblogged') ? messages.cancel_reblog : messages.reblog; | ||||||
| 
 | 
 | ||||||
|     const visibilityIconInfo = { |  | ||||||
|       'public': { icon: 'globe', text: intl.formatMessage(messages.public_short) }, |  | ||||||
|       'unlisted': { icon: 'unlock', text: intl.formatMessage(messages.unlisted_short) }, |  | ||||||
|       'private': { icon: 'lock', text: intl.formatMessage(messages.private_short) }, |  | ||||||
|       'direct': { icon: 'at', text: intl.formatMessage(messages.direct_short) }, |  | ||||||
|     }; |  | ||||||
| 
 |  | ||||||
|     const visibilityIcon = visibilityIconInfo[status.get('visibility')]; |  | ||||||
| 
 |  | ||||||
|     return ( |     return ( | ||||||
|       <div className='modal-root__modal boost-modal'> |       <div className='modal-root__modal boost-modal'> | ||||||
|         <div className='boost-modal__container'> |         <div className='boost-modal__container'> | ||||||
|           <div className={classNames('status', `status-${status.get('visibility')}`, 'light')}> |           <div className={classNames('status', `status-${status.get('visibility')}`, 'light')}> | ||||||
|             <div className='status__info'> |             <div className='status__info'> | ||||||
|               <a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'> |               <a href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} className='status__relative-time' target='_blank' rel='noopener noreferrer'> | ||||||
|                 <span className='status__visibility-icon'><Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></span> |                 <span className='status__visibility-icon'><VisibilityIcon visibility={status.get('visibility')} /></span> | ||||||
|                 <RelativeTimestamp timestamp={status.get('created_at')} /> |                 <RelativeTimestamp timestamp={status.get('created_at')} /> | ||||||
|               </a> |               </a> | ||||||
| 
 | 
 | ||||||
| @ -116,7 +106,7 @@ class BoostModal extends ImmutablePureComponent { | |||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|         <div className='boost-modal__action-bar'> |         <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' /></span> }} /></div> |           <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> | ||||||
|           {status.get('visibility') !== 'private' && !status.get('reblogged') && ( |           {status.get('visibility') !== 'private' && !status.get('reblogged') && ( | ||||||
|             <PrivacyDropdown |             <PrivacyDropdown | ||||||
|               noDirect |               noDirect | ||||||
|  | |||||||
| @ -3,6 +3,8 @@ import { PureComponent } from 'react'; | |||||||
| 
 | 
 | ||||||
| import { defineMessages, injectIntl } from 'react-intl'; | import { defineMessages, injectIntl } from 'react-intl'; | ||||||
| 
 | 
 | ||||||
|  | import { ReactComponent as RefreshIcon } from '@material-symbols/svg-600/outlined/refresh.svg'; | ||||||
|  | 
 | ||||||
| import { IconButton } from '../../../components/icon_button'; | import { IconButton } from '../../../components/icon_button'; | ||||||
| 
 | 
 | ||||||
| const messages = defineMessages({ | const messages = defineMessages({ | ||||||
| @ -31,7 +33,7 @@ class BundleModalError extends PureComponent { | |||||||
|     return ( |     return ( | ||||||
|       <div className='modal-root__modal error-modal'> |       <div className='modal-root__modal error-modal'> | ||||||
|         <div className='error-modal__body'> |         <div className='error-modal__body'> | ||||||
|           <IconButton title={formatMessage(messages.retry)} icon='refresh' onClick={this.handleRetry} size={64} /> |           <IconButton title={formatMessage(messages.retry)} icon='refresh' iconComponent={RefreshIcon} onClick={this.handleRetry} size={64} /> | ||||||
|           {formatMessage(messages.error)} |           {formatMessage(messages.error)} | ||||||
|         </div> |         </div> | ||||||
| 
 | 
 | ||||||
|  | |||||||
| @ -13,6 +13,7 @@ export default class Column extends PureComponent { | |||||||
|   static propTypes = { |   static propTypes = { | ||||||
|     heading: PropTypes.string, |     heading: PropTypes.string, | ||||||
|     icon: PropTypes.string, |     icon: PropTypes.string, | ||||||
|  |     iconComponent: PropTypes.func, | ||||||
|     children: PropTypes.node, |     children: PropTypes.node, | ||||||
|     active: PropTypes.bool, |     active: PropTypes.bool, | ||||||
|     hideHeadingOnMobile: PropTypes.bool, |     hideHeadingOnMobile: PropTypes.bool, | ||||||
| @ -50,13 +51,13 @@ export default class Column extends PureComponent { | |||||||
|   }; |   }; | ||||||
| 
 | 
 | ||||||
|   render () { |   render () { | ||||||
|     const { heading, icon, children, active, hideHeadingOnMobile } = this.props; |     const { heading, icon, iconComponent, children, active, hideHeadingOnMobile } = this.props; | ||||||
| 
 | 
 | ||||||
|     const showHeading = heading && (!hideHeadingOnMobile || (hideHeadingOnMobile && !isMobile(window.innerWidth))); |     const showHeading = heading && (!hideHeadingOnMobile || (hideHeadingOnMobile && !isMobile(window.innerWidth))); | ||||||
| 
 | 
 | ||||||
|     const columnHeaderId = showHeading && heading.replace(/ /g, '-'); |     const columnHeaderId = showHeading && heading.replace(/ /g, '-'); | ||||||
|     const header = showHeading && ( |     const header = showHeading && ( | ||||||
|       <ColumnHeader icon={icon} active={active} type={heading} onClick={this.handleHeaderClick} columnHeaderId={columnHeaderId} /> |       <ColumnHeader icon={icon} iconComponent={iconComponent} active={active} type={heading} onClick={this.handleHeaderClick} columnHeaderId={columnHeaderId} /> | ||||||
|     ); |     ); | ||||||
|     return ( |     return ( | ||||||
|       <div |       <div | ||||||
|  | |||||||
| @ -24,7 +24,7 @@ export default class ColumnHeader extends PureComponent { | |||||||
|     let iconElement = ''; |     let iconElement = ''; | ||||||
| 
 | 
 | ||||||
|     if (icon) { |     if (icon) { | ||||||
|       iconElement = <Icon id={icon} fixedWidth className='column-header__icon' />; |       iconElement = <Icon id={icon} className='column-header__icon' />; | ||||||
|     } |     } | ||||||
| 
 | 
 | ||||||
|     return ( |     return ( | ||||||
|  | |||||||
| @ -5,10 +5,10 @@ import { NavLink } from 'react-router-dom'; | |||||||
| 
 | 
 | ||||||
| import { Icon }  from 'mastodon/components/icon'; | import { Icon }  from 'mastodon/components/icon'; | ||||||
| 
 | 
 | ||||||
| const ColumnLink = ({ icon, text, to, href, method, badge, transparent, ...other }) => { | const ColumnLink = ({ icon, iconComponent, text, to, href, method, badge, transparent, ...other }) => { | ||||||
|   const className = classNames('column-link', { 'column-link--transparent': transparent }); |   const className = classNames('column-link', { 'column-link--transparent': transparent }); | ||||||
|   const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null; |   const badgeElement = typeof badge !== 'undefined' ? <span className='column-link__badge'>{badge}</span> : null; | ||||||
|   const iconElement = typeof icon === 'string' ? <Icon id={icon} fixedWidth className='column-link__icon' /> : icon; |   const iconElement = (typeof icon === 'string' || iconComponent) ? <Icon id={icon} icon={iconComponent} className='column-link__icon' /> : icon; | ||||||
| 
 | 
 | ||||||
|   if (href) { |   if (href) { | ||||||
|     return ( |     return ( | ||||||
| @ -31,6 +31,7 @@ const ColumnLink = ({ icon, text, to, href, method, badge, transparent, ...other | |||||||
| 
 | 
 | ||||||
| ColumnLink.propTypes = { | ColumnLink.propTypes = { | ||||||
|   icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, |   icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, | ||||||
|  |   iconComponent: PropTypes.func, | ||||||
|   text: PropTypes.string.isRequired, |   text: PropTypes.string.isRequired, | ||||||
|   to: PropTypes.string, |   to: PropTypes.string, | ||||||
|   href: PropTypes.string, |   href: PropTypes.string, | ||||||
|  | |||||||
| @ -1,32 +0,0 @@ | |||||||
| import PropTypes from 'prop-types'; |  | ||||||
| 
 |  | ||||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; |  | ||||||
| 
 |  | ||||||
| import Column from '../../../components/column'; |  | ||||||
| import ColumnHeader from '../../../components/column_header'; |  | ||||||
| 
 |  | ||||||
| export default class ColumnLoading extends ImmutablePureComponent { |  | ||||||
| 
 |  | ||||||
|   static propTypes = { |  | ||||||
|     title: PropTypes.oneOfType([PropTypes.node, PropTypes.string]), |  | ||||||
|     icon: PropTypes.string, |  | ||||||
|     multiColumn: PropTypes.bool, |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   static defaultProps = { |  | ||||||
|     title: '', |  | ||||||
|     icon: '', |  | ||||||
|   }; |  | ||||||
| 
 |  | ||||||
|   render() { |  | ||||||
|     let { title, icon, multiColumn } = this.props; |  | ||||||
| 
 |  | ||||||
|     return ( |  | ||||||
|       <Column> |  | ||||||
|         <ColumnHeader icon={icon} title={title} multiColumn={multiColumn} focusable={false} placeholder /> |  | ||||||
|         <div className='scrollable' /> |  | ||||||
|       </Column> |  | ||||||
|     ); |  | ||||||
|   } |  | ||||||
| 
 |  | ||||||
| } |  | ||||||
| @ -0,0 +1,13 @@ | |||||||
|  | import Column from '../../../components/column'; | ||||||
|  | import ColumnHeader from '../../../components/column_header'; | ||||||
|  | 
 | ||||||
|  | interface Props { | ||||||
|  |   multiColumn?: boolean; | ||||||
|  | } | ||||||
|  | 
 | ||||||
|  | export const ColumnLoading: React.FC<Props> = (otherProps) => ( | ||||||
|  |   <Column> | ||||||
|  |     <ColumnHeader {...otherProps} /> | ||||||
|  |     <div className='scrollable' /> | ||||||
|  |   </Column> | ||||||
|  | ); | ||||||
| @ -23,7 +23,7 @@ import { | |||||||
| } from '../util/async-components'; | } from '../util/async-components'; | ||||||
| 
 | 
 | ||||||
| import BundleColumnError from './bundle_column_error'; | import BundleColumnError from './bundle_column_error'; | ||||||
| import ColumnLoading from './column_loading'; | import { ColumnLoading } from './column_loading'; | ||||||
| import ComposePanel from './compose_panel'; | import ComposePanel from './compose_panel'; | ||||||
| import DrawerLoading from './drawer_loading'; | import DrawerLoading from './drawer_loading'; | ||||||
| import NavigationPanel from './navigation_panel'; | import NavigationPanel from './navigation_panel'; | ||||||
|  | |||||||
Some files were not shown because too many files have changed in this diff Show More
		Loading…
	
	
			
			x
			
			
		
	
		Reference in New Issue
	
	Block a user