This commit is contained in:
mgabdev
2020-03-02 17:26:25 -05:00
parent c6aa4e08a1
commit 0df3c073a5
43 changed files with 602 additions and 719 deletions

View File

@@ -0,0 +1,184 @@
import { Fragment } from 'react'
import { NavLink } from 'react-router-dom'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { me } from '../initial_state'
import Avatar from './avatar'
import DisplayName from './display_name'
import Button from './button'
import Text from './text'
const messages = defineMessages({
follow: { id: 'follow', defaultMessage: 'Follow' },
unfollow: { id: 'unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'requested', defaultMessage: 'Requested' },
unblock: { id: 'unblock', defaultMessage: 'Unblock' },
unmute: { id: 'unmute', defaultMessage: 'Unmute' },
mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'Mute notifications from @{name}' },
unmute_notifications: { id: 'account.unmute_notifications', defaultMessage: 'Unmute notifications from @{name}' },
})
export default
@injectIntl
class Account extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
onFollow: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired,
onMuteNotifications: PropTypes.func,
intl: PropTypes.object.isRequired,
hidden: PropTypes.bool,
actionIcon: PropTypes.string,
actionTitle: PropTypes.string,
onActionClick: PropTypes.func,
compact: PropTypes.bool,
showDismiss: PropTypes.bool,
dismissAction: PropTypes.func,
}
handleFollow = () => {
this.props.onFollow(this.props.account)
}
handleBlock = () => {
this.props.onBlock(this.props.account)
}
handleMute = () => {
this.props.onMute(this.props.account)
}
handleMuteNotifications = () => {
this.props.onMuteNotifications(this.props.account, true)
}
handleUnmuteNotifications = () => {
this.props.onMuteNotifications(this.props.account, false)
}
handleAction = () => {
this.props.onActionClick(this.props.account)
}
handleUnrequest = () => {
//
}
render() {
const {
account,
intl,
hidden,
onActionClick,
actionIcon,
actionTitle,
compact,
dismissAction,
showDismiss,
} = this.props
if (!account) return null
if (hidden) {
return (
<Fragment>
{account.get('display_name')}
{account.get('username')}
</Fragment>
)
}
let buttonOptions
let buttonText
if (onActionClick && actionIcon) {
buttonText = actionTitle
buttonOptions = {
onClick: this.handleAction,
outline: true,
color: 'brand',
backgroundColor: 'none',
}
} else if (account.get('id') !== me && account.get('relationship', null) !== null) {
const following = account.getIn(['relationship', 'following'])
const requested = account.getIn(['relationship', 'requested'])
const blocking = account.getIn(['relationship', 'blocking'])
if (requested || blocking) {
buttonText = intl.formatMessage(requested ? messages.requested : messages.blocking)
buttonOptions = {
narrow: true,
onClick: requested ? this.handleUnrequest : this.handleBlock,
color: 'primary',
backgroundColor: 'tertiary',
className: _s.marginTop5PX,
}
} else if (!account.get('moved') || following) {
buttonOptions = {
narrow: true,
outline: !following,
color: !following ? 'brand' : 'white',
backgroundColor: !following ? 'none' : 'brand',
className: _s.marginTop5PX,
onClick: this.handleFollow,
}
buttonText = intl.formatMessage(following ? messages.unfollow : messages.follow)
}
}
const button = !buttonOptions ? null : (
<Button {...buttonOptions}>
<Text color='inherit'>{buttonText}</Text>
</Button>
)
const avatarSize = compact ? 42 : 52
const dismissBtn = (
<Button
narrow
circle
backgroundColor='none'
className={_s.paddingHorizontal5PX}
onClick={dismissAction}
icon='close'
iconWidth='8px'
iconHeight='8px'
iconClassName={_s.fillColorSecondary}
/>
)
return (
<div className={[_s.default, _s.marginTop5PX, _s.marginBottom15PX].join(' ')}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<NavLink
className={[_s.default, _s.noUnderline].join(' ')}
title={account.get('acct')}
to={`/${account.get('acct')}`}
>
<Avatar account={account} size={avatarSize} />
</NavLink>
<NavLink
title={account.get('acct')}
to={`/${account.get('acct')}`}
className={[_s.default, _s.alignItemsStart, _s.paddingHorizontal10PX, _s.flexGrow1].join(' ')}
>
<DisplayName account={account} multiline={compact} />
{!compact && button}
</NavLink>
<div className={[_s.default].join(' ')}>
{showDismiss && dismissBtn}
{compact && button}
</div>
</div>
</div>
)
}
}

View File

@@ -1,211 +0,0 @@
import { Fragment } from 'react'
import { NavLink } from 'react-router-dom'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { me } from '../../initial_state'
import Avatar from '../avatar'
import DisplayName from '../display_name'
import IconButton from '../icon_button'
import Icon from '../icon'
import Button from '../button'
import Text from '../text'
const messages = defineMessages({
follow: { id: 'account.follow', defaultMessage: 'Follow' },
unfollow: { id: 'account.unfollow', defaultMessage: 'Unfollow' },
requested: { id: 'account.requested', defaultMessage: 'Awaiting approval' },
unblock: { id: 'account.unblock', defaultMessage: 'Unblock @{name}' },
unmute: { id: 'account.unmute', defaultMessage: 'Unmute @{name}' },
mute_notifications: { id: 'account.mute_notifications', defaultMessage: 'Mute notifications from @{name}' },
unmute_notifications: { id: 'account.unmute_notifications', defaultMessage: 'Unmute notifications from @{name}' },
})
export default
@injectIntl
class Account extends ImmutablePureComponent {
static propTypes = {
account: ImmutablePropTypes.map.isRequired,
onFollow: PropTypes.func.isRequired,
onBlock: PropTypes.func.isRequired,
onMute: PropTypes.func.isRequired,
onMuteNotifications: PropTypes.func,
intl: PropTypes.object.isRequired,
hidden: PropTypes.bool,
actionIcon: PropTypes.string,
actionTitle: PropTypes.string,
onActionClick: PropTypes.func,
compact: PropTypes.bool,
}
handleFollow = () => {
this.props.onFollow(this.props.account)
}
handleBlock = () => {
this.props.onBlock(this.props.account)
}
handleMute = () => {
this.props.onMute(this.props.account)
}
handleMuteNotifications = () => {
this.props.onMuteNotifications(this.props.account, true)
}
handleUnmuteNotifications = () => {
this.props.onMuteNotifications(this.props.account, false)
}
handleAction = () => {
this.props.onActionClick(this.props.account)
}
render() {
const {
account,
intl,
hidden,
onActionClick,
actionIcon,
actionTitle,
compact
} = this.props
if (!account) return null
if (hidden) {
return (
<Fragment>
{account.get('display_name')}
{account.get('username')}
</Fragment>
)
}
const avatarSize = compact ? 42 : 52
let buttons
if (onActionClick && actionIcon) {
buttons = <IconButton icon={actionIcon} title={actionTitle} onClick={this.handleAction} />
} else if (account.get('id') !== me && account.get('relationship', null) !== null) {
const following = account.getIn(['relationship', 'following'])
const requested = account.getIn(['relationship', 'requested'])
const blocking = account.getIn(['relationship', 'blocking'])
const muting = account.getIn(['relationship', 'muting'])
if (requested) {
buttons = <IconButton disabled icon='hourglass' title={intl.formatMessage(messages.requested)} />
} else if (blocking) {
buttons = <IconButton active icon='unlock' title={intl.formatMessage(messages.unblock, { name: account.get('username') })} onClick={this.handleBlock} />
} else if (muting) {
let hidingNotificationsButton
if (account.getIn(['relationship', 'muting_notifications'])) {
hidingNotificationsButton = <IconButton active icon='bell' title={intl.formatMessage(messages.unmute_notifications, { name: account.get('username') })} onClick={this.handleUnmuteNotifications} />
} else {
hidingNotificationsButton = <IconButton active icon='bell-slash' title={intl.formatMessage(messages.mute_notifications, { name: account.get('username') })} onClick={this.handleMuteNotifications} />
}
buttons = (
<Fragment>
<IconButton active icon='volume-up' title={intl.formatMessage(messages.unmute, { name: account.get('username') })} onClick={this.handleMute} />
{hidingNotificationsButton}
</Fragment>
)
} else if (!account.get('moved') || following) {
buttons = <IconButton icon={following ? 'user-times' : 'user-plus'} title={intl.formatMessage(following ? messages.unfollow : messages.follow)} onClick={this.handleFollow} active={following} />
}
}
// : todo : clean up
if (compact) {
return (
<div className={[_s.default, _s.marginTop5PX, _s.marginBottom15PX].join(' ')}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<NavLink
className={[_s.default, _s.noUnderline].join(' ')}
title={account.get('acct')}
to={`/${account.get('acct')}`}
>
<Avatar account={account} size={avatarSize} />
</NavLink>
<div className={[_s.default, _s.alignItemsStart, _s.paddingHorizontal10PX].join(' ')}>
<NavLink
className={[_s.default, _s.noUnderline].join(' ')}
title={account.get('acct')}
to={`/${account.get('acct')}`}
>
<DisplayName account={account} multiline />
</NavLink>
</div>
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
<Button
outline
narrow
color='brand'
backgroundColor='none'
className={_s.marginTop5PX}
>
<Text color='inherit'>
{intl.formatMessage(messages.follow)}
</Text>
</Button>
</div>
</div>
</div>
)
}
return (
<div className={[_s.default, _s.marginTop5PX, _s.marginBottom15PX].join(' ')}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<NavLink
className={[_s.default, _s.noUnderline].join(' ')}
title={account.get('acct')}
to={`/${account.get('acct')}`}
>
<Avatar account={account} size={avatarSize} />
</NavLink>
<div className={[_s.default, _s.alignItemsStart, _s.paddingHorizontal10PX].join(' ')}>
<NavLink
className={[_s.default, _s.noUnderline].join(' ')}
title={account.get('acct')}
to={`/${account.get('acct')}`}
>
<DisplayName account={account} />
</NavLink>
<Button
outline
narrow
color='brand'
backgroundColor='none'
className={_s.marginTop5PX}
>
<Text color='inherit'>
{intl.formatMessage(messages.follow)}
</Text>
</Button>
</div>
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
<button className={[_s.default, _s.circle, _s.backgroundTransparent, _s.paddingVertical5PX, _s.paddingHorizontal5PX, _s.cursorPointer].join(' ')}>
<Icon className={_s.fillcolorSecondary} id='close' width='8px' height='8px' />
</button>
</div>
</div>
</div>
)
}
}

View File

@@ -1,42 +0,0 @@
.account {
padding: 10px;
&:not(:last-of-type) {
border-bottom: 1px solid lighten($ui-base-color, 8%);
}
&__display-name {
display: block;
flex: 1 1 auto;
color: $darker-text-color;
overflow: hidden;
text-decoration: none;
font-size: 14px;
strong {
display: block;
color: $primary-text-color;
@include text-overflow(nowrap);
}
}
&__wrapper {
display: flex;
}
&__relationship {
height: auto;
position: relative;
padding: 0 0 0 5px;
}
&__avatar-wrapper {
float: left;
margin-right: 12px;
}
&__avatar {
display: block;
}
}

View File

@@ -1 +0,0 @@
export { default } from './account'

View File

@@ -60,7 +60,7 @@ class ColumnHeader extends PureComponent {
{
showBackBtn &&
<button className={[_s.default, _s.cursorPointer, _s.backgroundTransparent, _s.alignItemsCenter, _s.marginRight10PX, _s.justifyContentCenter].join(' ')}>
<Icon className={[_s.marginRight5PX, _s.fillColorBrand].join(' ')} id='back' width='20px' height='20px' />
<Icon className={[_s.marginRight5PX, _s.fillColorPrimary].join(' ')} id='back' width='20px' height='20px' />
</button>
}
@@ -85,7 +85,7 @@ class ColumnHeader extends PureComponent {
key={`column-header-action-btn-${i}`}
className={[_s.default, _s.marginLeft5PX, _s.cursorPointer, _s.backgroundSubtle2, _s.paddingHorizontal10PX, _s.paddingVertical10PX, _s.radiusSmall].join(' ')}
>
<Icon className={_s.fillcolorSecondary} id={action.icon} width='20px' height='20px' />
<Icon className={_s.fillColorSecondary} id={action.icon} width='20px' height='20px' />
</button>
))
}

View File

@@ -48,7 +48,7 @@ export default class HashtagItem extends ImmutablePureComponent {
icon='caret-down'
iconWidth='8px'
iconHeight='8px'
iconClassName={_s.fillcolorSecondary}
iconClassName={_s.fillColorSecondary}
className={_s.marginLeftAuto}
/>
</div>

View File

@@ -67,6 +67,8 @@ export default class Icon extends PureComponent {
return <I.RepostIcon {...options} />
case 'search':
return <I.SearchIcon {...options} />
case 'search-alt':
return <I.SearchAltIcon {...options} />
case 'share':
return <I.ShareIcon {...options} />
case 'shop':

View File

@@ -1,121 +0,0 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import classNames from 'classnames';
import { changeUploadCompose } from '../../actions/compose';
import { getPointerPosition } from '../../utils/element_position';
import ImageLoader from '../image_loader';
const mapStateToProps = (state, { id }) => ({
media: state.getIn(['compose', 'media_attachments']).find(item => item.get('id') === id),
});
const mapDispatchToProps = (dispatch, { id }) => ({
onSave: (x, y) => {
dispatch(changeUploadCompose(id, { focus: `${x.toFixed(2)},${y.toFixed(2)}` }));
},
});
export default
@connect(mapStateToProps, mapDispatchToProps)
class FocalPointModal extends ImmutablePureComponent {
static propTypes = {
media: ImmutablePropTypes.map.isRequired,
};
state = {
x: 0,
y: 0,
focusX: 0,
focusY: 0,
dragging: false,
};
componentWillMount () {
this.updatePositionFromMedia(this.props.media);
}
componentWillReceiveProps (nextProps) {
if (this.props.media.get('id') !== nextProps.media.get('id')) {
this.updatePositionFromMedia(nextProps.media);
}
}
componentWillUnmount () {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
}
handleMouseDown = e => {
document.addEventListener('mousemove', this.handleMouseMove);
document.addEventListener('mouseup', this.handleMouseUp);
this.updatePosition(e);
this.setState({ dragging: true });
}
handleMouseMove = e => {
this.updatePosition(e);
}
handleMouseUp = () => {
document.removeEventListener('mousemove', this.handleMouseMove);
document.removeEventListener('mouseup', this.handleMouseUp);
this.setState({ dragging: false });
this.props.onSave(this.state.focusX, this.state.focusY);
}
updatePosition = e => {
const { x, y } = getPointerPosition(this.node, e);
const focusX = (x - .5) * 2;
const focusY = (y - .5) * -2;
this.setState({ x, y, focusX, focusY });
}
updatePositionFromMedia = media => {
const focusX = media.getIn(['meta', 'focus', 'x']);
const focusY = media.getIn(['meta', 'focus', 'y']);
if (focusX && focusY) {
const x = (focusX / 2) + .5;
const y = (focusY / -2) + .5;
this.setState({ x, y, focusX, focusY });
} else {
this.setState({ x: 0.5, y: 0.5, focusX: 0, focusY: 0 });
}
}
setRef = c => {
this.node = c;
}
render () {
const { media } = this.props;
const { x, y, dragging } = this.state;
const width = media.getIn(['meta', 'original', 'width']) || null;
const height = media.getIn(['meta', 'original', 'height']) || null;
return (
<div className='modal-root__modal focal-point-modal'>
<div className={classNames('focal-point', { dragging })} ref={this.setRef}>
<ImageLoader
previewSrc={media.get('preview_url')}
src={media.get('url')}
width={width}
height={height}
/>
<div className='focal-point__reticle' style={{ top: `${y * 100}%`, left: `${x * 100}%` }} />
<div className='focal-point__overlay' onMouseDown={this.handleMouseDown} />
</div>
</div>
);
}
}

View File

@@ -1,36 +0,0 @@
.focal-point-modal {
position: relative;
@include max-size(80vw, 80vh);
}
.focal-point {
position: relative;
cursor: pointer;
overflow: hidden;
&.dragging {
cursor: move;
}
img {
margin: auto;
@include max-size(80vw, 80vh);
@include size(auto);
}
&__reticle {
position: absolute;
transform: translate(-50%, -50%);
background: url('/assets/images/reticle.png') no-repeat 0 0;
box-shadow: 0 0 0 9999em rgba($base-shadow-color, 0.35);
@include circle(100px);
}
&__overlay {
@include size(100%);
@include abs-position(0, auto, auto, 0);
}
}

View File

@@ -16,7 +16,6 @@ import MediaModal from './media_modal'
import VideoModal from './video_modal'
import BoostModal from './boost_modal'
import ConfirmationModal from './confirmation_modal'
import FocalPointModal from './focal_point_modal'
import HotkeysModal from './hotkeys_modal'
import ComposeModal from './compose_modal'
import UnauthorizedModal from './unauthorized_modal'
@@ -29,7 +28,6 @@ const MODAL_COMPONENTS = {
'COMPOSE': () => Promise.resolve({ default: ComposeModal }),
'CONFIRM': () => Promise.resolve({ default: ConfirmationModal }),
'EMBED': EmbedModal,
'FOCAL_POINT': () => Promise.resolve({ default: FocalPointModal }),
'HOTKEYS': () => Promise.resolve({ default: HotkeysModal }),
'MEDIA': () => Promise.resolve({ default: MediaModal }),
'MUTE': MuteModal,

View File

@@ -70,7 +70,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
}
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
<Icon id='calendar' width='12px' height='12px' className={_s.fillcolorSecondary} />
<Icon id='calendar' width='12px' height='12px' className={_s.fillColorSecondary} />
<Text
size='small'
color='secondary'

View File

@@ -0,0 +1,35 @@
import { Fragment } from 'react'
import { defineMessages, injectIntl } from 'react-intl'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { shortNumberFormat } from '../../utils/numbers'
import PanelLayout from './panel_layout'
import Button from '../button'
import Divider from '../divider'
import Heading from '../heading'
import Icon from '../icon'
import Text from '../text'
const messages = defineMessages({
title: { id: 'search_filters', defaultMessage: 'Search Filters' },
})
export default
@injectIntl
class SearchFilterPanel extends ImmutablePureComponent {
static propTypes = {
group: ImmutablePropTypes.list.isRequired,
intl: PropTypes.object.isRequired,
}
render() {
const { intl, group } = this.props
return (
<PanelLayout title={intl.formatMessage(messages.title)}>
</PanelLayout>
)
}
}

View File

@@ -57,11 +57,10 @@ class WhoToFollowPanel extends ImmutablePureComponent {
<div className={_s.default}>
{suggestions && suggestions.map(accountId => (
<AccountContainer
showDismiss
key={accountId}
id={accountId}
actionIcon='times'
actionTitle={intl.formatMessage(messages.dismissSuggestion)}
onActionClick={dismissSuggestion}
dismissAction={dismissSuggestion}
/>
))}
</div>

View File

@@ -114,17 +114,17 @@ class Sidebar extends ImmutablePureComponent {
to: '/',
count: 124,
},
{
title: 'Search',
icon: 'search-sidebar',
to: '/search',
},
{
title: 'Notifications',
icon: 'notifications',
to: '/notifications',
count: 40,
},
{
title: 'Search',
icon: 'search-alt',
to: '/search',
},
{
title: 'Groups',
icon: 'group',

View File

@@ -63,7 +63,7 @@ export default class SidebarSectionItem extends PureComponent {
const iconClasses = cx({
fillColorBlack: shouldShowActive,
fillcolorSecondary: !hovering && !active,
fillColorSecondary: !hovering && !active,
})
const countClasses = cx({

View File

@@ -324,7 +324,7 @@ class Status extends ImmutablePureComponent {
id='pin'
width='10px'
height='10px'
className={_s.fillcolorSecondary}
className={_s.fillColorSecondary}
/>
<Text size='small' color='secondary' className={_s.marginLeft5PX}>
{intl.formatMessage(messages.pinned)}

View File

@@ -43,7 +43,7 @@ export default class StatusActionBarItem extends PureComponent {
active={active}
disabled={disabled}
>
<Icon width='16px' height='16px' id={icon} className={[_s.default, _s.marginRight10PX, _s.fillcolorSecondary].join(' ')} />
<Icon width='16px' height='16px' id={icon} className={[_s.default, _s.marginRight10PX, _s.fillColorSecondary].join(' ')} />
{title}
</button>
</div>

View File

@@ -197,7 +197,7 @@ export default class StatusHeader extends ImmutablePureComponent {
icon='ellipsis'
iconWidth='20px'
iconHeight='20px'
iconClassName={_s.fillcolorSecondary}
iconClassName={_s.fillColorSecondary}
className={_s.marginLeftAuto}
onClick={this.handleStatusOptionsClick}
/>
@@ -218,7 +218,7 @@ export default class StatusHeader extends ImmutablePureComponent {
<DotTextSeperator />
<Icon id='globe' width='12px' height='12px' className={[_s.default, _s.displayInline, _s.marginLeft5PX, _s.fillcolorSecondary].join(' ')} />
<Icon id='globe' width='12px' height='12px' className={[_s.default, _s.displayInline, _s.marginLeft5PX, _s.fillColorSecondary].join(' ')} />
{
!!status.get('group') &&