Progress
This commit is contained in:
@@ -121,7 +121,7 @@ class Account extends ImmutablePureComponent {
|
||||
return (
|
||||
<Fragment>
|
||||
{account.get('display_name')}
|
||||
{account.get('username')}
|
||||
{`@${account.get('username')}`}
|
||||
</Fragment>
|
||||
)
|
||||
}
|
||||
@@ -151,8 +151,8 @@ class Account extends ImmutablePureComponent {
|
||||
)
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.mt5, _s.mb15].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow].join(' ')}>
|
||||
<div className={[_s.default, _s.px15, _s.py5, _s.backgroundSubtle_onHover, _s.mb5].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
|
||||
<NavLink
|
||||
className={[_s.default, _s.noUnderline].join(' ')}
|
||||
@@ -165,9 +165,9 @@ class Account extends ImmutablePureComponent {
|
||||
<NavLink
|
||||
title={account.get('acct')}
|
||||
to={`/${account.get('acct')}`}
|
||||
className={[_s.default, _s.alignItemsStart, _s.px10, _s.flexGrow1].join(' ')}
|
||||
className={[_s.default, _s.alignItemsStart, _s.noUnderline, _s.px10, _s.flexGrow1].join(' ')}
|
||||
>
|
||||
<DisplayName account={account} multiline={compact} />
|
||||
<DisplayName account={account} isMultiline={compact} />
|
||||
{!compact && actionButton}
|
||||
</NavLink>
|
||||
|
||||
|
||||
@@ -30,7 +30,7 @@ class AutosuggestAccount extends ImmutablePureComponent {
|
||||
<div className='autosuggest-account__icon'>
|
||||
<Avatar account={account} size={18} />
|
||||
</div>
|
||||
<DisplayName account={account} />
|
||||
<DisplayName account={account} noRelationship noHover />
|
||||
</div>
|
||||
);
|
||||
}
|
||||
|
||||
@@ -1,17 +1,31 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { autoPlayGif } from '../initial_state'
|
||||
import { openPopover, closePopover } from '../actions/popover'
|
||||
import Image from './image'
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openUserInfoPopover(props) {
|
||||
dispatch(openPopover('USER_INFO', props))
|
||||
},
|
||||
closeUserInfoPopover() {
|
||||
dispatch(closePopover('USER_INFO'))
|
||||
}
|
||||
})
|
||||
|
||||
/**
|
||||
* Renders an avatar component
|
||||
* @param {map} [props.account] - the account for image
|
||||
* @param {number} [props.size=40] - the size of the avatar
|
||||
*/
|
||||
export default class Avatar extends ImmutablePureComponent {
|
||||
export default
|
||||
@connect(null, mapDispatchToProps)
|
||||
class Avatar extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
noHover: PropTypes.bool,
|
||||
openUserInfoPopover: PropTypes.func.isRequired,
|
||||
size: PropTypes.number,
|
||||
}
|
||||
|
||||
@@ -24,6 +38,14 @@ export default class Avatar extends ImmutablePureComponent {
|
||||
sameImg: !this.props.account ? false : this.props.account.get('avatar') === this.props.account.get('avatar_static'),
|
||||
}
|
||||
|
||||
updateOnProps = [
|
||||
'account',
|
||||
'noHover',
|
||||
'size',
|
||||
]
|
||||
|
||||
mouseOverTimeout = null
|
||||
|
||||
componentDidUpdate (prevProps) {
|
||||
if (prevProps.account !== this.props.account) {
|
||||
this.setState({
|
||||
@@ -33,12 +55,30 @@ export default class Avatar extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleMouseEnter = () => {
|
||||
// : todo : user popover
|
||||
this.setState({ hovering: true })
|
||||
|
||||
if (this.mouseOverTimeout || this.props.noHover) return null
|
||||
this.mouseOverTimeout = setTimeout(() => {
|
||||
this.props.openUserInfoPopover({
|
||||
targetRef: this.node,
|
||||
position: 'top',
|
||||
account: this.props.account,
|
||||
})
|
||||
}, 650)
|
||||
}
|
||||
|
||||
handleMouseLeave = () => {
|
||||
this.setState({ hovering: false })
|
||||
|
||||
if (this.mouseOverTimeout && !this.props.noHover) {
|
||||
clearTimeout(this.mouseOverTimeout)
|
||||
this.mouseOverTimeout = null
|
||||
this.props.closeUserInfoPopover()
|
||||
}
|
||||
}
|
||||
|
||||
setRef = (n) => {
|
||||
this.node = n
|
||||
}
|
||||
|
||||
render() {
|
||||
@@ -46,10 +86,14 @@ export default class Avatar extends ImmutablePureComponent {
|
||||
const { hovering, sameImg } = this.state
|
||||
|
||||
const shouldAnimate = autoPlayGif || !sameImg
|
||||
const isPro = !!account ? account.get('is_pro') : false
|
||||
const alt = !account ? '' : `${account.get('display_name')} ${isPro ? '(PRO)' : ''}`.trim()
|
||||
const classes = [_s.default, _s.circle, _s.overflowHidden]
|
||||
if (isPro) {
|
||||
classes.push(_s.boxShadowAvatarPro)
|
||||
}
|
||||
|
||||
const options = {
|
||||
alt: !account ? 'Avatar' : account.get('display_name'),
|
||||
className: [_s.default, _s.circle, _s.overflowHidden].join(' '),
|
||||
onMouseEnter: shouldAnimate ? this.handleMouseEnter : undefined,
|
||||
onMouseLeave: shouldAnimate ? this.handleMouseLeave : undefined,
|
||||
src: !account ? undefined : account.get((hovering || autoPlayGif) ? 'avatar' : 'avatar_static'),
|
||||
@@ -61,7 +105,12 @@ export default class Avatar extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
return (
|
||||
<Image {...options} />
|
||||
<Image
|
||||
alt={alt}
|
||||
ref={this.setRef}
|
||||
className={classes.join(' ')}
|
||||
{...options}
|
||||
/>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -11,7 +11,7 @@ export default class Block extends PureComponent {
|
||||
const { children } = this.props
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.backgroundColorPrimary, _s.overflowHidden, _s.radiusSmall, _s.borderColorSecondary, _s.border1PX].join(' ')}>
|
||||
<div className={[_s.default, _s.boxShadowBlock, _s.backgroundColorPrimary, _s.overflowHidden, _s.radiusSmall].join(' ')}>
|
||||
{children}
|
||||
</div>
|
||||
)
|
||||
|
||||
@@ -12,10 +12,10 @@ export default class CharacterCounter extends PureComponent {
|
||||
max: PropTypes.number.isRequired,
|
||||
}
|
||||
|
||||
render () {
|
||||
render() {
|
||||
const { text, max } = this.props
|
||||
|
||||
const actualRadius = '16'
|
||||
const actualRadius = 16
|
||||
const radius = 12
|
||||
const circumference = 2 * Math.PI * radius
|
||||
const diff = Math.min(length(text), max) / max
|
||||
@@ -23,9 +23,22 @@ export default class CharacterCounter extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.mr10, _s.justifyContentCenter, _s.alignItemsCenter].join(' ')}>
|
||||
<svg width={actualRadius * 2} height={actualRadius * 2} viewBox={`0 0 ${actualRadius * 2} ${actualRadius * 2}`}>
|
||||
<circle fill='none' cx={actualRadius} cy={actualRadius} r={radius} fill="none" stroke="#e6e6e6" strokeWidth="2" />
|
||||
<circle style={{
|
||||
<svg
|
||||
width={actualRadius * 2}
|
||||
height={actualRadius * 2}
|
||||
viewBox={`0 0 ${actualRadius * 2} ${actualRadius * 2}`}
|
||||
>
|
||||
<circle
|
||||
fill='none'
|
||||
cx={actualRadius}
|
||||
cy={actualRadius}
|
||||
r={radius}
|
||||
fill='none'
|
||||
stroke='#e6e6e6'
|
||||
strokeWidth='2'
|
||||
/>
|
||||
<circle
|
||||
style={{
|
||||
strokeDashoffset: dashoffset,
|
||||
strokeDasharray: circumference,
|
||||
}}
|
||||
@@ -33,7 +46,7 @@ export default class CharacterCounter extends PureComponent {
|
||||
cx={actualRadius}
|
||||
cy={actualRadius}
|
||||
r={radius}
|
||||
strokeWidth="2"
|
||||
strokeWidth='2'
|
||||
strokeLinecap='round'
|
||||
stroke='#21cf7a'
|
||||
/>
|
||||
|
||||
@@ -60,7 +60,7 @@ export default class ColumnHeader extends PureComponent {
|
||||
|
||||
{
|
||||
!!actions &&
|
||||
<div className={[_s.default, _s.backgroundTransparent, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.marginLeftAuto].join(' ')}>
|
||||
<div className={[_s.default, _s.backgroundTransparent, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.mlAuto].join(' ')}>
|
||||
{
|
||||
actions.map((action, i) => (
|
||||
<Button
|
||||
|
||||
@@ -1,20 +1,17 @@
|
||||
import { Fragment } from 'react'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { makeGetStatus } from '../selectors';
|
||||
import CommentHeader from './comment_header'
|
||||
import Avatar from './avatar'
|
||||
import Button from './button'
|
||||
import DisplayName from './display_name'
|
||||
import DotTextSeperator from './dot_text_seperator'
|
||||
import RelativeTimestamp from './relative_timestamp'
|
||||
import Text from './text'
|
||||
import CommentHeader from './comment_header'
|
||||
import StatusContent from './status_content'
|
||||
import Text from './text'
|
||||
|
||||
const messages = defineMessages({
|
||||
follow: { id: 'follow', defaultMessage: 'Follow' },
|
||||
reply: { id: 'status.reply', defaultMessage: 'Reply' },
|
||||
like: { id: 'status.like', defaultMessage: 'Like' },
|
||||
})
|
||||
|
||||
const makeMapStateToProps = (state, props) => ({
|
||||
@@ -27,14 +24,23 @@ export default
|
||||
class Comment extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
indent: PropTypes.number,
|
||||
intl: PropTypes.object.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
indent: ImmutablePropTypes.number,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { status, indent } = this.props
|
||||
updateOnProps = [
|
||||
'status',
|
||||
'indent',
|
||||
]
|
||||
|
||||
render() {
|
||||
const {
|
||||
indent,
|
||||
intl,
|
||||
status,
|
||||
} = this.props
|
||||
|
||||
console.log("status:", status)
|
||||
const style = {
|
||||
paddingLeft: `${indent * 40}px`,
|
||||
}
|
||||
@@ -55,58 +61,22 @@ class Comment extends ImmutablePureComponent {
|
||||
</NavLink>
|
||||
|
||||
<div className={[_s.default, _s.flexNormal].join(' ')}>
|
||||
<div className={[_s.default, _s.px10, _s.py5, _s.radiusSmall, _s.backgroundSubtle].join(' ')}>
|
||||
<div className={_s.pt2}>
|
||||
<CommentHeader status={status} />
|
||||
</div>
|
||||
<div className={[_s.py5].join(' ')}>
|
||||
<StatusContent
|
||||
status={status}
|
||||
onClick={this.handleClick}
|
||||
isComment
|
||||
collapsable
|
||||
/>
|
||||
</div>
|
||||
<div className={[_s.default, _s.px10, _s.pt5, _s.pb10, _s.radiusSmall, _s.backgroundSubtle].join(' ')}>
|
||||
<CommentHeader status={status} />
|
||||
<StatusContent
|
||||
status={status}
|
||||
onClick={this.handleClick}
|
||||
isComment
|
||||
collapsable
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.mt5].join(' ')}>
|
||||
|
||||
<Button
|
||||
isText
|
||||
radiusSmall
|
||||
backgroundColor='none'
|
||||
color='tertiary'
|
||||
className={[_s.px5, _s.backgroundSubtle_onHover, _s.py2, _s.mr5].join(' ')}
|
||||
>
|
||||
<Text size='extraSmall' color='inherit' weight='bold'>
|
||||
Like
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
isText
|
||||
radiusSmall
|
||||
backgroundColor='none'
|
||||
color='tertiary'
|
||||
className={[_s.px5, _s.backgroundSubtle_onHover, _s.py2, _s.mr5].join(' ')}
|
||||
>
|
||||
<Text size='extraSmall' color='inherit' weight='bold'>
|
||||
Reply
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<Button
|
||||
isText
|
||||
radiusSmall
|
||||
backgroundColor='none'
|
||||
color='tertiary'
|
||||
className={[_s.px5, _s.backgroundSubtle_onHover, _s.py2, _s.mr5].join(' ')}
|
||||
>
|
||||
<Text size='extraSmall' color='inherit' weight='bold'>
|
||||
···
|
||||
</Text>
|
||||
</Button>
|
||||
|
||||
<CommentButton title={intl.formatMessage(messages.like)} />
|
||||
<CommentButton title={intl.formatMessage(messages.reply)} />
|
||||
<CommentButton title='···' />
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -116,3 +86,31 @@ class Comment extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
}
|
||||
|
||||
class CommentButton extends PureComponent {
|
||||
|
||||
static propTypes = {
|
||||
onClick: PropTypes.func.isRequired,
|
||||
title: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { onClick, title } = this.props
|
||||
|
||||
return (
|
||||
<Button
|
||||
isText
|
||||
radiusSmall
|
||||
backgroundColor='none'
|
||||
color='tertiary'
|
||||
className={[_s.px5, _s.backgroundSubtle_onHover, _s.py2, _s.mr5].join(' ')}
|
||||
onClick={onClick}
|
||||
>
|
||||
<Text size='extraSmall' color='inherit' weight='bold'>
|
||||
{title}
|
||||
</Text>
|
||||
</Button>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -26,7 +26,7 @@ class CommentHeader extends ImmutablePureComponent {
|
||||
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`;
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.alignItemsStart, _s.flexGrow1].join(' ')}>
|
||||
<div className={[_s.default, _s.alignItemsStart, _s.py2, _s.flexGrow1].join(' ')}>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.width100PC, _s.alignItemsCenter].join(' ')}>
|
||||
<NavLink
|
||||
@@ -34,7 +34,7 @@ class CommentHeader extends ImmutablePureComponent {
|
||||
to={`/${status.getIn(['account', 'acct'])}`}
|
||||
title={status.getIn(['account', 'acct'])}
|
||||
>
|
||||
<DisplayName account={status.get('account')} small />
|
||||
<DisplayName account={status.get('account')} isSmall />
|
||||
</NavLink>
|
||||
|
||||
{
|
||||
|
||||
@@ -44,7 +44,7 @@ export default class CommentList extends ImmutablePureComponent {
|
||||
View more comments
|
||||
</Text>
|
||||
</Button>
|
||||
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
|
||||
<div className={[_s.default, _s.mlAuto].join(' ')}>
|
||||
<Text color='tertiary'>
|
||||
{max}
|
||||
of
|
||||
|
||||
@@ -1,17 +1,20 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import classNames from 'classnames/bind'
|
||||
import { me } from '../initial_state'
|
||||
import {
|
||||
CX,
|
||||
POPOVER_USER_INFO,
|
||||
} from '../constants'
|
||||
import { openPopover, closePopover } from '../actions/popover'
|
||||
import Icon from './icon'
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
import Text from './text'
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
openUserInfoPopover(props) {
|
||||
dispatch(openPopover('USER_INFO', props))
|
||||
dispatch(openPopover(POPOVER_USER_INFO, props))
|
||||
},
|
||||
closeUserInfoPopover() {
|
||||
dispatch(closePopover('USER_INFO'))
|
||||
dispatch(closePopover(POPOVER_USER_INFO))
|
||||
}
|
||||
})
|
||||
|
||||
@@ -23,13 +26,24 @@ class DisplayName extends ImmutablePureComponent {
|
||||
account: ImmutablePropTypes.map,
|
||||
openUserInfoPopover: PropTypes.func.isRequired,
|
||||
closeUserInfoPopover: PropTypes.func.isRequired,
|
||||
multiline: PropTypes.bool,
|
||||
small: PropTypes.bool,
|
||||
large: PropTypes.bool,
|
||||
isLarge: PropTypes.bool,
|
||||
isMultiline: PropTypes.bool,
|
||||
isSmall: PropTypes.bool,
|
||||
noHover: PropTypes.bool,
|
||||
noRelationship: PropTypes.bool,
|
||||
noUsername: PropTypes.bool,
|
||||
}
|
||||
|
||||
updateOnProps = [
|
||||
'account',
|
||||
'isMultiline',
|
||||
'isSmall',
|
||||
'isLarge',
|
||||
'noHover',
|
||||
'noRelationship',
|
||||
'noUsername',
|
||||
]
|
||||
|
||||
mouseOverTimeout = null
|
||||
|
||||
handleMouseEnter = () => {
|
||||
@@ -58,41 +72,38 @@ class DisplayName extends ImmutablePureComponent {
|
||||
render() {
|
||||
const {
|
||||
account,
|
||||
multiline,
|
||||
large,
|
||||
isMultiline,
|
||||
isLarge,
|
||||
noHover,
|
||||
noUsername,
|
||||
small
|
||||
noRelationship,
|
||||
isSmall
|
||||
} = this.props
|
||||
|
||||
if (!account) return null
|
||||
|
||||
const containerOptions = {
|
||||
className: cx({
|
||||
default: 1,
|
||||
maxWidth100PC: 1,
|
||||
alignItemsCenter: !multiline,
|
||||
flexRow: !multiline,
|
||||
cursorPointer: !noHover,
|
||||
}),
|
||||
onMouseEnter: noHover ? undefined : this.handleMouseEnter,
|
||||
onMouseLeave: noHover ? undefined : this.handleMouseLeave,
|
||||
}
|
||||
const containerClassName = CX({
|
||||
default: 1,
|
||||
maxWidth100PC: 1,
|
||||
alignItemsCenter: !isMultiline,
|
||||
flexRow: !isMultiline,
|
||||
cursorPointer: !noHover,
|
||||
})
|
||||
|
||||
const displayNameClasses = cx({
|
||||
const displayNameClasses = CX({
|
||||
text: 1,
|
||||
overflowWrapBreakWord: 1,
|
||||
whiteSpaceNoWrap: 1,
|
||||
fontWeightBold: 1,
|
||||
colorPrimary: 1,
|
||||
mr2: 1,
|
||||
lineHeight125: !small,
|
||||
fontSize14PX: small,
|
||||
fontSize15PX: !large,
|
||||
fontSize24PX: large && !small,
|
||||
lineHeight125: !isSmall,
|
||||
fontSize14PX: isSmall,
|
||||
fontSize15PX: !isLarge,
|
||||
fontSize24PX: isLarge && !isSmall,
|
||||
})
|
||||
|
||||
const usernameClasses = cx({
|
||||
const usernameClasses = CX({
|
||||
text: 1,
|
||||
displayFlex: 1,
|
||||
flexNormal: 1,
|
||||
@@ -101,57 +112,83 @@ class DisplayName extends ImmutablePureComponent {
|
||||
textOverflowEllipsis: 1,
|
||||
colorSecondary: 1,
|
||||
fontWeightNormal: 1,
|
||||
lineHeight15: multiline,
|
||||
lineHeight125: !multiline,
|
||||
ml5: !multiline,
|
||||
fontSize14PX: small,
|
||||
fontSize15PX: !large,
|
||||
fontSize16PX: large && !small,
|
||||
lineHeight15: isMultiline,
|
||||
lineHeight125: !isMultiline,
|
||||
ml5: !isMultiline,
|
||||
fontSize14PX: isSmall,
|
||||
fontSize15PX: !isLarge,
|
||||
fontSize16PX: isLarge && !isSmall,
|
||||
})
|
||||
|
||||
const iconSize =
|
||||
!!large ? '19px' :
|
||||
!!small ? '14px' : '16px'
|
||||
!!isLarge ? '19px' :
|
||||
!!isSmall ? '14px' : '16px'
|
||||
|
||||
const domain = account.get('acct').split('@')[1]
|
||||
const isRemoteUser = !!domain
|
||||
|
||||
let relationshipLabel
|
||||
if (me && account) {
|
||||
const accountId = account.get('id')
|
||||
const isFollowedBy = (me !== accountId && account.getIn(['relationship', 'followed_by']))
|
||||
|
||||
if (isFollowedBy) {
|
||||
relationshipLabel = 'Follows you'//intl.formatMessage(messages.accountFollowsYou)
|
||||
}
|
||||
}
|
||||
|
||||
// {
|
||||
// /* : todo : audio-mute
|
||||
// account.getIn(['relationship', 'muting'])
|
||||
// */
|
||||
// }
|
||||
|
||||
// : todo : remote
|
||||
console.log("domain, isRemoteUser:", domain, isRemoteUser)
|
||||
|
||||
return (
|
||||
<span {...containerOptions} ref={this.setRef}>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
<div
|
||||
className={containerClassName}
|
||||
onMouseEnter={noHover ? undefined : this.handleMouseEnter}
|
||||
onMouseLeave={noHover ? undefined : this.handleMouseLeave}
|
||||
ref={this.setRef}
|
||||
>
|
||||
<span className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
<bdi className={[_s.text, _s.whiteSpaceNoWrap, _s.textOverflowEllipsis].join(' ')}>
|
||||
<strong
|
||||
className={displayNameClasses}
|
||||
dangerouslySetInnerHTML={{ __html: account.get('display_name_html') }}
|
||||
/>
|
||||
{
|
||||
!noRelationship && account.get('locked') &&
|
||||
<Icon id='lock-filled' size={iconSize} className={_s.ml5} />
|
||||
}
|
||||
</bdi>
|
||||
{
|
||||
account.get('is_verified') &&
|
||||
<Icon id='verified' size={iconSize} className={_s.default} />
|
||||
}
|
||||
{
|
||||
account.get('is_pro') &&
|
||||
<Icon id='pro' size={iconSize} className={_s.default} />
|
||||
}
|
||||
{
|
||||
account.get('is_donor') &&
|
||||
<Icon id='donor' size={iconSize} className={_s.default} />
|
||||
}
|
||||
{
|
||||
account.get('is_investor') &&
|
||||
<Icon id='investor' size={iconSize} className={_s.default} />
|
||||
}
|
||||
</div>
|
||||
</span>
|
||||
{
|
||||
!noUsername &&
|
||||
<span className={usernameClasses}>
|
||||
@{account.get('acct')}
|
||||
{
|
||||
!noRelationship && !!relationshipLabel &&
|
||||
<span className={[_s.default, _s.ml5, _s.justifyContentCenter].join(' ')}>
|
||||
<Text
|
||||
size='extraSmall'
|
||||
isBadge
|
||||
color='tertiary'
|
||||
className={[_s.backgroundSubtle2, _s.py2].join(' ')}
|
||||
>
|
||||
{relationshipLabel}
|
||||
</Text>
|
||||
</span>
|
||||
}
|
||||
</span>
|
||||
}
|
||||
</span>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
|
||||
@@ -81,7 +81,7 @@ class GroupHeader extends ImmutablePureComponent {
|
||||
<div className={[_s.default, _s.height53PX, _s.width100PC].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.height100PC, _s.px10].join(' ')}>
|
||||
<TabBar tabs={tabs} />
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.height100PC, _s.marginLeftAuto].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.height100PC, _s.mlAuto].join(' ')}>
|
||||
<Button
|
||||
color='primary'
|
||||
backgroundColor='tertiary'
|
||||
|
||||
@@ -2,43 +2,33 @@ import { FormattedMessage } from 'react-intl'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { shortNumberFormat } from '../utils/numbers'
|
||||
import Text from './text'
|
||||
import Button from './button'
|
||||
import Text from './text'
|
||||
|
||||
export default class HashtagItem extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
hashtag: ImmutablePropTypes.map.isRequired,
|
||||
};
|
||||
|
||||
state = {
|
||||
hovering: false,
|
||||
}
|
||||
|
||||
handleOnMouseEnter = () => {
|
||||
this.setState({ hovering: true })
|
||||
}
|
||||
|
||||
handleOnMouseLeave = () => {
|
||||
this.setState({ hovering: false })
|
||||
}
|
||||
updateOnProps = ['hashtag']
|
||||
|
||||
render() {
|
||||
const { hashtag } = this.props
|
||||
const { hovering } = this.state
|
||||
|
||||
const count = hashtag.get('history').map((block) => {
|
||||
return parseInt(block.get('uses'))
|
||||
}).reduce((a, c) => a + c)
|
||||
|
||||
return (
|
||||
<NavLink
|
||||
to='/tags/test'
|
||||
to={`/tags/${hashtag.get('name')}`}
|
||||
className={[_s.default, _s.noUnderline, _s.backgroundSubtle_onHover, _s.px15, _s.py5].join(' ')}
|
||||
onMouseEnter={this.handleOnMouseEnter}
|
||||
onMouseLeave={this.handleOnMouseLeave}
|
||||
>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
<div>
|
||||
<Text color='brand' size='medium' weight='bold' className={[_s.py2, _s.lineHeight15].join(' ')}>
|
||||
#randomhashtag
|
||||
{hashtag.get('name')}
|
||||
</Text>
|
||||
</div>
|
||||
<Button
|
||||
@@ -46,14 +36,16 @@ export default class HashtagItem extends ImmutablePureComponent {
|
||||
backgroundColor='none'
|
||||
color='none'
|
||||
title='Remove'
|
||||
icon='caret-down'
|
||||
icon='close'
|
||||
iconSize='8px'
|
||||
iconClassName={_s.fillColorSecondary}
|
||||
className={_s.marginLeftAuto}
|
||||
className={_s.mlAuto}
|
||||
/>
|
||||
</div>
|
||||
<Text color='secondary' size='small' className={_s.py2}>
|
||||
10,240 Gabs
|
||||
<FormattedMessage id='number_of_gabs' defaultMessage='{count} Gabs' values={{
|
||||
count,
|
||||
}} />
|
||||
</Text>
|
||||
</NavLink>
|
||||
)
|
||||
|
||||
@@ -39,8 +39,8 @@ export default class Heading extends PureComponent {
|
||||
text: 1,
|
||||
textAlignCenter: center,
|
||||
|
||||
colorPrimary: [SIZES.h1, SIZES.h3].indexOf(size) > -1,
|
||||
colorSecondary: [SIZES.h2, SIZES.h4, SIZES.h5].indexOf(size) > -1,
|
||||
colorPrimary: [SIZES.h1, SIZES.h2].indexOf(size) > -1,
|
||||
colorSecondary: [SIZES.h3, SIZES.h4, SIZES.h5].indexOf(size) > -1,
|
||||
|
||||
fontSize24PX: size === SIZES.h1,
|
||||
fontSize19PX: size === SIZES.h2,
|
||||
@@ -48,13 +48,13 @@ export default class Heading extends PureComponent {
|
||||
fontSize13PX: size === SIZES.h4,
|
||||
fontSize12PX: size === SIZES.h5,
|
||||
|
||||
mt5: [SIZES.h2, SIZES.h4].indexOf(size) > -1,
|
||||
mt5: [SIZES.h4].indexOf(size) > -1,
|
||||
|
||||
lineHeight2: size === SIZES.h5,
|
||||
py2: size === SIZES.h5,
|
||||
|
||||
fontWeightMedium: [SIZES.h1, SIZES.h5].indexOf(size) > -1,
|
||||
fontWeightBold: [SIZES.h3, SIZES.h4].indexOf(size) > -1,
|
||||
fontWeightMedium: [SIZES.h1, SIZES.h3, SIZES.h5].indexOf(size) > -1,
|
||||
fontWeightBold: [SIZES.h2, SIZES.h4].indexOf(size) > -1,
|
||||
})
|
||||
|
||||
return React.createElement(
|
||||
|
||||
@@ -19,6 +19,7 @@ export default class Image extends PureComponent {
|
||||
fit: PropTypes.oneOf(['contain', 'cover', 'tile', 'none']),
|
||||
nullable: PropTypes.bool,
|
||||
lazy: PropTypes.bool,
|
||||
ref: PropTypes.func,
|
||||
}
|
||||
|
||||
static defaultProps = {
|
||||
@@ -36,6 +37,7 @@ export default class Image extends PureComponent {
|
||||
|
||||
render() {
|
||||
const {
|
||||
alt,
|
||||
src,
|
||||
fit,
|
||||
className,
|
||||
@@ -64,6 +66,7 @@ export default class Image extends PureComponent {
|
||||
|
||||
return (
|
||||
<img
|
||||
alt={alt}
|
||||
className={classes}
|
||||
{...otherProps}
|
||||
src={src}
|
||||
|
||||
@@ -114,7 +114,7 @@ class LinkFooter extends PureComponent {
|
||||
onClick={linkFooterItem.onClick || null}
|
||||
className={[_s.my5, _s.pr15].join(' ')}
|
||||
>
|
||||
<Text size='small' color='secondary'>
|
||||
<Text size='small' color='tertiary'>
|
||||
{linkFooterItem.text}
|
||||
</Text>
|
||||
</Button>
|
||||
@@ -123,11 +123,11 @@ class LinkFooter extends PureComponent {
|
||||
}
|
||||
</nav>
|
||||
|
||||
<Text size='small' color='secondary' className={_s.mt10}>
|
||||
<Text size='small' color='tertiary' className={_s.mt10}>
|
||||
© {currentYear} Gab AI, Inc.
|
||||
</Text>
|
||||
|
||||
<Text size='small' color='secondary' tagName='p' className={_s.mt10}>
|
||||
<Text size='small' color='tertiary' tagName='p' className={_s.mt10}>
|
||||
<FormattedMessage
|
||||
id='getting_started.open_source_notice'
|
||||
defaultMessage='Gab Social is open source software. You can contribute or report issues on our self-hosted GitLab at {gitlab}.'
|
||||
|
||||
@@ -86,7 +86,7 @@ export default class ListItem extends PureComponent {
|
||||
<Icon
|
||||
id='angle-right'
|
||||
size='10px'
|
||||
className={[_s.marginLeftAuto, _s.fillColorBlack].join(' ')}
|
||||
className={[_s.mlAuto, _s.fillColorSecondary].join(' ')}
|
||||
/>
|
||||
}
|
||||
</Button>
|
||||
|
||||
@@ -46,7 +46,7 @@ class LoadMore extends PureComponent {
|
||||
>
|
||||
{
|
||||
!gap &&
|
||||
<Text color='inherit'>
|
||||
<Text color='inherit' align='center'>
|
||||
{intl.formatMessage(messages.load_more)}
|
||||
</Text>
|
||||
}
|
||||
|
||||
@@ -1,27 +1,86 @@
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import Button from '../button'
|
||||
import Text from '../text'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { me } from '../../initial_state'
|
||||
import ModalLayout from './modal_layout'
|
||||
import Avatar from '../avatar'
|
||||
import Button from '../button'
|
||||
import Divider from '../divider'
|
||||
import Image from '../image'
|
||||
import Input from '../input'
|
||||
import Text from '../text'
|
||||
import Textarea from '../textarea'
|
||||
|
||||
const messages = defineMessages({
|
||||
edit_profile: { id: 'account.edit_profile', defaultMessage: 'Edit profile' },
|
||||
headerPhoto: { id: 'header_photo', defaultMessage: 'Header photo' },
|
||||
})
|
||||
|
||||
const mapStateToProps = (state) => ({
|
||||
account: state.getIn(['accounts', me]),
|
||||
})
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
@connect(mapStateToProps)
|
||||
class EditProfileModal extends ImmutablePureComponent {
|
||||
|
||||
static propTypes = {
|
||||
account: ImmutablePropTypes.map,
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props
|
||||
const { account, intl } = this.props
|
||||
|
||||
const headerSrc = !!account ? account.get('header') : ''
|
||||
|
||||
return (
|
||||
<ModalLayout>
|
||||
test
|
||||
<ModalLayout
|
||||
title={intl.formatMessage(messages.edit_profile)}
|
||||
noPadding
|
||||
width={460}
|
||||
>
|
||||
<div className={[_s.default, _s.py5, _s.px5, _s.width100PC, _s.overflowHidden].join(' ')}>
|
||||
<Image
|
||||
alt={intl.formatMessage(messages.headerPhoto)}
|
||||
className={_s.radiusSmall}
|
||||
height='180px'
|
||||
src={headerSrc}
|
||||
/>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.pl25].join(' ')}>
|
||||
<div className={[_s.default, _s.circle, _s.mtNeg75PX, _s.boxShadowProfileAvatar].join(' ')}>
|
||||
<Avatar
|
||||
size={98}
|
||||
account={account}
|
||||
noHover
|
||||
/>
|
||||
</div>
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.px15, _s.py15].join(' ')}>
|
||||
<Input
|
||||
title='Name'
|
||||
value=''
|
||||
disabled={false}
|
||||
// onChange={this.handleTitleChange}
|
||||
placeholder='Add your name...'
|
||||
/>
|
||||
|
||||
<Divider isInvisible />
|
||||
|
||||
<Textarea
|
||||
title='Bio'
|
||||
value=''
|
||||
disabled={false}
|
||||
// onChange={this.handleDescriptionChange}
|
||||
placeholder='Add your bio...'
|
||||
/>
|
||||
|
||||
</div>
|
||||
|
||||
</ModalLayout>
|
||||
)
|
||||
}
|
||||
|
||||
@@ -127,7 +127,7 @@ class GifPickerModal extends PureComponent {
|
||||
<Button
|
||||
backgroundColor='none'
|
||||
title={intl.formatMessage(messages.close)}
|
||||
className={_s.marginLeftAuto}
|
||||
className={_s.mlAuto}
|
||||
onClick={this.onHandleCloseModal}
|
||||
color='secondary'
|
||||
icon='close'
|
||||
|
||||
@@ -53,7 +53,7 @@ class ModalLayout extends PureComponent {
|
||||
<div style={{width: `${width}px`}}>
|
||||
<Block>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.borderBottom1PX, _s.borderColorSecondary, _s.height53PX, _s.px15].join(' ')}>
|
||||
<Heading size='h3'>
|
||||
<Heading size='h2'>
|
||||
{title}
|
||||
</Heading>
|
||||
{
|
||||
@@ -61,7 +61,7 @@ class ModalLayout extends PureComponent {
|
||||
<Button
|
||||
backgroundColor='none'
|
||||
title={intl.formatMessage(messages.close)}
|
||||
className={_s.marginLeftAuto}
|
||||
className={_s.mlAuto}
|
||||
onClick={this.onHandleCloseModal}
|
||||
color='secondary'
|
||||
icon='close'
|
||||
|
||||
@@ -34,7 +34,7 @@ class UnauthorizedModal extends ImmutablePureComponent {
|
||||
<Text className={_s.mb15}>
|
||||
{intl.formatMessage(messages.text)}
|
||||
</Text>
|
||||
<Button href='/auth/sign_up' className={[_s.width240PX, _s.marginLeftAuto, _s.marginLeftAuto].join(' ')}>
|
||||
<Button href='/auth/sign_up' className={[_s.width240PX, _s.mlAuto, _s.mlAuto].join(' ')}>
|
||||
{intl.formatMessage(messages.register)}
|
||||
</Button>
|
||||
</div>
|
||||
|
||||
139
app/javascript/gabsocial/components/notification.js
Normal file
139
app/javascript/gabsocial/components/notification.js
Normal file
@@ -0,0 +1,139 @@
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import { injectIntl, defineMessages } from 'react-intl'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { HotKeys } from 'react-hotkeys'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import StatusContainer from '../containers/status_container'
|
||||
import Avatar from './avatar'
|
||||
import Icon from './icon'
|
||||
import Text from './text'
|
||||
import DisplayName from './display_name'
|
||||
|
||||
const messages = defineMessages({
|
||||
poll: { id: 'notification.poll', defaultMessage: 'A poll you have voted in has ended' },
|
||||
mentionedInPost: { id: 'mentioned_in_post', defaultMessage: 'mentioned you in their post' },
|
||||
mentionedInComment: { id: 'mentioned_in_comment', defaultMessage: 'mentioned you in their comment' },
|
||||
followedYouOne: { id: 'followed_you_one', defaultMessage: 'followed you' },
|
||||
followedYouMultiple: { id: 'followed_you_multiple', defaultMessage: 'and {count} others followed you' },
|
||||
likedStatusOne: { id: 'liked_status_one', defaultMessage: 'liked your status' },
|
||||
likedStatusMultiple: { id: 'liked_status_multiple', defaultMessage: 'and {count} others liked your status' },
|
||||
repostedStatusOne: { id: 'reposted_status_one', defaultMessage: 'reposted your status' },
|
||||
repostedStatusMultiple: { id: 'reposted_status_multiple', defaultMessage: 'and {count} others reposted your status' },
|
||||
})
|
||||
|
||||
const notificationForScreenReader = (intl, message, timestamp) => {
|
||||
const output = [message]
|
||||
|
||||
output.push(intl.formatDate(timestamp, { hour: '2-digit', minute: '2-digit', month: 'short', day: 'numeric' }))
|
||||
|
||||
return output.join(', ')
|
||||
}
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
class Notification extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
accounts: ImmutablePropTypes.list.isRequired,
|
||||
createdAt: PropTypes.string,
|
||||
statusId: PropTypes.string,
|
||||
type: PropTypes.string.isRequired,
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl, accounts, createdAt, type, statusId } = this.props
|
||||
|
||||
const count = !!accounts ? accounts.size : 0
|
||||
|
||||
let message
|
||||
let icon
|
||||
switch (type) {
|
||||
case 'follow':
|
||||
icon = 'group'
|
||||
message = intl.formatMessage(count > 1 ? messages.followedYouMultiple : messages.followedYouOne, {
|
||||
count: count - 1,
|
||||
})
|
||||
break
|
||||
case 'mention':
|
||||
icon = 'comment'
|
||||
message = intl.formatMessage(messages.mentionedInPost)
|
||||
break
|
||||
case 'like':
|
||||
icon = 'like'
|
||||
message = intl.formatMessage(count > 1 ? messages.likedStatusMultiple : messages.likedStatusOne, {
|
||||
count: count - 1,
|
||||
})
|
||||
break
|
||||
case 'repost':
|
||||
icon = 'repost'
|
||||
message = intl.formatMessage(count > 1 ? messages.repostedStatusMultiple : messages.repostedStatusOne, {
|
||||
count: count - 1,
|
||||
})
|
||||
break
|
||||
case 'poll':
|
||||
icon = 'poll'
|
||||
message = intl.formatMessage(messages.poll)
|
||||
break
|
||||
default:
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.px10, _s.cursorPointer, _s.backgroundSubtle_onHover].join(' ')}>
|
||||
<div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.my10, _s.py10, _s.px10].join(' ')}>
|
||||
|
||||
<Icon id={icon} size='20px' className={_s.mt5} />
|
||||
|
||||
<div className={[_s.default, _s.ml15, _s.flexNormal].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow].join(' ')}>
|
||||
{
|
||||
accounts.slice(0, 8).map((account, i) => (
|
||||
<NavLink
|
||||
to={`/${account.get('acct')}`}
|
||||
key={`fav-avatar-${i}`}
|
||||
className={_s.mr5}
|
||||
>
|
||||
<Avatar size={30} account={account} />
|
||||
</NavLink>
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<div className={[_s.default, _s.pt10].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow].join(' ')}>
|
||||
<div className={_s.text}>
|
||||
{
|
||||
accounts.slice(0, 1).map((account, i) => (
|
||||
<DisplayName key={i} account={account} noUsername />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
<Text size='medium'>
|
||||
{' '}
|
||||
{message}
|
||||
</Text>
|
||||
</div>
|
||||
</div>
|
||||
{
|
||||
!!statusId &&
|
||||
<div className={[_s.default, _s.pt10, _s.mt5].join(' ')}>
|
||||
<StatusContainer
|
||||
id={statusId}
|
||||
isChild
|
||||
/>
|
||||
</div>
|
||||
}
|
||||
</div>
|
||||
|
||||
</div>
|
||||
</div>
|
||||
</div>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -33,7 +33,7 @@ class GroupInfoPanel extends ImmutablePureComponent {
|
||||
!!group &&
|
||||
<Fragment>
|
||||
|
||||
<Heading size='h3'>
|
||||
<Heading size='h2'>
|
||||
{group.get('title')}
|
||||
</Heading>
|
||||
|
||||
@@ -51,7 +51,7 @@ class GroupInfoPanel extends ImmutablePureComponent {
|
||||
to={`/groups/${group.get('id')}/members`}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={_s.marginLeftAuto}
|
||||
className={_s.mlAuto}
|
||||
>
|
||||
<Text color='inherit' weight='medium' size='normal' className={_s.underline_onHover}>
|
||||
{shortNumberFormat(group.get('member_count'))}
|
||||
|
||||
@@ -4,7 +4,6 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import PanelLayout from './panel_layout'
|
||||
import HashtagItem from '../hashtag_item'
|
||||
import Button from '../button'
|
||||
|
||||
const messages = defineMessages({
|
||||
title: { id: 'hashtags.title', defaultMessage: 'Popular Hashtags' },
|
||||
@@ -30,6 +29,10 @@ class HashtagsPanel extends ImmutablePureComponent {
|
||||
intl: PropTypes.object.isRequired,
|
||||
}
|
||||
|
||||
updateOnProps = [
|
||||
'hashtags',
|
||||
]
|
||||
|
||||
componentDidMount() {
|
||||
this.props.fetchHashtags()
|
||||
}
|
||||
@@ -37,27 +40,23 @@ class HashtagsPanel extends ImmutablePureComponent {
|
||||
render() {
|
||||
const { intl, hashtags } = this.props
|
||||
|
||||
// !!! TESTING !!!
|
||||
// if (hashtags.isEmpty()) {
|
||||
// return null
|
||||
// }
|
||||
if (hashtags.isEmpty()) {
|
||||
return null
|
||||
}
|
||||
|
||||
return (
|
||||
<PanelLayout
|
||||
noPadding
|
||||
title={intl.formatMessage(messages.title)}
|
||||
footerButtonTitle={intl.formatMessage(messages.show_all)}
|
||||
footerButtonTo='/explore'
|
||||
footerButtonTo='/search'
|
||||
>
|
||||
<div className={_s.default}>
|
||||
{ /* hashtags && hashtags.map(hashtag => (
|
||||
<HashtagingItem key={hashtag.get('name')} hashtag={hashtag} />
|
||||
)) */ }
|
||||
<HashtagItem />
|
||||
<HashtagItem />
|
||||
<HashtagItem />
|
||||
<HashtagItem />
|
||||
<HashtagItem />
|
||||
{
|
||||
hashtags.map(hashtag => (
|
||||
<HashtagItem key={hashtag.get('name')} hashtag={hashtag} />
|
||||
))
|
||||
}
|
||||
</div>
|
||||
</PanelLayout>
|
||||
)
|
||||
|
||||
@@ -58,9 +58,8 @@ class ListsPanel extends ImmutablePureComponent {
|
||||
footerButtonTo={count > maxCount ? '/lists' : undefined}
|
||||
noPadding
|
||||
>
|
||||
<div className={_s.default}>
|
||||
<div className={[_s.default, _s.boxShadowNone].join(' ')}>
|
||||
<List
|
||||
unrounded
|
||||
scrollKey='lists_sidebar_panel'
|
||||
items={listItems}
|
||||
/>
|
||||
|
||||
@@ -36,14 +36,14 @@ export default class PanelLayout extends PureComponent {
|
||||
<Block>
|
||||
{
|
||||
(title || subtitle) &&
|
||||
<div className={[_s.default, _s.px15, _s.py10, _s.borderColorSecondary, _s.borderBottom1PX].join(' ')}>
|
||||
<div className={[_s.default, _s.px15, _s.py10].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
<Heading size='h3'>
|
||||
<Heading size='h2'>
|
||||
{title}
|
||||
</Heading>
|
||||
{
|
||||
(!!headerButtonTitle && (!!headerButtonAction || !!headerButtonTo)) &&
|
||||
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
|
||||
<div className={[_s.default, _s.mlAuto].join(' ')}>
|
||||
<Button
|
||||
isText
|
||||
backgroundColor='none'
|
||||
@@ -80,7 +80,7 @@ export default class PanelLayout extends PureComponent {
|
||||
|
||||
{
|
||||
(!!footerButtonTitle && (!!footerButtonAction || !!footerButtonTo)) &&
|
||||
<div className={[_s.default, _s.borderColorSecondary, _s.borderTop1PX].join(' ')}>
|
||||
<div className={_s.default}>
|
||||
<Button
|
||||
isText
|
||||
color='none'
|
||||
|
||||
@@ -54,6 +54,10 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
||||
const content = { __html: account.get('note_emojified') }
|
||||
const memberSinceDate = intl.formatDate(account.get('created_at'), { month: 'long', year: 'numeric' })
|
||||
const hasNote = !!content ? (account.get('note').length > 0 && account.get('note') !== '<p></p>') : false
|
||||
const isPro = account.get('is_pro')
|
||||
const isDonor = account.get('is_donor')
|
||||
const isInvestor = account.get('is_investor')
|
||||
const hasBadges = isPro || isDonor || isInvestor
|
||||
|
||||
return (
|
||||
<PanelLayout title={intl.formatMessage(messages.title)}>
|
||||
@@ -80,6 +84,18 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
||||
}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
{
|
||||
hasBadges &&
|
||||
<Fragment>
|
||||
<Divider isSmall />
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
{ isPro && <Icon id='pro' size='16px' className={_s.mr5} /> }
|
||||
{ isInvestor && <Icon id='investor' size='16px' className={_s.mr5} /> }
|
||||
{ isDonor && <Icon id='donor' size='16px' /> }
|
||||
</div>
|
||||
</Fragment>
|
||||
}
|
||||
|
||||
{(fields.size > 0 || identityProofs.size > 0) && (
|
||||
<div className={[_s.default]}>
|
||||
@@ -121,7 +137,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
|
||||
title={pair.get('name')}
|
||||
/>
|
||||
<dd
|
||||
className={[_s.dangerousContent, _s.marginLeftAuto].join(' ')}
|
||||
className={[_s.dangerousContent, _s.mlAuto].join(' ')}
|
||||
title={pair.get('value_plain')}
|
||||
dangerouslySetInnerHTML={{ __html: pair.get('value_emojified') }}
|
||||
/>
|
||||
|
||||
@@ -106,11 +106,11 @@ class UserPanel extends ImmutablePureComponent {
|
||||
className={[_s.default, _s.flexRow, _s.py10, _s.px15, _s.noUnderline].join(' ')}
|
||||
to={`/${acct}`}
|
||||
>
|
||||
<div className={[_s.default, _s.mtNeg30PX, _s.circle, _s.borderColorWhite, _s.border2PX].join(' ')}>
|
||||
<Avatar account={account} size={62} />
|
||||
<div className={[_s.default, _s.mtNeg32PX, _s.circle, _s.borderColorWhite, _s.border6PX].join(' ')}>
|
||||
<Avatar account={account} size={62} noHover />
|
||||
</div>
|
||||
<div className={[_s.default, _s.ml15].join(' ')}>
|
||||
<DisplayName account={account} multiline />
|
||||
<DisplayName account={account} isMultiline noRelationship noHover />
|
||||
</div>
|
||||
</NavLink>
|
||||
|
||||
|
||||
@@ -166,7 +166,7 @@ class Poll extends ImmutablePureComponent {
|
||||
|
||||
{
|
||||
showResults &&
|
||||
<span className={_s.marginLeftAuto}>
|
||||
<span className={_s.mlAuto}>
|
||||
{Math.round(percent)}%
|
||||
</span>
|
||||
}
|
||||
|
||||
@@ -8,6 +8,7 @@ import DatePickerPopover from './date_picker_popover'
|
||||
import EmojiPickerPopover from './emoji_picker_popover'
|
||||
import GroupInfoPopover from './group_info_popover'
|
||||
import ProfileOptionsPopover from './profile_options_popover'
|
||||
import RepostOptionsPopover from './repost_options_popover'
|
||||
import SearchPopover from './search_popover'
|
||||
import SidebarMorePopover from './sidebar_more_popover'
|
||||
import StatusOptionsPopover from './status_options_popover'
|
||||
@@ -23,6 +24,7 @@ const POPOVER_COMPONENTS = {
|
||||
EMOJI_PICKER: () => Promise.resolve({ default: EmojiPickerPopover }),
|
||||
GROUP_INFO: () => GroupInfoPopover,
|
||||
PROFILE_OPTIONS: () => Promise.resolve({ default: ProfileOptionsPopover }),
|
||||
REPOST_OPTIONS: () => Promise.resolve({ default: RepostOptionsPopover }),
|
||||
SEARCH: () => Promise.resolve({ default: SearchPopover }),
|
||||
SIDEBAR_MORE: () => Promise.resolve({ default: SidebarMorePopover }),
|
||||
STATUS_OPTIONS: () => Promise.resolve({ default: StatusOptionsPopover }),
|
||||
|
||||
@@ -50,7 +50,6 @@ const messages = defineMessages({
|
||||
admin_account: { id: 'admin_account', defaultMessage: 'Open moderation interface' },
|
||||
add_or_remove_from_list: { id: 'account.add_or_remove_from_list', defaultMessage: 'Add or Remove from lists' },
|
||||
add_or_remove_from_shortcuts: { id: 'account.add_or_remove_from_shortcuts', defaultMessage: 'Add or Remove from shortcuts' },
|
||||
accountFollowsYou: { id: 'account.follows_you', defaultMessage: 'Follows you' },
|
||||
accountBlocked: { id: 'account.blocked', defaultMessage: 'Blocked' },
|
||||
accountMuted: { id: 'account.muted', defaultMessage: 'Muted' },
|
||||
domainBlocked: { id: 'account.domain_blocked', defaultMessage: 'Domain hidden' },
|
||||
|
||||
@@ -0,0 +1,97 @@
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import { quoteCompose } from '../../actions/compose'
|
||||
import { repost, unrepost } from '../../actions/interactions'
|
||||
import { openModal } from '../../actions/modal'
|
||||
import { boostModal, me } from '../../initial_state'
|
||||
import PopoverLayout from './popover_layout'
|
||||
import List from '../list'
|
||||
|
||||
const messages = defineMessages({
|
||||
repost: { id: 'repost', defaultMessage: 'Repost' },
|
||||
repostWithComment: { id: 'repost_with_comment', defaultMessage: 'Repost with comment' },
|
||||
});
|
||||
|
||||
const mapDispatchToProps = (dispatch, { intl }) => ({
|
||||
onQuote (status, router) {
|
||||
if (!me) return dispatch(openModal('UNAUTHORIZED'))
|
||||
|
||||
dispatch((_, getState) => {
|
||||
const state = getState();
|
||||
if (state.getIn(['compose', 'text']).trim().length !== 0) {
|
||||
dispatch(openModal('CONFIRM', {
|
||||
message: intl.formatMessage(messages.quoteMessage),
|
||||
confirm: intl.formatMessage(messages.quoteConfirm),
|
||||
onConfirm: () => dispatch(quoteCompose(status, router)),
|
||||
}));
|
||||
} else {
|
||||
dispatch(quoteCompose(status, router));
|
||||
}
|
||||
});
|
||||
},
|
||||
|
||||
onRepost (status) {
|
||||
if (!me) return dispatch(openModal('UNAUTHORIZED'))
|
||||
|
||||
if (status.get('reblogged')) {
|
||||
dispatch(unrepost(status));
|
||||
} else {
|
||||
dispatch(repost(status));
|
||||
}
|
||||
},
|
||||
});
|
||||
|
||||
|
||||
export default
|
||||
@injectIntl
|
||||
@connect(null, mapDispatchToProps)
|
||||
class RepostOptionsPopover extends ImmutablePureComponent {
|
||||
|
||||
static defaultProps = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
onQuote: PropTypes.func.isRequired,
|
||||
onRepost: PropTypes.func.isRequired,
|
||||
}
|
||||
|
||||
updateOnProps = ['status']
|
||||
|
||||
handleOnRepost = () => {
|
||||
|
||||
}
|
||||
|
||||
handleOnQuote = () => {
|
||||
|
||||
}
|
||||
|
||||
render() {
|
||||
const { intl } = this.props
|
||||
|
||||
const listItems = [
|
||||
{
|
||||
hideArrow: true,
|
||||
icon: 'repost',
|
||||
title: intl.formatMessage(messages.repost),
|
||||
onClick: this.handleOnRepost,
|
||||
},
|
||||
{
|
||||
hideArrow: true,
|
||||
icon: 'pencil',
|
||||
title: intl.formatMessage(messages.repostWithComment),
|
||||
onClick: this.handleBlockDomain,
|
||||
}
|
||||
]
|
||||
|
||||
return (
|
||||
<PopoverLayout width={220}>
|
||||
<List
|
||||
scrollKey='repost_options'
|
||||
items={listItems}
|
||||
size='large'
|
||||
/>
|
||||
</PopoverLayout>
|
||||
)
|
||||
}
|
||||
|
||||
}
|
||||
@@ -28,11 +28,11 @@ export default class UserInfoPopover extends ImmutablePureComponent {
|
||||
to={to}
|
||||
className={[_s.default, _s.noUnderline, _s.flexGrow1].join(' ')}
|
||||
>
|
||||
<Avatar account={account} size={42} />
|
||||
<DisplayName account={account} multiline noHover />
|
||||
<Avatar account={account} size={42} noHover />
|
||||
<DisplayName account={account} isMultiline noHover />
|
||||
</NavLink>
|
||||
|
||||
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
|
||||
<div className={[_s.default, _s.mlAuto, _s.right0, _s.posAbs, _s.mt5].join(' ')}>
|
||||
<AccountActionButton account={account} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -2,32 +2,37 @@ import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import Sticky from 'react-stickynode'
|
||||
import classNames from 'classnames/bind'
|
||||
import {
|
||||
CX,
|
||||
POPOVER_PROFILE_OPTIONS,
|
||||
PLACEHOLDER_MISSING_HEADER_SRC,
|
||||
} from '../constants'
|
||||
import { openPopover } from '../actions/popover'
|
||||
import { me } from '../initial_state'
|
||||
import AccountActionButton from './account_action_button'
|
||||
import Avatar from './avatar'
|
||||
import Button from './button'
|
||||
import DisplayName from './display_name'
|
||||
import Icon from './icon'
|
||||
import Image from './image'
|
||||
import MovedNote from './moved_note'
|
||||
import TabBar from './tab_bar'
|
||||
import Text from './text'
|
||||
|
||||
const cx = classNames.bind(_s)
|
||||
|
||||
const messages = defineMessages({
|
||||
followers: { id: 'account.followers', defaultMessage: 'Followers' },
|
||||
follows: { id: 'account.follows', defaultMessage: 'Follows' },
|
||||
profile: { id: 'account.profile', defaultMessage: 'Profile' },
|
||||
headerPhoto: { id: 'header_photo', defaultMessage: 'Header photo' },
|
||||
timeline: { id: 'timeline', defaultMessage: 'Timeline' },
|
||||
comments: { id: 'comments', defaultMessage: 'Comments' },
|
||||
media: { id: 'media', defaultMessage: 'Media' },
|
||||
accountFollowsYou: { id: 'account.follows_you', defaultMessage: 'Follows you' },
|
||||
})
|
||||
|
||||
const mapDispatchToProps = (dispatch) => ({
|
||||
|
||||
openProfileOptionsPopover(props) {
|
||||
dispatch(openPopover('PROFILE_OPTIONS', props))
|
||||
dispatch(openPopover(POPOVER_PROFILE_OPTIONS, props))
|
||||
},
|
||||
|
||||
});
|
||||
@@ -49,44 +54,19 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
|
||||
handleOpenMore = () => {
|
||||
const { openProfileOptionsPopover, account } = this.props
|
||||
|
||||
openProfileOptionsPopover({
|
||||
account,
|
||||
targetRef: this.openMoreNode,
|
||||
position: 'top',
|
||||
account: this.props.account,
|
||||
})
|
||||
}
|
||||
|
||||
// : todo :
|
||||
makeInfo = () => {
|
||||
const { account, intl } = this.props
|
||||
|
||||
const info = []
|
||||
|
||||
if (!account || !me) return info;
|
||||
|
||||
if (me !== account.get('id') && account.getIn(['relationship', 'followed_by'])) {
|
||||
info.push(<span key='followed_by' className='relationship-tag'>{intl.formatMessage(messages.accountFollowsYou)}</span>);
|
||||
} else if (me !== account.get('id') && account.getIn(['relationship', 'blocking'])) {
|
||||
info.push(<span key='blocked' className='relationship-tag'>{intl.formatMessage(messages.accountBlocked)}</span>);
|
||||
}
|
||||
|
||||
if (me !== account.get('id') && account.getIn(['relationship', 'muting'])) {
|
||||
info.push(<span key='muted' className='relationship-tag'>{intl.formatMessage(messages.accountMuted)}</span>);
|
||||
} else if (me !== account.get('id') && account.getIn(['relationship', 'domain_blocking'])) {
|
||||
info.push(<span key='domain_blocked' className='relationship-tag'>{intl.formatMessage(messages.domainBlocked)}</span>);
|
||||
}
|
||||
|
||||
return info
|
||||
}
|
||||
|
||||
onStickyStateChange = (status) => {
|
||||
switch (status.status) {
|
||||
case Sticky.STATUS_FIXED:
|
||||
this.setState({ stickied: true })
|
||||
break;
|
||||
default:
|
||||
this.setState({ stickied: false })
|
||||
break;
|
||||
if (status.status === Sticky.STATUS_FIXED) {
|
||||
this.setState({ stickied: true })
|
||||
} else {
|
||||
this.setState({ stickied: false })
|
||||
}
|
||||
}
|
||||
|
||||
@@ -101,34 +81,30 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
const tabs = !account ? null : [
|
||||
{
|
||||
to: `/${account.get('acct')}`,
|
||||
title: 'Timeline',
|
||||
title: intl.formatMessage(messages.timeline),
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/comments`,
|
||||
title: 'Comments',
|
||||
title: intl.formatMessage(messages.comments),
|
||||
},
|
||||
{
|
||||
to: `/${account.get('acct')}/media`,
|
||||
title: 'Media',
|
||||
},
|
||||
{
|
||||
to: '',
|
||||
title: 'More',
|
||||
title: intl.formatMessage(messages.media),
|
||||
},
|
||||
]
|
||||
|
||||
const headerSrc = !!account ? account.get('header') : ''
|
||||
const headerMissing = headerSrc.indexOf('/headers/original/missing.png') > -1 || !headerSrc
|
||||
const headerMissing = headerSrc.indexOf(PLACEHOLDER_MISSING_HEADER_SRC) > -1 || !headerSrc
|
||||
const avatarSize = headerMissing ? 75 : 150
|
||||
|
||||
const avatarContainerClasses = cx({
|
||||
const avatarContainerClasses = CX({
|
||||
default: 1,
|
||||
circle: 1,
|
||||
mtNeg75PX: !headerMissing,
|
||||
borderColorWhite: 1,
|
||||
border2PX: 1,
|
||||
boxShadowProfileAvatar: !headerMissing,
|
||||
})
|
||||
|
||||
const stickyBarContainerClasses = cx({
|
||||
const stickyBarContainerClasses = CX({
|
||||
default: 1,
|
||||
flexRow: 1,
|
||||
px15: 1,
|
||||
@@ -136,14 +112,11 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
displayNone: !stickied,
|
||||
})
|
||||
|
||||
const tabBarContainerClasses = cx({
|
||||
const tabBarContainerClasses = CX({
|
||||
default: 1,
|
||||
displayNone: stickied,
|
||||
})
|
||||
|
||||
|
||||
// : todo : "follows you", "mutual follow"
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.z1, _s.width100PC].join(' ')}>
|
||||
|
||||
@@ -160,22 +133,13 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
|
||||
<div className={[_s.default, _s.borderBottom1PX, _s.borderColorSecondary, _s.width100PC].join(' ')}>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.px15, _s.mb5].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.pr15, _s.pl25, _s.mb5].join(' ')}>
|
||||
<div className={avatarContainerClasses}>
|
||||
<Avatar size={avatarSize} account={account} />
|
||||
<Avatar size={avatarSize} account={account} noHover />
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.flexRow, _s.px15, _s.py10].join(' ')}>
|
||||
<DisplayName account={account} multiline large />
|
||||
{
|
||||
account && account.get('locked') &&
|
||||
<Icon id='lock-filled' size='14px' className={[_s.mt10, _s.ml10].join(' ')} />
|
||||
}
|
||||
{
|
||||
/* : todo :
|
||||
account.getIn(['relationship', 'muting'])
|
||||
*/
|
||||
}
|
||||
<div className={[_s.default, _s.flexRow, _s.px15, _s.flexNormal, _s.py10].join(' ')}>
|
||||
<DisplayName account={account} isMultiline noRelationship isLarge noHover />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -186,15 +150,15 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
</div>
|
||||
|
||||
<div className={stickyBarContainerClasses}>
|
||||
<Avatar size={36} account={account} />
|
||||
<Avatar size={36} account={account} noHover />
|
||||
<div className={[_s.default, _s.ml10].join(' ')}>
|
||||
<DisplayName account={account} noUsername large />
|
||||
<DisplayName account={account} noUsername noRelationship noHover isLarge />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
{
|
||||
account && account.get('id') === me &&
|
||||
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.py5].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.mlAuto, _s.py5].join(' ')}>
|
||||
<Button
|
||||
isOutline
|
||||
backgroundColor='none'
|
||||
@@ -216,19 +180,18 @@ class ProfileHeader extends ImmutablePureComponent {
|
||||
|
||||
{
|
||||
account && account.get('id') !== me &&
|
||||
<div className={[_s.default, _s.flexRow, _s.marginLeftAuto, _s.py5].join(' ')}>
|
||||
<div ref={this.setOpenMoreNodeRef}>
|
||||
<Button
|
||||
isOutline
|
||||
icon='ellipsis'
|
||||
iconSize='18px'
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||
onClick={this.handleOpenMore}
|
||||
/>
|
||||
</div>
|
||||
<div className={[_s.default, _s.flexRow, _s.mlAuto, _s.py5].join(' ')}>
|
||||
<Button
|
||||
isOutline
|
||||
icon='ellipsis'
|
||||
iconSize='18px'
|
||||
iconClassName={_s.inheritFill}
|
||||
color='brand'
|
||||
backgroundColor='none'
|
||||
className={[_s.justifyContentCenter, _s.alignItemsCenter, _s.mr10, _s.px10].join(' ')}
|
||||
onClick={this.handleOpenMore}
|
||||
buttonRef={this.setOpenMoreNodeRef}
|
||||
/>
|
||||
|
||||
<form action='https://chat.gab.com/private-message' method='POST'>
|
||||
<Button
|
||||
|
||||
@@ -42,11 +42,11 @@ export default class ProgressBar extends PureComponent {
|
||||
noClasses
|
||||
className={containerClassName}
|
||||
>
|
||||
<div className={[_s.default, _s.backgroundColorBrandDark, _s.circle, _s.height100PC].join(' ')} style={style} />
|
||||
<div className={[_s.default, _s.backgroundColorBrand, _s.circle, _s.height100PC, _s.backgroundCandy].join(' ')} style={style} />
|
||||
<div className={[_s.default, _s.posAbs, _s.width100PC, _s.height100PC, _s.alignItemsCenter, _s.justifyContentCenter].join(' ')}>
|
||||
{
|
||||
!!title &&
|
||||
<Text size='small' weight='bold' color='white'>
|
||||
<Text size='small' weight='bold' color='white' className={_s.textShadow}>
|
||||
{title}
|
||||
</Text>
|
||||
}
|
||||
|
||||
@@ -132,7 +132,7 @@ class RichTextEditorBar extends PureComponent {
|
||||
color='secondary'
|
||||
onClick={this.handleOnTogglePopoutEditor}
|
||||
title='Fullscreen'
|
||||
className={[_s.px10, _s.noSelect, _s.marginLeftAuto].join(' ')}
|
||||
className={[_s.px10, _s.noSelect, _s.mlAuto].join(' ')}
|
||||
icon='fullscreen'
|
||||
iconClassName={_s.inheritFill}
|
||||
iconSize='12px'
|
||||
|
||||
@@ -1,3 +1,5 @@
|
||||
import { withRouter } from 'react-router-dom'
|
||||
import queryString from 'query-string'
|
||||
import {
|
||||
changeSearch,
|
||||
clearSearch,
|
||||
@@ -19,6 +21,7 @@ const mapDispatchToProps = (dispatch) => ({
|
||||
})
|
||||
|
||||
export default
|
||||
@withRouter
|
||||
@connect(mapStateToProps, mapDispatchToProps)
|
||||
class Search extends PureComponent {
|
||||
|
||||
@@ -42,6 +45,18 @@ class Search extends PureComponent {
|
||||
|
||||
textbox = React.createRef()
|
||||
|
||||
componentDidUpdate(prevProps) {
|
||||
// If user navigates to different page, ensure tab bar item
|
||||
// with this.props.to that is on location is set to active.
|
||||
if (this.props.location !== prevProps.location) {
|
||||
const isCurrent = this.props.to === this.props.location.pathname
|
||||
|
||||
if (this.state.isCurrent !== isCurrent) {
|
||||
this.setState({ isCurrent })
|
||||
}
|
||||
}
|
||||
}
|
||||
|
||||
handleChange = (e) => {
|
||||
this.props.onChange(e.target.value)
|
||||
}
|
||||
|
||||
@@ -30,7 +30,7 @@ class SensitiveMediaItem extends PureComponent {
|
||||
{intl.formatMessage(messages.warning)}
|
||||
</Text>
|
||||
</div>
|
||||
<div className={[_s.default, _s.justifyContentCenter, _s.marginLeftAuto].join(' ')}>
|
||||
<div className={[_s.default, _s.justifyContentCenter, _s.mlAuto].join(' ')}>
|
||||
<Button
|
||||
onClick={onClick}
|
||||
color='tertiary'
|
||||
|
||||
@@ -65,7 +65,6 @@ export default class SidebarSectionItem extends PureComponent {
|
||||
px10: 1,
|
||||
alignItemsCenter: 1,
|
||||
radiusSmall: 1,
|
||||
mt2: 1,
|
||||
border1PX: 1,
|
||||
outlineNone: 1,
|
||||
borderColorTransparent: !shouldShowActive,
|
||||
@@ -90,7 +89,7 @@ export default class SidebarSectionItem extends PureComponent {
|
||||
const countClasses = cx({
|
||||
default: 1,
|
||||
text: 1,
|
||||
marginLeftAuto: 1,
|
||||
mlAuto: 1,
|
||||
fontSize12PX: 1,
|
||||
px5: 1,
|
||||
mr2: 1,
|
||||
|
||||
@@ -11,7 +11,7 @@ export default class SidebarSectionTitle extends PureComponent {
|
||||
|
||||
return (
|
||||
<div className={[_s.default, _s.py5, _s.px10, _s.mt10].join(' ')}>
|
||||
<Text size='small' weight='bold' color='secondary'>
|
||||
<Text size='small' weight='bold' color='tertiary'>
|
||||
{children}
|
||||
</Text>
|
||||
</div>
|
||||
|
||||
@@ -73,7 +73,7 @@ class Status extends ImmutablePureComponent {
|
||||
isIntersecting: PropTypes.bool,
|
||||
onClick: PropTypes.func,
|
||||
onReply: PropTypes.func,
|
||||
onQuote: PropTypes.func,
|
||||
onRepost: PropTypes.func,
|
||||
onFavorite: PropTypes.func,
|
||||
onMention: PropTypes.func,
|
||||
onOpenMedia: PropTypes.func,
|
||||
@@ -448,10 +448,12 @@ class Status extends ImmutablePureComponent {
|
||||
pb15: isFeatured,
|
||||
radiusSmall: !isChild,
|
||||
backgroundColorPrimary: !isChild,
|
||||
boxShadowBlock: !isChild,
|
||||
outlineNone: 1,
|
||||
mb15: !isChild,
|
||||
border1PX: !isChild,
|
||||
borderBottom1PX: isFeatured && !isChild,
|
||||
borderColorSecondary: !isChild,
|
||||
// border1PX: !isChild,
|
||||
// borderBottom1PX: isFeatured && !isChild,
|
||||
// borderColorSecondary: !isChild,
|
||||
})
|
||||
|
||||
const innerContainerClasses = cx({
|
||||
@@ -510,7 +512,7 @@ class Status extends ImmutablePureComponent {
|
||||
status={status}
|
||||
onFavorite={this.props.onFavorite}
|
||||
onReply={this.props.onReply}
|
||||
onQuote={this.props.onQuote}
|
||||
onRepost={this.props.onRepost}
|
||||
onShare={this.props.onShare}
|
||||
/>
|
||||
}
|
||||
|
||||
@@ -1,6 +1,7 @@
|
||||
import ImmutablePropTypes from 'react-immutable-proptypes'
|
||||
import ImmutablePureComponent from 'react-immutable-pure-component'
|
||||
import { defineMessages, injectIntl } from 'react-intl'
|
||||
import { NavLink } from 'react-router-dom'
|
||||
import classNames from 'classnames/bind'
|
||||
import Text from './text'
|
||||
import StatusActionBarItem from './status_action_bar_item'
|
||||
@@ -22,23 +23,19 @@ export default
|
||||
@injectIntl
|
||||
class StatusActionBar extends ImmutablePureComponent {
|
||||
|
||||
static contextTypes = {
|
||||
router: PropTypes.object,
|
||||
}
|
||||
|
||||
static propTypes = {
|
||||
intl: PropTypes.object.isRequired,
|
||||
onFavorite: PropTypes.func.isRequired,
|
||||
onShare: PropTypes.func.isRequired,
|
||||
onReply: PropTypes.func.isRequired,
|
||||
onQuote: PropTypes.func.isRequired,
|
||||
onRepost: PropTypes.func.isRequired,
|
||||
status: ImmutablePropTypes.map.isRequired,
|
||||
}
|
||||
|
||||
updateOnProps = ['status']
|
||||
|
||||
handleReplyClick = () => {
|
||||
this.props.onReply(this.props.status, this.context.router.history)
|
||||
this.props.onReply(this.props.status)
|
||||
}
|
||||
|
||||
handleFavoriteClick = () => {
|
||||
@@ -46,8 +43,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
}
|
||||
|
||||
handleRepostClick = (e) => {
|
||||
// this.props.onRepost(this.props.status, e)
|
||||
this.props.onQuote(this.props.status, this.context.router.history)
|
||||
this.props.onRepost(this.repostButton, this.props.status, e)
|
||||
}
|
||||
|
||||
handleShareClick = () => {
|
||||
@@ -66,6 +62,10 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
// : todo :
|
||||
}
|
||||
|
||||
setRepostButton = (n) => {
|
||||
this.repostButton = n
|
||||
}
|
||||
|
||||
setShareButton = (n) => {
|
||||
this.shareButton = n
|
||||
}
|
||||
@@ -86,6 +86,8 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
!!status.get('quote')
|
||||
) && !hasInteractions
|
||||
|
||||
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`
|
||||
|
||||
const containerClasses = cx({
|
||||
default: 1,
|
||||
px10: 1,
|
||||
@@ -108,6 +110,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
text: 1,
|
||||
cursorPointer: 1,
|
||||
fontWeightNormal: 1,
|
||||
noUnderline: 1,
|
||||
underline_onHover: 1,
|
||||
mr10: 1,
|
||||
py5: 1,
|
||||
@@ -117,7 +120,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
<div className={containerClasses}>
|
||||
{
|
||||
hasInteractions &&
|
||||
<div className={[_s.default, _s.flexRow, _s.px5].join(' ')}>
|
||||
<div className={[_s.default, _s.flexRow, _s.alignItemsEnd, _s.px5].join(' ')}>
|
||||
{
|
||||
favoriteCount > 0 &&
|
||||
<button className={interactionBtnClasses} onClick={this.openLikesList}>
|
||||
@@ -130,13 +133,16 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
}
|
||||
{
|
||||
replyCount > 0 &&
|
||||
<button className={interactionBtnClasses} onClick={this.toggleCommentsVisible}>
|
||||
<NavLink
|
||||
className={interactionBtnClasses}
|
||||
to={statusUrl}
|
||||
>
|
||||
<Text color='secondary' size='small'>
|
||||
{intl.formatMessage(messages.commentsLabel, {
|
||||
number: replyCount,
|
||||
})}
|
||||
</Text>
|
||||
</button>
|
||||
</NavLink>
|
||||
}
|
||||
{
|
||||
repostCount > 0 &&
|
||||
@@ -169,6 +175,7 @@ class StatusActionBar extends ImmutablePureComponent {
|
||||
icon={!publicStatus ? 'lock' : 'repost'}
|
||||
disabled={!publicStatus}
|
||||
active={!!status.get('reblogged')}
|
||||
buttonRef={this.setRepostButton}
|
||||
onClick={this.handleRepostClick}
|
||||
/>
|
||||
<StatusActionBarItem
|
||||
|
||||
@@ -76,7 +76,7 @@ class StatusCheckBox extends ImmutablePureComponent {
|
||||
{media}
|
||||
</div>
|
||||
|
||||
<div className={[_s.default, _s.marginLeftAuto].join(' ')}>
|
||||
<div className={[_s.default, _s.mlAuto].join(' ')}>
|
||||
<Switch checked={checked} onChange={onToggle} disabled={disabled} />
|
||||
</div>
|
||||
</div>
|
||||
|
||||
@@ -238,6 +238,7 @@ class StatusContent extends ImmutablePureComponent {
|
||||
const containerClasses = cx({
|
||||
statusContent: 1,
|
||||
px15: !isComment,
|
||||
outlineNone: 1,
|
||||
})
|
||||
|
||||
return (
|
||||
|
||||
@@ -58,7 +58,7 @@ class StatusHeader extends ImmutablePureComponent {
|
||||
render() {
|
||||
const { status, reduced } = this.props
|
||||
|
||||
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`;
|
||||
const statusUrl = `/${status.getIn(['account', 'acct'])}/posts/${status.get('id')}`
|
||||
|
||||
const containerClasses = cx({
|
||||
default: 1,
|
||||
@@ -93,7 +93,7 @@ class StatusHeader extends ImmutablePureComponent {
|
||||
to={`/${status.getIn(['account', 'acct'])}`}
|
||||
title={status.getIn(['account', 'acct'])}
|
||||
>
|
||||
<DisplayName account={status.get('account')} />
|
||||
<DisplayName account={status.get('account')} noRelationship />
|
||||
</NavLink>
|
||||
|
||||
{
|
||||
@@ -105,7 +105,7 @@ class StatusHeader extends ImmutablePureComponent {
|
||||
icon='ellipsis'
|
||||
iconSize='20px'
|
||||
iconClassName={_s.fillColorSecondary}
|
||||
className={_s.marginLeftAuto}
|
||||
className={_s.mlAuto}
|
||||
onClick={this.handleOpenStatusOptionsPopover}
|
||||
buttonRef={this.setStatusOptionsButton}
|
||||
/>
|
||||
|
||||
@@ -50,9 +50,9 @@ class ReplyIndicator extends ImmutablePureComponent {
|
||||
|
||||
<NavLink to={`/${status.getIn(['account', 'acct'])}`} className='reply-indicator__display-name'>
|
||||
<div className='reply-indicator__display-avatar'>
|
||||
<Avatar account={status.get('account')} size={24} />
|
||||
<Avatar account={status.get('account')} size={24} noHover />
|
||||
</div>
|
||||
<DisplayName account={status.get('account')} />
|
||||
<DisplayName account={status.get('account')} noHover />
|
||||
</NavLink>
|
||||
</div>
|
||||
|
||||
|
||||
@@ -34,7 +34,7 @@ export default class Switch extends PureComponent {
|
||||
width50PX: 1,
|
||||
circle: 1,
|
||||
border1PX: 1,
|
||||
marginLeftAuto: 1,
|
||||
mlAuto: 1,
|
||||
borderColorSecondary: 1,
|
||||
backgroundColorBrand: checked,
|
||||
})
|
||||
|
||||
@@ -53,9 +53,9 @@ class TimelineComposeBlock extends ImmutablePureComponent {
|
||||
return (
|
||||
<section className={[_s.default, _s.mb15].join(' ')}>
|
||||
<Block>
|
||||
<div className={[_s.default, _s.backgroundSubtle, _s.borderBottom1PX, _s.borderColorSecondary, _s.px15, _s.py2, _s.alignItemsCenter, _s.flexRow].join(' ')}>
|
||||
<div className={[_s.default, _s.backgroundSubtle, _s.borderTop1PX, _s.borderBottom1PX, _s.borderColorSecondary, _s.px15, _s.py2, _s.alignItemsCenter, _s.flexRow].join(' ')}>
|
||||
<div className={_s.mr10}>
|
||||
<Avatar account={account} size={20} />
|
||||
<Avatar account={account} size={20} noHover />
|
||||
</div>
|
||||
<Heading size='h5'>
|
||||
{intl.formatMessage(messages.createPost)}
|
||||
|
||||
@@ -1,4 +1,5 @@
|
||||
import classNames from 'classnames/bind'
|
||||
import { urlRegex } from '../features/compose/util/url_regex'
|
||||
import Button from './button'
|
||||
import DotTextSeperator from './dot_text_seperator'
|
||||
import Image from './image'
|
||||
@@ -71,6 +72,7 @@ export default class TrendingItem extends PureComponent {
|
||||
|
||||
const correctedAuthor = author.replace('www.', '')
|
||||
const correctedDescription = description.length >= 120 ? `${description.substring(0, 120).trim()}...` : description
|
||||
const descriptionHasLink = correctedDescription.match(urlRegex)
|
||||
|
||||
const image = (
|
||||
<Image
|
||||
@@ -105,7 +107,7 @@ export default class TrendingItem extends PureComponent {
|
||||
</div>
|
||||
|
||||
{
|
||||
!!correctedDescription &&
|
||||
!!correctedDescription && !descriptionHasLink &&
|
||||
<div className={[_s.default, _s.heightMax56PX, _s.overflowHidden, _s.pt5, _s.mb5].join(' ')}>
|
||||
<Text size='small' color='secondary'>
|
||||
{correctedDescription}
|
||||
|
||||
@@ -604,7 +604,7 @@ class Video extends PureComponent {
|
||||
className={_s.pl0}
|
||||
/>
|
||||
|
||||
<div className={[_s.default, _s.marginLeftAuto, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
<div className={[_s.default, _s.mlAuto, _s.flexRow, _s.alignItemsCenter].join(' ')}>
|
||||
<Text color='white' size='small'>
|
||||
{formatTime(currentTime)}
|
||||
/
|
||||
|
||||
Reference in New Issue
Block a user