v4.3.0-alpha.0+glitch+cat+1.0.8

This commit is contained in:
Jeremy Kescher 2024-01-14 17:47:14 +01:00
commit 2047683539
No known key found for this signature in database
GPG key ID: 80A419A7A613DFA4
156 changed files with 1471 additions and 1087 deletions

View file

@ -7,7 +7,9 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { Icon } from 'flavours/glitch/components/icon'; import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
import { Icon } from 'flavours/glitch/components/icon';
const filename = url => url.split('/').pop().split('#')[0].split('?')[0]; const filename = url => url.split('/').pop().split('#')[0].split('?')[0];
@ -25,7 +27,7 @@ export default class AttachmentList extends ImmutablePureComponent {
<div className={classNames('attachment-list', { compact })}> <div className={classNames('attachment-list', { compact })}>
{!compact && ( {!compact && (
<div className='attachment-list__icon'> <div className='attachment-list__icon'>
<Icon id='link' /> <Icon id='link' icon={LinkIcon} />
</div> </div>
)} )}
@ -36,7 +38,7 @@ export default class AttachmentList extends ImmutablePureComponent {
return ( return (
<li key={attachment.get('id')}> <li key={attachment.get('id')}>
<a href={displayUrl} target='_blank' rel='noopener noreferrer'> <a href={displayUrl} target='_blank' rel='noopener noreferrer'>
{compact && <Icon id='link' />} {compact && <Icon id='link' icon={LinkIcon} />}
{compact && ' ' } {compact && ' ' }
{displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />} {displayUrl ? filename(displayUrl) : <FormattedMessage id='attachments_list.unprocessed' defaultMessage='(unprocessed)' />}
</a> </a>

View file

@ -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>
);

View file

@ -1,63 +0,0 @@
import PropTypes from 'prop-types';
import { PureComponent } from 'react';
import { createPortal } from 'react-dom';
import { FormattedMessage } from 'react-intl';
import { withRouter } from 'react-router-dom';
import { Icon } from 'flavours/glitch/components/icon';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
export class ColumnBackButton extends PureComponent {
static propTypes = {
multiColumn: PropTypes.bool,
onClick: PropTypes.func,
...WithRouterPropTypes,
};
handleClick = () => {
const { onClick, history } = this.props;
if (onClick) {
onClick();
} else if (history.location?.state?.fromMastodon) {
history.goBack();
} else {
history.push('/');
}
};
render () {
const { multiColumn } = this.props;
const component = (
<button onClick={this.handleClick} className='column-back-button'>
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
</button>
);
if (multiColumn) {
return component;
} else {
// The portal container and the component may be rendered to the DOM in
// the same React render pass, so the container might not be available at
// the time `render()` is called.
const container = document.getElementById('tabs-bar__portal');
if (container === null) {
// The container wasn't available, force a re-render so that the
// component can eventually be inserted in the container and not scroll
// with the rest of the area.
this.forceUpdate();
return component;
} else {
return createPortal(component, container);
}
}
}
}
export default withRouter(ColumnBackButton);

View file

@ -0,0 +1,45 @@
import { useCallback } from 'react';
import { FormattedMessage } from 'react-intl';
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
import { Icon } from 'flavours/glitch/components/icon';
import { ButtonInTabsBar } from 'flavours/glitch/features/ui/util/columns_context';
import { useAppHistory } from './router';
type OnClickCallback = () => void;
function useHandleClick(onClick?: OnClickCallback) {
const history = useAppHistory();
return useCallback(() => {
if (onClick) {
onClick();
} else if (history.location.state?.fromMastodon) {
history.goBack();
} else {
history.push('/');
}
}, [history, onClick]);
}
export const ColumnBackButton: React.FC<{ onClick: OnClickCallback }> = ({
onClick,
}) => {
const handleClick = useHandleClick(onClick);
const component = (
<button onClick={handleClick} className='column-back-button'>
<Icon
id='chevron-left'
icon={ArrowBackIcon}
className='column-back-button__icon'
/>
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
</button>
);
return <ButtonInTabsBar>{component}</ButtonInTabsBar>;
};

View file

@ -1,40 +0,0 @@
import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl';
import { withRouter } from 'react-router-dom';
import { Icon } from 'flavours/glitch/components/icon';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
class ColumnBackButtonSlim extends PureComponent {
static propTypes = {
...WithRouterPropTypes,
};
handleClick = () => {
const { location, history } = this.props;
// Check if there is a previous page in the app to go back to per https://stackoverflow.com/a/70532858/9703201
// When upgrading to V6, check `location.key !== 'default'` instead per https://github.com/remix-run/history/blob/main/docs/api-reference.md#location
if (location.key) {
history.goBack();
} else {
history.push('/');
}
};
render () {
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 />
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
</div>
</div>
);
}
}
export default withRouter(ColumnBackButtonSlim);

View file

@ -1,15 +1,24 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { PureComponent } from 'react'; import { PureComponent, useCallback } from 'react';
import { createPortal } from 'react-dom';
import { FormattedMessage, injectIntl, defineMessages } from 'react-intl'; import { FormattedMessage, injectIntl, defineMessages } from 'react-intl';
import classNames from 'classnames'; import classNames from 'classnames';
import { withRouter } from 'react-router-dom'; import { withRouter } from 'react-router-dom';
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
import { ReactComponent as ArrowBackIcon } from '@material-symbols/svg-600/outlined/arrow_back.svg';
import { ReactComponent as ChevronLeftIcon } from '@material-symbols/svg-600/outlined/chevron_left.svg';
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { ReactComponent as TuneIcon } from '@material-symbols/svg-600/outlined/tune.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { ButtonInTabsBar, useColumnsContext } from 'flavours/glitch/features/ui/util/columns_context';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
import { useAppHistory } from './router';
const messages = defineMessages({ const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' }, show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' }, hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
@ -17,6 +26,34 @@ const messages = defineMessages({
moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' }, moveRight: { id: 'column_header.moveRight_settings', defaultMessage: 'Move column to the right' },
}); });
const BackButton = ({ pinned, show }) => {
const history = useAppHistory();
const { multiColumn } = useColumnsContext();
const handleBackClick = useCallback(() => {
if (history.location?.state?.fromMastodon) {
history.goBack();
} else {
history.push('/');
}
}, [history]);
const showButton = history && !pinned && ((multiColumn && history.location?.state?.fromMastodon) || show);
if(!showButton) return null;
return (<button onClick={handleBackClick} className='column-header__back-button'>
<Icon id='chevron-left' icon={ArrowBackIcon} className='column-back-button__icon' />
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
</button>);
};
BackButton.propTypes = {
pinned: PropTypes.bool,
show: PropTypes.bool,
};
class ColumnHeader extends PureComponent { class ColumnHeader extends PureComponent {
static contextTypes = { static contextTypes = {
@ -27,6 +64,7 @@ class ColumnHeader extends PureComponent {
intl: PropTypes.object.isRequired, intl: PropTypes.object.isRequired,
title: PropTypes.node, title: PropTypes.node,
icon: PropTypes.string, icon: PropTypes.string,
iconComponent: PropTypes.func,
active: PropTypes.bool, active: PropTypes.bool,
multiColumn: PropTypes.bool, multiColumn: PropTypes.bool,
extraButton: PropTypes.node, extraButton: PropTypes.node,
@ -64,16 +102,6 @@ class ColumnHeader extends PureComponent {
this.props.onMove(1); this.props.onMove(1);
}; };
handleBackClick = () => {
const { history } = this.props;
if (history.location?.state?.fromMastodon) {
history.goBack();
} else {
history.push('/');
}
};
handleTransitionEnd = () => { handleTransitionEnd = () => {
this.setState({ animating: false }); this.setState({ animating: false });
}; };
@ -87,7 +115,7 @@ class ColumnHeader extends PureComponent {
}; };
render () { render () {
const { title, icon, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues, history } = this.props; const { title, icon, iconComponent, active, children, pinned, multiColumn, extraButton, showBackButton, intl: { formatMessage }, placeholder, appendContent, collapseIssues } = this.props;
const { collapsed, animating } = this.state; const { collapsed, animating } = this.state;
const wrapperClassName = classNames('column-header__wrapper', { const wrapperClassName = classNames('column-header__wrapper', {
@ -118,26 +146,19 @@ class ColumnHeader extends PureComponent {
} }
if (multiColumn && pinned) { if (multiColumn && pinned) {
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='times' /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>; pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='times' icon={CloseIcon} /> <FormattedMessage id='column_header.unpin' defaultMessage='Unpin' /></button>;
moveButtons = ( moveButtons = (
<div key='move-buttons' className='column-header__setting-arrows'> <div key='move-buttons' className='column-header__setting-arrows'>
<button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='icon-button column-header__setting-btn' onClick={this.handleMoveLeft}><Icon id='chevron-left' /></button> <button title={formatMessage(messages.moveLeft)} aria-label={formatMessage(messages.moveLeft)} className='icon-button column-header__setting-btn' onClick={this.handleMoveLeft}><Icon id='chevron-left' icon={ChevronLeftIcon} /></button>
<button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='icon-button column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' /></button> <button title={formatMessage(messages.moveRight)} aria-label={formatMessage(messages.moveRight)} className='icon-button column-header__setting-btn' onClick={this.handleMoveRight}><Icon id='chevron-right' icon={ChevronRightIcon} /></button>
</div> </div>
); );
} else if (multiColumn && this.props.onPin) { } else if (multiColumn && this.props.onPin) {
pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>; pinButton = <button key='pin-button' className='text-btn column-header__setting-btn' onClick={this.handlePin}><Icon id='plus' icon={AddIcon} /> <FormattedMessage id='column_header.pin' defaultMessage='Pin' /></button>;
} }
if (!pinned && ((multiColumn && history.location?.state?.fromMastodon) || showBackButton)) { backButton = <BackButton pinned={pinned} show={showBackButton} />;
backButton = (
<button onClick={this.handleBackClick} className='column-header__back-button'>
<Icon id='chevron-left' className='column-back-button__icon' fixedWidth />
<FormattedMessage id='column_back_button.label' defaultMessage='Back' />
</button>
);
}
const collapsedContent = [ const collapsedContent = [
extraContent, extraContent,
@ -157,21 +178,21 @@ class ColumnHeader extends PureComponent {
onClick={this.handleToggleClick} onClick={this.handleToggleClick}
> >
<i className='icon-with-badge'> <i className='icon-with-badge'>
<Icon id='sliders' /> <Icon id='sliders' icon={TuneIcon} />
{collapseIssues && <i className='icon-with-badge__issue-badge' />} {collapseIssues && <i className='icon-with-badge__issue-badge' />}
</i> </i>
</button> </button>
); );
} }
const hasTitle = icon && title; const hasTitle = (icon || iconComponent) && title;
const component = ( const component = (
<div className={wrapperClassName}> <div className={wrapperClassName}>
<h1 className={buttonClassName}> <h1 className={buttonClassName}>
{hasTitle && ( {hasTitle && (
<button onClick={this.handleTitleClick}> <button onClick={this.handleTitleClick}>
<Icon id={icon} fixedWidth className='column-header__icon' /> <Icon id={icon} icon={iconComponent} className='column-header__icon' />
{title} {title}
</button> </button>
)} )}
@ -195,22 +216,12 @@ class ColumnHeader extends PureComponent {
</div> </div>
); );
if (multiColumn || placeholder) { if (placeholder) {
return component; return component;
} else { } else {
// The portal container and the component may be rendered to the DOM in return (<ButtonInTabsBar>
// the same React render pass, so the container might not be available at {component}
// the time `render()` is called. </ButtonInTabsBar>);
const container = document.getElementById('tabs-bar__portal');
if (container === null) {
// The container wasn't available, force a re-render so that the
// component can eventually be inserted in the container and not scroll
// with the rest of the area.
this.forceUpdate();
return component;
} else {
return createPortal(component, container);
}
} }
} }

View file

@ -8,6 +8,8 @@ import { useCallback, useState, useEffect } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { changeSetting } from 'flavours/glitch/actions/settings'; import { changeSetting } from 'flavours/glitch/actions/settings';
import { bannerSettings } from 'flavours/glitch/settings'; import { bannerSettings } from 'flavours/glitch/settings';
import { useAppSelector, useAppDispatch } from 'flavours/glitch/store'; import { useAppSelector, useAppDispatch } from 'flavours/glitch/store';
@ -55,6 +57,7 @@ export const DismissableBanner: React.FC<PropsWithChildren<Props>> = ({
<div className='dismissable-banner__action'> <div className='dismissable-banner__action'>
<IconButton <IconButton
icon='times' icon='times'
iconComponent={CloseIcon}
title={intl.formatMessage(messages.dismiss)} title={intl.formatMessage(messages.dismiss)}
onClick={handleDismiss} onClick={handleDismiss}
/> />

View file

@ -2,6 +2,8 @@ import { useCallback } from 'react';
import { defineMessages, useIntl } from 'react-intl'; import { defineMessages, useIntl } from 'react-intl';
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
import { IconButton } from './icon_button'; import { IconButton } from './icon_button';
const messages = defineMessages({ const messages = defineMessages({
@ -34,6 +36,7 @@ export const Domain: React.FC<Props> = ({ domain, onUnblockDomain }) => {
<IconButton <IconButton
active active
icon='unlock' icon='unlock'
iconComponent={LockOpenIcon}
title={intl.formatMessage(messages.unblockDomain, { domain })} title={intl.formatMessage(messages.unblockDomain, { domain })}
onClick={handleDomainUnblock} onClick={handleDomainUnblock}
/> />

View file

@ -6,6 +6,7 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { supportsPassiveEvents } from 'detect-passive-events'; import { supportsPassiveEvents } from 'detect-passive-events';
import Overlay from 'react-overlays/Overlay'; import Overlay from 'react-overlays/Overlay';
@ -163,6 +164,7 @@ class Dropdown extends PureComponent {
static propTypes = { static propTypes = {
children: PropTypes.node, children: PropTypes.node,
icon: PropTypes.string, icon: PropTypes.string,
iconComponent: PropTypes.func,
items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired, items: PropTypes.oneOfType([PropTypes.array, ImmutablePropTypes.list]).isRequired,
loading: PropTypes.bool, loading: PropTypes.bool,
size: PropTypes.number, size: PropTypes.number,
@ -255,7 +257,7 @@ class Dropdown extends PureComponent {
}; };
findTarget = () => { findTarget = () => {
return this.target; return this.target?.buttonRef?.current ?? this.target;
}; };
componentWillUnmount = () => { componentWillUnmount = () => {
@ -271,6 +273,7 @@ class Dropdown extends PureComponent {
render () { render () {
const { const {
icon, icon,
iconComponent,
items, items,
size, size,
title, title,
@ -291,9 +294,11 @@ class Dropdown extends PureComponent {
onMouseDown: this.handleMouseDown, onMouseDown: this.handleMouseDown,
onKeyDown: this.handleButtonKeyDown, onKeyDown: this.handleButtonKeyDown,
onKeyPress: this.handleKeyPress, onKeyPress: this.handleKeyPress,
ref: this.setTargetRef,
}) : ( }) : (
<IconButton <IconButton
icon={icon} icon={!open ? icon : 'close'}
iconComponent={!open ? iconComponent : CloseIcon}
title={title} title={title}
active={open} active={open}
disabled={disabled} disabled={disabled}
@ -302,14 +307,14 @@ class Dropdown extends PureComponent {
onMouseDown={this.handleMouseDown} onMouseDown={this.handleMouseDown}
onKeyDown={this.handleButtonKeyDown} onKeyDown={this.handleButtonKeyDown}
onKeyPress={this.handleKeyPress} onKeyPress={this.handleKeyPress}
ref={this.setTargetRef}
/> />
); );
return ( return (
<> <>
<span ref={this.setTargetRef}> {button}
{button}
</span>
<Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}> <Overlay show={open} offset={[5, 5]} placement={'bottom'} flip target={this.findTarget} popperConfig={{ strategy: 'fixed' }}>
{({ props, arrowProps, placement }) => ( {({ props, arrowProps, placement }) => (
<div {...props}> <div {...props}>

View file

@ -5,6 +5,8 @@ import { FormattedMessage, injectIntl } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as ArrowDropDownIcon } from '@material-symbols/svg-600/outlined/arrow_drop_down.svg';
import { openModal } from 'flavours/glitch/actions/modal'; import { openModal } from 'flavours/glitch/actions/modal';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import InlineAccount from 'flavours/glitch/components/inline_account'; import InlineAccount from 'flavours/glitch/components/inline_account';
@ -66,7 +68,7 @@ class EditedTimestamp extends PureComponent {
return ( return (
<DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}> <DropdownMenu statusId={statusId} renderItem={this.renderItem} scrollable renderHeader={this.renderHeader} onItemClick={this.handleItemClick}>
<button className='dropdown-menu__text-button'> <button className='dropdown-menu__text-button'>
<FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { hour12: false, month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} /> <Icon id='caret-down' /> <FormattedMessage id='status.edited' defaultMessage='Edited {date}' values={{ date: intl.formatDate(timestamp, { hour12: false, month: 'short', day: '2-digit', hour: '2-digit', minute: '2-digit' }) }} /> <Icon id='caret-down' icon={ArrowDropDownIcon} />
</button> </button>
</DropdownMenu> </DropdownMenu>
); );

View file

@ -1,20 +1,52 @@
import classNames from 'classnames'; import classNames from 'classnames';
interface Props extends React.HTMLAttributes<HTMLImageElement> { import { ReactComponent as CheckBoxOutlineBlankIcon } from '@material-symbols/svg-600/outlined/check_box_outline_blank.svg';
id: string;
className?: string; interface SVGPropsWithTitle extends React.SVGProps<SVGSVGElement> {
fixedWidth?: boolean; title?: string;
}
export type IconProp = React.FC<SVGPropsWithTitle>;
interface Props extends React.SVGProps<SVGSVGElement> {
children?: never; children?: never;
id: string;
icon: IconProp;
title?: string;
} }
export const Icon: React.FC<Props> = ({ export const Icon: React.FC<Props> = ({
id, id,
icon: IconComponent,
className, className,
fixedWidth, title: titleProp,
...other ...other
}) => ( }) => {
<i // eslint-disable-next-line @typescript-eslint/no-unnecessary-condition
className={classNames('fa', `fa-${id}`, className, { 'fa-fw': fixedWidth })} if (!IconComponent) {
{...other} if (process.env.NODE_ENV !== 'production') {
/> throw new Error(
); `<Icon id="${id}" className="${className}"> 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}
/>
);
};

View file

@ -1,19 +1,20 @@
import { PureComponent } from 'react'; import { PureComponent, createRef } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { AnimatedNumber } from './animated_number'; import { AnimatedNumber } from './animated_number';
import type { IconProp } from './icon';
import { Icon } from './icon'; import { Icon } from './icon';
interface Props { interface Props {
className?: string; className?: string;
title: string; title: string;
icon: string; icon: string;
iconComponent: IconProp;
onClick?: React.MouseEventHandler<HTMLButtonElement>; onClick?: React.MouseEventHandler<HTMLButtonElement>;
onMouseDown?: React.MouseEventHandler<HTMLButtonElement>; onMouseDown?: React.MouseEventHandler<HTMLButtonElement>;
onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>; onKeyDown?: React.KeyboardEventHandler<HTMLButtonElement>;
onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>; onKeyPress?: React.KeyboardEventHandler<HTMLButtonElement>;
size: number;
active: boolean; active: boolean;
expanded?: boolean; expanded?: boolean;
style?: React.CSSProperties; style?: React.CSSProperties;
@ -34,8 +35,9 @@ interface States {
deactivate: boolean; deactivate: boolean;
} }
export class IconButton extends PureComponent<Props, States> { export class IconButton extends PureComponent<Props, States> {
buttonRef = createRef<HTMLButtonElement>();
static defaultProps = { static defaultProps = {
size: 18,
active: false, active: false,
disabled: false, disabled: false,
animate: false, animate: false,
@ -86,24 +88,10 @@ export class IconButton extends PureComponent<Props, States> {
}; };
render() { render() {
// Hack required for some icons which have an overriden size
let containerSize = '1.28571429em';
if (this.props.style?.fontSize) {
containerSize = `${this.props.size * 1.28571429}px`;
}
const style = { const style = {
fontSize: `${this.props.size}px`,
height: containerSize,
lineHeight: `${this.props.size}px`,
...this.props.style, ...this.props.style,
...(this.props.active ? this.props.activeStyle : {}), ...(this.props.active ? this.props.activeStyle : {}),
}; };
if (!this.props.label) {
style.width = containerSize;
} else {
style.textAlign = 'left';
}
const { const {
active, active,
@ -111,6 +99,7 @@ export class IconButton extends PureComponent<Props, States> {
disabled, disabled,
expanded, expanded,
icon, icon,
iconComponent,
inverted, inverted,
overlay, overlay,
tabIndex, tabIndex,
@ -133,13 +122,9 @@ export class IconButton extends PureComponent<Props, States> {
'icon-button--with-counter': typeof counter !== 'undefined', 'icon-button--with-counter': typeof counter !== 'undefined',
}); });
if (typeof counter !== 'undefined') {
style.width = 'auto';
}
let contents = ( let contents = (
<> <>
<Icon id={icon} fixedWidth aria-hidden='true' />{' '} <Icon id={icon} icon={iconComponent} aria-hidden='true' />{' '}
{typeof counter !== 'undefined' && ( {typeof counter !== 'undefined' && (
<span className='icon-button__counter'> <span className='icon-button__counter'>
<AnimatedNumber value={counter} obfuscate={obfuscateCount} /> <AnimatedNumber value={counter} obfuscate={obfuscateCount} />
@ -172,6 +157,7 @@ export class IconButton extends PureComponent<Props, States> {
style={style} style={style}
tabIndex={tabIndex} tabIndex={tabIndex}
disabled={disabled} disabled={disabled}
ref={this.buttonRef}
> >
{contents} {contents}
</button> </button>

View file

@ -1,21 +1,24 @@
import type { IconProp } from './icon';
import { Icon } from './icon'; import { Icon } from './icon';
const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num); const formatNumber = (num: number): number | string => (num > 40 ? '40+' : num);
interface Props { interface Props {
id: string; id: string;
icon: IconProp;
count: number; count: number;
issueBadge: boolean; issueBadge: boolean;
className: string; className: string;
} }
export const IconWithBadge: React.FC<Props> = ({ export const IconWithBadge: React.FC<Props> = ({
id, id,
icon,
count, count,
issueBadge, issueBadge,
className, className,
}) => ( }) => (
<i className='icon-with-badge'> <i className='icon-with-badge'>
<Icon id={id} fixedWidth className={className} /> <Icon id={id} icon={icon} className={className} />
{count > 0 && ( {count > 0 && (
<i className='icon-with-badge__badge'>{formatNumber(count)}</i> <i className='icon-with-badge__badge'>{formatNumber(count)}</i>
)} )}

View file

@ -2,6 +2,8 @@ import { useCallback } from 'react';
import { useIntl, defineMessages } from 'react-intl'; import { useIntl, defineMessages } from 'react-intl';
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
const messages = defineMessages({ const messages = defineMessages({
@ -28,7 +30,7 @@ export const LoadGap: React.FC<Props> = ({ disabled, maxId, onClick }) => {
onClick={handleClick} onClick={handleClick}
aria-label={intl.formatMessage(messages.load_more)} aria-label={intl.formatMessage(messages.load_more)}
> >
<Icon id='ellipsis-h' /> <Icon id='ellipsis-h' icon={MoreHorizIcon} />
</button> </button>
); );
}; };

View file

@ -8,6 +8,7 @@ import classNames from 'classnames';
import { is } from 'immutable'; import { is } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { Blurhash } from 'flavours/glitch/components/blurhash'; import { Blurhash } from 'flavours/glitch/components/blurhash';
@ -362,7 +363,7 @@ class MediaGallery extends PureComponent {
</button> </button>
); );
} else if (visible) { } else if (visible) {
spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' overlay onClick={this.handleOpen} ariaHidden />; spoilerButton = <IconButton title={intl.formatMessage(messages.toggle_visible, { number: size })} icon='eye-slash' iconComponent={VisibilityOffIcon} overlay onClick={this.handleOpen} ariaHidden />;
} else { } else {
spoilerButton = ( spoilerButton = (
<button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'> <button type='button' onClick={this.handleOpen} className='spoiler-button__overlay'>

View file

@ -4,8 +4,6 @@
* a Confirm and Abort buttons are shown in its place. * a Confirm and Abort buttons are shown in its place.
*/ */
// Package imports //
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
@ -14,6 +12,8 @@ import classNames from 'classnames';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
const messages = defineMessages({ const messages = defineMessages({
@ -53,7 +53,7 @@ class NotificationPurgeButtons extends ImmutablePureComponent {
</button> </button>
<button onClick={this.props.onDeleteMarked} className='column-header__button'> <button onClick={this.props.onDeleteMarked} className='column-header__button'>
<Icon id='trash' /><br />{intl.formatMessage(messages.btnApply)} <Icon id='trash' icon={DeleteIcon} /><br />{intl.formatMessage(messages.btnApply)}
</button> </button>
</div> </div>
); );

View file

@ -5,6 +5,8 @@ import { FormattedMessage } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as CancelPresentationIcon } from '@material-symbols/svg-600/outlined/cancel_presentation.svg';
import { removePictureInPicture } from 'flavours/glitch/actions/picture_in_picture'; import { removePictureInPicture } from 'flavours/glitch/actions/picture_in_picture';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -22,7 +24,7 @@ class PictureInPicturePlaceholder extends PureComponent {
render () { render () {
return ( return (
<div className='picture-in-picture-placeholder' role='button' tabIndex={0} onClick={this.handleClick}> <div className='picture-in-picture-placeholder' role='button' tabIndex={0} onClick={this.handleClick}>
<Icon id='window-restore' /> <Icon id='window-restore' icon={CancelPresentationIcon} />
<FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' /> <FormattedMessage id='picture_in_picture.restore' defaultMessage='Put it back' />
</div> </div>
); );

View file

@ -7,6 +7,7 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
import escapeTextContentForBrowser from 'escape-html'; import escapeTextContentForBrowser from 'escape-html';
import spring from 'react-motion/lib/spring'; import spring from 'react-motion/lib/spring';
@ -192,7 +193,7 @@ class Poll extends ImmutablePureComponent {
/> />
{!!voted && <span className='poll__voted'> {!!voted && <span className='poll__voted'>
<Icon id='check' className='poll__voted__mark' title={intl.formatMessage(messages.voted)} /> <Icon id='check' icon={CheckIcon} className='poll__voted__mark' title={intl.formatMessage(messages.voted)} />
</span>} </span>}
</label> </label>

View file

@ -1,7 +1,7 @@
import type { PropsWithChildren } from 'react'; import type { PropsWithChildren } from 'react';
import React from 'react'; import React from 'react';
import { Router as OriginalRouter } from 'react-router'; import { Router as OriginalRouter, useHistory } from 'react-router';
import type { import type {
LocationDescriptor, LocationDescriptor,
@ -16,18 +16,23 @@ interface MastodonLocationState {
fromMastodon?: boolean; fromMastodon?: boolean;
mastodonModalKey?: string; mastodonModalKey?: string;
} }
type HistoryPath = Path | LocationDescriptor<MastodonLocationState>;
const browserHistory = createBrowserHistory< type LocationState = MastodonLocationState | null | undefined;
MastodonLocationState | undefined
>(); type HistoryPath = Path | LocationDescriptor<LocationState>;
const browserHistory = createBrowserHistory<LocationState>();
const originalPush = browserHistory.push.bind(browserHistory); const originalPush = browserHistory.push.bind(browserHistory);
const originalReplace = browserHistory.replace.bind(browserHistory); const originalReplace = browserHistory.replace.bind(browserHistory);
export function useAppHistory() {
return useHistory<LocationState>();
}
function normalizePath( function normalizePath(
path: HistoryPath, path: HistoryPath,
state?: MastodonLocationState, state?: LocationState,
): LocationDescriptorObject<MastodonLocationState> { ): LocationDescriptorObject<LocationState> {
const location = typeof path === 'string' ? { pathname: path } : { ...path }; const location = typeof path === 'string' ? { pathname: path } : { ...path };
if (location.state === undefined && state !== undefined) { if (location.state === undefined && state !== undefined) {

View file

@ -8,9 +8,21 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as 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 'flavours/glitch/permissions'; import { PERMISSION_MANAGE_USERS, PERMISSION_MANAGE_FEDERATION } from 'flavours/glitch/permissions';
import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links'; import { accountAdminLink, statusAdminLink } from 'flavours/glitch/utils/backend_links';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
import { ReactComponent as RepeatDisabledIcon } from 'mastodon/../svg-icons/repeat_disabled.svg';
import { ReactComponent as RepeatPrivateIcon } from 'mastodon/../svg-icons/repeat_private.svg';
import DropdownMenuContainer from '../containers/dropdown_menu_container'; import DropdownMenuContainer from '../containers/dropdown_menu_container';
import EmojiPickerDropdown from '../features/compose/containers/emoji_picker_dropdown_container'; import EmojiPickerDropdown from '../features/compose/containers/emoji_picker_dropdown_container';
@ -217,6 +229,7 @@ class StatusActionBar extends ImmutablePureComponent {
let menu = []; let menu = [];
let reblogIcon = 'retweet'; let reblogIcon = 'retweet';
let replyIcon; let replyIcon;
let replyIconComponent;
let replyTitle; let replyTitle;
menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen }); menu.push({ text: intl.formatMessage(messages.open), action: this.handleOpen });
@ -286,27 +299,34 @@ class StatusActionBar extends ImmutablePureComponent {
if (status.get('in_reply_to_id', null) === null) { if (status.get('in_reply_to_id', null) === null) {
replyIcon = 'reply'; replyIcon = 'reply';
replyIconComponent = ReplyIcon;
replyTitle = intl.formatMessage(messages.reply); replyTitle = intl.formatMessage(messages.reply);
} else { } else {
replyIcon = 'reply-all'; replyIcon = 'reply-all';
replyIconComponent = ReplyAllIcon;
replyTitle = intl.formatMessage(messages.replyAll); replyTitle = intl.formatMessage(messages.replyAll);
} }
const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
let reblogTitle = ''; let reblogTitle, reblogIconComponent;
if (status.get('reblogged')) { if (status.get('reblogged')) {
reblogTitle = intl.formatMessage(messages.cancel_reblog_private); reblogTitle = intl.formatMessage(messages.cancel_reblog_private);
reblogIconComponent = publicStatus ? RepeatIcon : RepeatPrivateIcon;
} else if (publicStatus) { } else if (publicStatus) {
reblogTitle = intl.formatMessage(messages.reblog); reblogTitle = intl.formatMessage(messages.reblog);
reblogIconComponent = RepeatIcon;
} else if (reblogPrivate) { } else if (reblogPrivate) {
reblogTitle = intl.formatMessage(messages.reblog_private); reblogTitle = intl.formatMessage(messages.reblog_private);
reblogIconComponent = RepeatPrivateIcon;
} else { } else {
reblogTitle = intl.formatMessage(messages.cannot_reblog); reblogTitle = intl.formatMessage(messages.cannot_reblog);
reblogIconComponent = RepeatDisabledIcon;
} }
const filterButton = this.props.onFilter && ( const filterButton = this.props.onFilter && (
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.hide)} icon='eye' onClick={this.handleHideClick} /> <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.hide)} icon='eye' iconComponent={VisibilityIcon} onClick={this.handleHideClick} />
); );
const canReact = permissions && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions; const canReact = permissions && status.get('reactions').filter(r => r.get('count') > 0 && r.get('me')).size < maxReactions;
@ -326,32 +346,32 @@ class StatusActionBar extends ImmutablePureComponent {
className='status__action-bar-button' className='status__action-bar-button'
title={replyTitle} title={replyTitle}
icon={replyIcon} icon={replyIcon}
iconComponent={replyIconComponent}
onClick={this.handleReplyClick} onClick={this.handleReplyClick}
counter={showReplyCount ? status.get('replies_count') : undefined} counter={showReplyCount ? status.get('replies_count') : undefined}
obfuscateCount obfuscateCount
/> />
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon={reblogIcon} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} /> <IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon={reblogIcon} iconComponent={reblogIconComponent} onClick={this.handleReblogClick} counter={withCounters ? status.get('reblogs_count') : undefined} />
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} /> <IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={status.get('favourited') ? StarIcon : StarBorderIcon} onClick={this.handleFavouriteClick} counter={withCounters ? status.get('favourites_count') : undefined} />
{ {
permissions permissions
? <EmojiPickerDropdown className='status__action-bar-button' onPickEmoji={this.handleEmojiPick} button={reactButton} disabled={!canReact} /> ? <EmojiPickerDropdown className='status__action-bar-button' onPickEmoji={this.handleEmojiPick} button={reactButton} disabled={!canReact} />
: reactButton : reactButton
} }
<IconButton className='status__action-bar-button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' onClick={this.handleBookmarkClick} /> <IconButton className='status__action-bar-button bookmark-icon' disabled={!signedIn} active={status.get('bookmarked')} title={intl.formatMessage(messages.bookmark)} icon='bookmark' iconComponent={status.get('bookmarked') ? BookmarkIcon : BookmarkBorderIcon} onClick={this.handleBookmarkClick} />
{filterButton} {filterButton}
<div className='status__action-bar-dropdown'> <DropdownMenuContainer
<DropdownMenuContainer scrollKey={scrollKey}
scrollKey={scrollKey} status={status}
status={status} items={menu}
items={menu} icon='ellipsis-h'
icon='ellipsis-h' size={18}
size={18} iconComponent={MoreHorizIcon}
direction='right' direction='right'
ariaLabel={intl.formatMessage(messages.more)} ariaLabel={intl.formatMessage(messages.more)}
/> />
</div>
<div className='status__action-bar-spacer' /> <div className='status__action-bar-spacer' />
<a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'> <a href={status.get('url')} className='status__relative-time' target='_blank' rel='noopener'>

View file

@ -9,6 +9,12 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as ImageIcon } from '@material-symbols/svg-600/outlined/image.svg';
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
import { ReactComponent as MovieIcon } from '@material-symbols/svg-600/outlined/movie.svg';
import { ReactComponent as MusicNoteIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state'; import { autoPlayGif, languages as preloadedLanguages } from 'flavours/glitch/initial_state';
import { decode as decodeIDNA } from 'flavours/glitch/utils/idna'; import { decode as decodeIDNA } from 'flavours/glitch/utils/idna';
@ -381,12 +387,21 @@ class StatusContent extends PureComponent {
/>, />,
]; ];
if (mediaIcons) { if (mediaIcons) {
const mediaComponents = {
'link': LinkIcon,
'picture-o': ImageIcon,
'tasks': InsertChartIcon,
'video-camera': MovieIcon,
'music': MusicNoteIcon,
};
mediaIcons.forEach((mediaIcon, idx) => { mediaIcons.forEach((mediaIcon, idx) => {
toggleText.push( toggleText.push(
<Icon <Icon
fixedWidth fixedWidth
className='status__content__spoiler-icon' className='status__content__spoiler-icon'
id={mediaIcon} id={mediaIcon}
icon={mediaComponents[mediaIcon]}
aria-hidden='true' aria-hidden='true'
key={`icon-${idx}`} key={`icon-${idx}`}
/>, />,

View file

@ -6,15 +6,21 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { ReactComponent as ExpandLessIcon } from '@material-symbols/svg-600/outlined/expand_less.svg';
import { ReactComponent as ForumIcon } from '@material-symbols/svg-600/outlined/forum.svg';
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home.svg';
import { ReactComponent as ImageIcon } from '@material-symbols/svg-600/outlined/image.svg';
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
import { ReactComponent as LinkIcon } from '@material-symbols/svg-600/outlined/link.svg';
import { ReactComponent as MovieIcon } from '@material-symbols/svg-600/outlined/movie.svg';
import { ReactComponent as MusicNoteIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
// Mastodon imports.
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { languages } from 'flavours/glitch/initial_state'; import { languages } from 'flavours/glitch/initial_state';
import { IconButton } from './icon_button'; import { IconButton } from './icon_button';
import VisibilityIcon from './status_visibility_icon'; import { VisibilityIcon } from './visibility_icon';
// Messages for use with internationalization stuff.
const messages = defineMessages({ const messages = defineMessages({
collapse: { id: 'status.collapse', defaultMessage: 'Collapse' }, collapse: { id: 'status.collapse', defaultMessage: 'Collapse' },
uncollapse: { id: 'status.uncollapse', defaultMessage: 'Uncollapse' }, uncollapse: { id: 'status.uncollapse', defaultMessage: 'Uncollapse' },
@ -65,34 +71,47 @@ class StatusIcons extends PureComponent {
} }
}; };
mediaIconTitleText (mediaIcon) { renderIcon (mediaIcon) {
const { intl } = this.props; const { intl } = this.props;
const message = { let title, iconComponent;
'link': messages.previewCard,
'picture-o': messages.pictures,
'tasks': messages.poll,
'video-camera': messages.video,
'music': messages.audio,
}[mediaIcon];
return message && intl.formatMessage(message); switch (mediaIcon) {
} case 'link':
title = messages.previewCard;
iconComponent = LinkIcon;
break;
case 'picture-o':
title = messages.pictures;
iconComponent = ImageIcon;
break;
case 'tasks':
title = messages.poll;
iconComponent = InsertChartIcon;
break;
case 'video-camera':
title = messages.video;
iconComponent = MovieIcon;
break;
case 'music':
title = messages.audio;
iconComponent = MusicNoteIcon;
break;
}
renderIcon (mediaIcon) {
return ( return (
<Icon <Icon
fixedWidth fixedWidth
className='status__media-icon' className='status__media-icon'
key={`media-icon--${mediaIcon}`} key={`media-icon--${mediaIcon}`}
id={mediaIcon} id={mediaIcon}
icon={iconComponent}
aria-hidden='true' aria-hidden='true'
title={this.mediaIconTitleText(mediaIcon)} title={title && intl.formatMessage(title)}
/> />
); );
} }
// Rendering.
render () { render () {
const { const {
status, status,
@ -109,16 +128,16 @@ class StatusIcons extends PureComponent {
{settings.get('reply') && status.get('in_reply_to_id', null) !== null ? ( {settings.get('reply') && status.get('in_reply_to_id', null) !== null ? (
<Icon <Icon
className='status__reply-icon' className='status__reply-icon'
fixedWidth
id='comment' id='comment'
icon={ForumIcon}
aria-hidden='true' aria-hidden='true'
title={intl.formatMessage(messages.inReplyTo)} title={intl.formatMessage(messages.inReplyTo)}
/> />
) : null} ) : null}
{settings.get('local_only') && status.get('local_only') && {settings.get('local_only') && status.get('local_only') &&
<Icon <Icon
fixedWidth
id='home' id='home'
icon={HomeIcon}
aria-hidden='true' aria-hidden='true'
title={intl.formatMessage(messages.localOnly)} title={intl.formatMessage(messages.localOnly)}
/>} />}
@ -135,6 +154,7 @@ class StatusIcons extends PureComponent {
intl.formatMessage(messages.collapse) intl.formatMessage(messages.collapse)
} }
icon='angle-double-up' icon='angle-double-up'
iconComponent={ExpandLessIcon}
onClick={this.handleCollapsedClick} onClick={this.handleCollapsedClick}
/> />
)} )}

View file

@ -6,6 +6,13 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.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 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 StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { me } from 'flavours/glitch/initial_state'; import { me } from 'flavours/glitch/initial_state';
@ -115,30 +122,36 @@ export default class StatusPrepend extends PureComponent {
const { Message } = this; const { Message } = this;
const { type } = this.props; const { type } = this.props;
let iconId; let iconId, iconComponent;
switch(type) { switch(type) {
case 'favourite': case 'favourite':
iconId = 'star'; iconId = 'star';
iconComponent = StarIcon;
break; break;
case 'reaction': case 'reaction':
iconId = 'plus'; iconId = 'plus';
break; break;
case 'featured': case 'featured':
iconId = 'thumb-tack'; iconId = 'thumb-tack';
iconComponent = PushPinIcon;
break; break;
case 'poll': case 'poll':
iconId = 'tasks'; iconId = 'tasks';
iconComponent = InsertChartIcon;
break; break;
case 'reblog': case 'reblog':
case 'reblogged_by': case 'reblogged_by':
iconId = 'retweet'; iconId = 'retweet';
iconComponent = RepeatIcon;
break; break;
case 'status': case 'status':
iconId = 'bell'; iconId = 'bell';
iconComponent = HomeIcon;
break; break;
case 'update': case 'update':
iconId = 'pencil'; iconId = 'pencil';
iconComponent = EditIcon;
break; break;
} }
@ -148,6 +161,7 @@ export default class StatusPrepend extends PureComponent {
<Icon <Icon
className={`status__prepend-icon ${type === 'favourite' ? 'star-icon' : ''}`} className={`status__prepend-icon ${type === 'favourite' ? 'star-icon' : ''}`}
id={iconId} id={iconId}
icon={iconComponent}
/> />
</div> </div>
<Message /> <Message />

View file

@ -1,54 +0,0 @@
// Package imports //
import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { Icon } from 'flavours/glitch/components/icon';
const messages = defineMessages({
public: { id: 'privacy.public.short', defaultMessage: 'Public' },
unlisted: { id: 'privacy.unlisted.short', defaultMessage: 'Unlisted' },
private: { id: 'privacy.private.short', defaultMessage: 'Followers only' },
direct: { id: 'privacy.direct.short', defaultMessage: 'Mentioned people only' },
});
class VisibilityIcon extends ImmutablePureComponent {
static propTypes = {
visibility: PropTypes.string,
intl: PropTypes.object.isRequired,
withLabel: PropTypes.bool,
};
render() {
const { withLabel, visibility, intl } = this.props;
const visibilityIcon = {
public: 'globe',
unlisted: 'unlock',
private: 'lock',
direct: 'envelope',
}[visibility];
const label = intl.formatMessage(messages[visibility]);
const icon = (<Icon
className='status__visibility-icon'
fixedWidth
id={visibilityIcon}
title={label}
aria-hidden='true'
/>);
if (withLabel) {
return (<span style={{ whiteSpace: 'nowrap' }}>{icon} {label}</span>);
} else {
return icon;
}
}
}
export default injectIntl(VisibilityIcon);

View file

@ -1,3 +1,5 @@
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
import { Icon } from './icon'; import { Icon } from './icon';
const domParser = new DOMParser(); const domParser = new DOMParser();
@ -21,7 +23,7 @@ interface Props {
} }
export const VerifiedBadge: React.FC<Props> = ({ link }) => ( export const VerifiedBadge: React.FC<Props> = ({ link }) => (
<span className='verified-badge'> <span className='verified-badge'>
<Icon id='check' className='verified-badge__mark' /> <Icon id='check' icon={CheckIcon} className='verified-badge__mark' />
<span dangerouslySetInnerHTML={stripRelMe(link)} /> <span dangerouslySetInnerHTML={stripRelMe(link)} />
</span> </span>
); );

View file

@ -0,0 +1,63 @@
import { defineMessages, useIntl } from 'react-intl';
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 MailIcon } from '@material-symbols/svg-600/outlined/mail.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: 'envelope',
iconComponent: MailIcon,
text: intl.formatMessage(messages.direct_short),
},
};
const visibilityIcon = visibilityIconInfo[visibility];
return (
<Icon
id={visibilityIcon.icon}
icon={visibilityIcon.iconComponent}
title={visibilityIcon.text}
className={'status__visibility-icon'}
/>
);
};

View file

@ -10,6 +10,9 @@ import { List as ImmutableList } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
import { ReactComponent as ExpandMoreIcon } from '@material-symbols/svg-600/outlined/expand_more.svg';
import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'flavours/glitch/actions/server'; import { fetchServer, fetchExtendedDescription, fetchDomainBlocks } from 'flavours/glitch/actions/server';
import Column from 'flavours/glitch/components/column'; import Column from 'flavours/glitch/components/column';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -73,7 +76,7 @@ class Section extends PureComponent {
return ( return (
<div className={classNames('about__section', { active: !collapsed })}> <div className={classNames('about__section', { active: !collapsed })}>
<div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}> <div className='about__section__title' role='button' tabIndex={0} onClick={this.handleClick}>
<Icon id={collapsed ? 'chevron-right' : 'chevron-down'} fixedWidth /> {title} <Icon id={collapsed ? 'chevron-right' : 'chevron-down'} icon={collapsed ? ChevronRightIcon : ExpandMoreIcon} /> {title}
</div> </div>
{!collapsed && ( {!collapsed && (

View file

@ -6,6 +6,8 @@ import { NavLink } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { ReactComponent as InfoIcon } from '@material-symbols/svg-600/outlined/info.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
class ActionBar extends PureComponent { class ActionBar extends PureComponent {
@ -28,7 +30,7 @@ class ActionBar extends PureComponent {
return ( return (
<div> <div>
<div className='account__disclaimer'> <div className='account__disclaimer'>
<Icon id='info-circle' fixedWidth /> <FormattedMessage <Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage
id='account.suspended_disclaimer_full' id='account.suspended_disclaimer_full'
defaultMessage='This user has been suspended by a moderator.' defaultMessage='This user has been suspended by a moderator.'
/> />
@ -42,7 +44,7 @@ class ActionBar extends PureComponent {
if (account.get('acct') !== account.get('username')) { if (account.get('acct') !== account.get('username')) {
extraInfo = ( extraInfo = (
<div className='account__disclaimer'> <div className='account__disclaimer'>
<Icon id='info-circle' fixedWidth /> <FormattedMessage <Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage
id='account.disclaimer_full' id='account.disclaimer_full'
defaultMessage="Information below may reflect the user's profile incompletely." defaultMessage="Information below may reflect the user's profile incompletely."
/> />

View file

@ -3,7 +3,10 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { Icon } from 'flavours/glitch/components/icon'; 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 'flavours/glitch/components/icon';
export default class FollowRequestNote extends ImmutablePureComponent { export default class FollowRequestNote extends ImmutablePureComponent {
@ -22,12 +25,12 @@ export default class FollowRequestNote extends ImmutablePureComponent {
<div className='follow-request-banner__action'> <div className='follow-request-banner__action'>
<button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}> <button type='button' className='button button-tertiary button--confirmation' onClick={onAuthorize}>
<Icon id='check' fixedWidth /> <Icon id='check' icon={CheckIcon} />
<FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' /> <FormattedMessage id='follow_request.authorize' defaultMessage='Authorize' />
</button> </button>
<button type='button' className='button button-tertiary button--destructive' onClick={onReject}> <button type='button' className='button button-tertiary button--destructive' onClick={onReject}>
<Icon id='times' fixedWidth /> <Icon id='times' icon={CloseIcon} />
<FormattedMessage id='follow_request.reject' defaultMessage='Reject' /> <FormattedMessage id='follow_request.reject' defaultMessage='Reject' />
</button> </button>
</div> </div>

View file

@ -9,6 +9,12 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as 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.svg';
import { ReactComponent as NotificationsActiveIcon } from '@material-symbols/svg-600/outlined/notifications_active-fill.svg';
import { Avatar } from 'flavours/glitch/components/avatar'; import { Avatar } from 'flavours/glitch/components/avatar';
import { Badge, AutomatedBadge, GroupBadge } from 'flavours/glitch/components/badge'; import { Badge, AutomatedBadge, GroupBadge } from 'flavours/glitch/components/badge';
import { Button } from 'flavours/glitch/components/button'; import { Button } from 'flavours/glitch/components/button';
@ -189,7 +195,7 @@ class Header extends ImmutablePureComponent {
} }
if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) { if (account.getIn(['relationship', 'requested']) || account.getIn(['relationship', 'following'])) {
bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} size={24} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />; bellBtn = <IconButton icon={account.getIn(['relationship', 'notifying']) ? 'bell' : 'bell-o'} iconComponent={account.getIn(['relationship', 'notifying']) ? NotificationsActiveIcon : NotificationsIcon} active={account.getIn(['relationship', 'notifying'])} title={intl.formatMessage(account.getIn(['relationship', 'notifying']) ? messages.disableNotifications : messages.enableNotifications, { name: account.get('username') })} onClick={this.props.onNotifyToggle} />;
} }
if (me !== account.get('id')) { if (me !== account.get('id')) {
@ -215,7 +221,7 @@ class Header extends ImmutablePureComponent {
} }
if (account.get('locked')) { if (account.get('locked')) {
lockedIcon = <Icon id='lock' title={intl.formatMessage(messages.account_locked)} />; lockedIcon = <Icon id='lock' icon={LockIcon} title={intl.formatMessage(messages.account_locked)} />;
} }
if (signedIn && account.get('id') !== me && !account.get('suspended')) { if (signedIn && account.get('id') !== me && !account.get('suspended')) {
@ -347,7 +353,7 @@ class Header extends ImmutablePureComponent {
</> </>
)} )}
<DropdownMenuContainer disabled={menu.length === 0} items={menu} icon='ellipsis-v' size={24} direction='right' /> <DropdownMenuContainer disabled={menu.length === 0} items={menu} icon='ellipsis-v' iconComponent={MoreHorizIcon} size={24} direction='right' />
</div> </div>
</div> </div>
@ -378,7 +384,7 @@ class Header extends ImmutablePureComponent {
<dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} /> <dt dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }} title={pair.get('name')} />
<dd className={pair.get('verified_at') && 'verified'} title={pair.get('value_plain')}> <dd className={pair.get('verified_at') && 'verified'} 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') }} className='translate' /> {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') }} className='translate' />
</dd> </dd>
</dl> </dl>
))} ))}

View file

@ -3,6 +3,8 @@ import { PureComponent } from 'react';
import { injectIntl, defineMessages } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl';
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person.svg';
import ColumnHeader from '../../../components/column_header'; import ColumnHeader from '../../../components/column_header';
const messages = defineMessages({ const messages = defineMessages({
@ -23,6 +25,7 @@ class ProfileColumnHeader extends PureComponent {
return ( return (
<ColumnHeader <ColumnHeader
icon='user-circle' icon='user-circle'
iconComponent={PersonIcon}
title={intl.formatMessage(messages.profile)} title={intl.formatMessage(messages.profile)}
onClick={onClick} onClick={onClick}
showBackButton showBackButton

View file

@ -5,6 +5,10 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as AudiotrackIcon } from '@material-symbols/svg-600/outlined/music_note.svg';
import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow.svg';
import { ReactComponent as VisibilityOffIcon } from '@material-symbols/svg-600/outlined/visibility_off.svg';
import { Blurhash } from 'flavours/glitch/components/blurhash'; import { Blurhash } from 'flavours/glitch/components/blurhash';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/initial_state'; import { autoPlayGif, displayMedia, useBlurhash } from 'flavours/glitch/initial_state';
@ -69,7 +73,7 @@ export default class MediaItem extends ImmutablePureComponent {
if (!visible) { if (!visible) {
icon = ( icon = (
<span className='account-gallery__item__icons'> <span className='account-gallery__item__icons'>
<Icon id='eye-slash' /> <Icon id='eye-slash' icon={VisibilityOffIcon} />
</span> </span>
); );
} else { } else {
@ -84,9 +88,9 @@ export default class MediaItem extends ImmutablePureComponent {
); );
if (attachment.get('type') === 'audio') { if (attachment.get('type') === 'audio') {
label = <Icon id='music' />; label = <Icon id='music' icon={AudiotrackIcon} />;
} else { } else {
label = <Icon id='play' />; label = <Icon id='play' icon={PlayArrowIcon} />;
} }
} else if (attachment.get('type') === 'image') { } else if (attachment.get('type') === 'image') {
const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0; const focusX = attachment.getIn(['meta', 'focus', 'x']) || 0;

View file

@ -5,6 +5,8 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as TripIcon } from '@material-symbols/svg-600/outlined/trip.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
@ -35,7 +37,7 @@ class MovedNote extends ImmutablePureComponent {
return ( return (
<div className='account__moved-note'> <div className='account__moved-note'>
<div className='account__moved-note__message'> <div className='account__moved-note__message'>
<div className='account__moved-note__icon-wrapper'><Icon id='suitcase' className='account__moved-note__icon' fixedWidth /></div> <div className='account__moved-note__icon-wrapper'><Icon id='suitcase' className='account__moved-note__icon' icon={TripIcon} /></div>
<FormattedMessage id='account.moved_to' defaultMessage='{name} has indicated that their new account is now:' values={{ name: <bdi><strong dangerouslySetInnerHTML={displayNameHtml} /></bdi> }} /> <FormattedMessage id='account.moved_to' defaultMessage='{name} has indicated that their new account is now:' values={{ name: <bdi><strong dangerouslySetInnerHTML={displayNameHtml} /></bdi> }} />
</div> </div>

View file

@ -7,6 +7,12 @@ import classNames from 'classnames';
import { is } from 'immutable'; import { is } from 'immutable';
import { ReactComponent as DownloadIcon } from '@material-symbols/svg-600/outlined/download.svg';
import { ReactComponent as PauseIcon } from '@material-symbols/svg-600/outlined/pause.svg';
import { ReactComponent as PlayArrowIcon } from '@material-symbols/svg-600/outlined/play_arrow-fill.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-fill.svg';
import { ReactComponent as VolumeUpIcon } from '@material-symbols/svg-600/outlined/volume_up-fill.svg';
import { throttle, debounce } from 'lodash'; import { throttle, debounce } from 'lodash';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -560,8 +566,8 @@ class Audio extends PureComponent {
<div className='video-player__controls active'> <div className='video-player__controls active'>
<div className='video-player__buttons-bar'> <div className='video-player__buttons-bar'>
<div className='video-player__buttons left'> <div className='video-player__buttons left'>
<button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} fixedWidth /></button> <button type='button' title={intl.formatMessage(paused ? messages.play : messages.pause)} aria-label={intl.formatMessage(paused ? messages.play : messages.pause)} className='player-button' onClick={this.togglePlay}><Icon id={paused ? 'play' : 'pause'} icon={paused ? PlayArrowIcon : PauseIcon} /></button>
<button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} fixedWidth /></button> <button type='button' title={intl.formatMessage(muted ? messages.unmute : messages.mute)} aria-label={intl.formatMessage(muted ? messages.unmute : messages.mute)} className='player-button' onClick={this.toggleMute}><Icon id={muted ? 'volume-off' : 'volume-up'} icon={muted ? VolumeOffIcon : VolumeUpIcon} /></button>
<div className={classNames('video-player__volume', { active: this.state.hovered })} ref={this.setVolumeRef} onMouseDown={this.handleVolumeMouseDown}> <div className={classNames('video-player__volume', { active: this.state.hovered })} ref={this.setVolumeRef} onMouseDown={this.handleVolumeMouseDown}>
<div className='video-player__volume__current' style={{ width: `${muted ? 0 : volume * 100}%`, backgroundColor: this._getAccentColor() }} /> <div className='video-player__volume__current' style={{ width: `${muted ? 0 : volume * 100}%`, backgroundColor: this._getAccentColor() }} />
@ -581,9 +587,9 @@ class Audio extends PureComponent {
</div> </div>
<div className='video-player__buttons right'> <div className='video-player__buttons right'>
{!editable && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' fixedWidth /></button>} {!editable && <button type='button' title={intl.formatMessage(messages.hide)} aria-label={intl.formatMessage(messages.hide)} className='player-button' onClick={this.toggleReveal}><Icon id='eye-slash' icon={VisibilityOffIcon} /></button>}
<a title={intl.formatMessage(messages.download)} aria-label={intl.formatMessage(messages.download)} className='video-player__download__icon player-button' href={this.props.src} download> <a title={intl.formatMessage(messages.download)} aria-label={intl.formatMessage(messages.download)} className='video-player__download__icon player-button' href={this.props.src} download>
<Icon id={'download'} fixedWidth /> <Icon id={'download'} icon={DownloadIcon} />
</a> </a>
</div> </div>
</div> </div>

View file

@ -6,10 +6,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as BlockIcon } from '@material-symbols/svg-600/outlined/block-fill.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { fetchBlocks, expandBlocks } from '../../actions/blocks'; import { fetchBlocks, expandBlocks } from '../../actions/blocks';
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
import { LoadingIndicator } from '../../components/loading_indicator'; import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container'; import AccountContainer from '../../containers/account_container';
@ -59,8 +59,7 @@ class Blocks extends ImmutablePureComponent {
const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />; const emptyMessage = <FormattedMessage id='empty_column.blocks' defaultMessage="You haven't blocked any users yet." />;
return ( return (
<Column bindToDocument={!multiColumn} icon='ban' heading={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} icon='ban' iconComponent={BlockIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<ColumnBackButtonSlim />
<ScrollableList <ScrollableList
scrollKey='blocks' scrollKey='blocks'
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}

View file

@ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'flavours/glitch/actions/bookmarks'; import { fetchBookmarkedStatuses, expandBookmarkedStatuses } from 'flavours/glitch/actions/bookmarks';
@ -79,7 +80,8 @@ class Bookmarks extends ImmutablePureComponent {
return ( return (
<Column bindToDocument={!multiColumn} ref={this.setRef}> <Column bindToDocument={!multiColumn} ref={this.setRef}>
<ColumnHeader <ColumnHeader
icon='bookmark' icon='bookmarks'
iconComponent={BookmarksIcon}
title={intl.formatMessage(messages.heading)} title={intl.formatMessage(messages.heading)}
onPin={this.handlePin} onPin={this.handlePin}
onMove={this.handleMove} onMove={this.handleMove}

View file

@ -7,6 +7,8 @@ import { Helmet } from 'react-helmet';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
import { domain } from 'flavours/glitch/initial_state'; import { domain } from 'flavours/glitch/initial_state';
@ -131,6 +133,7 @@ class CommunityTimeline extends PureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='users' icon='users'
iconComponent={PeopleIcon}
active={hasUnread} active={hasUnread}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onPin={this.handlePin} onPin={this.handlePin}

View file

@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.svg';
import { preferencesLink, profileLink } from 'flavours/glitch/utils/backend_links'; import { preferencesLink, profileLink } from 'flavours/glitch/utils/backend_links';
import DropdownMenuContainer from '../../../containers/dropdown_menu_container'; import DropdownMenuContainer from '../../../containers/dropdown_menu_container';
@ -62,7 +64,7 @@ class ActionBar extends PureComponent {
return ( return (
<div className='compose__action-bar'> <div className='compose__action-bar'>
<div className='compose__action-bar-dropdown'> <div className='compose__action-bar-dropdown'>
<DropdownMenuContainer items={menu} icon='bars' size={18} direction='right' /> <DropdownMenuContainer items={menu} icon='bars' iconComponent={MenuIcon} size={24} direction='right' />
</div> </div>
</div> </div>
); );

View file

@ -18,8 +18,10 @@ export default class ComposerOptionsDropdown extends PureComponent {
isUserTouching: PropTypes.func, isUserTouching: PropTypes.func,
disabled: PropTypes.bool, disabled: PropTypes.bool,
icon: PropTypes.string, icon: PropTypes.string,
iconComponent: PropTypes.func,
items: PropTypes.arrayOf(PropTypes.shape({ items: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.string, icon: PropTypes.string,
iconComponent: PropTypes.func,
meta: PropTypes.string, meta: PropTypes.string,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
text: PropTypes.string, text: PropTypes.string,
@ -174,6 +176,7 @@ export default class ComposerOptionsDropdown extends PureComponent {
disabled, disabled,
title, title,
icon, icon,
iconComponent,
items, items,
onChange, onChange,
value, value,
@ -183,33 +186,30 @@ export default class ComposerOptionsDropdown extends PureComponent {
} = this.props; } = this.props;
const { open, placement } = this.state; const { open, placement } = this.state;
const active = value && items.findIndex(({ name }) => name === value) === (placement === 'bottom' ? 0 : (items.length - 1));
return ( return (
<div <div
className={classNames('privacy-dropdown', placement, { active: open })} className={classNames('privacy-dropdown', placement, { active: open })}
onKeyDown={this.handleKeyDown} onKeyDown={this.handleKeyDown}
ref={this.setTargetRef} ref={this.setTargetRef}
> >
<div className={classNames('privacy-dropdown__value', { active })}> <IconButton
<IconButton active={open}
active={open} className='privacy-dropdown__value-icon'
className='privacy-dropdown__value-icon' disabled={disabled}
disabled={disabled} icon={icon}
icon={icon} iconComponent={iconComponent}
inverted inverted
onClick={this.handleToggle} onClick={this.handleToggle}
onMouseDown={this.handleMouseDown} onMouseDown={this.handleMouseDown}
onKeyDown={this.handleButtonKeyDown} onKeyDown={this.handleButtonKeyDown}
onKeyPress={this.handleKeyPress} onKeyPress={this.handleKeyPress}
size={18} size={18}
style={{ style={{
height: null, height: null,
lineHeight: '27px', lineHeight: '27px',
}} }}
title={title} title={title}
/> />
</div>
<Overlay <Overlay
containerPadding={20} containerPadding={20}

View file

@ -17,6 +17,7 @@ export default class ComposerOptionsDropdownContent extends PureComponent {
static propTypes = { static propTypes = {
items: PropTypes.arrayOf(PropTypes.shape({ items: PropTypes.arrayOf(PropTypes.shape({
icon: PropTypes.string, icon: PropTypes.string,
iconComponent: PropTypes.func,
meta: PropTypes.node, meta: PropTypes.node,
name: PropTypes.string.isRequired, name: PropTypes.string.isRequired,
text: PropTypes.node, text: PropTypes.node,
@ -144,7 +145,7 @@ export default class ComposerOptionsDropdownContent extends PureComponent {
}; };
renderItem = (item, i) => { renderItem = (item, i) => {
const { name, icon, meta, text } = item; const { name, icon, iconComponent, meta, text } = item;
const active = (name === (this.props.value || this.state.value)); const active = (name === (this.props.value || this.state.value));
@ -155,7 +156,7 @@ export default class ComposerOptionsDropdownContent extends PureComponent {
if (!contents) { if (!contents) {
contents = ( contents = (
<> <>
{icon && <Icon className='icon' fixedWidth id={icon} />} {icon && <Icon className='icon' id={icon} icon={iconComponent} />}
<div className='privacy-dropdown__option__content'> <div className='privacy-dropdown__option__content'>
<strong>{text}</strong> <strong>{text}</strong>

View file

@ -7,6 +7,14 @@ import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as 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 ManufacturingIcon } from '@material-symbols/svg-600/outlined/manufacturing.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 { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { signOutLink } from 'flavours/glitch/utils/backend_links'; import { signOutLink } from 'flavours/glitch/utils/backend_links';
import { conditionalRender } from 'flavours/glitch/utils/react_helpers'; import { conditionalRender } from 'flavours/glitch/utils/react_helpers';
@ -79,22 +87,25 @@ class Header extends ImmutablePureComponent {
aria-label={intl.formatMessage(messages.start)} aria-label={intl.formatMessage(messages.start)}
title={intl.formatMessage(messages.start)} title={intl.formatMessage(messages.start)}
to='/getting-started' to='/getting-started'
><Icon id='asterisk' /></Link> className='drawer__tab'
><Icon id='bars' icon={MenuIcon} /></Link>
{renderForColumn('HOME', ( {renderForColumn('HOME', (
<Link <Link
aria-label={intl.formatMessage(messages.home_timeline)} aria-label={intl.formatMessage(messages.home_timeline)}
title={intl.formatMessage(messages.home_timeline)} title={intl.formatMessage(messages.home_timeline)}
to='/home' to='/home'
><Icon id='home' /></Link> className='drawer__tab'
><Icon id='home' icon={HomeIcon} /></Link>
))} ))}
{renderForColumn('NOTIFICATIONS', ( {renderForColumn('NOTIFICATIONS', (
<Link <Link
aria-label={intl.formatMessage(messages.notifications)} aria-label={intl.formatMessage(messages.notifications)}
title={intl.formatMessage(messages.notifications)} title={intl.formatMessage(messages.notifications)}
to='/notifications' to='/notifications'
className='drawer__tab'
> >
<span className='icon-badge-wrapper'> <span className='icon-badge-wrapper'>
<Icon id='bell' /> <Icon id='bell' icon={NotificationsIcon} />
{ showNotificationsBadge && unreadNotifications > 0 && <div className='icon-badge' />} { showNotificationsBadge && unreadNotifications > 0 && <div className='icon-badge' />}
</span> </span>
</Link> </Link>
@ -104,27 +115,31 @@ class Header extends ImmutablePureComponent {
aria-label={intl.formatMessage(messages.community)} aria-label={intl.formatMessage(messages.community)}
title={intl.formatMessage(messages.community)} title={intl.formatMessage(messages.community)}
to='/public/local' to='/public/local'
><Icon id='users' /></Link> className='drawer__tab'
><Icon id='users' icon={PeopleIcon} /></Link>
))} ))}
{renderForColumn('PUBLIC', ( {renderForColumn('PUBLIC', (
<Link <Link
aria-label={intl.formatMessage(messages.public)} aria-label={intl.formatMessage(messages.public)}
title={intl.formatMessage(messages.public)} title={intl.formatMessage(messages.public)}
to='/public' to='/public'
><Icon id='globe' /></Link> className='drawer__tab'
><Icon id='globe' icon={PublicIcon} /></Link>
))} ))}
<a <a
aria-label={intl.formatMessage(messages.settings)} aria-label={intl.formatMessage(messages.settings)}
onClick={onSettingsClick} onClick={onSettingsClick}
href='/settings/preferences' href='/settings/preferences'
title={intl.formatMessage(messages.settings)} title={intl.formatMessage(messages.settings)}
><Icon id='cogs' /></a> className='drawer__tab'
><Icon id='cogs' icon={ManufacturingIcon} /></a>
<a <a
aria-label={intl.formatMessage(messages.logout)} aria-label={intl.formatMessage(messages.logout)}
onClick={this.handleLogoutClick} onClick={this.handleLogoutClick}
href={signOutLink} href={signOutLink}
title={intl.formatMessage(messages.logout)} title={intl.formatMessage(messages.logout)}
><Icon id='sign-out' /></a> className='drawer__tab'
><Icon id='sign-out' icon={LogoutIcon} /></a>
</nav> </nav>
); );
} }

View file

@ -6,6 +6,14 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as AttachFileIcon } from '@material-symbols/svg-600/outlined/attach_file.svg';
import { ReactComponent as BrushIcon } from '@material-symbols/svg-600/outlined/brush.svg';
import { ReactComponent as CodeIcon } from '@material-symbols/svg-600/outlined/code.svg';
import { ReactComponent as DescriptionIcon } from '@material-symbols/svg-600/outlined/description.svg';
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
import { ReactComponent as MarkdownIcon } from '@material-symbols/svg-600/outlined/markdown.svg';
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
import { ReactComponent as UploadFileIcon } from '@material-symbols/svg-600/outlined/upload_file.svg';
import Toggle from 'react-toggle'; import Toggle from 'react-toggle';
import { IconButton } from 'flavours/glitch/components/icon_button'; import { IconButton } from 'flavours/glitch/components/icon_button';
@ -195,16 +203,19 @@ class ComposerOptions extends ImmutablePureComponent {
const contentTypeItems = { const contentTypeItems = {
plain: { plain: {
icon: 'file-text', icon: 'file-text',
iconComponent: DescriptionIcon,
name: 'text/plain', name: 'text/plain',
text: formatMessage(messages.plain), text: formatMessage(messages.plain),
}, },
html: { html: {
icon: 'code', icon: 'code',
iconComponent: CodeIcon,
name: 'text/html', name: 'text/html',
text: formatMessage(messages.html), text: formatMessage(messages.html),
}, },
markdown: { markdown: {
icon: 'arrow-circle-down', icon: 'arrow-circle-down',
iconComponent: MarkdownIcon,
name: 'text/markdown', name: 'text/markdown',
text: formatMessage(messages.markdown), text: formatMessage(messages.markdown),
}, },
@ -226,14 +237,17 @@ class ComposerOptions extends ImmutablePureComponent {
<DropdownContainer <DropdownContainer
disabled={disabled || !allowMedia} disabled={disabled || !allowMedia}
icon='paperclip' icon='paperclip'
iconComponent={AttachFileIcon}
items={[ items={[
{ {
icon: 'cloud-upload', icon: 'cloud-upload',
iconComponent: UploadFileIcon,
name: 'upload', name: 'upload',
text: formatMessage(messages.upload), text: formatMessage(messages.upload),
}, },
{ {
icon: 'paint-brush', icon: 'paint-brush',
iconComponent: BrushIcon,
name: 'doodle', name: 'doodle',
text: formatMessage(messages.doodle), text: formatMessage(messages.doodle),
}, },
@ -246,6 +260,7 @@ class ComposerOptions extends ImmutablePureComponent {
active={hasPoll} active={hasPoll}
disabled={disabled || !allowPoll} disabled={disabled || !allowPoll}
icon='tasks' icon='tasks'
iconComponent={InsertChartIcon}
inverted inverted
onClick={onTogglePoll} onClick={onTogglePoll}
size={18} size={18}
@ -256,12 +271,12 @@ class ComposerOptions extends ImmutablePureComponent {
title={formatMessage(hasPoll ? messages.remove_poll : messages.add_poll)} title={formatMessage(hasPoll ? messages.remove_poll : messages.add_poll)}
/> />
)} )}
<hr />
<PrivacyDropdownContainer disabled={disabled || isEditing} /> <PrivacyDropdownContainer disabled={disabled || isEditing} />
{showContentTypeChoice && ( {showContentTypeChoice && (
<DropdownContainer <DropdownContainer
disabled={disabled} disabled={disabled}
icon={(contentTypeItems[contentType.split('/')[1]] || {}).icon} icon={(contentTypeItems[contentType.split('/')[1]] || {}).icon}
iconComponent={(contentTypeItems[contentType.split('/')[1]] || {}).iconComponent}
items={[ items={[
contentTypeItems.plain, contentTypeItems.plain,
contentTypeItems.html, contentTypeItems.html,
@ -285,6 +300,7 @@ class ComposerOptions extends ImmutablePureComponent {
<DropdownContainer <DropdownContainer
disabled={disabled || isEditing} disabled={disabled || isEditing}
icon='ellipsis-h' icon='ellipsis-h'
iconComponent={MoreHorizIcon}
items={advancedOptions ? [ items={advancedOptions ? [
{ {
meta: formatMessage(messages.local_only_long), meta: formatMessage(messages.local_only_long),

View file

@ -8,6 +8,9 @@ import classNames from 'classnames';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import AutosuggestInput from 'flavours/glitch/components/autosuggest_input'; import AutosuggestInput from 'flavours/glitch/components/autosuggest_input';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { IconButton } from 'flavours/glitch/components/icon_button'; import { IconButton } from 'flavours/glitch/components/icon_button';
@ -87,7 +90,7 @@ class OptionIntl extends PureComponent {
</label> </label>
<div className='poll__cancel'> <div className='poll__cancel'>
<IconButton disabled={index < pollLimits.min_options} title={intl.formatMessage(messages.remove_option)} icon='times' onClick={this.handleOptionRemove} /> <IconButton disabled={index < pollLimits.min_options} title={intl.formatMessage(messages.remove_option)} icon='times' iconComponent={CloseIcon} onClick={this.handleOptionRemove} />
</div> </div>
</li> </li>
); );
@ -143,7 +146,7 @@ class PollForm extends ImmutablePureComponent {
{options.size < pollLimits.max_options && ( {options.size < pollLimits.max_options && (
<label className='poll__text editable'> <label className='poll__text editable'>
<span className={classNames('poll__input')} style={{ opacity: 0 }} /> <span className={classNames('poll__input')} style={{ opacity: 0 }} />
<button className='button button-secondary' onClick={this.handleAddOption} type='button'><Icon id='plus' /> <FormattedMessage {...messages.add_option} /></button> <button className='button button-secondary' onClick={this.handleAddOption} type='button'><Icon id='plus' icon={AddIcon} /> <FormattedMessage {...messages.add_option} /></button>
</label> </label>
)} )}
</ul> </ul>

View file

@ -3,6 +3,11 @@ import { PureComponent } from 'react';
import { injectIntl, defineMessages } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl';
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 MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
import Dropdown from './dropdown'; import Dropdown from './dropdown';
const messages = defineMessages({ const messages = defineMessages({
@ -39,24 +44,28 @@ class PrivacyDropdown extends PureComponent {
const privacyItems = { const privacyItems = {
direct: { direct: {
icon: 'envelope', icon: 'envelope',
iconComponent: MailIcon,
meta: formatMessage(messages.direct_long), meta: formatMessage(messages.direct_long),
name: 'direct', name: 'direct',
text: formatMessage(messages.direct_short), text: formatMessage(messages.direct_short),
}, },
private: { private: {
icon: 'lock', icon: 'lock',
iconComponent: LockIcon,
meta: formatMessage(messages.private_long), meta: formatMessage(messages.private_long),
name: 'private', name: 'private',
text: formatMessage(messages.private_short), text: formatMessage(messages.private_short),
}, },
public: { public: {
icon: 'globe', icon: 'globe',
iconComponent: PublicIcon,
meta: formatMessage(messages.public_long), meta: formatMessage(messages.public_long),
name: 'public', name: 'public',
text: formatMessage(messages.public_short), text: formatMessage(messages.public_short),
}, },
unlisted: { unlisted: {
icon: 'unlock', icon: 'unlock',
iconComponent: LockOpenIcon,
meta: formatMessage(messages.unlisted_long), meta: formatMessage(messages.unlisted_long),
name: 'unlisted', name: 'unlisted',
text: formatMessage(messages.unlisted_short), text: formatMessage(messages.unlisted_short),
@ -73,6 +82,7 @@ class PrivacyDropdown extends PureComponent {
<Dropdown <Dropdown
disabled={disabled} disabled={disabled}
icon={(privacyItems[value] || {}).icon} icon={(privacyItems[value] || {}).icon}
iconComponent={(privacyItems[value] || {}).iconComponent}
items={items} items={items}
onChange={onChange} onChange={onChange}
isUserTouching={isUserTouching} isUserTouching={isUserTouching}

View file

@ -4,6 +4,11 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as LockIcon } from '@material-symbols/svg-600/outlined/lock.svg';
import { ReactComponent as LockOpenIcon } from '@material-symbols/svg-600/outlined/lock_open.svg';
import { ReactComponent as MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
import { Button } from 'flavours/glitch/components/button'; import { Button } from 'flavours/glitch/components/button';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -37,16 +42,33 @@ class Publisher extends ImmutablePureComponent {
render () { render () {
const { disabled, intl, onSecondarySubmit, privacy, sideArm, isEditing } = this.props; const { disabled, intl, onSecondarySubmit, privacy, sideArm, isEditing } = this.props;
const privacyIcons = { direct: 'envelope', private: 'lock', public: 'globe', unlisted: 'unlock' }; const privacyIcons = {
direct: {
id: 'envelope',
icon: MailIcon,
},
private: {
id: 'lock',
icon: LockIcon,
},
public: {
id: 'globe',
icon: PublicIcon,
},
unlisted: {
id: 'unlock',
icon: LockOpenIcon,
},
};
let publishText; let publishText;
if (isEditing) { if (isEditing) {
publishText = intl.formatMessage(messages.saveChanges); publishText = intl.formatMessage(messages.saveChanges);
} else if (privacy === 'private' || privacy === 'direct') { } else if (privacy === 'private' || privacy === 'direct') {
const iconId = privacyIcons[privacy]; const icon = privacyIcons[privacy];
publishText = ( publishText = (
<span> <span>
<Icon id={iconId} /> {intl.formatMessage(messages.publish)} <Icon {...icon} /> {intl.formatMessage(messages.publish)}
</span> </span>
); );
} else { } else {
@ -69,7 +91,7 @@ class Publisher extends ImmutablePureComponent {
disabled={disabled} disabled={disabled}
onClick={onSecondarySubmit} onClick={onSecondarySubmit}
style={{ padding: null }} style={{ padding: null }}
text={<Icon id={privacyIcons[sideArm]} />} text={<Icon {...privacyIcons[sideArm]} />}
title={`${intl.formatMessage(messages.publish)}: ${intl.formatMessage(privacyNames[sideArm])}`} title={`${intl.formatMessage(messages.publish)}: ${intl.formatMessage(privacyNames[sideArm])}`}
/> />
</div> </div>

View file

@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import AttachmentList from 'flavours/glitch/components/attachment_list'; import AttachmentList from 'flavours/glitch/components/attachment_list';
import { WithOptionalRouterPropTypes, withOptionalRouter } from 'flavours/glitch/utils/react_router'; import { WithOptionalRouterPropTypes, withOptionalRouter } from 'flavours/glitch/utils/react_router';
@ -48,7 +50,7 @@ class ReplyIndicator extends ImmutablePureComponent {
return ( return (
<div className='reply-indicator'> <div className='reply-indicator'>
<div className='reply-indicator__header'> <div className='reply-indicator__header'>
<div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' onClick={this.handleClick} inverted /></div> <div className='reply-indicator__cancel'><IconButton title={intl.formatMessage(messages.cancel)} icon='times' iconComponent={CloseIcon} onClick={this.handleClick} inverted /></div>
<a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name' target='_blank' rel='noopener noreferrer'> <a href={status.getIn(['account', 'url'])} onClick={this.handleAccountClick} className='reply-indicator__display-name' target='_blank' rel='noopener noreferrer'>
<div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div> <div className='reply-indicator__display-avatar'><Avatar account={status.get('account')} size={24} /></div>

View file

@ -8,6 +8,10 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { ReactComponent as CancelIcon } from '@material-symbols/svg-600/outlined/cancel-fill.svg';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { domain, searchEnabled } from 'flavours/glitch/initial_state'; import { domain, searchEnabled } from 'flavours/glitch/initial_state';
import { HASHTAG_REGEX } from 'flavours/glitch/utils/hashtags'; import { HASHTAG_REGEX } from 'flavours/glitch/utils/hashtags';
@ -333,8 +337,8 @@ class Search extends PureComponent {
/> />
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}> <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
<Icon id='search' className={hasValue ? '' : 'active'} /> <Icon id='search' icon={SearchIcon} className={hasValue ? '' : 'active'} />
<Icon id='times-circle' className={hasValue ? 'active' : ''} /> <Icon id='times-circle' icon={CancelIcon} className={hasValue ? 'active' : ''} />
</div> </div>
<div className='search__popout'> <div className='search__popout'>
@ -346,7 +350,7 @@ class Search extends PureComponent {
{recent.size > 0 ? this._getOptions().map(({ label, action, forget }, i) => ( {recent.size > 0 ? this._getOptions().map(({ label, action, forget }, i) => (
<button key={label} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}> <button key={label} onMouseDown={action} className={classNames('search__popout__menu__item search__popout__menu__item--flex', { selected: selectedOption === i })}>
<span>{label}</span> <span>{label}</span>
<button className='icon-button' onMouseDown={forget}><Icon id='times' /></button> <button className='icon-button' onMouseDown={forget}><Icon id='times' icon={CloseIcon} /></button>
</button> </button>
)) : ( )) : (
<div className='search__popout__menu__message'> <div className='search__popout__menu__message'>

View file

@ -5,6 +5,11 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as FindInPageIcon } from '@material-symbols/svg-600/outlined/find_in_page.svg';
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { LoadMore } from 'flavours/glitch/components/load_more'; import { LoadMore } from 'flavours/glitch/components/load_more';
import { SearchSection } from 'flavours/glitch/features/explore/components/search_section'; import { SearchSection } from 'flavours/glitch/features/explore/components/search_section';
@ -44,7 +49,7 @@ class SearchResults extends ImmutablePureComponent {
if (results.get('accounts') && results.get('accounts').size > 0) { if (results.get('accounts') && results.get('accounts').size > 0) {
accounts = ( accounts = (
<SearchSection title={<><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}> <SearchSection title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>}>
{withoutLastResult(results.get('accounts')).map(accountId => <AccountContainer key={accountId} id={accountId} />)} {withoutLastResult(results.get('accounts')).map(accountId => <AccountContainer key={accountId} id={accountId} />)}
{(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreAccounts} />} {(results.get('accounts').size > INITIAL_PAGE_LIMIT && results.get('accounts').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreAccounts} />}
</SearchSection> </SearchSection>
@ -53,7 +58,7 @@ class SearchResults extends ImmutablePureComponent {
if (results.get('hashtags') && results.get('hashtags').size > 0) { if (results.get('hashtags') && results.get('hashtags').size > 0) {
hashtags = ( hashtags = (
<SearchSection title={<><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>}> <SearchSection title={<><Icon id='hashtag' icon={TagIcon} /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>}>
{withoutLastResult(results.get('hashtags')).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} {withoutLastResult(results.get('hashtags')).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
{(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreHashtags} />} {(results.get('hashtags').size > INITIAL_PAGE_LIMIT && results.get('hashtags').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreHashtags} />}
</SearchSection> </SearchSection>
@ -62,7 +67,7 @@ class SearchResults extends ImmutablePureComponent {
if (results.get('statuses') && results.get('statuses').size > 0) { if (results.get('statuses') && results.get('statuses').size > 0) {
statuses = ( statuses = (
<SearchSection title={<><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>}> <SearchSection title={<><Icon id='quote-right' icon={FindInPageIcon} /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>}>
{withoutLastResult(results.get('statuses')).map(statusId => <StatusContainer key={statusId} id={statusId} />)} {withoutLastResult(results.get('statuses')).map(statusId => <StatusContainer key={statusId} id={statusId} />)}
{(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreStatuses} />} {(results.get('statuses').size > INITIAL_PAGE_LIMIT && results.get('statuses').size % INITIAL_PAGE_LIMIT === 1) && <LoadMore visible onClick={this.handleLoadMoreStatuses} />}
</SearchSection> </SearchSection>
@ -73,7 +78,7 @@ class SearchResults extends ImmutablePureComponent {
return ( return (
<div className='drawer--results'> <div className='drawer--results'>
<header className='search-results__header'> <header className='search-results__header'>
<Icon id='search' fixedWidth /> <Icon id='search' icon={SearchIcon} />
<FormattedMessage id='explore.search_results' defaultMessage='Search results' /> <FormattedMessage id='explore.search_results' defaultMessage='Search results' />
</header> </header>

View file

@ -1,4 +1,3 @@
// Package imports.
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
@ -6,9 +5,11 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
// Components. import { ReactComponent as ForumIcon } from '@material-symbols/svg-600/outlined/forum.svg';
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
// Messages.
const messages = defineMessages({ const messages = defineMessages({
localOnly: { localOnly: {
defaultMessage: 'This post is local-only', defaultMessage: 'This post is local-only',
@ -23,8 +24,8 @@ const messages = defineMessages({
// We use an array of tuples here instead of an object because it // We use an array of tuples here instead of an object because it
// preserves order. // preserves order.
const iconMap = [ const iconMap = [
['do_not_federate', 'home', messages.localOnly], ['do_not_federate', 'home', HomeIcon, messages.localOnly],
['threaded_mode', 'comments', messages.threadedMode], ['threaded_mode', 'comments', ForumIcon, messages.threadedMode],
]; ];
class TextareaIcons extends ImmutablePureComponent { class TextareaIcons extends ImmutablePureComponent {
@ -38,20 +39,17 @@ class TextareaIcons extends ImmutablePureComponent {
const { advancedOptions, intl } = this.props; const { advancedOptions, intl } = this.props;
return ( return (
<div className='compose-form__textarea-icons'> <div className='compose-form__textarea-icons'>
{advancedOptions ? iconMap.map( {advancedOptions && iconMap.map(
([key, icon, message]) => advancedOptions.get(key) ? ( ([key, icon, iconComponent, message]) => advancedOptions.get(key) && (
<span <span
className='textarea_icon' className='textarea_icon'
key={key} key={key}
title={intl.formatMessage(message)} title={intl.formatMessage(message)}
> >
<Icon <Icon id={icon} icon={iconComponent} />
fixedWidth
id={icon}
/>
</span> </span>
) : null, ),
) : null} )}
</div> </div>
); );
} }

View file

@ -5,6 +5,9 @@ import { FormattedMessage } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
import { ReactComponent as InfoIcon } from '@material-symbols/svg-600/outlined/info.svg';
import spring from 'react-motion/lib/spring'; import spring from 'react-motion/lib/spring';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -47,13 +50,13 @@ export default class Upload extends ImmutablePureComponent {
{({ scale }) => ( {({ scale }) => (
<div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}> <div className='compose-form__upload-thumbnail' style={{ transform: `scale(${scale})`, backgroundImage: `url(${media.get('preview_url')})`, backgroundPosition: `${x}% ${y}%` }}>
<div className='compose-form__upload__actions'> <div className='compose-form__upload__actions'>
<button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button> <button type='button' className='icon-button' onClick={this.handleUndoClick}><Icon id='times' icon={CloseIcon} /> <FormattedMessage id='upload_form.undo' defaultMessage='Delete' /></button>
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button> <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='upload_form.edit' defaultMessage='Edit' /></button>
</div> </div>
{(media.get('description') || '').length === 0 && ( {(media.get('description') || '').length === 0 && (
<div className='compose-form__upload__warning'> <div className='compose-form__upload__warning'>
<button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button> <button type='button' className='icon-button' onClick={this.handleFocalPointClick}><Icon id='info-circle' icon={InfoIcon} /> <FormattedMessage id='upload_form.description_missing' defaultMessage='No description added' /></button>
</div> </div>
)} )}
</div> </div>

View file

@ -3,6 +3,7 @@ import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { ReactComponent as UploadFileIcon } from '@material-symbols/svg-600/outlined/upload_file.svg';
import spring from 'react-motion/lib/spring'; import spring from 'react-motion/lib/spring';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -35,7 +36,7 @@ export default class UploadProgress extends PureComponent {
return ( return (
<div className='upload-progress'> <div className='upload-progress'>
<div className='upload-progress__icon'> <div className='upload-progress__icon'>
<Icon id='upload' /> <Icon id='upload' icon={UploadFileIcon} />
</div> </div>
<div className='upload-progress__message'> <div className='upload-progress__message'>

View file

@ -8,6 +8,8 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as MoreHorizIcon } from '@material-symbols/svg-600/outlined/more_horiz.svg';
import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import AttachmentList from 'flavours/glitch/components/attachment_list'; import AttachmentList from 'flavours/glitch/components/attachment_list';
@ -208,7 +210,7 @@ class Conversation extends ImmutablePureComponent {
/> />
<div className='status__action-bar'> <div className='status__action-bar'>
<IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' onClick={this.handleReply} /> <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.reply)} icon='reply' iconComponent={ReplyIcon} onClick={this.handleReply} />
<div className='status__action-bar-dropdown'> <div className='status__action-bar-dropdown'>
<DropdownMenuContainer <DropdownMenuContainer
@ -216,6 +218,7 @@ class Conversation extends ImmutablePureComponent {
status={lastStatus} status={lastStatus}
items={menu} items={menu}
icon='ellipsis-h' icon='ellipsis-h'
iconComponent={MoreHorizIcon}
size={18} size={18}
direction='right' direction='right'
title={intl.formatMessage(messages.more)} title={intl.formatMessage(messages.more)}

View file

@ -7,6 +7,8 @@ import { Helmet } from 'react-helmet';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns'; import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
import { mountConversations, unmountConversations, expandConversations } from 'flavours/glitch/actions/conversations'; import { mountConversations, unmountConversations, expandConversations } from 'flavours/glitch/actions/conversations';
import { connectDirectStream } from 'flavours/glitch/actions/streaming'; import { connectDirectStream } from 'flavours/glitch/actions/streaming';
@ -139,6 +141,7 @@ class DirectTimeline extends PureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='envelope' icon='envelope'
iconComponent={MailIcon}
active={hasUnread} active={hasUnread}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onPin={this.handlePin} onPin={this.handlePin}

View file

@ -9,6 +9,8 @@ import { List as ImmutableList } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
import { addColumn, removeColumn, moveColumn, changeColumnParams } from 'flavours/glitch/actions/columns'; import { addColumn, removeColumn, moveColumn, changeColumnParams } from 'flavours/glitch/actions/columns';
import { fetchDirectory, expandDirectory } from 'flavours/glitch/actions/directory'; import { fetchDirectory, expandDirectory } from 'flavours/glitch/actions/directory';
import Column from 'flavours/glitch/components/column'; import Column from 'flavours/glitch/components/column';
@ -156,6 +158,7 @@ class Directory extends PureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='address-book-o' icon='address-book-o'
iconComponent={PeopleIcon}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onPin={this.handlePin} onPin={this.handlePin}
onMove={this.handleMove} onMove={this.handleMove}

View file

@ -8,10 +8,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as BlockIcon } from '@material-symbols/svg-600/outlined/block-fill.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { fetchDomainBlocks, expandDomainBlocks } from '../../actions/domain_blocks'; import { fetchDomainBlocks, expandDomainBlocks } from '../../actions/domain_blocks';
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
import { LoadingIndicator } from '../../components/loading_indicator'; import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import DomainContainer from '../../containers/domain_container'; import DomainContainer from '../../containers/domain_container';
@ -60,9 +60,7 @@ class Blocks extends ImmutablePureComponent {
const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no blocked domains yet.' />; const emptyMessage = <FormattedMessage id='empty_column.domain_blocks' defaultMessage='There are no blocked domains yet.' />;
return ( return (
<Column bindToDocument={!multiColumn} icon='minus-circle' heading={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} icon='ban' iconComponent={BlockIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<ColumnBackButtonSlim />
<ScrollableList <ScrollableList
scrollKey='domain_blocks' scrollKey='domain_blocks'
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}

View file

@ -8,6 +8,9 @@ import { NavLink, Switch, Route } from 'react-router-dom';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg';
import Column from 'flavours/glitch/components/column'; import Column from 'flavours/glitch/components/column';
import ColumnHeader from 'flavours/glitch/components/column_header'; import ColumnHeader from 'flavours/glitch/components/column_header';
import Search from 'flavours/glitch/features/compose/containers/search_container'; import Search from 'flavours/glitch/features/compose/containers/search_container';
@ -57,6 +60,7 @@ class Explore extends PureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon={isSearching ? 'search' : 'hashtag'} icon={isSearching ? 'search' : 'hashtag'}
iconComponent={isSearching ? SearchIcon : TagIcon}
title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)} title={intl.formatMessage(isSearching ? messages.searchResults : messages.title)}
onClick={this.handleHeaderClick} onClick={this.handleHeaderClick}
multiColumn={multiColumn} multiColumn={multiColumn}

View file

@ -9,6 +9,10 @@ import { List as ImmutableList } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as FindInPageIcon } from '@material-symbols/svg-600/outlined/find_in_page.svg';
import { ReactComponent as PeopleIcon } from '@material-symbols/svg-600/outlined/group.svg';
import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg';
import { submitSearch, expandSearch } from 'flavours/glitch/actions/search'; import { submitSearch, expandSearch } from 'flavours/glitch/actions/search';
import { ImmutableHashtag as Hashtag } from 'flavours/glitch/components/hashtag'; import { ImmutableHashtag as Hashtag } from 'flavours/glitch/components/hashtag';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -165,19 +169,19 @@ class Results extends PureComponent {
filteredResults = (accounts.size + hashtags.size + statuses.size) > 0 ? ( filteredResults = (accounts.size + hashtags.size + statuses.size) > 0 ? (
<> <>
{accounts.size > 0 && ( {accounts.size > 0 && (
<SearchSection key='accounts' title={<><Icon id='users' fixedWidth /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>} onClickMore={this.handleLoadMoreAccounts}> <SearchSection key='accounts' title={<><Icon id='users' icon={PeopleIcon} /><FormattedMessage id='search_results.accounts' defaultMessage='Profiles' /></>} onClickMore={this.handleLoadMoreAccounts}>
{accounts.take(INITIAL_DISPLAY).map(id => <Account key={id} id={id} />)} {accounts.take(INITIAL_DISPLAY).map(id => <Account key={id} id={id} />)}
</SearchSection> </SearchSection>
)} )}
{hashtags.size > 0 && ( {hashtags.size > 0 && (
<SearchSection key='hashtags' title={<><Icon id='hashtag' fixedWidth /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>} onClickMore={this.handleLoadMoreHashtags}> <SearchSection key='hashtags' title={<><Icon id='hashtag' icon={TagIcon} /><FormattedMessage id='search_results.hashtags' defaultMessage='Hashtags' /></>} onClickMore={this.handleLoadMoreHashtags}>
{hashtags.take(INITIAL_DISPLAY).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)} {hashtags.take(INITIAL_DISPLAY).map(hashtag => <Hashtag key={hashtag.get('name')} hashtag={hashtag} />)}
</SearchSection> </SearchSection>
)} )}
{statuses.size > 0 && ( {statuses.size > 0 && (
<SearchSection key='statuses' title={<><Icon id='quote-right' fixedWidth /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>} onClickMore={this.handleLoadMoreStatuses}> <SearchSection key='statuses' title={<><Icon id='quote-right' icon={FindInPageIcon} /><FormattedMessage id='search_results.statuses' defaultMessage='Posts' /></>} onClickMore={this.handleLoadMoreStatuses}>
{statuses.take(INITIAL_DISPLAY).map(id => <Status key={id} id={id} />)} {statuses.take(INITIAL_DISPLAY).map(id => <Status key={id} id={id} />)}
</SearchSection> </SearchSection>
)} )}

View file

@ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns'; import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
@ -80,6 +81,7 @@ class Favourites extends ImmutablePureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.heading)}>
<ColumnHeader <ColumnHeader
icon='star' icon='star'
iconComponent={StarIcon}
title={intl.formatMessage(messages.heading)} title={intl.formatMessage(messages.heading)}
onPin={this.handlePin} onPin={this.handlePin}
onMove={this.handleMove} onMove={this.handleMove}

View file

@ -8,6 +8,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as RefreshIcon } from '@material-symbols/svg-600/outlined/refresh.svg';
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { fetchFavourites, expandFavourites } from 'flavours/glitch/actions/interactions'; import { fetchFavourites, expandFavourites } from 'flavours/glitch/actions/interactions';
@ -80,12 +82,13 @@ class Favourites extends ImmutablePureComponent {
<Column ref={this.setRef}> <Column ref={this.setRef}>
<ColumnHeader <ColumnHeader
icon='star' icon='star'
iconComponent={StarIcon}
title={intl.formatMessage(messages.heading)} title={intl.formatMessage(messages.heading)}
onClick={this.handleHeaderClick} onClick={this.handleHeaderClick}
showBackButton showBackButton
multiColumn={multiColumn} multiColumn={multiColumn}
extraButton={( extraButton={(
<button type='button' className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' /></button> <button type='button' className='column-header__button' title={intl.formatMessage(messages.refresh)} aria-label={intl.formatMessage(messages.refresh)} onClick={this.handleRefresh}><Icon id='refresh' icon={RefreshIcon} /></button>
)} )}
/> />

View file

@ -5,6 +5,7 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
import fuzzysort from 'fuzzysort'; import fuzzysort from 'fuzzysort';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -78,7 +79,7 @@ class SelectFilter extends PureComponent {
renderCreateNew (name) { renderCreateNew (name) {
return ( return (
<div key='add-new-filter' role='button' tabIndex={0} className='language-dropdown__dropdown__results__item' onClick={this.handleNewFilterClick} onKeyDown={this.handleKeyDown}> <div key='add-new-filter' role='button' tabIndex={0} className='language-dropdown__dropdown__results__item' onClick={this.handleNewFilterClick} onKeyDown={this.handleKeyDown}>
<Icon id='plus' fixedWidth /> <FormattedMessage id='filter_modal.select_filter.prompt_new' defaultMessage='New category: {name}' values={{ name }} /> <Icon id='plus' icon={AddIcon} /> <FormattedMessage id='filter_modal.select_filter.prompt_new' defaultMessage='New category: {name}' values={{ name }} />
</div> </div>
); );
} }

View file

@ -6,6 +6,8 @@ import { useIntl, defineMessages, FormattedMessage } from 'react-intl';
import { Helmet } from 'react-helmet'; import { Helmet } from 'react-helmet';
import { NavLink } from 'react-router-dom'; import { NavLink } from 'react-router-dom';
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
import { addColumn } from 'flavours/glitch/actions/columns'; import { addColumn } from 'flavours/glitch/actions/columns';
import { changeSetting } from 'flavours/glitch/actions/settings'; import { changeSetting } from 'flavours/glitch/actions/settings';
import { connectPublicStream, connectCommunityStream } from 'flavours/glitch/actions/streaming'; import { connectPublicStream, connectCommunityStream } from 'flavours/glitch/actions/streaming';
@ -179,6 +181,7 @@ const Firehose = ({ feedType, multiColumn }) => {
<Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={columnRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='globe' icon='globe'
iconComponent={PublicIcon}
active={hasUnread} active={hasUnread}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onPin={handlePin} onPin={handlePin}

View file

@ -5,6 +5,9 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { Avatar } from '../../../components/avatar'; import { Avatar } from '../../../components/avatar';
import { DisplayName } from '../../../components/display_name'; import { DisplayName } from '../../../components/display_name';
import { IconButton } from '../../../components/icon_button'; import { IconButton } from '../../../components/icon_button';
@ -40,8 +43,8 @@ class AccountAuthorize extends ImmutablePureComponent {
</div> </div>
<div className='account--panel'> <div className='account--panel'>
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /></div> <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.authorize)} icon='check' iconComponent={CheckIcon} onClick={onAuthorize} /></div>
<div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /></div> <div className='account--panel__button'><IconButton title={intl.formatMessage(messages.reject)} icon='times' iconComponent={CloseIcon} onClick={onReject} /></div>
</div> </div>
</div> </div>
); );

View file

@ -8,10 +8,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts'; import { fetchFollowRequests, expandFollowRequests } from '../../actions/accounts';
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import { me } from '../../initial_state'; import { me } from '../../initial_state';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
@ -67,8 +67,7 @@ class FollowRequests extends ImmutablePureComponent {
); );
return ( return (
<Column bindToDocument={!multiColumn} icon='user-plus' heading={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} icon='user-plus' iconComponent={PersonAddIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<ColumnBackButtonSlim />
<ScrollableList <ScrollableList
scrollKey='follow_requests' scrollKey='follow_requests'
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}

View file

@ -8,6 +8,7 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { expandFollowedHashtags, fetchFollowedHashtags } from 'flavours/glitch/actions/tags'; import { expandFollowedHashtags, fetchFollowedHashtags } from 'flavours/glitch/actions/tags';
@ -55,6 +56,7 @@ class FollowedTags extends ImmutablePureComponent {
<Column bindToDocument={!multiColumn}> <Column bindToDocument={!multiColumn}>
<ColumnHeader <ColumnHeader
icon='hashtag' icon='hashtag'
iconComponent={TagIcon}
title={intl.formatMessage(messages.heading)} title={intl.formatMessage(messages.heading)}
showBackButton showBackButton
multiColumn={multiColumn} multiColumn={multiColumn}

View file

@ -9,6 +9,9 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
import { ReactComponent as ChevronLeftIcon } from '@material-symbols/svg-600/outlined/chevron_left.svg';
import { ReactComponent as ChevronRightIcon } from '@material-symbols/svg-600/outlined/chevron_right.svg';
import TransitionMotion from 'react-motion/lib/TransitionMotion'; import TransitionMotion from 'react-motion/lib/TransitionMotion';
import spring from 'react-motion/lib/spring'; import spring from 'react-motion/lib/spring';
import ReactSwipeableViews from 'react-swipeable-views'; import ReactSwipeableViews from 'react-swipeable-views';
@ -294,7 +297,7 @@ class ReactionsBar extends ImmutablePureComponent {
/> />
))} ))}
{visibleReactions.size < 8 && <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={<Icon id='plus' />} />} {visibleReactions.size < 8 && <EmojiPickerDropdown onPickEmoji={this.handleEmojiPick} button={<Icon id='plus' icon={AddIcon} />} />}
</div> </div>
)} )}
</TransitionMotion> </TransitionMotion>
@ -440,9 +443,9 @@ class Announcements extends ImmutablePureComponent {
{announcements.size > 1 && ( {announcements.size > 1 && (
<div className='announcements__pagination'> <div className='announcements__pagination'>
<IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.previous)} icon='chevron-left' onClick={this.handlePrevClick} size={13} /> <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.previous)} icon='chevron-left' iconComponent={ChevronLeftIcon} onClick={this.handlePrevClick} size={13} />
<span>{index + 1} / {announcements.size}</span> <span>{index + 1} / {announcements.size}</span>
<IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.next)} icon='chevron-right' onClick={this.handleNextClick} size={13} /> <IconButton disabled={announcements.size === 1} title={intl.formatMessage(messages.next)} icon='chevron-right' iconComponent={ChevronRightIcon} onClick={this.handleNextClick} size={13} />
</div> </div>
)} )}
</div> </div>

View file

@ -10,6 +10,20 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as BookmarksIcon } from '@material-symbols/svg-600/outlined/bookmarks-fill.svg';
import { 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 MailIcon } from '@material-symbols/svg-600/outlined/mail.svg';
import { ReactComponent as ManufacturingIcon } from '@material-symbols/svg-600/outlined/manufacturing.svg';
import { ReactComponent as MenuIcon } from '@material-symbols/svg-600/outlined/menu.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.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 TagIcon } from '@material-symbols/svg-600/outlined/tag.svg';
import { fetchFollowRequests } from 'flavours/glitch/actions/accounts'; import { fetchFollowRequests } from 'flavours/glitch/actions/accounts';
import { fetchLists } from 'flavours/glitch/actions/lists'; import { fetchLists } from 'flavours/glitch/actions/lists';
import { openModal } from 'flavours/glitch/actions/modal'; import { openModal } from 'flavours/glitch/actions/modal';
@ -127,53 +141,53 @@ class GettingStarted extends ImmutablePureComponent {
if (multiColumn) { if (multiColumn) {
if (signedIn && !columns.find(item => item.get('id') === 'HOME')) { if (signedIn && !columns.find(item => item.get('id') === 'HOME')) {
navItems.push(<ColumnLink key='home' icon='home' text={intl.formatMessage(messages.home_timeline)} to='/home' />); navItems.push(<ColumnLink key='home' icon='home' iconComponent={HomeIcon} text={intl.formatMessage(messages.home_timeline)} to='/home' />);
} }
if (!columns.find(item => item.get('id') === 'NOTIFICATIONS')) { if (!columns.find(item => item.get('id') === 'NOTIFICATIONS')) {
navItems.push(<ColumnLink key='notifications' icon='bell' text={intl.formatMessage(messages.notifications)} badge={badgeDisplay(unreadNotifications)} to='/notifications' />); navItems.push(<ColumnLink key='notifications' icon='bell' iconComponent={NotificationsIcon} text={intl.formatMessage(messages.notifications)} badge={badgeDisplay(unreadNotifications)} to='/notifications' />);
} }
if (!columns.find(item => item.get('id') === 'COMMUNITY')) { if (!columns.find(item => item.get('id') === 'COMMUNITY')) {
navItems.push(<ColumnLink key='community_timeline' icon='users' text={intl.formatMessage(messages.community_timeline)} to='/public/local' />); navItems.push(<ColumnLink key='community_timeline' icon='users' iconComponent={PeopleIcon} text={intl.formatMessage(messages.community_timeline)} to='/public/local' />);
} }
if (!columns.find(item => item.get('id') === 'PUBLIC')) { if (!columns.find(item => item.get('id') === 'PUBLIC')) {
navItems.push(<ColumnLink key='public_timeline' icon='globe' text={intl.formatMessage(messages.public_timeline)} to='/public' />); navItems.push(<ColumnLink key='public_timeline' icon='globe' iconComponent={PublicIcon} text={intl.formatMessage(messages.public_timeline)} to='/public' />);
} }
} }
if (showTrends) { if (showTrends) {
navItems.push(<ColumnLink key='explore' icon='hashtag' text={intl.formatMessage(messages.explore)} to='/explore' />); navItems.push(<ColumnLink key='explore' icon='hashtag' iconComponent={TagIcon} text={intl.formatMessage(messages.explore)} to='/explore' />);
} }
if (signedIn) { if (signedIn) {
if (!multiColumn || !columns.find(item => item.get('id') === 'DIRECT')) { if (!multiColumn || !columns.find(item => item.get('id') === 'DIRECT')) {
navItems.push(<ColumnLink key='conversations' icon='envelope' text={intl.formatMessage(messages.direct)} to='/conversations' />); navItems.push(<ColumnLink key='conversations' icon='envelope' iconComponent={MailIcon} text={intl.formatMessage(messages.direct)} to='/conversations' />);
} }
if (!multiColumn || !columns.find(item => item.get('id') === 'BOOKMARKS')) { if (!multiColumn || !columns.find(item => item.get('id') === 'BOOKMARKS')) {
navItems.push(<ColumnLink key='bookmarks' icon='bookmark' text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />); navItems.push(<ColumnLink key='bookmarks' icon='bookmark' iconComponent={BookmarksIcon} text={intl.formatMessage(messages.bookmarks)} to='/bookmarks' />);
} }
if (myAccount.get('locked') || unreadFollowRequests > 0) { if (myAccount.get('locked') || unreadFollowRequests > 0) {
navItems.push(<ColumnLink key='follow_requests' icon='user-plus' text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />); navItems.push(<ColumnLink key='follow_requests' icon='user-plus' iconComponent={PersonAddIcon} text={intl.formatMessage(messages.follow_requests)} badge={badgeDisplay(unreadFollowRequests, 40)} to='/follow_requests' />);
} }
navItems.push(<ColumnLink key='getting_started' icon='ellipsis-h' text={intl.formatMessage(messages.misc)} to='/getting-started-misc' />); navItems.push(<ColumnLink key='getting_started' icon='ellipsis-h' iconComponent={MoreHorizIcon} text={intl.formatMessage(messages.misc)} to='/getting-started-misc' />);
listItems = listItems.concat([ listItems = listItems.concat([
<div key='9'> <div key='9'>
<ColumnLink key='lists' icon='bars' text={intl.formatMessage(messages.lists)} to='/lists' /> <ColumnLink key='lists' icon='bars' iconComponent={ListAltIcon} text={intl.formatMessage(messages.lists)} to='/lists' />
{lists.filter(list => !columns.find(item => item.get('id') === 'LIST' && item.getIn(['params', 'id']) === list.get('id'))).map(list => {lists.filter(list => !columns.find(item => item.get('id') === 'LIST' && item.getIn(['params', 'id']) === list.get('id'))).map(list =>
<ColumnLink key={`list-${list.get('id')}`} to={`/lists/${list.get('id')}`} icon='list-ul' text={list.get('title')} />, <ColumnLink key={`list-${list.get('id')}`} to={`/lists/${list.get('id')}`} icon='list-ul' iconComponent={ListAltIcon} text={list.get('title')} />,
)} )}
</div>, </div>,
]); ]);
} }
return ( return (
<Column bindToDocument={!multiColumn} icon='asterisk' heading={intl.formatMessage(messages.heading)} label={intl.formatMessage(messages.menu)} hideHeadingOnMobile> <Column bindToDocument={!multiColumn} icon='bars' iconComponent={MenuIcon} heading={intl.formatMessage(messages.heading)} label={intl.formatMessage(messages.menu)} hideHeadingOnMobile>
<div className='scrollable optionally-scrollable'> <div className='scrollable optionally-scrollable'>
<div className='getting-started__wrapper'> <div className='getting-started__wrapper'>
{!multiColumn && signedIn && <NavigationBar account={myAccount} />} {!multiColumn && signedIn && <NavigationBar account={myAccount} />}
@ -184,8 +198,8 @@ class GettingStarted extends ImmutablePureComponent {
<ColumnSubheading text={intl.formatMessage(messages.lists_subheading)} /> <ColumnSubheading text={intl.formatMessage(messages.lists_subheading)} />
{listItems} {listItems}
<ColumnSubheading text={intl.formatMessage(messages.settings_subheading)} /> <ColumnSubheading text={intl.formatMessage(messages.settings_subheading)} />
{ preferencesLink !== undefined && <ColumnLink icon='cog' text={intl.formatMessage(messages.preferences)} href={preferencesLink} /> } { preferencesLink !== undefined && <ColumnLink icon='cog' iconComponent={SettingsIcon} text={intl.formatMessage(messages.preferences)} href={preferencesLink} /> }
<ColumnLink icon='cogs' text={intl.formatMessage(messages.settings)} onClick={openSettings} /> <ColumnLink icon='cogs' iconComponent={ManufacturingIcon} text={intl.formatMessage(messages.settings)} onClick={openSettings} />
</> </>
)} )}
</div> </div>

View file

@ -5,13 +5,18 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as BlockIcon } from '@material-symbols/svg-600/outlined/block.svg';
import { ReactComponent as InfoIcon } from '@material-symbols/svg-600/outlined/info.svg';
import { ReactComponent as PersonCheckIcon } from '@material-symbols/svg-600/outlined/person_check.svg';
import { ReactComponent as PushPinIcon } from '@material-symbols/svg-600/outlined/push_pin.svg';
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star-fill.svg';
import { ReactComponent as VolumeOffIcon } from '@material-symbols/svg-600/outlined/volume_off.svg';
import { openModal } from 'flavours/glitch/actions/modal'; import { openModal } from 'flavours/glitch/actions/modal';
import ColumnBackButtonSlim from 'flavours/glitch/components/column_back_button_slim';
import Column from 'flavours/glitch/features/ui/components/column'; import Column from 'flavours/glitch/features/ui/components/column';
import ColumnLink from 'flavours/glitch/features/ui/components/column_link'; import ColumnLink from 'flavours/glitch/features/ui/components/column_link';
import ColumnSubheading from 'flavours/glitch/features/ui/components/column_subheading'; import ColumnSubheading from 'flavours/glitch/features/ui/components/column_subheading';
const messages = defineMessages({ const messages = defineMessages({
heading: { id: 'column.heading', defaultMessage: 'Misc' }, heading: { id: 'column.heading', defaultMessage: 'Misc' },
subheading: { id: 'column.subheading', defaultMessage: 'Miscellaneous options' }, subheading: { id: 'column.subheading', defaultMessage: 'Miscellaneous options' },
@ -46,18 +51,16 @@ class GettingStartedMisc extends ImmutablePureComponent {
const { signedIn } = this.context.identity; const { signedIn } = this.context.identity;
return ( return (
<Column icon='ellipsis-h' heading={intl.formatMessage(messages.heading)}> <Column icon='ellipsis-h' heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<ColumnBackButtonSlim />
<div className='scrollable'> <div className='scrollable'>
<ColumnSubheading text={intl.formatMessage(messages.subheading)} /> <ColumnSubheading text={intl.formatMessage(messages.subheading)} />
{signedIn && (<ColumnLink key='favourites' icon='star' text={intl.formatMessage(messages.favourites)} to='/favourites' />)} {signedIn && (<ColumnLink key='favourites' icon='star' iconComponent={StarIcon} text={intl.formatMessage(messages.favourites)} to='/favourites' />)}
{signedIn && (<ColumnLink key='pinned' icon='thumb-tack' text={intl.formatMessage(messages.pins)} to='/pinned' />)} {signedIn && (<ColumnLink key='pinned' icon='thumb-tack' iconComponent={PushPinIcon} text={intl.formatMessage(messages.pins)} to='/pinned' />)}
{signedIn && (<ColumnLink key='featured_users' icon='users' text={intl.formatMessage(messages.featured_users)} onClick={this.openFeaturedAccountsModal} />)} {signedIn && (<ColumnLink key='featured_users' icon='users' iconComponent={PersonCheckIcon} text={intl.formatMessage(messages.featured_users)} onClick={this.openFeaturedAccountsModal} />)}
{signedIn && (<ColumnLink key='mutes' icon='volume-off' text={intl.formatMessage(messages.mutes)} to='/mutes' />)} {signedIn && (<ColumnLink key='mutes' icon='volume-off' iconComponent={VolumeOffIcon} text={intl.formatMessage(messages.mutes)} to='/mutes' />)}
{signedIn && (<ColumnLink key='blocks' icon='ban' text={intl.formatMessage(messages.blocks)} to='/blocks' />)} {signedIn && (<ColumnLink key='blocks' icon='ban' iconComponent={BlockIcon} text={intl.formatMessage(messages.blocks)} to='/blocks' />)}
{signedIn && (<ColumnLink key='domain_blocks' icon='minus-circle' text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />)} {signedIn && (<ColumnLink key='domain_blocks' icon='minus-circle' iconComponent={BlockIcon} text={intl.formatMessage(messages.domain_blocks)} to='/domain_blocks' />)}
<ColumnLink key='shortcuts' icon='question' text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' /> <ColumnLink key='shortcuts' icon='question' iconComponent={InfoIcon} text={intl.formatMessage(messages.keyboard_shortcuts)} to='/keyboard-shortcuts' />
</div> </div>
</Column> </Column>
); );

View file

@ -8,6 +8,7 @@ import { Helmet } from 'react-helmet';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as TagIcon } from '@material-symbols/svg-600/outlined/tag.svg';
import { isEqual } from 'lodash'; import { isEqual } from 'lodash';
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns'; import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
@ -190,6 +191,7 @@ class HashtagTimeline extends PureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={`#${id}`}>
<ColumnHeader <ColumnHeader
icon='hashtag' icon='hashtag'
iconComponent={TagIcon}
active={hasUnread} active={hasUnread}
title={this.title()} title={this.title()}
onPin={this.handlePin} onPin={this.handlePin}

View file

@ -10,6 +10,9 @@ import { createSelector } from '@reduxjs/toolkit';
import { List as ImmutableList } from 'immutable'; import { List as ImmutableList } from 'immutable';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
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 'flavours/glitch/actions/announcements'; import { fetchAnnouncements, toggleShowAnnouncements } from 'flavours/glitch/actions/announcements';
import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge'; import { IconWithBadge } from 'flavours/glitch/components/icon_with_badge';
import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator'; import { NotSignedInIndicator } from 'flavours/glitch/components/not_signed_in_indicator';
@ -182,7 +185,7 @@ class HomeTimeline extends PureComponent {
aria-label={intl.formatMessage(showAnnouncements ? messages.hide_announcements : messages.show_announcements)} aria-label={intl.formatMessage(showAnnouncements ? messages.hide_announcements : messages.show_announcements)}
onClick={this.handleToggleAnnouncementsClick} onClick={this.handleToggleAnnouncementsClick}
> >
<IconWithBadge id='bullhorn' count={unreadAnnouncements} /> <IconWithBadge id='bullhorn' icon={CampaignIcon} count={unreadAnnouncements} />
</button> </button>
); );
} }
@ -199,6 +202,7 @@ class HomeTimeline extends PureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='home' icon='home'
iconComponent={HomeIcon}
active={hasUnread} active={hasUnread}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onPin={this.handlePin} onPin={this.handlePin}

View file

@ -7,6 +7,10 @@ import classNames from 'classnames';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg';
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg';
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star.svg';
import { throttle, escapeRegExp } from 'lodash'; import { throttle, escapeRegExp } from 'lodash';
import { openModal, closeModal } from 'flavours/glitch/actions/modal'; import { openModal, closeModal } from 'flavours/glitch/actions/modal';
@ -354,22 +358,22 @@ class InteractionModal extends React.PureComponent {
switch(type) { switch(type) {
case 'reply': case 'reply':
icon = <Icon id='reply' />; icon = <Icon id='reply' icon={ReplyIcon} />;
title = <FormattedMessage id='interaction_modal.title.reply' defaultMessage="Reply to {name}'s post" values={{ name }} />; title = <FormattedMessage id='interaction_modal.title.reply' defaultMessage="Reply to {name}'s post" values={{ name }} />;
actionDescription = <FormattedMessage id='interaction_modal.description.reply' defaultMessage='With an account on Mastodon, you can respond to this post.' />; actionDescription = <FormattedMessage id='interaction_modal.description.reply' defaultMessage='With an account on Mastodon, you can respond to this post.' />;
break; break;
case 'reblog': case 'reblog':
icon = <Icon id='retweet' />; icon = <Icon id='retweet' icon={RepeatIcon} />;
title = <FormattedMessage id='interaction_modal.title.reblog' defaultMessage="Boost {name}'s post" values={{ name }} />; title = <FormattedMessage id='interaction_modal.title.reblog' defaultMessage="Boost {name}'s post" values={{ name }} />;
actionDescription = <FormattedMessage id='interaction_modal.description.reblog' defaultMessage='With an account on Mastodon, you can boost this post to share it with your own followers.' />; actionDescription = <FormattedMessage id='interaction_modal.description.reblog' defaultMessage='With an account on Mastodon, you can boost this post to share it with your own followers.' />;
break; break;
case 'favourite': case 'favourite':
icon = <Icon id='star' />; icon = <Icon id='star' icon={StarIcon} />;
title = <FormattedMessage id='interaction_modal.title.favourite' defaultMessage="Favorite {name}'s post" values={{ name }} />; title = <FormattedMessage id='interaction_modal.title.favourite' defaultMessage="Favorite {name}'s post" values={{ name }} />;
actionDescription = <FormattedMessage id='interaction_modal.description.favourite' defaultMessage='With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.' />; actionDescription = <FormattedMessage id='interaction_modal.description.favourite' defaultMessage='With an account on Mastodon, you can favorite this post to let the author know you appreciate it and save it for later.' />;
break; break;
case 'follow': case 'follow':
icon = <Icon id='user-plus' />; icon = <Icon id='user-plus' icon={PersonAddIcon} />;
title = <FormattedMessage id='interaction_modal.title.follow' defaultMessage='Follow {name}' values={{ name }} />; title = <FormattedMessage id='interaction_modal.title.follow' defaultMessage='Follow {name}' values={{ name }} />;
actionDescription = <FormattedMessage id='interaction_modal.description.follow' defaultMessage='With an account on Mastodon, you can follow {name} to receive their posts in your home feed.' values={{ name }} />; actionDescription = <FormattedMessage id='interaction_modal.description.follow' defaultMessage='With an account on Mastodon, you can follow {name} to receive their posts in your home feed.' values={{ name }} />;
break; break;

View file

@ -7,6 +7,8 @@ import { Helmet } from 'react-helmet';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as InfoIcon } from '@material-symbols/svg-600/outlined/info.svg';
import Column from 'flavours/glitch/components/column'; import Column from 'flavours/glitch/components/column';
import ColumnHeader from 'flavours/glitch/components/column_header'; import ColumnHeader from 'flavours/glitch/components/column_header';
@ -33,7 +35,8 @@ class KeyboardShortcuts extends ImmutablePureComponent {
<Column> <Column>
<ColumnHeader <ColumnHeader
title={intl.formatMessage(messages.heading)} title={intl.formatMessage(messages.heading)}
icon='question' icon='info-circle'
iconComponent={InfoIcon}
multiColumn={multiColumn} multiColumn={multiColumn}
/> />

View file

@ -6,6 +6,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { ReactComponent as ListAltIcon } from '@material-symbols/svg-600/outlined/list_alt.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { removeFromListAdder, addToListAdder } from '../../../actions/lists'; import { removeFromListAdder, addToListAdder } from '../../../actions/lists';
@ -46,16 +50,16 @@ class List extends ImmutablePureComponent {
let button; let button;
if (added) { if (added) {
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />; button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
} else { } else {
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />; button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={onAdd} />;
} }
return ( return (
<div className='list'> <div className='list'>
<div className='list__wrapper'> <div className='list__wrapper'>
<div className='list__display-name'> <div className='list__display-name'>
<Icon id='list-ul' className='column-link__icon' fixedWidth /> <Icon id='list-ul' icon={ListAltIcon} className='column-link__icon' />
{list.get('title')} {list.get('title')}
</div> </div>

View file

@ -6,6 +6,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as AddIcon } from '@material-symbols/svg-600/outlined/add.svg';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { removeFromListEditor, addToListEditor } from '../../../actions/lists'; import { removeFromListEditor, addToListEditor } from '../../../actions/lists';
import { Avatar } from '../../../components/avatar'; import { Avatar } from '../../../components/avatar';
import { DisplayName } from '../../../components/display_name'; import { DisplayName } from '../../../components/display_name';
@ -53,9 +56,9 @@ class Account extends ImmutablePureComponent {
let button; let button;
if (added) { if (added) {
button = <IconButton icon='times' title={intl.formatMessage(messages.remove)} onClick={onRemove} />; button = <IconButton icon='times' iconComponent={CloseIcon} title={intl.formatMessage(messages.remove)} onClick={onRemove} />;
} else { } else {
button = <IconButton icon='plus' title={intl.formatMessage(messages.add)} onClick={onAdd} />; button = <IconButton icon='plus' iconComponent={AddIcon} title={intl.formatMessage(messages.add)} onClick={onAdd} />;
} }
return ( return (

View file

@ -5,6 +5,8 @@ import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
import { changeListEditorTitle, submitListEditor } from '../../../actions/lists'; import { changeListEditorTitle, submitListEditor } from '../../../actions/lists';
import { IconButton } from '../../../components/icon_button'; import { IconButton } from '../../../components/icon_button';
@ -61,6 +63,7 @@ class ListForm extends PureComponent {
<IconButton <IconButton
disabled={disabled} disabled={disabled}
icon='check' icon='check'
iconComponent={CheckIcon}
title={title} title={title}
onClick={this.handleClick} onClick={this.handleClick}
/> />

View file

@ -7,6 +7,9 @@ import classNames from 'classnames';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as CancelIcon } from '@material-symbols/svg-600/outlined/cancel.svg';
import { ReactComponent as SearchIcon } from '@material-symbols/svg-600/outlined/search.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists'; import { fetchListSuggestions, clearListSuggestions, changeListSuggestions } from '../../../actions/lists';
@ -69,8 +72,8 @@ class Search extends PureComponent {
</label> </label>
<div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}> <div role='button' tabIndex={0} className='search__icon' onClick={this.handleClear}>
<Icon id='search' className={classNames({ active: !hasValue })} /> <Icon id='search' icon={SearchIcon} className={classNames({ active: !hasValue })} />
<Icon id='times-circle' aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} /> <Icon id='times-circle' icon={CancelIcon} aria-label={intl.formatMessage(messages.search)} className={classNames({ active: hasValue })} />
</div> </div>
</div> </div>
); );

View file

@ -9,6 +9,9 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as DeleteIcon } from '@material-symbols/svg-600/outlined/delete.svg';
import { ReactComponent as EditIcon } from '@material-symbols/svg-600/outlined/edit.svg';
import { ReactComponent as ListAltIcon } from '@material-symbols/svg-600/outlined/list_alt.svg';
import Toggle from 'react-toggle'; import Toggle from 'react-toggle';
import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns'; import { addColumn, removeColumn, moveColumn } from 'flavours/glitch/actions/columns';
@ -181,6 +184,7 @@ class ListTimeline extends PureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={title}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={title}>
<ColumnHeader <ColumnHeader
icon='list-ul' icon='list-ul'
iconComponent={ListAltIcon}
active={hasUnread} active={hasUnread}
title={title} title={title}
onPin={this.handlePin} onPin={this.handlePin}
@ -191,11 +195,11 @@ class ListTimeline extends PureComponent {
> >
<div className='column-settings__row column-header__links'> <div className='column-settings__row column-header__links'>
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}> <button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleEditClick}>
<Icon id='pencil' /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' /> <Icon id='pencil' icon={EditIcon} /> <FormattedMessage id='lists.edit' defaultMessage='Edit list' />
</button> </button>
<button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}> <button type='button' className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.handleDeleteClick}>
<Icon id='trash' /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' /> <Icon id='trash' icon={DeleteIcon} /> <FormattedMessage id='lists.delete' defaultMessage='Delete list' />
</button> </button>
</div> </div>

View file

@ -6,7 +6,7 @@ import { defineMessages, injectIntl } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { changeListEditorTitle, submitListEditor } from 'flavours/glitch/actions/lists'; import { changeListEditorTitle, submitListEditor } from 'flavours/glitch/actions/lists';
import { IconButton } from 'flavours/glitch/components/icon_button'; import { Button } from 'flavours/glitch/components/button';
const messages = defineMessages({ const messages = defineMessages({
label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' }, label: { id: 'lists.new.title_placeholder', defaultMessage: 'New list title' },
@ -66,9 +66,8 @@ class NewListForm extends PureComponent {
/> />
</label> </label>
<IconButton <Button
disabled={disabled || !value} disabled={disabled || !value}
icon='plus'
title={title} title={title}
onClick={this.handleClick} onClick={this.handleClick}
/> />

View file

@ -9,8 +9,9 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as ListAltIcon } from '@material-symbols/svg-600/outlined/list_alt.svg';
import { fetchLists } from 'flavours/glitch/actions/lists'; import { fetchLists } from 'flavours/glitch/actions/lists';
import ColumnBackButtonSlim from 'flavours/glitch/components/column_back_button_slim';
import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator'; import { LoadingIndicator } from 'flavours/glitch/components/loading_indicator';
import ScrollableList from 'flavours/glitch/components/scrollable_list'; import ScrollableList from 'flavours/glitch/components/scrollable_list';
import Column from 'flavours/glitch/features/ui/components/column'; import Column from 'flavours/glitch/features/ui/components/column';
@ -64,9 +65,7 @@ class Lists extends ImmutablePureComponent {
const emptyMessage = <FormattedMessage id='empty_column.lists' defaultMessage="You don't have any lists yet. When you create one, it will show up here." />; const emptyMessage = <FormattedMessage id='empty_column.lists' defaultMessage="You don't have any lists yet. When you create one, it will show up here." />;
return ( return (
<Column bindToDocument={!multiColumn} icon='bars' heading={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} icon='bars' iconComponent={ListAltIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<ColumnBackButtonSlim />
<NewListForm /> <NewListForm />
<ColumnSubheading text={intl.formatMessage(messages.subheading)} /> <ColumnSubheading text={intl.formatMessage(messages.subheading)} />
@ -76,7 +75,7 @@ class Lists extends ImmutablePureComponent {
bindToDocument={!multiColumn} bindToDocument={!multiColumn}
> >
{lists.map(list => {lists.map(list =>
<ColumnLink key={list.get('id')} to={`/lists/${list.get('id')}`} icon='list-ul' text={list.get('title')} />, <ColumnLink key={list.get('id')} to={`/lists/${list.get('id')}`} icon='list-ul' iconComponent={ListAltIcon} text={list.get('title')} />,
)} )}
</ScrollableList> </ScrollableList>

View file

@ -4,13 +4,17 @@ import { PureComponent } from 'react';
import { injectIntl, defineMessages } from 'react-intl'; import { injectIntl, defineMessages } from 'react-intl';
// Our imports 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 ExpandLessIcon } from '@material-symbols/svg-600/outlined/expand_less.svg';
import { ReactComponent as ImageIcon } from '@material-symbols/svg-600/outlined/image.svg';
import { ReactComponent as ManufacturingIcon } from '@material-symbols/svg-600/outlined/manufacturing.svg';
import { ReactComponent as SettingsIcon } from '@material-symbols/svg-600/outlined/settings-fill.svg';
import { preferencesLink } from 'flavours/glitch/utils/backend_links'; import { preferencesLink } from 'flavours/glitch/utils/backend_links';
import LocalSettingsNavigationItem from './item'; import LocalSettingsNavigationItem from './item';
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
const messages = defineMessages({ const messages = defineMessages({
general: { id: 'settings.general', defaultMessage: 'General' }, general: { id: 'settings.general', defaultMessage: 'General' },
compose: { id: 'settings.compose_box_opts', defaultMessage: 'Compose box' }, compose: { id: 'settings.compose_box_opts', defaultMessage: 'Compose box' },
@ -41,6 +45,7 @@ class LocalSettingsNavigation extends PureComponent {
index={0} index={0}
onNavigate={onNavigate} onNavigate={onNavigate}
icon='cogs' icon='cogs'
iconComponent={ManufacturingIcon}
title={intl.formatMessage(messages.general)} title={intl.formatMessage(messages.general)}
/> />
<LocalSettingsNavigationItem <LocalSettingsNavigationItem
@ -48,6 +53,7 @@ class LocalSettingsNavigation extends PureComponent {
index={1} index={1}
onNavigate={onNavigate} onNavigate={onNavigate}
icon='pencil' icon='pencil'
iconComponent={EditIcon}
title={intl.formatMessage(messages.compose)} title={intl.formatMessage(messages.compose)}
/> />
<LocalSettingsNavigationItem <LocalSettingsNavigationItem
@ -62,6 +68,7 @@ class LocalSettingsNavigation extends PureComponent {
index={3} index={3}
onNavigate={onNavigate} onNavigate={onNavigate}
icon='angle-double-up' icon='angle-double-up'
iconComponent={ExpandLessIcon}
title={intl.formatMessage(messages.collapsed)} title={intl.formatMessage(messages.collapsed)}
/> />
<LocalSettingsNavigationItem <LocalSettingsNavigationItem
@ -69,6 +76,7 @@ class LocalSettingsNavigation extends PureComponent {
index={4} index={4}
onNavigate={onNavigate} onNavigate={onNavigate}
icon='image' icon='image'
iconComponent={ImageIcon}
title={intl.formatMessage(messages.media)} title={intl.formatMessage(messages.media)}
/> />
<LocalSettingsNavigationItem <LocalSettingsNavigationItem
@ -76,6 +84,7 @@ class LocalSettingsNavigation extends PureComponent {
href={preferencesLink} href={preferencesLink}
index={5} index={5}
icon='cog' icon='cog'
iconComponent={SettingsIcon}
title={intl.formatMessage(messages.preferences)} title={intl.formatMessage(messages.preferences)}
/> />
<LocalSettingsNavigationItem <LocalSettingsNavigationItem
@ -84,6 +93,7 @@ class LocalSettingsNavigation extends PureComponent {
index={6} index={6}
onNavigate={onClose} onNavigate={onClose}
icon='times' icon='times'
iconComponent={CloseIcon}
title={intl.formatMessage(messages.close)} title={intl.formatMessage(messages.close)}
/> />
</nav> </nav>

View file

@ -6,8 +6,6 @@ import classNames from 'classnames';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
// * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * * *
export default class LocalSettingsPage extends PureComponent { export default class LocalSettingsPage extends PureComponent {
static propTypes = { static propTypes = {
@ -16,6 +14,7 @@ export default class LocalSettingsPage extends PureComponent {
href: PropTypes.string, href: PropTypes.string,
icon: PropTypes.string, icon: PropTypes.string,
textIcon: PropTypes.string, textIcon: PropTypes.string,
iconComponent: PropTypes.func,
index: PropTypes.number.isRequired, index: PropTypes.number.isRequired,
onNavigate: PropTypes.func, onNavigate: PropTypes.func,
title: PropTypes.string, title: PropTypes.string,
@ -36,6 +35,7 @@ export default class LocalSettingsPage extends PureComponent {
className, className,
href, href,
icon, icon,
iconComponent,
textIcon, textIcon,
onNavigate, onNavigate,
title, title,
@ -45,7 +45,7 @@ export default class LocalSettingsPage extends PureComponent {
active, active,
}, className); }, className);
const iconElem = icon ? <Icon fixedWidth id={icon} /> : (textIcon ? <span className='text-icon-button'>{textIcon}</span> : null); const iconElem = icon ? <Icon id={icon} icon={iconComponent} /> : (textIcon ? <span className='text-icon-button'>{textIcon}</span> : null);
if (href) return ( if (href) return (
<a <a

View file

@ -8,10 +8,10 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as VolumeOffIcon } from '@material-symbols/svg-600/outlined/volume_off.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { fetchMutes, expandMutes } from '../../actions/mutes'; import { fetchMutes, expandMutes } from '../../actions/mutes';
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
import { LoadingIndicator } from '../../components/loading_indicator'; import { LoadingIndicator } from '../../components/loading_indicator';
import ScrollableList from '../../components/scrollable_list'; import ScrollableList from '../../components/scrollable_list';
import AccountContainer from '../../containers/account_container'; import AccountContainer from '../../containers/account_container';
@ -61,8 +61,7 @@ class Mutes extends ImmutablePureComponent {
const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />; const emptyMessage = <FormattedMessage id='empty_column.mutes' defaultMessage="You haven't muted any users yet." />;
return ( return (
<Column bindToDocument={!multiColumn} icon='volume-off' heading={intl.formatMessage(messages.heading)}> <Column bindToDocument={!multiColumn} icon='volume-off' iconComponent={VolumeOffIcon} heading={intl.formatMessage(messages.heading)} alwaysShowBackButton>
<ColumnBackButtonSlim />
<ScrollableList <ScrollableList
scrollKey='mutes' scrollKey='mutes'
onLoadMore={this.handleLoadMore} onLoadMore={this.handleLoadMore}

View file

@ -8,6 +8,7 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as FlagIcon } from '@material-symbols/svg-600/outlined/flag-fill.svg';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -95,7 +96,7 @@ class AdminReport extends ImmutablePureComponent {
<div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex={0}> <div className={classNames('notification notification-admin-report focusable', { unread })} tabIndex={0}>
<div className='notification__message'> <div className='notification__message'>
<div className='notification__favourite-icon-wrapper'> <div className='notification__favourite-icon-wrapper'>
<Icon id='flag' fixedWidth /> <Icon id='flag' icon={FlagIcon} />
</div> </div>
<span title={notification.get('created_at')}> <span title={notification.get('created_at')}>

View file

@ -8,6 +8,7 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add-fill.svg';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -86,7 +87,7 @@ class NotificationAdminSignup extends ImmutablePureComponent {
<div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex={0}> <div className={classNames('notification notification-admin-sign-up focusable', { unread })} tabIndex={0}>
<div className='notification__message'> <div className='notification__message'>
<div className='notification__favourite-icon-wrapper'> <div className='notification__favourite-icon-wrapper'>
<Icon fixedWidth id='user-plus' /> <Icon id='user-plus' icon={PersonAddIcon} />
</div> </div>
<FormattedMessage <FormattedMessage

View file

@ -3,6 +3,8 @@ import { PureComponent } from 'react';
import { FormattedMessage } from 'react-intl'; import { FormattedMessage } from 'react-intl';
import { ReactComponent as DeleteForeverIcon } from '@material-symbols/svg-600/outlined/delete_forever.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
export default class ClearColumnButton extends PureComponent { export default class ClearColumnButton extends PureComponent {
@ -13,7 +15,7 @@ export default class ClearColumnButton extends PureComponent {
render () { render () {
return ( return (
<button className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.props.onClick}><Icon id='eraser' /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button> <button className='text-btn column-header__setting-btn' tabIndex={0} onClick={this.props.onClick}><Icon id='eraser' icon={DeleteForeverIcon} /> <FormattedMessage id='notifications.clear' defaultMessage='Clear notifications' /></button>
); );
} }

View file

@ -3,6 +3,13 @@ import { PureComponent } from 'react';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'; import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { ReactComponent as HomeIcon } from '@material-symbols/svg-600/outlined/home-fill.svg';
import { ReactComponent as InsertChartIcon } from '@material-symbols/svg-600/outlined/insert_chart.svg';
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg';
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg';
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
const tooltips = defineMessages({ const tooltips = defineMessages({
@ -67,14 +74,14 @@ class FilterBar extends PureComponent {
onClick={this.onClick('mention')} onClick={this.onClick('mention')}
title={intl.formatMessage(tooltips.mentions)} title={intl.formatMessage(tooltips.mentions)}
> >
<Icon id='reply-all' fixedWidth /> <Icon id='reply-all' icon={ReplyAllIcon} />
</button> </button>
<button <button
className={selectedFilter === 'favourite' ? 'active' : ''} className={selectedFilter === 'favourite' ? 'active' : ''}
onClick={this.onClick('favourite')} onClick={this.onClick('favourite')}
title={intl.formatMessage(tooltips.favourites)} title={intl.formatMessage(tooltips.favourites)}
> >
<Icon id='star' fixedWidth /> <Icon id='star' icon={StarIcon} />
</button> </button>
<button <button
className={selectedFilter === 'reaction' ? 'active' : ''} className={selectedFilter === 'reaction' ? 'active' : ''}
@ -88,28 +95,28 @@ class FilterBar extends PureComponent {
onClick={this.onClick('reblog')} onClick={this.onClick('reblog')}
title={intl.formatMessage(tooltips.boosts)} title={intl.formatMessage(tooltips.boosts)}
> >
<Icon id='retweet' fixedWidth /> <Icon id='retweet' icon={RepeatIcon} />
</button> </button>
<button <button
className={selectedFilter === 'poll' ? 'active' : ''} className={selectedFilter === 'poll' ? 'active' : ''}
onClick={this.onClick('poll')} onClick={this.onClick('poll')}
title={intl.formatMessage(tooltips.polls)} title={intl.formatMessage(tooltips.polls)}
> >
<Icon id='tasks' fixedWidth /> <Icon id='tasks' icon={InsertChartIcon} />
</button> </button>
<button <button
className={selectedFilter === 'status' ? 'active' : ''} className={selectedFilter === 'status' ? 'active' : ''}
onClick={this.onClick('status')} onClick={this.onClick('status')}
title={intl.formatMessage(tooltips.statuses)} title={intl.formatMessage(tooltips.statuses)}
> >
<Icon id='home' fixedWidth /> <Icon id='home' icon={HomeIcon} />
</button> </button>
<button <button
className={selectedFilter === 'follow' ? 'active' : ''} className={selectedFilter === 'follow' ? 'active' : ''}
onClick={this.onClick('follow')} onClick={this.onClick('follow')}
title={intl.formatMessage(tooltips.follows)} title={intl.formatMessage(tooltips.follows)}
> >
<Icon id='user-plus' fixedWidth /> <Icon id='user-plus' icon={PersonAddIcon} />
</button> </button>
</div> </div>
); );

View file

@ -8,6 +8,7 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add-fill.svg';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
@ -86,7 +87,7 @@ class NotificationFollow extends ImmutablePureComponent {
<div className={classNames('notification notification-follow focusable', { unread })} tabIndex={0}> <div className={classNames('notification notification-follow focusable', { unread })} tabIndex={0}>
<div className='notification__message'> <div className='notification__message'>
<div className='notification__favourite-icon-wrapper'> <div className='notification__favourite-icon-wrapper'>
<Icon fixedWidth id='user-plus' /> <Icon id='user-plus' icon={PersonAddIcon} />
</div> </div>
<FormattedMessage <FormattedMessage

View file

@ -8,6 +8,9 @@ import { withRouter } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { ReactComponent as PersonIcon } from '@material-symbols/svg-600/outlined/person-fill.svg';
import { HotKeys } from 'react-hotkeys'; import { HotKeys } from 'react-hotkeys';
import { Avatar } from 'flavours/glitch/components/avatar'; import { Avatar } from 'flavours/glitch/components/avatar';
@ -106,7 +109,7 @@ class FollowRequest extends ImmutablePureComponent {
<div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex={0}> <div className={classNames('notification notification-follow-request focusable', { unread })} tabIndex={0}>
<div className='notification__message'> <div className='notification__message'>
<div className='notification__favourite-icon-wrapper'> <div className='notification__favourite-icon-wrapper'>
<Icon id='user' fixedWidth /> <Icon id='user' icon={PersonIcon} />
</div> </div>
<FormattedMessage <FormattedMessage
@ -124,8 +127,8 @@ class FollowRequest extends ImmutablePureComponent {
</Permalink> </Permalink>
<div className='account__relationship'> <div className='account__relationship'>
<IconButton title={intl.formatMessage(messages.authorize)} icon='check' onClick={onAuthorize} /> <IconButton title={intl.formatMessage(messages.authorize)} icon='check' iconComponent={CheckIcon} onClick={onAuthorize} />
<IconButton title={intl.formatMessage(messages.reject)} icon='times' onClick={onReject} /> <IconButton title={intl.formatMessage(messages.reject)} icon='times' iconComponent={CloseIcon} onClick={onReject} />
</div> </div>
</div> </div>
</div> </div>

View file

@ -5,6 +5,9 @@ import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { ReactComponent as TuneIcon } from '@material-symbols/svg-600/outlined/tune.svg';
import { requestBrowserPermission } from 'flavours/glitch/actions/notifications'; import { requestBrowserPermission } from 'flavours/glitch/actions/notifications';
import { changeSetting } from 'flavours/glitch/actions/settings'; import { changeSetting } from 'flavours/glitch/actions/settings';
import { Button } from 'flavours/glitch/components/button'; import { Button } from 'flavours/glitch/components/button';
@ -36,11 +39,11 @@ class NotificationsPermissionBanner extends PureComponent {
return ( return (
<div className='notifications-permission-banner'> <div className='notifications-permission-banner'>
<div className='notifications-permission-banner__close'> <div className='notifications-permission-banner__close'>
<IconButton icon='times' onClick={this.handleClose} title={intl.formatMessage(messages.close)} /> <IconButton icon='times' iconComponent={CloseIcon} onClick={this.handleClose} title={intl.formatMessage(messages.close)} />
</div> </div>
<h2><FormattedMessage id='notifications_permission_banner.title' defaultMessage='Never miss a thing' /></h2> <h2><FormattedMessage id='notifications_permission_banner.title' defaultMessage='Never miss a thing' /></h2>
<p><FormattedMessage id='notifications_permission_banner.how_to_control' defaultMessage="To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled." values={{ icon: <Icon id='sliders' /> }} /></p> <p><FormattedMessage id='notifications_permission_banner.how_to_control' defaultMessage="To receive notifications when Mastodon isn't open, enable desktop notifications. You can control precisely which types of interactions generate desktop notifications through the {icon} button above once they're enabled." values={{ icon: <Icon id='sliders' icon={TuneIcon} /> }} /></p>
<Button onClick={this.handleClick}><FormattedMessage id='notifications_permission_banner.enable' defaultMessage='Enable desktop notifications' /></Button> <Button onClick={this.handleClick}><FormattedMessage id='notifications_permission_banner.enable' defaultMessage='Enable desktop notifications' /></Button>
</div> </div>
); );

View file

@ -2,8 +2,6 @@
* Notification overlay * Notification overlay
*/ */
// Package imports.
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { defineMessages, injectIntl } from 'react-intl'; import { defineMessages, injectIntl } from 'react-intl';
@ -11,6 +9,8 @@ import { defineMessages, injectIntl } from 'react-intl';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/check.svg';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
const messages = defineMessages({ const messages = defineMessages({
@ -49,7 +49,7 @@ class NotificationOverlay extends ImmutablePureComponent {
> >
<div className='wrappy'> <div className='wrappy'>
<div className='ckbox' aria-hidden='true' title={label}> <div className='ckbox' aria-hidden='true' title={label}>
{active ? (<Icon id='check' />) : ''} {active ? (<Icon id='check' icon={CheckIcon} />) : ''}
</div> </div>
</div> </div>
</div> </div>

View file

@ -11,6 +11,9 @@ import { List as ImmutableList } from 'immutable';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as DeleteForeverIcon } from '@material-symbols/svg-600/outlined/delete_forever.svg';
import { ReactComponent as DoneAllIcon } from '@material-symbols/svg-600/outlined/done_all.svg';
import { ReactComponent as NotificationsIcon } from '@material-symbols/svg-600/outlined/notifications-fill.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { compareId } from 'flavours/glitch/compare_id'; import { compareId } from 'flavours/glitch/compare_id';
@ -293,7 +296,7 @@ class Notifications extends PureComponent {
onClick={this.handleMarkAsRead} onClick={this.handleMarkAsRead}
className='column-header__button' className='column-header__button'
> >
<Icon id='check' /> <Icon id='done-all' icon={DoneAllIcon} />
</button>, </button>,
); );
} }
@ -317,7 +320,7 @@ class Notifications extends PureComponent {
onClick={this.onEnterCleaningMode} onClick={this.onEnterCleaningMode}
className={notifCleaningButtonClassName} className={notifCleaningButtonClassName}
> >
<Icon id='eraser' /> <Icon id='eraser' icon={DeleteForeverIcon} />
</button>, </button>,
); );
@ -338,6 +341,7 @@ class Notifications extends PureComponent {
> >
<ColumnHeader <ColumnHeader
icon='bell' icon='bell'
iconComponent={NotificationsIcon}
active={isUnread} active={isUnread}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onPin={this.handlePin} onPin={this.handlePin}

View file

@ -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;

View file

@ -3,7 +3,9 @@ import { Fragment } from 'react';
import classNames from 'classnames'; import classNames from 'classnames';
import { Check } from 'flavours/glitch/components/check'; import { ReactComponent as CheckIcon } from '@material-symbols/svg-600/outlined/done.svg';
import { Icon } from 'flavours/glitch/components/icon';
const ProgressIndicator = ({ steps, completed }) => ( const ProgressIndicator = ({ steps, completed }) => (
@ -13,7 +15,7 @@ const ProgressIndicator = ({ steps, completed }) => (
{i > 0 && <div className={classNames('onboarding__progress-indicator__line', { active: completed > i })} />} {i > 0 && <div className={classNames('onboarding__progress-indicator__line', { active: completed > i })} />}
<div className={classNames('onboarding__progress-indicator__step', { active: completed > i })}> <div className={classNames('onboarding__progress-indicator__step', { active: completed > i })}>
{completed > i && <Check />} {completed > i && <Icon icon={CheckIcon} />}
</div> </div>
</Fragment> </Fragment>
))} ))}

View file

@ -1,15 +1,15 @@
import PropTypes from 'prop-types'; import PropTypes from 'prop-types';
import { Check } from 'flavours/glitch/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 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import ArrowSmallRight from './arrow_small_right'; const Step = ({ label, description, icon, iconComponent, completed, onClick, href }) => {
const Step = ({ label, description, icon, completed, onClick, href }) => {
const content = ( const content = (
<> <>
<div className='onboarding__steps__item__icon'> <div className='onboarding__steps__item__icon'>
<Icon id={icon} /> <Icon id={icon} icon={iconComponent} />
</div> </div>
<div className='onboarding__steps__item__description'> <div className='onboarding__steps__item__description'>
@ -18,7 +18,7 @@ const Step = ({ label, description, icon, completed, onClick, href }) => {
</div> </div>
<div className={completed ? 'onboarding__steps__item__progress' : 'onboarding__steps__item__go'}> <div className={completed ? 'onboarding__steps__item__progress' : 'onboarding__steps__item__go'}>
{completed ? <Check /> : <ArrowSmallRight />} {completed ? <Icon icon={CheckIcon} /> : <Icon icon={ArrowRightAltIcon} />}
</div> </div>
</> </>
); );
@ -42,6 +42,7 @@ Step.propTypes = {
label: PropTypes.node, label: PropTypes.node,
description: PropTypes.node, description: PropTypes.node,
icon: PropTypes.string, icon: PropTypes.string,
iconComponent: PropTypes.func,
completed: PropTypes.bool, completed: PropTypes.bool,
href: PropTypes.string, href: PropTypes.string,
onClick: PropTypes.func, onClick: PropTypes.func,

View file

@ -9,7 +9,7 @@ import { connect } from 'react-redux';
import { fetchSuggestions } from 'flavours/glitch/actions/suggestions'; import { fetchSuggestions } from 'flavours/glitch/actions/suggestions';
import { markAsPartial } from 'flavours/glitch/actions/timelines'; import { markAsPartial } from 'flavours/glitch/actions/timelines';
import Column from 'flavours/glitch/components/column'; import Column from 'flavours/glitch/components/column';
import ColumnBackButton from 'flavours/glitch/components/column_back_button'; import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
import { EmptyAccount } from 'flavours/glitch/components/empty_account'; import { EmptyAccount } from 'flavours/glitch/components/empty_account';
import Account from 'flavours/glitch/containers/account_container'; import Account from 'flavours/glitch/containers/account_container';
@ -25,7 +25,6 @@ class Follows extends PureComponent {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
suggestions: ImmutablePropTypes.list, suggestions: ImmutablePropTypes.list,
isLoading: PropTypes.bool, isLoading: PropTypes.bool,
multiColumn: PropTypes.bool,
}; };
componentDidMount () { componentDidMount () {
@ -39,7 +38,7 @@ class Follows extends PureComponent {
} }
render () { render () {
const { onBack, isLoading, suggestions, multiColumn } = this.props; const { onBack, isLoading, suggestions } = this.props;
let loadedContent; let loadedContent;
@ -53,7 +52,7 @@ class Follows extends PureComponent {
return ( return (
<Column> <Column>
<ColumnBackButton multiColumn={multiColumn} onClick={onBack} /> <ColumnBackButton onClick={onBack} />
<div className='scrollable privacy-policy'> <div className='scrollable privacy-policy'>
<div className='column-title'> <div className='column-title'>

View file

@ -10,11 +10,17 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as AccountCircleIcon } from '@material-symbols/svg-600/outlined/account_circle.svg';
import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg';
import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg';
import { ReactComponent as EditNoteIcon } from '@material-symbols/svg-600/outlined/edit_note.svg';
import { ReactComponent as PersonAddIcon } from '@material-symbols/svg-600/outlined/person_add.svg';
import { debounce } from 'lodash'; import { debounce } from 'lodash';
import { fetchAccount } from 'flavours/glitch/actions/accounts'; import { fetchAccount } from 'flavours/glitch/actions/accounts';
import { focusCompose } from 'flavours/glitch/actions/compose'; import { focusCompose } from 'flavours/glitch/actions/compose';
import { closeOnboarding } from 'flavours/glitch/actions/onboarding'; import { closeOnboarding } from 'flavours/glitch/actions/onboarding';
import { Icon } from 'flavours/glitch/components/icon';
import Column from 'flavours/glitch/features/ui/components/column'; import Column from 'flavours/glitch/features/ui/components/column';
import { me } from 'flavours/glitch/initial_state'; import { me } from 'flavours/glitch/initial_state';
import { makeGetAccount } from 'flavours/glitch/selectors'; import { makeGetAccount } from 'flavours/glitch/selectors';
@ -22,7 +28,6 @@ import { assetHost } from 'flavours/glitch/utils/config';
import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router'; import { WithRouterPropTypes } from 'flavours/glitch/utils/react_router';
import illustration from 'mastodon/../images/elephant_ui_conversation.svg'; import illustration from 'mastodon/../images/elephant_ui_conversation.svg';
import ArrowSmallRight from './components/arrow_small_right';
import Step from './components/step'; import Step from './components/step';
import Follows from './follows'; import Follows from './follows';
import Share from './share'; import Share from './share';
@ -43,7 +48,6 @@ class Onboarding extends ImmutablePureComponent {
static propTypes = { static propTypes = {
dispatch: PropTypes.func.isRequired, dispatch: PropTypes.func.isRequired,
account: ImmutablePropTypes.record, account: ImmutablePropTypes.record,
multiColumn: PropTypes.bool,
...WithRouterPropTypes, ...WithRouterPropTypes,
}; };
@ -96,14 +100,14 @@ class Onboarding extends ImmutablePureComponent {
} }
render () { render () {
const { account, multiColumn } = this.props; const { account } = this.props;
const { step, shareClicked } = this.state; const { step, shareClicked } = this.state;
switch(step) { switch(step) {
case 'follows': case 'follows':
return <Follows onBack={this.handleBackClick} multiColumn={multiColumn} />; return <Follows onBack={this.handleBackClick} />;
case 'share': case 'share':
return <Share onBack={this.handleBackClick} multiColumn={multiColumn} />; return <Share onBack={this.handleBackClick} />;
} }
return ( return (
@ -116,10 +120,10 @@ class Onboarding extends ImmutablePureComponent {
</div> </div>
<div className='onboarding__steps'> <div className='onboarding__steps'>
<Step onClick={this.handleProfileClick} href='/settings/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} /> <Step onClick={this.handleProfileClick} href='/settings/profile' completed={(!account.get('avatar').endsWith('missing.png')) || (account.get('display_name').length > 0 && account.get('note').length > 0)} icon='address-book-o' iconComponent={AccountCircleIcon} label={<FormattedMessage id='onboarding.steps.setup_profile.title' defaultMessage='Customize your profile' />} description={<FormattedMessage id='onboarding.steps.setup_profile.body' defaultMessage='Others are more likely to interact with you with a filled out profile.' />} />
<Step onClick={this.handleFollowClick} completed={(account.get('following_count') * 1) >= 7} icon='user-plus' label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} /> <Step onClick={this.handleFollowClick} completed={(account.get('following_count') * 1) >= 7} icon='user-plus' iconComponent={PersonAddIcon} label={<FormattedMessage id='onboarding.steps.follow_people.title' defaultMessage='Find at least {count, plural, one {one person} other {# people}} to follow' values={{ count: 7 }} />} description={<FormattedMessage id='onboarding.steps.follow_people.body' defaultMessage="You curate your own home feed. Let's fill it with interesting people." />} />
<Step onClick={this.handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' values={{ emoji: <img className='emojione' alt='🐘' src={`${assetHost}/emoji/1f418.svg`} /> }} />} /> <Step onClick={this.handleComposeClick} completed={(account.get('statuses_count') * 1) >= 1} icon='pencil-square-o' iconComponent={EditNoteIcon} label={<FormattedMessage id='onboarding.steps.publish_status.title' defaultMessage='Make your first post' />} description={<FormattedMessage id='onboarding.steps.publish_status.body' defaultMessage='Say hello to the world.' values={{ emoji: <img className='emojione' alt='🐘' src={`${assetHost}/emoji/1f418.svg`} /> }} />} />
<Step onClick={this.handleShareClick} completed={shareClicked} icon='copy' label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} /> <Step onClick={this.handleShareClick} completed={shareClicked} icon='copy' iconComponent={ContentCopyIcon} label={<FormattedMessage id='onboarding.steps.share_profile.title' defaultMessage='Share your profile' />} description={<FormattedMessage id='onboarding.steps.share_profile.body' defaultMessage='Let your friends know how to find you on Mastodon!' />} />
</div> </div>
<p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p> <p className='onboarding__lead'><FormattedMessage id='onboarding.start.skip' defaultMessage="Don't need help getting started?" /></p>
@ -127,12 +131,12 @@ class Onboarding extends ImmutablePureComponent {
<div className='onboarding__links'> <div className='onboarding__links'>
<Link to='/explore' className='onboarding__link'> <Link to='/explore' className='onboarding__link'>
<FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' />
<ArrowSmallRight /> <Icon icon={ArrowRightAltIcon} />
</Link> </Link>
<Link to='/home' className='onboarding__link'> <Link to='/home' className='onboarding__link'>
<FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' />
<ArrowSmallRight /> <Icon icon={ArrowRightAltIcon} />
</Link> </Link>
</div> </div>
</div> </div>

View file

@ -9,15 +9,15 @@ import { Link } from 'react-router-dom';
import ImmutablePropTypes from 'react-immutable-proptypes'; import ImmutablePropTypes from 'react-immutable-proptypes';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as ArrowRightAltIcon } from '@material-symbols/svg-600/outlined/arrow_right_alt.svg';
import { ReactComponent as ContentCopyIcon } from '@material-symbols/svg-600/outlined/content_copy.svg';
import SwipeableViews from 'react-swipeable-views'; import SwipeableViews from 'react-swipeable-views';
import Column from 'flavours/glitch/components/column'; import Column from 'flavours/glitch/components/column';
import ColumnBackButton from 'flavours/glitch/components/column_back_button'; import { ColumnBackButton } from 'flavours/glitch/components/column_back_button';
import { Icon } from 'flavours/glitch/components/icon'; import { Icon } from 'flavours/glitch/components/icon';
import { me, domain } from 'flavours/glitch/initial_state'; import { me, domain } from 'flavours/glitch/initial_state';
import ArrowSmallRight from './components/arrow_small_right';
const messages = defineMessages({ const messages = defineMessages({
shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' }, shareableMessage: { id: 'onboarding.share.message', defaultMessage: 'I\'m {username} on #Mastodon! Come follow me at {url}' },
}); });
@ -79,7 +79,7 @@ class CopyPasteText extends PureComponent {
<textarea readOnly value={value} ref={this.setRef} onClick={this.handleInputClick} onFocus={this.handleFocus} onBlur={this.handleBlur} /> <textarea readOnly value={value} ref={this.setRef} onClick={this.handleInputClick} onFocus={this.handleFocus} onBlur={this.handleBlur} />
<button className='button' onClick={this.handleButtonClick}> <button className='button' onClick={this.handleButtonClick}>
<Icon id='copy' /> {copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : <FormattedMessage id='copypaste.copy_to_clipboard' defaultMessage='Copy to clipboard' />} <Icon id='copy' icon={ContentCopyIcon} /> {copied ? <FormattedMessage id='copypaste.copied' defaultMessage='Copied' /> : <FormattedMessage id='copypaste.copy_to_clipboard' defaultMessage='Copy to clipboard' />}
</button> </button>
</div> </div>
); );
@ -146,18 +146,17 @@ class Share extends PureComponent {
static propTypes = { static propTypes = {
onBack: PropTypes.func, onBack: PropTypes.func,
account: ImmutablePropTypes.record, account: ImmutablePropTypes.record,
multiColumn: PropTypes.bool,
intl: PropTypes.object, intl: PropTypes.object,
}; };
render () { render () {
const { onBack, account, multiColumn, intl } = this.props; const { onBack, account, intl } = this.props;
const url = (new URL(`/@${account.get('username')}`, document.baseURI)).href; const url = (new URL(`/@${account.get('username')}`, document.baseURI)).href;
return ( return (
<Column> <Column>
<ColumnBackButton multiColumn={multiColumn} onClick={onBack} /> <ColumnBackButton onClick={onBack} />
<div className='scrollable privacy-policy'> <div className='scrollable privacy-policy'>
<div className='column-title'> <div className='column-title'>
@ -178,12 +177,12 @@ class Share extends PureComponent {
<div className='onboarding__links'> <div className='onboarding__links'>
<Link to='/home' className='onboarding__link'> <Link to='/home' className='onboarding__link'>
<FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' /> <FormattedMessage id='onboarding.actions.go_to_home' defaultMessage='Take me to my home feed' />
<ArrowSmallRight /> <Icon icon={ArrowRightAltIcon} />
</Link> </Link>
<Link to='/explore' className='onboarding__link'> <Link to='/explore' className='onboarding__link'>
<FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' /> <FormattedMessage id='onboarding.actions.go_to_explore' defaultMessage='Take me to trending' />
<ArrowSmallRight /> <Icon icon={ArrowRightAltIcon} />
</Link> </Link>
</div> </div>

View file

@ -9,6 +9,12 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as OpenInNewIcon } from '@material-symbols/svg-600/outlined/open_in_new.svg';
import { ReactComponent as RepeatIcon } from '@material-symbols/svg-600/outlined/repeat.svg';
import { ReactComponent as ReplyIcon } from '@material-symbols/svg-600/outlined/reply.svg';
import { ReactComponent as ReplyAllIcon } from '@material-symbols/svg-600/outlined/reply_all.svg';
import { ReactComponent as StarIcon } from '@material-symbols/svg-600/outlined/star.svg';
import { initBoostModal } from 'flavours/glitch/actions/boosts'; import { initBoostModal } from 'flavours/glitch/actions/boosts';
import { replyCompose } from 'flavours/glitch/actions/compose'; import { replyCompose } from 'flavours/glitch/actions/compose';
import { reblog, favourite, unreblog, unfavourite } from 'flavours/glitch/actions/interactions'; import { reblog, favourite, unreblog, unfavourite } from 'flavours/glitch/actions/interactions';
@ -171,13 +177,15 @@ class Footer extends ImmutablePureComponent {
const publicStatus = ['public', 'unlisted'].includes(status.get('visibility')); const publicStatus = ['public', 'unlisted'].includes(status.get('visibility'));
const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private'; const reblogPrivate = status.getIn(['account', 'id']) === me && status.get('visibility') === 'private';
let replyIcon, replyTitle; let replyIcon, replyIconComponent, replyTitle;
if (status.get('in_reply_to_id', null) === null) { if (status.get('in_reply_to_id', null) === null) {
replyIcon = 'reply'; replyIcon = 'reply';
replyIconComponent = ReplyIcon;
replyTitle = intl.formatMessage(messages.reply); replyTitle = intl.formatMessage(messages.reply);
} else { } else {
replyIcon = 'reply-all'; replyIcon = 'reply-all';
replyIconComponent = ReplyAllIcon;
replyTitle = intl.formatMessage(messages.replyAll); replyTitle = intl.formatMessage(messages.replyAll);
} }
@ -200,6 +208,7 @@ class Footer extends ImmutablePureComponent {
className='status__action-bar-button' className='status__action-bar-button'
title={replyTitle} title={replyTitle}
icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} 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} onClick={this.handleReplyClick}
counter={status.get('replies_count')} counter={status.get('replies_count')}
obfuscateCount obfuscateCount
@ -211,6 +220,7 @@ class Footer extends ImmutablePureComponent {
className='status__action-bar-button' className='status__action-bar-button'
title={replyTitle} title={replyTitle}
icon={status.get('in_reply_to_account_id') === status.getIn(['account', 'id']) ? 'reply' : replyIcon} 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} onClick={this.handleReplyClick}
/> />
); );
@ -219,9 +229,9 @@ class Footer extends ImmutablePureComponent {
return ( return (
<div className='picture-in-picture__footer'> <div className='picture-in-picture__footer'>
{replyButton} {replyButton}
<IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' onClick={this.handleReblogClick} counter={status.get('reblogs_count')} /> <IconButton className={classNames('status__action-bar-button', { reblogPrivate })} disabled={!publicStatus && !reblogPrivate} active={status.get('reblogged')} title={reblogTitle} icon='retweet' iconComponent={RepeatIcon} onClick={this.handleReblogClick} counter={status.get('reblogs_count')} />
<IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} /> <IconButton className='status__action-bar-button star-icon' animate active={status.get('favourited')} title={intl.formatMessage(messages.favourite)} icon='star' iconComponent={StarIcon} onClick={this.handleFavouriteClick} counter={status.get('favourites_count')} />
{withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' onClick={this.handleOpenClick} href={status.get('url')} />} {withOpenButton && <IconButton className='status__action-bar-button' title={intl.formatMessage(messages.open)} icon='external-link' iconComponent={OpenInNewIcon} onClick={this.handleOpenClick} href={status.get('url')} />}
</div> </div>
); );
} }

View file

@ -8,6 +8,8 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as CloseIcon } from '@material-symbols/svg-600/outlined/close.svg';
import { Avatar } from 'flavours/glitch/components/avatar'; import { Avatar } from 'flavours/glitch/components/avatar';
import { DisplayName } from 'flavours/glitch/components/display_name'; import { DisplayName } from 'flavours/glitch/components/display_name';
import { IconButton } from 'flavours/glitch/components/icon_button'; import { IconButton } from 'flavours/glitch/components/icon_button';
@ -40,7 +42,7 @@ class Header extends ImmutablePureComponent {
<DisplayName account={account} /> <DisplayName account={account} />
</Link> </Link>
<IconButton icon='times' onClick={onClose} title={intl.formatMessage(messages.close)} /> <IconButton icon='times' iconComponent={CloseIcon} onClick={onClose} title={intl.formatMessage(messages.close)} />
</div> </div>
); );
} }

View file

@ -8,10 +8,11 @@ import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component'; import ImmutablePureComponent from 'react-immutable-pure-component';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as PushPinIcon } from '@material-symbols/svg-600/outlined/push_pin.svg';
import { getStatusList } from 'flavours/glitch/selectors'; import { getStatusList } from 'flavours/glitch/selectors';
import { fetchPinnedStatuses } from '../../actions/pin_statuses'; import { fetchPinnedStatuses } from '../../actions/pin_statuses';
import ColumnBackButtonSlim from '../../components/column_back_button_slim';
import StatusList from '../../components/status_list'; import StatusList from '../../components/status_list';
import Column from '../ui/components/column'; import Column from '../ui/components/column';
@ -50,8 +51,7 @@ class PinnedStatuses extends ImmutablePureComponent {
const { intl, statusIds, hasMore, multiColumn } = this.props; const { intl, statusIds, hasMore, multiColumn } = this.props;
return ( return (
<Column bindToDocument={!multiColumn} icon='thumb-tack' heading={intl.formatMessage(messages.heading)} ref={this.setRef}> <Column bindToDocument={!multiColumn} icon='thumb-tack' iconComponent={PushPinIcon} heading={intl.formatMessage(messages.heading)} ref={this.setRef} alwaysShowBackButton>
<ColumnBackButtonSlim />
<StatusList <StatusList
statusIds={statusIds} statusIds={statusIds}
scrollKey='pinned_statuses' scrollKey='pinned_statuses'

View file

@ -7,6 +7,8 @@ import { Helmet } from 'react-helmet';
import { connect } from 'react-redux'; import { connect } from 'react-redux';
import { ReactComponent as PublicIcon } from '@material-symbols/svg-600/outlined/public.svg';
import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner'; import { DismissableBanner } from 'flavours/glitch/components/dismissable_banner';
import { domain } from 'flavours/glitch/initial_state'; import { domain } from 'flavours/glitch/initial_state';
@ -136,6 +138,7 @@ class PublicTimeline extends PureComponent {
<Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}> <Column bindToDocument={!multiColumn} ref={this.setRef} label={intl.formatMessage(messages.title)}>
<ColumnHeader <ColumnHeader
icon='globe' icon='globe'
iconComponent={PublicIcon}
active={hasUnread} active={hasUnread}
title={intl.formatMessage(messages.title)} title={intl.formatMessage(messages.title)}
onPin={this.handlePin} onPin={this.handlePin}

Some files were not shown because too many files have changed in this diff Show more