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 { FollowersCounter } from './counters'; | ||||
| import { DisplayName } from './display_name'; | ||||
| import { IconButton } from './icon_button'; | ||||
| import { RelativeTimestamp } from './relative_timestamp'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
| @ -45,10 +44,7 @@ class Account extends ImmutablePureComponent { | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     hidden: PropTypes.bool, | ||||
|     minimal: PropTypes.bool, | ||||
|     actionIcon: PropTypes.string, | ||||
|     actionTitle: PropTypes.string, | ||||
|     defaultAction: PropTypes.string, | ||||
|     onActionClick: PropTypes.func, | ||||
|     withBio: PropTypes.bool, | ||||
|   }; | ||||
| 
 | ||||
| @ -76,12 +72,8 @@ class Account extends ImmutablePureComponent { | ||||
|     this.props.onMuteNotifications(this.props.account, false); | ||||
|   }; | ||||
| 
 | ||||
|   handleAction = () => { | ||||
|     this.props.onActionClick(this.props.account); | ||||
|   }; | ||||
| 
 | ||||
|   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) { | ||||
|       return <EmptyAccount size={size} minimal={minimal} />; | ||||
| @ -98,9 +90,7 @@ class Account extends ImmutablePureComponent { | ||||
| 
 | ||||
|     let buttons; | ||||
| 
 | ||||
|     if (actionIcon && onActionClick) { | ||||
|       buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />; | ||||
|     } else if (!actionIcon && account.get('id') !== me && account.get('relationship', null) !== null) { | ||||
|     if (account.get('id') !== me && account.get('relationship', null) !== null) { | ||||
|       const following = account.getIn(['relationship', 'following']); | ||||
|       const requested = account.getIn(['relationship', 'requested']); | ||||
|       const blocking  = account.getIn(['relationship', 'blocking']); | ||||
|  | ||||
| @ -7,6 +7,8 @@ import classNames from 'classnames'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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'; | ||||
| 
 | ||||
| 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 })}> | ||||
|         {!compact && ( | ||||
|           <div className='attachment-list__icon'> | ||||
|             <Icon id='link' /> | ||||
|             <Icon id='link' icon={LinkIcon} /> | ||||
|           </div> | ||||
|         )} | ||||
| 
 | ||||
| @ -36,7 +38,7 @@ export default class AttachmentList extends ImmutablePureComponent { | ||||
|             return ( | ||||
|               <li key={attachment.get('id')}> | ||||
|                 <a href={displayUrl} target='_blank' rel='noopener noreferrer'> | ||||
|                   {compact && <Icon id='link' />} | ||||
|                   {compact && <Icon id='link' icon={LinkIcon} />} | ||||
|                   {compact && ' ' } | ||||
|                   {displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />} | ||||
|                 </a> | ||||
|  | ||||
| @ -2,9 +2,9 @@ import PropTypes from 'prop-types'; | ||||
| 
 | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import { ReactComponent as GroupsIcon } from '@material-design-icons/svg/outlined/group.svg'; | ||||
| import { ReactComponent as PersonIcon } from '@material-design-icons/svg/outlined/person.svg'; | ||||
| import { ReactComponent as SmartToyIcon } from '@material-design-icons/svg/outlined/smart_toy.svg'; | ||||
| import { ReactComponent as GroupsIcon } from '@material-symbols/svg-600/outlined/group.svg'; | ||||
| import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg'; | ||||
| import { ReactComponent as SmartToyIcon } from '@material-symbols/svg-600/outlined/smart_toy.svg'; | ||||
| 
 | ||||
| 
 | ||||
| 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 { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg'; | ||||
| 
 | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| @ -34,7 +36,7 @@ class ColumnBackButton extends PureComponent { | ||||
| 
 | ||||
|     const component = ( | ||||
|       <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' /> | ||||
|       </button> | ||||
|     ); | ||||
|  | ||||
| @ -1,5 +1,7 @@ | ||||
| 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 ColumnBackButton from './column_back_button'; | ||||
| @ -10,7 +12,7 @@ export default class ColumnBackButtonSlim extends ColumnBackButton { | ||||
|     return ( | ||||
|       <div className='column-back-button--slim'> | ||||
|         <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' /> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
| @ -7,6 +7,13 @@ import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; | ||||
| import classNames from 'classnames'; | ||||
| 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 { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| @ -27,6 +34,7 @@ class ColumnHeader extends PureComponent { | ||||
|     intl: PropTypes.object.isRequired, | ||||
|     title: PropTypes.node, | ||||
|     icon: PropTypes.string, | ||||
|     iconComponent: PropTypes.func, | ||||
|     active: PropTypes.bool, | ||||
|     multiColumn: PropTypes.bool, | ||||
|     extraButton: PropTypes.node, | ||||
| @ -87,7 +95,7 @@ class ColumnHeader extends PureComponent { | ||||
|   }; | ||||
| 
 | ||||
|   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 wrapperClassName = classNames('column-header__wrapper', { | ||||
| @ -118,22 +126,22 @@ class ColumnHeader extends PureComponent { | ||||
|     } | ||||
| 
 | ||||
|     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 = ( | ||||
|         <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.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.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' icon={ChevronRightIcon} /></button> | ||||
|         </div> | ||||
|       ); | ||||
|     } 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)) { | ||||
|       backButton = ( | ||||
|         <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' /> | ||||
|         </button> | ||||
|       ); | ||||
| @ -157,21 +165,21 @@ class ColumnHeader extends PureComponent { | ||||
|           onClick={this.handleToggleClick} | ||||
|         > | ||||
|           <i className='icon-with-badge'> | ||||
|             <Icon id='sliders' /> | ||||
|             <Icon id='sliders' icon={TuneIcon} /> | ||||
|             {collapseIssues && <i className='icon-with-badge__issue-badge' />} | ||||
|           </i> | ||||
|         </button> | ||||
|       ); | ||||
|     } | ||||
| 
 | ||||
|     const hasTitle = icon && title; | ||||
|     const hasTitle = (icon || iconComponent) && title; | ||||
| 
 | ||||
|     const component = ( | ||||
|       <div className={wrapperClassName}> | ||||
|         <h1 className={buttonClassName}> | ||||
|           {hasTitle && ( | ||||
|             <button onClick={this.handleTitleClick}> | ||||
|               <Icon id={icon} fixedWidth className='column-header__icon' /> | ||||
|               <Icon id={icon} icon={iconComponent} className='column-header__icon' /> | ||||
|               {title} | ||||
|             </button> | ||||
|           )} | ||||
|  | ||||
| @ -3,6 +3,8 @@ import { useCallback, useState } from 'react'; | ||||
| 
 | ||||
| import { defineMessages, useIntl } from 'react-intl'; | ||||
| 
 | ||||
| import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||
| 
 | ||||
| import { bannerSettings } from 'mastodon/settings'; | ||||
| 
 | ||||
| import { IconButton } from './icon_button'; | ||||
| @ -36,6 +38,7 @@ export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({ | ||||
|       <div className='dismissable-banner__action'> | ||||
|         <IconButton | ||||
|           icon='times' | ||||
|           iconComponent={CloseIcon} | ||||
|           title={intl.formatMessage(messages.dismiss)} | ||||
|           onClick={handleDismiss} | ||||
|         /> | ||||
|  | ||||
| @ -2,6 +2,8 @@ import { useCallback } from 'react'; | ||||
| 
 | ||||
| import { defineMessages, useIntl } from 'react-intl'; | ||||
| 
 | ||||
| import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg'; | ||||
| 
 | ||||
| import { IconButton } from './icon_button'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
| @ -34,6 +36,7 @@ export const Domain: React.FC<Props> = ({ domain, onUnblockDomain }) => { | ||||
|           <IconButton | ||||
|             active | ||||
|             icon='unlock' | ||||
|             iconComponent={LockOpenIcon} | ||||
|             title={intl.formatMessage(messages.unblockDomain, { domain })} | ||||
|             onClick={handleDomainUnblock} | ||||
|           /> | ||||
|  | ||||
| @ -6,6 +6,7 @@ import { withRouter } from 'react-router-dom'; | ||||
| 
 | ||||
| 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 Overlay from 'react-overlays/Overlay'; | ||||
| 
 | ||||
| @ -163,6 +164,7 @@ class Dropdown extends PureComponent { | ||||
|   static propTypes = { | ||||
|     children: PropTypes.node, | ||||
|     icon: PropTypes.string, | ||||
|     iconComponent: PropTypes.func, | ||||
|     items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired, | ||||
|     loading: PropTypes.bool, | ||||
|     size: PropTypes.number, | ||||
| @ -255,7 +257,7 @@ class Dropdown extends PureComponent { | ||||
|   }; | ||||
| 
 | ||||
|   findTarget = () => { | ||||
|     return this.target; | ||||
|     return this.target?.buttonRef?.current; | ||||
|   }; | ||||
| 
 | ||||
|   componentWillUnmount = () => { | ||||
| @ -271,6 +273,7 @@ class Dropdown extends PureComponent { | ||||
|   render () { | ||||
|     const { | ||||
|       icon, | ||||
|       iconComponent, | ||||
|       items, | ||||
|       size, | ||||
|       title, | ||||
| @ -291,9 +294,11 @@ class Dropdown extends PureComponent { | ||||
|       onMouseDown: this.handleMouseDown, | ||||
|       onKeyDown: this.handleButtonKeyDown, | ||||
|       onKeyPress: this.handleKeyPress, | ||||
|       ref: this.setTargetRef, | ||||
|     }) : ( | ||||
|       <IconButton | ||||
|         icon={!open ? icon : 'close'} | ||||
|         iconComponent={!open ? iconComponent : CloseIcon} | ||||
|         title={title} | ||||
|         active={open} | ||||
|         disabled={disabled} | ||||
| @ -302,14 +307,14 @@ class Dropdown extends PureComponent { | ||||
|         onMouseDown={this.handleMouseDown} | ||||
|         onKeyDown={this.handleButtonKeyDown} | ||||
|         onKeyPress={this.handleKeyPress} | ||||
|         ref={this.setTargetRef} | ||||
|       /> | ||||
|     ); | ||||
| 
 | ||||
|     return ( | ||||
|       <> | ||||
|         <span ref={this.setTargetRef}> | ||||
|         {button} | ||||
|         </span> | ||||
| 
 | ||||
|         <Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}> | ||||
|           {({ props, arrowProps, placement }) => ( | ||||
|             <div {...props}> | ||||
|  | ||||
| @ -5,6 +5,8 @@ import { FormattedMessage, injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| import InlineAccount from 'mastodon/components/inline_account'; | ||||
| @ -66,7 +68,7 @@ class EditedTimestamp extends PureComponent { | ||||
|     return ( | ||||
|       <DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}> | ||||
|         <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> | ||||
|       </DropdownMenu> | ||||
|     ); | ||||
|  | ||||
| @ -1,20 +1,50 @@ | ||||
| import classNames from 'classnames'; | ||||
| 
 | ||||
| interface Props extends React.HTMLAttributes<HTMLImageElement> { | ||||
|   id: string; | ||||
|   className?: string; | ||||
|   fixedWidth?: boolean; | ||||
| import { ReactComponent as CheckBoxOutlineBlankIcon } from '@material-symbols/svg-600/outlined/check_box_outline_blank.svg'; | ||||
| 
 | ||||
| interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> { | ||||
|   title?: string; | ||||
| } | ||||
| 
 | ||||
| export type IconProp = React.FC<SVGPropsWithTitle>; | ||||
| 
 | ||||
| interface Props extends React.SVGProps<SVGSVGElement> { | ||||
|   children?: never; | ||||
|   id: string; | ||||
|   icon: IconProp; | ||||
|   title?: string; | ||||
| } | ||||
| 
 | ||||
| export const Icon: React.FC<Props> = ({ | ||||
|   id, | ||||
|   icon: IconComponent, | ||||
|   className, | ||||
|   fixedWidth, | ||||
|   title: titleProp, | ||||
|   ...other | ||||
| }) => ( | ||||
|   <i | ||||
|     className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })} | ||||
| }) => { | ||||
|   // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
 | ||||
|   if (!IconComponent) { | ||||
|     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 { AnimatedNumber } from './animated_number'; | ||||
| import type { IconProp } from './icon'; | ||||
| import { Icon } from './icon'; | ||||
| 
 | ||||
| interface Props { | ||||
|   className?: string; | ||||
|   title: string; | ||||
|   icon: string; | ||||
|   iconComponent: IconProp; | ||||
|   onClick?: React.MouseEventHandler<HTMLButtonElement>; | ||||
|   onMouseDown?: React.MouseEventHandler<HTMLButtonElement>; | ||||
|   onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>; | ||||
|   onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>; | ||||
|   size: number; | ||||
|   active: boolean; | ||||
|   expanded?: boolean; | ||||
|   style?: React.CSSProperties; | ||||
| @ -32,8 +33,9 @@ interface States { | ||||
|   deactivate: boolean; | ||||
| } | ||||
| export class IconButton extends PureComponent<Props, States> { | ||||
|   buttonRef = createRef<HTMLButtonElement>(); | ||||
| 
 | ||||
|   static defaultProps = { | ||||
|     size: 18, | ||||
|     active: false, | ||||
|     disabled: false, | ||||
|     animate: false, | ||||
| @ -85,10 +87,6 @@ export class IconButton extends PureComponent<Props, States> { | ||||
| 
 | ||||
|   render() { | ||||
|     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.active ? this.props.activeStyle : {}), | ||||
|     }; | ||||
| @ -99,6 +97,7 @@ export class IconButton extends PureComponent<Props, States> { | ||||
|       disabled, | ||||
|       expanded, | ||||
|       icon, | ||||
|       iconComponent, | ||||
|       inverted, | ||||
|       overlay, | ||||
|       tabIndex, | ||||
| @ -120,13 +119,9 @@ export class IconButton extends PureComponent<Props, States> { | ||||
|       'icon-button--with-counter': typeof counter !== 'undefined', | ||||
|     }); | ||||
| 
 | ||||
|     if (typeof counter !== 'undefined') { | ||||
|       style.width = 'auto'; | ||||
|     } | ||||
| 
 | ||||
|     let contents = ( | ||||
|       <> | ||||
|         <Icon id={icon} fixedWidth aria-hidden='true' />{' '} | ||||
|         <Icon id={icon} icon={iconComponent} aria-hidden='true' />{' '} | ||||
|         {typeof counter !== 'undefined' && ( | ||||
|           <span className='icon-button__counter'> | ||||
|             <AnimatedNumber value={counter} /> | ||||
| @ -158,6 +153,7 @@ export class IconButton extends PureComponent<Props, States> { | ||||
|         style={style} | ||||
|         tabIndex={tabIndex} | ||||
|         disabled={disabled} | ||||
|         ref={this.buttonRef} | ||||
|       > | ||||
|         {contents} | ||||
|       </button> | ||||
|  | ||||
| @ -1,21 +1,24 @@ | ||||
| import type { IconProp } from './icon'; | ||||
| import { Icon } from './icon'; | ||||
| 
 | ||||
| const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num); | ||||
| 
 | ||||
| interface Props { | ||||
|   id: string; | ||||
|   icon: IconProp; | ||||
|   count: number; | ||||
|   issueBadge: boolean; | ||||
|   className: string; | ||||
| } | ||||
| export const IconWithBadge: React.FC<Props> = ({ | ||||
|   id, | ||||
|   icon, | ||||
|   count, | ||||
|   issueBadge, | ||||
|   className, | ||||
| }) => ( | ||||
|   <i className='icon-with-badge'> | ||||
|     <Icon id={id} fixedWidth className={className} /> | ||||
|     <Icon id={id} icon={icon} className={className} /> | ||||
|     {count > 0 && ( | ||||
|       <i className='icon-with-badge__badge'>{formatNumber(count)}</i> | ||||
|     )} | ||||
|  | ||||
| @ -2,6 +2,8 @@ import { useCallback } from 'react'; | ||||
| 
 | ||||
| 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'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
| @ -28,7 +30,7 @@ export const LoadGap: React.FC<Props> = ({ disabled, maxId, onClick }) => { | ||||
|       onClick={handleClick} | ||||
|       aria-label={intl.formatMessage(messages.load_more)} | ||||
|     > | ||||
|       <Icon id='ellipsis-h' /> | ||||
|       <Icon id='ellipsis-h' icon={MoreHorizIcon} /> | ||||
|     </button> | ||||
|   ); | ||||
| }; | ||||
|  | ||||
| @ -8,6 +8,7 @@ import classNames from 'classnames'; | ||||
| import { is } from 'immutable'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 
 | ||||
| import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg'; | ||||
| import { debounce } from 'lodash'; | ||||
| 
 | ||||
| import { Blurhash } from 'mastodon/components/blurhash'; | ||||
| @ -323,7 +324,7 @@ class MediaGallery extends PureComponent { | ||||
|         </button> | ||||
|       ); | ||||
|     } 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 { | ||||
|       spoilerButton = ( | ||||
|         <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 { ReactComponent as CancelPresentationIcon } from '@material-symbols/svg-600/outlined/cancel_presentation.svg'; | ||||
| 
 | ||||
| import { removePictureInPicture } from 'mastodon/actions/picture_in_picture'; | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| 
 | ||||
| @ -25,7 +27,7 @@ class PictureInPicturePlaceholder extends PureComponent { | ||||
| 
 | ||||
|     return ( | ||||
|       <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' /> | ||||
|       </div> | ||||
|     ); | ||||
|  | ||||
| @ -7,6 +7,7 @@ import classNames from 'classnames'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 spring from 'react-motion/lib/spring'; | ||||
| 
 | ||||
| @ -192,7 +193,7 @@ class Poll extends ImmutablePureComponent { | ||||
|           /> | ||||
| 
 | ||||
|           {!!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>} | ||||
|         </label> | ||||
| 
 | ||||
|  | ||||
| @ -7,6 +7,10 @@ import classNames from 'classnames'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| @ -27,6 +31,7 @@ import { getHashtagBarForStatus } from './hashtag_bar'; | ||||
| import { RelativeTimestamp } from './relative_timestamp'; | ||||
| import StatusActionBar from './status_action_bar'; | ||||
| import StatusContent from './status_content'; | ||||
| import { VisibilityIcon } from './visibility_icon'; | ||||
| 
 | ||||
| const domParser = new DOMParser(); | ||||
| 
 | ||||
| @ -405,7 +410,7 @@ class Status extends ImmutablePureComponent { | ||||
|     if (featured) { | ||||
|       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' /> | ||||
|         </div> | ||||
|       ); | ||||
| @ -414,7 +419,7 @@ class Status extends ImmutablePureComponent { | ||||
| 
 | ||||
|       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> }} /> | ||||
|         </div> | ||||
|       ); | ||||
| @ -426,7 +431,7 @@ class Status extends ImmutablePureComponent { | ||||
|     } else if (status.get('visibility') === 'direct') { | ||||
|       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' /> | ||||
|         </div> | ||||
|       ); | ||||
| @ -435,7 +440,7 @@ class Status extends ImmutablePureComponent { | ||||
| 
 | ||||
|       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> }} /> | ||||
|         </div> | ||||
|       ); | ||||
| @ -534,15 +539,6 @@ class Status extends ImmutablePureComponent { | ||||
|       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 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 */} | ||||
|             <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'> | ||||
|                 <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>} | ||||
|               </a> | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,16 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 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 { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| @ -336,12 +346,15 @@ class StatusActionBar extends ImmutablePureComponent { | ||||
|     } | ||||
| 
 | ||||
|     let replyIcon; | ||||
|     let replyIconComponent; | ||||
|     let replyTitle; | ||||
|     if (status.get('in_reply_to_id', null) === null) { | ||||
|       replyIcon = 'reply'; | ||||
|       replyIconComponent = ReplyIcon; | ||||
|       replyTitle = intl.formatMessage(messages.reply); | ||||
|     } else { | ||||
|       replyIcon = 'reply-all'; | ||||
|       replyIconComponent = ReplyAllIcon; | ||||
|       replyTitle = intl.formatMessage(messages.replyAll); | ||||
|     } | ||||
| 
 | ||||
| @ -359,30 +372,30 @@ class StatusActionBar extends ImmutablePureComponent { | ||||
|     } | ||||
| 
 | ||||
|     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 ( | ||||
|       <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={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='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 bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /> | ||||
|         <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' 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' 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' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /> | ||||
| 
 | ||||
|         {filterButton} | ||||
| 
 | ||||
|         <div className='status__action-bar__dropdown'> | ||||
|         <DropdownMenuContainer | ||||
|           scrollKey={scrollKey} | ||||
|           status={status} | ||||
|           items={menu} | ||||
|           icon='ellipsis-h' | ||||
|             size={18} | ||||
|           iconComponent={MoreHorizIcon} | ||||
|           direction='right' | ||||
|           title={intl.formatMessage(messages.more)} | ||||
|         /> | ||||
|       </div> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,8 @@ import { Link, withRouter } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 PollContainer from 'mastodon/containers/poll_container'; | ||||
| import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state'; | ||||
| @ -257,7 +259,7 @@ class StatusContent extends PureComponent { | ||||
| 
 | ||||
|     const readMoreButton = renderReadMore && ( | ||||
|       <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> | ||||
|     ); | ||||
| 
 | ||||
|  | ||||
| @ -1,3 +1,5 @@ | ||||
| import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||
| 
 | ||||
| import { Icon } from './icon'; | ||||
| 
 | ||||
| const domParser = new DOMParser(); | ||||
| @ -21,7 +23,7 @@ interface Props { | ||||
| } | ||||
| export const VerifiedBadge: React.FC<Props> = ({ link }) => ( | ||||
|   <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> | ||||
| ); | ||||
|  | ||||
							
								
								
									
										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 { 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 Column from 'mastodon/components/column'; | ||||
| import { Icon  }  from 'mastodon/components/icon'; | ||||
| @ -73,7 +76,7 @@ class Section extends PureComponent { | ||||
|     return ( | ||||
|       <div className={classNames('about__section', { active: !collapsed })}> | ||||
|         <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> | ||||
| 
 | ||||
|         {!collapsed && ( | ||||
|  | ||||
| @ -3,6 +3,9 @@ import { FormattedMessage } from 'react-intl'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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'; | ||||
| 
 | ||||
| export default class FollowRequestNote extends ImmutablePureComponent { | ||||
| @ -22,12 +25,12 @@ export default class FollowRequestNote extends ImmutablePureComponent { | ||||
| 
 | ||||
|         <div className='follow-request-banner__action'> | ||||
|           <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' /> | ||||
|           </button> | ||||
| 
 | ||||
|           <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' /> | ||||
|           </button> | ||||
|         </div> | ||||
|  | ||||
| @ -9,6 +9,12 @@ import { NavLink, withRouter } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { Badge, AutomatedBadge, GroupBadge } from 'mastodon/components/badge'; | ||||
| import { Button } from 'mastodon/components/button'; | ||||
| @ -258,7 +264,7 @@ class Header extends ImmutablePureComponent { | ||||
|     } | ||||
| 
 | ||||
|     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')) { | ||||
| @ -280,7 +286,7 @@ class Header extends ImmutablePureComponent { | ||||
|     } | ||||
| 
 | ||||
|     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) { | ||||
| @ -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> | ||||
| @ -448,7 +454,7 @@ class Header extends ImmutablePureComponent { | ||||
|                       <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} className='translate' /> | ||||
| 
 | ||||
|                       <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> | ||||
|                     </dl> | ||||
|                   ))} | ||||
|  | ||||
| @ -5,6 +5,10 @@ import classNames from 'classnames'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| import { autoPlayGif, displayMedia, useBlurhash } from 'mastodon/initial_state'; | ||||
| @ -69,7 +73,7 @@ export default class MediaItem extends ImmutablePureComponent { | ||||
|     if (!visible) { | ||||
|       icon = ( | ||||
|         <span className='account-gallery__item__icons'> | ||||
|           <Icon id='eye-slash' /> | ||||
|           <Icon id='eye-slash' icon={VisibilityOffIcon} /> | ||||
|         </span> | ||||
|       ); | ||||
|     } else { | ||||
| @ -84,9 +88,9 @@ export default class MediaItem extends ImmutablePureComponent { | ||||
|         ); | ||||
| 
 | ||||
|         if (attachment.get('type') === 'audio') { | ||||
|           label = <Icon id='music' />; | ||||
|           label = <Icon id='music' icon={AudiotrackIcon} />; | ||||
|         } else { | ||||
|           label = <Icon id='play' />; | ||||
|           label = <Icon id='play' icon={PlayArrowIcon} />; | ||||
|         } | ||||
|       } else if (attachment.get('type') === 'image') { | ||||
|         const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; | ||||
|  | ||||
| @ -7,6 +7,12 @@ import classNames from 'classnames'; | ||||
| 
 | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| @ -554,8 +560,8 @@ class Audio extends PureComponent { | ||||
|         <div className='video-player__controls active'> | ||||
|           <div className='video-player__buttons-bar'> | ||||
|             <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(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(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'} icon={muted ? VolumeOffIcon : VolumeUpIcon} /></button> | ||||
| 
 | ||||
|               <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() }} /> | ||||
| @ -575,9 +581,9 @@ class Audio extends PureComponent { | ||||
|             </div> | ||||
| 
 | ||||
|             <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> | ||||
|                 <Icon id={'download'} fixedWidth /> | ||||
|                 <Icon id={'download'} icon={DownloadIcon} /> | ||||
|               </a> | ||||
|             </div> | ||||
|           </div> | ||||
|  | ||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg'; | ||||
| import { debounce } from 'lodash'; | ||||
| 
 | ||||
| import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'mastodon/actions/bookmarks'; | ||||
| @ -79,7 +80,8 @@ class Bookmarks extends ImmutablePureComponent { | ||||
|     return ( | ||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> | ||||
|         <ColumnHeader | ||||
|           icon='bookmark' | ||||
|           icon='bookmarks' | ||||
|           iconComponent={BookmarksIcon} | ||||
|           title={intl.formatMessage(messages.heading)} | ||||
|           onPin={this.handlePin} | ||||
|           onMove={this.handleMove} | ||||
|  | ||||
| @ -7,6 +7,8 @@ import { Helmet } from 'react-helmet'; | ||||
| 
 | ||||
| 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 { domain } from 'mastodon/initial_state'; | ||||
| 
 | ||||
| @ -128,6 +130,7 @@ class CommunityTimeline extends PureComponent { | ||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||
|         <ColumnHeader | ||||
|           icon='users' | ||||
|           iconComponent={PeopleIcon} | ||||
|           active={hasUnread} | ||||
|           title={intl.formatMessage(messages.title)} | ||||
|           onPin={this.handlePin} | ||||
|  | ||||
| @ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| 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'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
| @ -60,7 +62,7 @@ class ActionBar extends PureComponent { | ||||
|     return ( | ||||
|       <div className='compose__action-bar'> | ||||
|         <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> | ||||
|     ); | ||||
|  | ||||
| @ -7,6 +7,7 @@ import classNames from 'classnames'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 
 | ||||
| import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg'; | ||||
| import { length } from 'stringz'; | ||||
| 
 | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| @ -229,7 +230,7 @@ class ComposeForm extends ImmutablePureComponent { | ||||
|     if (this.props.isEditing) { | ||||
|       publishText = intl.formatMessage(messages.saveChanges); | ||||
|     } 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 { | ||||
|       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 { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg'; | ||||
| 
 | ||||
| import { IconButton } from '../../../components/icon_button'; | ||||
| 
 | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   add_poll: { id: 'poll_button.add_poll', defaultMessage: 'Add a 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'> | ||||
|         <IconButton | ||||
|           icon='tasks' | ||||
|           iconComponent={InsertChartIcon} | ||||
|           title={intl.formatMessage(active ? messages.remove_poll : messages.add_poll)} | ||||
|           disabled={disabled} | ||||
|           onClick={this.handleClick} | ||||
|  | ||||
| @ -8,6 +8,9 @@ import classNames from 'classnames'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| import { IconButton } from 'mastodon/components/icon_button'; | ||||
| @ -108,7 +111,7 @@ class OptionIntl extends PureComponent { | ||||
|         </label> | ||||
| 
 | ||||
|         <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> | ||||
|       </li> | ||||
|     ); | ||||
| @ -164,7 +167,7 @@ class PollForm extends ImmutablePureComponent { | ||||
|         </ul> | ||||
| 
 | ||||
|         <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 */} | ||||
|           <select value={expiresIn} onChange={this.handleSelectDuration}> | ||||
|  | ||||
| @ -5,9 +5,15 @@ import { injectIntl, defineMessages } from 'react-intl'; | ||||
| 
 | ||||
| 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 Overlay from 'react-overlays/Overlay'; | ||||
| 
 | ||||
| 
 | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| 
 | ||||
| import { IconButton } from '../../../components/icon_button'; | ||||
| @ -123,7 +129,7 @@ class PrivacyDropdownMenu extends PureComponent { | ||||
|         {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 className='privacy-dropdown__option__icon'> | ||||
|               <Icon id={item.icon} fixedWidth /> | ||||
|               <Icon id={item.icon} icon={item.iconComponent} /> | ||||
|             </div> | ||||
| 
 | ||||
|             <div className='privacy-dropdown__option__content'> | ||||
| @ -222,14 +228,14 @@ class PrivacyDropdown extends PureComponent { | ||||
|     const { intl: { formatMessage } } = this.props; | ||||
| 
 | ||||
|     this.options = [ | ||||
|       { icon: 'globe', 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: 'lock', value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, | ||||
|       { icon: 'globe', iconComponent: PublicIcon, value: 'public', text: formatMessage(messages.public_short), meta: formatMessage(messages.public_long) }, | ||||
|       { icon: 'unlock', iconComponent: LockOpenIcon,  value: 'unlisted', text: formatMessage(messages.unlisted_short), meta: formatMessage(messages.unlisted_long) }, | ||||
|       { icon: 'lock', iconComponent: LockIcon, value: 'private', text: formatMessage(messages.private_short), meta: formatMessage(messages.private_long) }, | ||||
|     ]; | ||||
| 
 | ||||
|     if (!this.props.noDirect) { | ||||
|       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,11 +259,11 @@ class PrivacyDropdown extends PureComponent { | ||||
|     const valueOption = this.options.find(item => item.value === value); | ||||
| 
 | ||||
|     return ( | ||||
|       <div className={classNames('privacy-dropdown', placement, { active: open })} onKeyDown={this.handleKeyDown}> | ||||
|         <div className={classNames('privacy-dropdown__value', { active: this.options.indexOf(valueOption) === (placement === 'bottom' ? 0 : (this.options.length - 1)) })} ref={this.setTargetRef}> | ||||
|       <div ref={this.setTargetRef} onKeyDown={this.handleKeyDown}> | ||||
|         <IconButton | ||||
|           className='privacy-dropdown__value-icon' | ||||
|           icon={valueOption.icon} | ||||
|           iconComponent={valueOption.iconComponent} | ||||
|           title={intl.formatMessage(messages.change_privacy)} | ||||
|           size={18} | ||||
|           expanded={open} | ||||
| @ -269,9 +275,8 @@ class PrivacyDropdown extends PureComponent { | ||||
|           style={{ height: null, lineHeight: '27px' }} | ||||
|           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 }) => ( | ||||
|             <div {...props}> | ||||
|               <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 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 { WithOptionalRouterPropTypes, withOptionalRouter } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| @ -48,7 +50,7 @@ class ReplyIndicator extends ImmutablePureComponent { | ||||
|     return ( | ||||
|       <div className='reply-indicator'> | ||||
|         <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'> | ||||
|             <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 { 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 { domain, searchEnabled } from 'mastodon/initial_state'; | ||||
| 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}> | ||||
|           <Icon id='search' className={hasValue ? '' : 'active'} /> | ||||
|           <Icon id='times-circle' className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} /> | ||||
|           <Icon id='search' icon={SearchIcon} className={hasValue ? '' : 'active'} /> | ||||
|           <Icon id='times-circle' icon={CancelIcon} className={hasValue ? 'active' : ''} aria-label={intl.formatMessage(messages.placeholder)} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div className='search__popout'> | ||||
| @ -345,7 +349,7 @@ class Search extends PureComponent { | ||||
|                 {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 })}> | ||||
|                     <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> | ||||
|                 )) : ( | ||||
|                   <div className='search__popout__menu__message'> | ||||
|  | ||||
| @ -5,6 +5,11 @@ import { FormattedMessage } from 'react-intl'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { LoadMore } from 'mastodon/components/load_more'; | ||||
| 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) { | ||||
|       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} />)} | ||||
|           {(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreAccounts} />} | ||||
|         </SearchSection> | ||||
| @ -53,7 +58,7 @@ class SearchResults extends ImmutablePureComponent { | ||||
| 
 | ||||
|     if (results.get('hashtags') && results.get('hashtags').size > 0) { | ||||
|       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} />)} | ||||
|           {(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreHashtags} />} | ||||
|         </SearchSection> | ||||
| @ -62,7 +67,7 @@ class SearchResults extends ImmutablePureComponent { | ||||
| 
 | ||||
|     if (results.get('statuses') && results.get('statuses').size > 0) { | ||||
|       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} />)} | ||||
|           {(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreStatuses} />} | ||||
|         </SearchSection> | ||||
| @ -73,7 +78,7 @@ class SearchResults extends ImmutablePureComponent { | ||||
|     return ( | ||||
|       <div className='search-results'> | ||||
|         <div className='search-results__header'> | ||||
|           <Icon id='search' fixedWidth /> | ||||
|           <Icon id='search' icon={SearchIcon} /> | ||||
|           <FormattedMessage id='explore.search_results' defaultMessage='Search results' /> | ||||
|         </div> | ||||
| 
 | ||||
|  | ||||
| @ -5,6 +5,9 @@ import { FormattedMessage } from 'react-intl'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| @ -47,13 +50,13 @@ export default class Upload extends ImmutablePureComponent { | ||||
|           {({ 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__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.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></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' icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button> | ||||
|               </div> | ||||
| 
 | ||||
|               {(media.get('description') || '').length === 0 && ( | ||||
|                 <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> | ||||
|  | ||||
| @ -6,6 +6,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 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'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
| @ -62,7 +64,7 @@ class UploadButton extends ImmutablePureComponent { | ||||
| 
 | ||||
|     return ( | ||||
|       <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> | ||||
|           <span style={{ display: 'none' }}>{message}</span> | ||||
|           <input | ||||
|  | ||||
| @ -3,6 +3,7 @@ import { PureComponent } from 'react'; | ||||
| 
 | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| @ -35,7 +36,7 @@ export default class UploadProgress extends PureComponent { | ||||
|     return ( | ||||
|       <div className='upload-progress'> | ||||
|         <div className='upload-progress__icon'> | ||||
|           <Icon id='upload' /> | ||||
|           <Icon id='upload' icon={UploadFileIcon} /> | ||||
|         </div> | ||||
| 
 | ||||
|         <div className='upload-progress__message'> | ||||
|  | ||||
| @ -9,6 +9,13 @@ import { Link } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { openModal } from 'mastodon/actions/modal'; | ||||
| @ -101,21 +108,21 @@ class Compose extends PureComponent { | ||||
|       return ( | ||||
|         <div className='drawer' role='region' aria-label={intl.formatMessage(messages.compose)}> | ||||
|           <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') && ( | ||||
|               <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') && ( | ||||
|               <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') && ( | ||||
|               <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') && ( | ||||
|               <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='/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='/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' icon={LogoutIcon} /></a> | ||||
|           </nav> | ||||
| 
 | ||||
|           {multiColumn && <SearchContainer /> } | ||||
|  | ||||
| @ -8,6 +8,8 @@ import { Link, withRouter } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 AttachmentList from 'mastodon/components/attachment_list'; | ||||
| @ -178,7 +180,7 @@ class Conversation extends ImmutablePureComponent { | ||||
|             )} | ||||
| 
 | ||||
|             <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'> | ||||
|                 <DropdownMenuContainer | ||||
| @ -186,6 +188,7 @@ class Conversation extends ImmutablePureComponent { | ||||
|                   status={lastStatus} | ||||
|                   items={menu} | ||||
|                   icon='ellipsis-h' | ||||
|                   iconComponent={MoreHorizIcon} | ||||
|                   size={18} | ||||
|                   direction='right' | ||||
|                   title={intl.formatMessage(messages.more)} | ||||
|  | ||||
| @ -7,6 +7,8 @@ import { Helmet } from 'react-helmet'; | ||||
| 
 | ||||
| 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 { mountConversations, unmountConversations, expandConversations } from 'mastodon/actions/conversations'; | ||||
| 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)}> | ||||
|         <ColumnHeader | ||||
|           icon='at' | ||||
|           iconComponent={AlternateEmailIcon} | ||||
|           active={hasUnread} | ||||
|           title={intl.formatMessage(messages.title)} | ||||
|           onPin={this.handlePin} | ||||
|  | ||||
| @ -9,6 +9,8 @@ import { List as ImmutableList } from 'immutable'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { fetchDirectory, expandDirectory } from 'mastodon/actions/directory'; | ||||
| 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)}> | ||||
|         <ColumnHeader | ||||
|           icon='address-book-o' | ||||
|           iconComponent={PeopleIcon} | ||||
|           title={intl.formatMessage(messages.title)} | ||||
|           onPin={this.handlePin} | ||||
|           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.' />; | ||||
| 
 | ||||
|     return ( | ||||
|       <Column bindToDocument={!multiColumn} icon='minus-circle' heading={intl.formatMessage(messages.heading)}> | ||||
|       <Column bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}> | ||||
|         <ColumnBackButtonSlim /> | ||||
| 
 | ||||
|         <ScrollableList | ||||
|  | ||||
| @ -8,6 +8,9 @@ import { NavLink, Switch, Route } from 'react-router-dom'; | ||||
| 
 | ||||
| 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 ColumnHeader from 'mastodon/components/column_header'; | ||||
| 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)}> | ||||
|         <ColumnHeader | ||||
|           icon={isSearching ? 'search' : 'hashtag'} | ||||
|           iconComponent={isSearching ? SearchIcon : TagIcon} | ||||
|           title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)} | ||||
|           onClick={this.handleHeaderClick} | ||||
|           multiColumn={multiColumn} | ||||
|  | ||||
| @ -9,6 +9,10 @@ import { List as ImmutableList } from 'immutable'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { ImmutableHashtag as Hashtag } from 'mastodon/components/hashtag'; | ||||
| import { Icon } from 'mastodon/components/icon'; | ||||
| @ -165,19 +169,19 @@ class Results extends PureComponent { | ||||
|       filteredResults = (accounts.size + hashtags.size + statuses.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} />)} | ||||
|             </SearchSection> | ||||
|           )} | ||||
| 
 | ||||
|           {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} />)} | ||||
|             </SearchSection> | ||||
|           )} | ||||
| 
 | ||||
|           {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} />)} | ||||
|             </SearchSection> | ||||
|           )} | ||||
|  | ||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg'; | ||||
| import { debounce } from 'lodash'; | ||||
| 
 | ||||
| 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)}> | ||||
|         <ColumnHeader | ||||
|           icon='star' | ||||
|           iconComponent={StarIcon} | ||||
|           title={intl.formatMessage(messages.heading)} | ||||
|           onPin={this.handlePin} | ||||
|           onMove={this.handleMove} | ||||
|  | ||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as RefreshIcon } from '@material-symbols/svg-600/outlined/refresh.svg'; | ||||
| import { debounce } from 'lodash'; | ||||
| 
 | ||||
| import { fetchFavourites, expandFavourites } from 'mastodon/actions/interactions'; | ||||
| @ -73,7 +74,7 @@ class Favourites extends ImmutablePureComponent { | ||||
|           showBackButton | ||||
|           multiColumn={multiColumn} | ||||
|           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 { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg'; | ||||
| import fuzzysort from 'fuzzysort'; | ||||
| 
 | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| @ -78,7 +79,7 @@ class SelectFilter extends PureComponent { | ||||
|   renderCreateNew (name) { | ||||
|     return ( | ||||
|       <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> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @ -6,6 +6,8 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl'; | ||||
| import { Helmet } from 'react-helmet'; | ||||
| 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 { changeSetting } from 'mastodon/actions/settings'; | ||||
| 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)}> | ||||
|       <ColumnHeader | ||||
|         icon='globe' | ||||
|         iconComponent={PublicIcon} | ||||
|         active={hasUnread} | ||||
|         title={intl.formatMessage(messages.title)} | ||||
|         onPin={handlePin} | ||||
|  | ||||
| @ -7,6 +7,9 @@ import { Link } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { DisplayName } from '../../../components/display_name'; | ||||
| import { IconButton } from '../../../components/icon_button'; | ||||
| @ -41,8 +44,8 @@ class AccountAuthorize extends ImmutablePureComponent { | ||||
|         </div> | ||||
| 
 | ||||
|         <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.reject)} icon='times' onClick={onReject} /></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' iconComponent={CloseIcon} onClick={onReject} /></div> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|  | ||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg'; | ||||
| import { debounce } from 'lodash'; | ||||
| 
 | ||||
| import { expandFollowedHashtags, fetchFollowedHashtags } from 'mastodon/actions/tags'; | ||||
| @ -55,6 +56,7 @@ class FollowedTags extends ImmutablePureComponent { | ||||
|       <Column bindToDocument={!multiColumn}> | ||||
|         <ColumnHeader | ||||
|           icon='hashtag' | ||||
|           iconComponent={TagIcon} | ||||
|           title={intl.formatMessage(messages.heading)} | ||||
|           showBackButton | ||||
|           multiColumn={multiColumn} | ||||
|  | ||||
| @ -9,6 +9,9 @@ import { withRouter } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 spring from 'react-motion/lib/spring'; | ||||
| 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> | ||||
|         )} | ||||
|       </TransitionMotion> | ||||
| @ -440,9 +443,9 @@ class Announcements extends ImmutablePureComponent { | ||||
| 
 | ||||
|           {announcements.size > 1 && ( | ||||
|             <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> | ||||
|               <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> | ||||
|  | ||||
| @ -9,6 +9,18 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 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 Column from 'mastodon/components/column'; | ||||
| import ColumnHeader from 'mastodon/components/column_header'; | ||||
| @ -101,38 +113,38 @@ class GettingStarted extends ImmutablePureComponent { | ||||
| 
 | ||||
|     if (showTrends) { | ||||
|       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( | ||||
|       <ColumnLink key='community_timeline' icon='users' 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='community_timeline' icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.community_timeline)} to='/public/local' />, | ||||
|       <ColumnLink key='public_timeline' icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.public_timeline)} to='/public' />, | ||||
|     ); | ||||
| 
 | ||||
|     if (signedIn) { | ||||
|       navItems.push( | ||||
|         <ColumnSubheading key='header-personal' text={intl.formatMessage(messages.personal)} />, | ||||
|         <ColumnLink key='home' icon='home' text={intl.formatMessage(messages.home_timeline)} to='/home' />, | ||||
|         <ColumnLink key='direct' icon='at' text={intl.formatMessage(messages.direct)} to='/conversations' />, | ||||
|         <ColumnLink key='bookmark' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />, | ||||
|         <ColumnLink key='favourites' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />, | ||||
|         <ColumnLink key='lists' icon='list-ul' text={intl.formatMessage(messages.lists)} to='/lists' />, | ||||
|         <ColumnLink key='home' icon='home' iconComponent={HomeIcon} text={intl.formatMessage(messages.home_timeline)} to='/home' />, | ||||
|         <ColumnLink key='direct' icon='at' iconComponent={AlternateEmailIcon} text={intl.formatMessage(messages.direct)} to='/conversations' />, | ||||
|         <ColumnLink key='bookmark' icon='bookmarks' iconComponent={BookmarksIcon} text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />, | ||||
|         <ColumnLink key='favourites' icon='star' iconComponent={StarIcon} text={intl.formatMessage(messages.favourites)} to='/favourites' />, | ||||
|         <ColumnLink key='lists' icon='list-ul' iconComponent={ListAltIcon} text={intl.formatMessage(messages.lists)} to='/lists' />, | ||||
|       ); | ||||
| 
 | ||||
|       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( | ||||
|         <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 ( | ||||
|       <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__wrapper'> | ||||
|  | ||||
| @ -8,6 +8,7 @@ import { Helmet } from 'react-helmet'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg'; | ||||
| import { isEqual } from 'lodash'; | ||||
| 
 | ||||
| import { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | ||||
| @ -190,6 +191,7 @@ class HashtagTimeline extends PureComponent { | ||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> | ||||
|         <ColumnHeader | ||||
|           icon='hashtag' | ||||
|           iconComponent={TagIcon} | ||||
|           active={hasUnread} | ||||
|           title={this.title()} | ||||
|           onPin={this.handlePin} | ||||
|  | ||||
| @ -10,6 +10,9 @@ import { List as ImmutableList } from 'immutable'; | ||||
| import { connect } from 'react-redux'; | ||||
| 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 { IconWithBadge } from 'mastodon/components/icon_with_badge'; | ||||
| 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)} | ||||
|           onClick={this.handleToggleAnnouncementsClick} | ||||
|         > | ||||
|           <IconWithBadge id='bullhorn' count={unreadAnnouncements} /> | ||||
|           <IconWithBadge id='bullhorn' icon={CampaignIcon} count={unreadAnnouncements} /> | ||||
|         </button> | ||||
|       ); | ||||
|     } | ||||
| @ -198,6 +201,7 @@ class HomeTimeline extends PureComponent { | ||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||
|         <ColumnHeader | ||||
|           icon='home' | ||||
|           iconComponent={HomeIcon} | ||||
|           active={hasUnread} | ||||
|           title={intl.formatMessage(messages.title)} | ||||
|           onPin={this.handlePin} | ||||
|  | ||||
| @ -7,6 +7,10 @@ import classNames from 'classnames'; | ||||
| 
 | ||||
| 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 { openModal, closeModal } from 'mastodon/actions/modal'; | ||||
| @ -354,22 +358,22 @@ class InteractionModal extends React.PureComponent { | ||||
| 
 | ||||
|     switch(type) { | ||||
|     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 }} />; | ||||
|       actionDescription = <FormattedMessage id='interaction_modal.description.reply' defaultMessage='With an account on Mastodon, you can respond to this post.' />; | ||||
|       break; | ||||
|     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 }} />; | ||||
|       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; | ||||
|     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 }} />; | ||||
|       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; | ||||
|     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 }} />; | ||||
|       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; | ||||
|  | ||||
| @ -6,6 +6,8 @@ import { Helmet } from 'react-helmet'; | ||||
| 
 | ||||
| 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 ColumnHeader from 'mastodon/components/column_header'; | ||||
| 
 | ||||
| @ -27,7 +29,8 @@ class KeyboardShortcuts extends ImmutablePureComponent { | ||||
|       <Column> | ||||
|         <ColumnHeader | ||||
|           title={intl.formatMessage(messages.heading)} | ||||
|           icon='question' | ||||
|           icon='info-circle' | ||||
|           iconComponent={InfoIcon} | ||||
|           multiColumn={multiColumn} | ||||
|         /> | ||||
| 
 | ||||
|  | ||||
| @ -6,6 +6,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 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 { removeFromListAdder, addToListAdder } from '../../../actions/lists'; | ||||
| @ -46,16 +50,16 @@ class List extends ImmutablePureComponent { | ||||
|     let button; | ||||
| 
 | ||||
|     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 { | ||||
|       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 ( | ||||
|       <div className='list'> | ||||
|         <div className='list__wrapper'> | ||||
|           <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')} | ||||
|           </div> | ||||
| 
 | ||||
|  | ||||
| @ -6,6 +6,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 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 { Avatar } from '../../../components/avatar'; | ||||
| import { DisplayName } from '../../../components/display_name'; | ||||
| @ -53,9 +56,9 @@ class Account extends ImmutablePureComponent { | ||||
|     let button; | ||||
| 
 | ||||
|     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 { | ||||
|       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 ( | ||||
|  | ||||
| @ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg'; | ||||
| 
 | ||||
| import { changeListEditorTitle, submitListEditor } from '../../../actions/lists'; | ||||
| import { IconButton } from '../../../components/icon_button'; | ||||
| 
 | ||||
| @ -61,6 +63,7 @@ class ListForm extends PureComponent { | ||||
|         <IconButton | ||||
|           disabled={disabled} | ||||
|           icon='check' | ||||
|           iconComponent={CheckIcon} | ||||
|           title={title} | ||||
|           onClick={this.handleClick} | ||||
|         /> | ||||
|  | ||||
| @ -7,6 +7,9 @@ import classNames from 'classnames'; | ||||
| 
 | ||||
| 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 { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; | ||||
| @ -69,8 +72,8 @@ class Search extends PureComponent { | ||||
|         </label> | ||||
| 
 | ||||
|         <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}> | ||||
|           <Icon id='search' className={classNames({ active: !hasValue })} /> | ||||
|           <Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} /> | ||||
|           <Icon id='search' icon={SearchIcon} className={classNames({ active: !hasValue })} /> | ||||
|           <Icon id='times-circle' icon={CancelIcon} aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} /> | ||||
|         </div> | ||||
|       </div> | ||||
|     ); | ||||
|  | ||||
| @ -9,6 +9,9 @@ import { withRouter } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { addColumn, removeColumn, moveColumn } from 'mastodon/actions/columns'; | ||||
| @ -181,6 +184,7 @@ class ListTimeline extends PureComponent { | ||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={title}> | ||||
|         <ColumnHeader | ||||
|           icon='list-ul' | ||||
|           iconComponent={ListAltIcon} | ||||
|           active={hasUnread} | ||||
|           title={title} | ||||
|           onPin={this.handlePin} | ||||
| @ -191,11 +195,11 @@ class ListTimeline extends PureComponent { | ||||
|         > | ||||
|           <div className='column-settings__row column-header__links'> | ||||
|             <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 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> | ||||
|           </div> | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,8 @@ import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| import { createSelector } from 'reselect'; | ||||
| 
 | ||||
| import { ReactComponent as ListAltIcon } from '@material-symbols/svg-600/outlined/list_alt.svg'; | ||||
| 
 | ||||
| import { fetchLists } from 'mastodon/actions/lists'; | ||||
| import Column from 'mastodon/components/column'; | ||||
| import ColumnHeader from 'mastodon/components/column_header'; | ||||
| @ -65,7 +67,7 @@ class Lists extends ImmutablePureComponent { | ||||
| 
 | ||||
|     return ( | ||||
|       <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 /> | ||||
| 
 | ||||
| @ -76,7 +78,7 @@ class Lists extends ImmutablePureComponent { | ||||
|           bindToDocument={!multiColumn} | ||||
|         > | ||||
|           {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> | ||||
| 
 | ||||
|  | ||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as VolumeOffIcon } from '@material-symbols/svg-600/outlined/volume_off.svg'; | ||||
| import { debounce } from 'lodash'; | ||||
| 
 | ||||
| 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." />; | ||||
| 
 | ||||
|     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 /> | ||||
|         <ScrollableList | ||||
|           scrollKey='mutes' | ||||
|  | ||||
| @ -3,6 +3,8 @@ import { PureComponent } from 'react'; | ||||
| 
 | ||||
| import { FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import { ReactComponent as DeleteForeverIcon } from '@material-symbols/svg-600/outlined/delete_forever.svg'; | ||||
| 
 | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| 
 | ||||
| export default class ClearColumnButton extends PureComponent { | ||||
| @ -13,7 +15,7 @@ export default class ClearColumnButton extends PureComponent { | ||||
| 
 | ||||
|   render () { | ||||
|     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 { 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'; | ||||
| 
 | ||||
| const tooltips = defineMessages({ | ||||
| @ -66,42 +73,42 @@ class FilterBar extends PureComponent { | ||||
|           onClick={this.onClick('mention')} | ||||
|           title={intl.formatMessage(tooltips.mentions)} | ||||
|         > | ||||
|           <Icon id='reply-all' fixedWidth /> | ||||
|           <Icon id='reply-all' icon={ReplyAllIcon} /> | ||||
|         </button> | ||||
|         <button | ||||
|           className={selectedFilter === 'favourite' ? 'active' : ''} | ||||
|           onClick={this.onClick('favourite')} | ||||
|           title={intl.formatMessage(tooltips.favourites)} | ||||
|         > | ||||
|           <Icon id='star' fixedWidth /> | ||||
|           <Icon id='star' icon={StarIcon} /> | ||||
|         </button> | ||||
|         <button | ||||
|           className={selectedFilter === 'reblog' ? 'active' : ''} | ||||
|           onClick={this.onClick('reblog')} | ||||
|           title={intl.formatMessage(tooltips.boosts)} | ||||
|         > | ||||
|           <Icon id='retweet' fixedWidth /> | ||||
|           <Icon id='retweet' icon={RepeatIcon} /> | ||||
|         </button> | ||||
|         <button | ||||
|           className={selectedFilter === 'poll' ? 'active' : ''} | ||||
|           onClick={this.onClick('poll')} | ||||
|           title={intl.formatMessage(tooltips.polls)} | ||||
|         > | ||||
|           <Icon id='tasks' fixedWidth /> | ||||
|           <Icon id='tasks' icon={InsertChartIcon} /> | ||||
|         </button> | ||||
|         <button | ||||
|           className={selectedFilter === 'status' ? 'active' : ''} | ||||
|           onClick={this.onClick('status')} | ||||
|           title={intl.formatMessage(tooltips.statuses)} | ||||
|         > | ||||
|           <Icon id='home' fixedWidth /> | ||||
|           <Icon id='home' icon={HomeIcon} /> | ||||
|         </button> | ||||
|         <button | ||||
|           className={selectedFilter === 'follow' ? 'active' : ''} | ||||
|           onClick={this.onClick('follow')} | ||||
|           title={intl.formatMessage(tooltips.follows)} | ||||
|         > | ||||
|           <Icon id='user-plus' fixedWidth /> | ||||
|           <Icon id='user-plus' icon={PersonAddIcon} /> | ||||
|         </button> | ||||
|       </div> | ||||
|     ); | ||||
|  | ||||
| @ -7,6 +7,9 @@ import { Link } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { DisplayName } from 'mastodon/components/display_name'; | ||||
| import { IconButton } from 'mastodon/components/icon_button'; | ||||
| @ -50,8 +53,8 @@ class FollowRequest extends ImmutablePureComponent { | ||||
|           </Link> | ||||
| 
 | ||||
|           <div className='account__relationship'> | ||||
|             <IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /> | ||||
|             <IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /> | ||||
|             <IconButton title={intl.formatMessage(messages.authorize)} icon='check' iconComponent={CheckIcon} onClick={onAuthorize} /> | ||||
|             <IconButton title={intl.formatMessage(messages.reject)} icon='times' iconComponent={CloseIcon} onClick={onReject} /> | ||||
|           </div> | ||||
|         </div> | ||||
|       </div> | ||||
|  | ||||
| @ -8,6 +8,14 @@ import { Link, withRouter } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| @ -128,9 +136,7 @@ class Notification extends ImmutablePureComponent { | ||||
|       <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='notification__message'> | ||||
|             <div className='notification__favourite-icon-wrapper'> | ||||
|               <Icon id='user-plus' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='user-plus' icon={PersonAddIcon} /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <FormattedMessage id='notification.follow' defaultMessage='{name} followed you' values={{ name: link }} /> | ||||
| @ -150,9 +156,7 @@ class Notification extends ImmutablePureComponent { | ||||
|       <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='notification__message'> | ||||
|             <div className='notification__favourite-icon-wrapper'> | ||||
|               <Icon id='user' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='user' icon={PersonIcon} /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <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()}> | ||||
|         <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__favourite-icon-wrapper'> | ||||
|               <Icon id='star' className='star-icon' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='star' icon={StarIcon} className='star-icon' /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <FormattedMessage id='notification.favourite' defaultMessage='{name} favorited your status' values={{ name: link }} /> | ||||
| @ -222,9 +224,7 @@ class Notification extends ImmutablePureComponent { | ||||
|       <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='notification__message'> | ||||
|             <div className='notification__favourite-icon-wrapper'> | ||||
|               <Icon id='retweet' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='retweet' icon={RepeatIcon} /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <FormattedMessage id='notification.reblog' defaultMessage='{name} boosted your status' values={{ name: link }} /> | ||||
| @ -258,9 +258,7 @@ class Notification extends ImmutablePureComponent { | ||||
|       <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='notification__message'> | ||||
|             <div className='notification__favourite-icon-wrapper'> | ||||
|               <Icon id='home' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='home' icon={HomeIcon} /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <FormattedMessage id='notification.status' defaultMessage='{name} just posted' values={{ name: link }} /> | ||||
| @ -295,9 +293,7 @@ class Notification extends ImmutablePureComponent { | ||||
|       <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='notification__message'> | ||||
|             <div className='notification__favourite-icon-wrapper'> | ||||
|               <Icon id='pencil' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='pencil' icon={EditIcon} /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <FormattedMessage id='notification.update' defaultMessage='{name} edited a post' values={{ name: link }} /> | ||||
| @ -334,9 +330,7 @@ class Notification extends ImmutablePureComponent { | ||||
|       <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='notification__message'> | ||||
|             <div className='notification__favourite-icon-wrapper'> | ||||
|               <Icon id='tasks' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='tasks' icon={InsertChartIcon} /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               {ownPoll ? ( | ||||
| @ -371,9 +365,7 @@ class Notification extends ImmutablePureComponent { | ||||
|       <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='notification__message'> | ||||
|             <div className='notification__favourite-icon-wrapper'> | ||||
|               <Icon id='user-plus' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='user-plus' icon={PersonAddIcon} /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <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()}> | ||||
|         <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__favourite-icon-wrapper'> | ||||
|               <Icon id='flag' fixedWidth /> | ||||
|             </div> | ||||
|             <Icon id='flag' icon={FlagIcon} /> | ||||
| 
 | ||||
|             <span title={notification.get('created_at')}> | ||||
|               <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 { 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 { changeSetting } from 'mastodon/actions/settings'; | ||||
| import { Button } from 'mastodon/components/button'; | ||||
| @ -36,11 +39,11 @@ class NotificationsPermissionBanner extends PureComponent { | ||||
|     return ( | ||||
|       <div className='notifications-permission-banner'> | ||||
|         <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> | ||||
| 
 | ||||
|         <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> | ||||
|       </div> | ||||
|     ); | ||||
|  | ||||
| @ -10,6 +10,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import { connect } from 'react-redux'; | ||||
| 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 { compareId } from 'mastodon/compare_id'; | ||||
| @ -260,7 +262,7 @@ class Notifications extends PureComponent { | ||||
|           onClick={this.handleMarkAsRead} | ||||
|           className='column-header__button' | ||||
|         > | ||||
|           <Icon id='check' /> | ||||
|           <Icon id='done-all' icon={DoneAllIcon} /> | ||||
|         </button> | ||||
|       ); | ||||
|     } | ||||
| @ -269,6 +271,7 @@ class Notifications extends PureComponent { | ||||
|       <Column bindToDocument={!multiColumn} ref={this.setColumnRef} label={intl.formatMessage(messages.title)}> | ||||
|         <ColumnHeader | ||||
|           icon='bell' | ||||
|           iconComponent={NotificationsIcon} | ||||
|           active={isUnread} | ||||
|           title={intl.formatMessage(messages.title)} | ||||
|           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 { 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 }) => ( | ||||
|   <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 })} />} | ||||
| 
 | ||||
|         <div className={classNames('onboarding__progress-indicator__step', { active: completed > i })}> | ||||
|           {completed > i && <Check />} | ||||
|           {completed > i && <Icon icon={CheckIcon} />} | ||||
|         </div> | ||||
|       </Fragment> | ||||
|     ))} | ||||
|  | ||||
| @ -1,15 +1,15 @@ | ||||
| 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 ArrowSmallRight from './arrow_small_right'; | ||||
| 
 | ||||
| const Step = ({ label, description, icon, completed, onClick, href }) => { | ||||
| const Step = ({ label, description, icon, iconComponent, completed, onClick, href }) => { | ||||
|   const content = ( | ||||
|     <> | ||||
|       <div className='onboarding__steps__item__icon'> | ||||
|         <Icon id={icon} /> | ||||
|         <Icon id={icon} icon={iconComponent} /> | ||||
|       </div> | ||||
| 
 | ||||
|       <div className='onboarding__steps__item__description'> | ||||
| @ -18,7 +18,7 @@ const Step = ({ label, description, icon, completed, onClick, href }) => { | ||||
|       </div> | ||||
| 
 | ||||
|       <div className={completed ? 'onboarding__steps__item__progress' : 'onboarding__steps__item__go'}> | ||||
|         {completed ? <Check /> : <ArrowSmallRight />} | ||||
|         {completed ? <Icon icon={CheckIcon} /> : <Icon icon={ArrowRightAltIcon} />} | ||||
|       </div> | ||||
|     </> | ||||
|   ); | ||||
| @ -42,6 +42,7 @@ Step.propTypes = { | ||||
|   label: PropTypes.node, | ||||
|   description: PropTypes.node, | ||||
|   icon: PropTypes.string, | ||||
|   iconComponent: PropTypes.func, | ||||
|   completed: PropTypes.bool, | ||||
|   href: PropTypes.string, | ||||
|   onClick: PropTypes.func, | ||||
|  | ||||
| @ -9,19 +9,24 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 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 illustration from 'mastodon/../images/elephant_ui_conversation.svg'; | ||||
| import { fetchAccount } from 'mastodon/actions/accounts'; | ||||
| import { focusCompose } from 'mastodon/actions/compose'; | ||||
| import { closeOnboarding } from 'mastodon/actions/onboarding'; | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| import Column from 'mastodon/features/ui/components/column'; | ||||
| import { me } from 'mastodon/initial_state'; | ||||
| import { makeGetAccount } from 'mastodon/selectors'; | ||||
| import { assetHost } from 'mastodon/utils/config'; | ||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| import ArrowSmallRight from './components/arrow_small_right'; | ||||
| import Step from './components/step'; | ||||
| import Follows from './follows'; | ||||
| import Share from './share'; | ||||
| @ -115,10 +120,10 @@ class Onboarding extends ImmutablePureComponent { | ||||
|           </div> | ||||
| 
 | ||||
|           <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.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.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.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.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' 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' 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' 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> | ||||
| 
 | ||||
|           <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'> | ||||
|             <Link to='/explore' className='onboarding__link'> | ||||
|               <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> | ||||
|               <ArrowSmallRight /> | ||||
|               <Icon icon={ArrowRightAltIcon} /> | ||||
|             </Link> | ||||
| 
 | ||||
|             <Link to='/home' className='onboarding__link'> | ||||
|               <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> | ||||
|               <ArrowSmallRight /> | ||||
|               <Icon icon={ArrowRightAltIcon} /> | ||||
|             </Link> | ||||
|           </div> | ||||
|         </div> | ||||
|  | ||||
| @ -9,6 +9,8 @@ import { Link } from 'react-router-dom'; | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 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 Column from 'mastodon/components/column'; | ||||
| @ -16,8 +18,6 @@ import ColumnBackButton from 'mastodon/components/column_back_button'; | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| import { me, domain } from 'mastodon/initial_state'; | ||||
| 
 | ||||
| import ArrowSmallRight from './components/arrow_small_right'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
|   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} /> | ||||
| 
 | ||||
|         <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> | ||||
|       </div> | ||||
|     ); | ||||
| @ -178,12 +178,12 @@ class Share extends PureComponent { | ||||
|           <div className='onboarding__links'> | ||||
|             <Link to='/home' className='onboarding__link'> | ||||
|               <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> | ||||
|               <ArrowSmallRight /> | ||||
|               <Icon icon={ArrowRightAltIcon} /> | ||||
|             </Link> | ||||
| 
 | ||||
|             <Link to='/explore' className='onboarding__link'> | ||||
|               <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> | ||||
|               <ArrowSmallRight /> | ||||
|               <Icon icon={ArrowRightAltIcon} /> | ||||
|             </Link> | ||||
|           </div> | ||||
| 
 | ||||
|  | ||||
| @ -9,6 +9,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| 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 { replyCompose } from 'mastodon/actions/compose'; | ||||
| 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 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) { | ||||
|       replyIcon = 'reply'; | ||||
|       replyIconComponent = RepeatIcon; | ||||
|       replyTitle = intl.formatMessage(messages.reply); | ||||
|     } else { | ||||
|       replyIcon = 'reply-all'; | ||||
|       replyIconComponent = ReplyAllIcon; | ||||
|       replyTitle = intl.formatMessage(messages.replyAll); | ||||
|     } | ||||
| 
 | ||||
| @ -193,10 +201,10 @@ class Footer extends ImmutablePureComponent { | ||||
| 
 | ||||
|     return ( | ||||
|       <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={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='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')} /> | ||||
|         {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')}`} />} | ||||
|         <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' 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' 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' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={`/@${status.getIn(['account', 'acct'])}/${status.get('id')}`} />} | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @ -8,6 +8,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||
| 
 | ||||
| import { Avatar } from 'mastodon/components/avatar'; | ||||
| import { DisplayName } from 'mastodon/components/display_name'; | ||||
| import { IconButton } from 'mastodon/components/icon_button'; | ||||
| @ -40,7 +42,7 @@ class Header extends ImmutablePureComponent { | ||||
|           <DisplayName account={account} /> | ||||
|         </Link> | ||||
| 
 | ||||
|         <IconButton icon='times' onClick={onClose} title={intl.formatMessage(messages.close)} /> | ||||
|         <IconButton icon='times' iconComponent={CloseIcon} onClick={onClose} title={intl.formatMessage(messages.close)} /> | ||||
|       </div> | ||||
|     ); | ||||
|   } | ||||
|  | ||||
| @ -8,6 +8,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as PushPinIcon } from '@material-symbols/svg-600/outlined/push_pin.svg'; | ||||
| 
 | ||||
| import { getStatusList } from 'mastodon/selectors'; | ||||
| 
 | ||||
| import { fetchPinnedStatuses } from '../../actions/pin_statuses'; | ||||
| @ -50,7 +52,7 @@ class PinnedStatuses extends ImmutablePureComponent { | ||||
|     const { intl, statusIds, hasMore, multiColumn } = this.props; | ||||
| 
 | ||||
|     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 /> | ||||
|         <StatusList | ||||
|           statusIds={statusIds} | ||||
|  | ||||
| @ -7,6 +7,8 @@ import { Helmet } from 'react-helmet'; | ||||
| 
 | ||||
| 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 { domain } from 'mastodon/initial_state'; | ||||
| 
 | ||||
| @ -131,6 +133,7 @@ class PublicTimeline extends PureComponent { | ||||
|       <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> | ||||
|         <ColumnHeader | ||||
|           icon='globe' | ||||
|           iconComponent={PublicIcon} | ||||
|           active={hasUnread} | ||||
|           title={intl.formatMessage(messages.title)} | ||||
|           onPin={this.handlePin} | ||||
|  | ||||
| @ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as RefreshIcon } from '@material-symbols/svg-600/outlined/refresh.svg'; | ||||
| import { debounce } from 'lodash'; | ||||
| 
 | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| @ -74,7 +75,7 @@ class Reblogs extends ImmutablePureComponent { | ||||
|           showBackButton | ||||
|           multiColumn={multiColumn} | ||||
|           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 { 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 { | ||||
| 
 | ||||
| @ -47,7 +49,7 @@ export default class Option extends PureComponent { | ||||
|           onKeyPress={this.handleKeyPress} | ||||
|           aria-checked={checked} | ||||
|           aria-label={label} | ||||
|         >{checked && <Check />}</span> | ||||
|         >{checked && <Icon icon={CheckIcon} />}</span> | ||||
| 
 | ||||
|         {labelComponent ? labelComponent : ( | ||||
|           <span className='poll__option__text'> | ||||
|  | ||||
| @ -1,26 +1,17 @@ | ||||
| import PropTypes from 'prop-types'; | ||||
| import { PureComponent } from 'react'; | ||||
| 
 | ||||
| import { injectIntl, defineMessages } from 'react-intl'; | ||||
| 
 | ||||
| import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| 
 | ||||
| import { Avatar } from 'mastodon/components/avatar'; | ||||
| import { DisplayName } from 'mastodon/components/display_name'; | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| import MediaAttachments from 'mastodon/components/media_attachments'; | ||||
| import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; | ||||
| import StatusContent from 'mastodon/components/status_content'; | ||||
| import { VisibilityIcon } from 'mastodon/components/visibility_icon'; | ||||
| 
 | ||||
| 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 { | ||||
| 
 | ||||
|   static propTypes = { | ||||
| @ -37,21 +28,12 @@ class StatusCheckBox extends PureComponent { | ||||
|   }; | ||||
| 
 | ||||
|   render () { | ||||
|     const { status, checked, intl } = this.props; | ||||
|     const { status, checked } = this.props; | ||||
| 
 | ||||
|     if (status.get('reblog')) { | ||||
|       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 = ( | ||||
|       <div className='status-check-box__status poll__option__text'> | ||||
|         <div className='detailed-status__display-name'> | ||||
| @ -60,7 +42,7 @@ class StatusCheckBox extends PureComponent { | ||||
|           </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> | ||||
| 
 | ||||
| @ -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 { 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 { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| @ -270,10 +279,13 @@ class ActionBar extends PureComponent { | ||||
|     } | ||||
| 
 | ||||
|     let replyIcon; | ||||
|     let replyIconComponent; | ||||
|     if (status.get('in_reply_to_id', null) === null) { | ||||
|       replyIcon = 'reply'; | ||||
|       replyIconComponent = ReplyIcon; | ||||
|     } else { | ||||
|       replyIcon = 'reply-all'; | ||||
|       replyIconComponent = ReplyAllIcon; | ||||
|     } | ||||
| 
 | ||||
|     const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; | ||||
| @ -291,13 +303,13 @@ class ActionBar extends PureComponent { | ||||
| 
 | ||||
|     return ( | ||||
|       <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 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='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='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 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' 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' 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' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} /></div> | ||||
| 
 | ||||
|         <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> | ||||
|     ); | ||||
|  | ||||
| @ -10,6 +10,10 @@ import classNames from 'classnames'; | ||||
| import Immutable from 'immutable'; | ||||
| 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 { Icon }  from 'mastodon/components/icon'; | ||||
| import { RelativeTimestamp } from 'mastodon/components/relative_timestamp'; | ||||
| @ -197,8 +201,8 @@ export default class Card extends PureComponent { | ||||
|             {revealed ? ( | ||||
|               <div className='status-card__actions' onClick={this.handleEmbedClick} role='none'> | ||||
|                 <div> | ||||
|                   <button type='button' onClick={this.handleEmbedClick}><Icon id='play' /></button> | ||||
|                   <a href={card.get('url')} target='_blank' rel='noopener noreferrer'><Icon id='external-link' /></a> | ||||
|                   <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' icon={OpenInNewIcon} /></a> | ||||
|                 </div> | ||||
|               </div> | ||||
|             ) : spoilerButton} | ||||
| @ -222,7 +226,7 @@ export default class Card extends PureComponent { | ||||
|     } else { | ||||
|       embed = ( | ||||
|         <div className='status-card__image'> | ||||
|           <Icon id='file-text' /> | ||||
|           <Icon id='file-text' icon={DescriptionIcon} /> | ||||
|         </div> | ||||
|       ); | ||||
|     } | ||||
|  | ||||
| @ -1,6 +1,6 @@ | ||||
| import PropTypes from 'prop-types'; | ||||
| 
 | ||||
| import { injectIntl, defineMessages, FormattedDate, FormattedMessage } from 'react-intl'; | ||||
| import { FormattedDate, FormattedMessage } from 'react-intl'; | ||||
| 
 | ||||
| import classNames from 'classnames'; | ||||
| 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 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 EditedTimestamp from 'mastodon/components/edited_timestamp'; | ||||
| import { getHashtagBarForStatus } from 'mastodon/components/hashtag_bar'; | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| import PictureInPicturePlaceholder from 'mastodon/components/picture_in_picture_placeholder'; | ||||
| import { VisibilityIcon } from 'mastodon/components/visibility_icon'; | ||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| import { Avatar } from '../../../components/avatar'; | ||||
| @ -25,13 +30,6 @@ import Video from '../../video'; | ||||
| 
 | ||||
| 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 { | ||||
| 
 | ||||
|   static propTypes = { | ||||
| @ -137,7 +135,7 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|   render () { | ||||
|     const status = this._properStatus(); | ||||
|     const outerStyle = { boxSizing: 'border-box' }; | ||||
|     const { intl, compact, pictureInPicture } = this.props; | ||||
|     const { compact, pictureInPicture } = this.props; | ||||
| 
 | ||||
|     if (!status) { | ||||
|       return null; | ||||
| @ -146,7 +144,8 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|     let media           = ''; | ||||
|     let applicationLink = ''; | ||||
|     let reblogLink = ''; | ||||
|     let reblogIcon = 'retweet'; | ||||
|     const reblogIcon = 'retweet'; | ||||
|     const reblogIconComponent = RepeatIcon; | ||||
|     let favouriteLink = ''; | ||||
|     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></>; | ||||
|     } | ||||
| 
 | ||||
|     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 visibilityLink = <> · <Icon id={visibilityIcon.icon} title={visibilityIcon.text} /></>; | ||||
|     const visibilityLink = <> · <VisibilityIcon visibility={status.get('visibility')} /></>; | ||||
| 
 | ||||
|     if (['private', 'direct'].includes(status.get('visibility'))) { | ||||
|       reblogLink = ''; | ||||
| @ -240,7 +231,7 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|         <> | ||||
|           {' · '} | ||||
|           <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'> | ||||
|               <AnimatedNumber value={status.get('reblogs_count')} /> | ||||
|             </span> | ||||
| @ -252,7 +243,7 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|         <> | ||||
|           {' · '} | ||||
|           <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'> | ||||
|               <AnimatedNumber value={status.get('reblogs_count')} /> | ||||
|             </span> | ||||
| @ -264,7 +255,7 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|     if (this.props.history) { | ||||
|       favouriteLink = ( | ||||
|         <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'> | ||||
|             <AnimatedNumber value={status.get('favourites_count')} /> | ||||
|           </span> | ||||
| @ -273,7 +264,7 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|     } else { | ||||
|       favouriteLink = ( | ||||
|         <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'> | ||||
|             <AnimatedNumber value={status.get('favourites_count')} /> | ||||
|           </span> | ||||
| @ -298,7 +289,7 @@ class DetailedStatus extends ImmutablePureComponent { | ||||
|         <div ref={this.setRef} className={classNames('detailed-status', { compact })}> | ||||
|           {status.get('visibility') === 'direct' && ( | ||||
|             <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' /> | ||||
|             </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 { 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 { Icon }  from 'mastodon/components/icon'; | ||||
| @ -685,7 +687,7 @@ class Status extends ImmutablePureComponent { | ||||
|           showBackButton | ||||
|           multiColumn={multiColumn} | ||||
|           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 { createSelector } from 'reselect'; | ||||
| 
 | ||||
| import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg'; | ||||
| 
 | ||||
| import { followAccount } from 'mastodon/actions/accounts'; | ||||
| import { Button } from 'mastodon/components/button'; | ||||
| import { IconButton } from 'mastodon/components/icon_button'; | ||||
| @ -101,7 +103,7 @@ class SubscribedLanguagesModal extends ImmutablePureComponent { | ||||
|     return ( | ||||
|       <div className='modal-root__modal report-dialog-modal'> | ||||
|         <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> }} /> | ||||
|         </div> | ||||
| 
 | ||||
|  | ||||
| @ -20,12 +20,12 @@ export default class ActionsModal extends ImmutablePureComponent { | ||||
|       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 ( | ||||
|       <li key={`${text}-${i}`}> | ||||
|         <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 className={classNames({ 'actions-modal__item-label': !!meta })}>{text}</div> | ||||
|             <div>{meta}</div> | ||||
|  | ||||
| @ -9,9 +9,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes'; | ||||
| import ImmutablePureComponent from 'react-immutable-pure-component'; | ||||
| import { connect } from 'react-redux'; | ||||
| 
 | ||||
| import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg'; | ||||
| 
 | ||||
| import { changeBoostPrivacy } from 'mastodon/actions/boosts'; | ||||
| import AttachmentList from 'mastodon/components/attachment_list'; | ||||
| import { Icon }  from 'mastodon/components/icon'; | ||||
| import { VisibilityIcon } from 'mastodon/components/visibility_icon'; | ||||
| import PrivacyDropdown from 'mastodon/features/compose/components/privacy_dropdown'; | ||||
| import { WithRouterPropTypes } from 'mastodon/utils/react_router'; | ||||
| 
 | ||||
| @ -24,10 +27,6 @@ import StatusContent from '../../../components/status_content'; | ||||
| const messages = defineMessages({ | ||||
|   cancel_reblog: { id: 'status.cancel_reblog_private', defaultMessage: 'Unboost' }, | ||||
|   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 => { | ||||
| @ -76,22 +75,13 @@ class BoostModal extends ImmutablePureComponent { | ||||
|     const { status, privacy, intl } = this.props; | ||||
|     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 ( | ||||
|       <div className='modal-root__modal boost-modal'> | ||||
|         <div className='boost-modal__container'> | ||||
|           <div className={classNames('status', `status-${status.get('visibility')}`, 'light')}> | ||||
|             <div className='status__info'> | ||||
|               <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')} /> | ||||
|               </a> | ||||
| 
 | ||||
| @ -116,7 +106,7 @@ class BoostModal extends ImmutablePureComponent { | ||||
|         </div> | ||||
| 
 | ||||
|         <div className='boost-modal__action-bar'> | ||||
|           <div><FormattedMessage id='boost_modal.combo' defaultMessage='You can press {combo} to skip this next time' values={{ combo: <span>Shift + <Icon id='retweet' /></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') && ( | ||||
|             <PrivacyDropdown | ||||
|               noDirect | ||||
|  | ||||
| @ -3,6 +3,8 @@ import { PureComponent } from 'react'; | ||||
| 
 | ||||
| import { defineMessages, injectIntl } from 'react-intl'; | ||||
| 
 | ||||
| import { ReactComponent as RefreshIcon } from '@material-symbols/svg-600/outlined/refresh.svg'; | ||||
| 
 | ||||
| import { IconButton } from '../../../components/icon_button'; | ||||
| 
 | ||||
| const messages = defineMessages({ | ||||
| @ -31,7 +33,7 @@ class BundleModalError extends PureComponent { | ||||
|     return ( | ||||
|       <div className='modal-root__modal error-modal'> | ||||
|         <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)} | ||||
|         </div> | ||||
| 
 | ||||
|  | ||||
| @ -13,6 +13,7 @@ export default class Column extends PureComponent { | ||||
|   static propTypes = { | ||||
|     heading: PropTypes.string, | ||||
|     icon: PropTypes.string, | ||||
|     iconComponent: PropTypes.func, | ||||
|     children: PropTypes.node, | ||||
|     active: PropTypes.bool, | ||||
|     hideHeadingOnMobile: PropTypes.bool, | ||||
| @ -50,13 +51,13 @@ export default class Column extends PureComponent { | ||||
|   }; | ||||
| 
 | ||||
|   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 columnHeaderId = showHeading && heading.replace(/ /g, '-'); | ||||
|     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 ( | ||||
|       <div | ||||
|  | ||||
| @ -24,7 +24,7 @@ export default class ColumnHeader extends PureComponent { | ||||
|     let iconElement = ''; | ||||
| 
 | ||||
|     if (icon) { | ||||
|       iconElement = <Icon id={icon} fixedWidth className='column-header__icon' />; | ||||
|       iconElement = <Icon id={icon} className='column-header__icon' />; | ||||
|     } | ||||
| 
 | ||||
|     return ( | ||||
|  | ||||
| @ -5,10 +5,10 @@ import { NavLink } from 'react-router-dom'; | ||||
| 
 | ||||
| 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 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) { | ||||
|     return ( | ||||
| @ -31,6 +31,7 @@ const ColumnLink = ({ icon, text, to, href, method, badge, transparent, ...other | ||||
| 
 | ||||
| ColumnLink.propTypes = { | ||||
|   icon: PropTypes.oneOfType([PropTypes.string, PropTypes.node]).isRequired, | ||||
|   iconComponent: PropTypes.func, | ||||
|   text: PropTypes.string.isRequired, | ||||
|   to: 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'; | ||||
| 
 | ||||
| import BundleColumnError from './bundle_column_error'; | ||||
| import ColumnLoading from './column_loading'; | ||||
| import { ColumnLoading } from './column_loading'; | ||||
| import ComposePanel from './compose_panel'; | ||||
| import DrawerLoading from './drawer_loading'; | ||||
| 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