This commit is contained in:
mgabdev
2020-03-06 23:53:28 -05:00
parent da3d0c3462
commit 557c6470f5
41 changed files with 1181 additions and 1106 deletions

View File

@@ -1,12 +1,13 @@
import { Fragment } from 'react'
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePropTypes from 'react-immutable-proptypes'
import classNames from 'classnames/bind'
import ImmutablePureComponent from 'react-immutable-pure-component';
import Textarea from 'react-textarea-autosize';
import { isRtl } from '../../utils/rtl';
import { textAtCursorMatchesToken } from '../../utils/cursor_token_match';
import AutosuggestAccount from '../autosuggest_account';
import AutosuggestEmoji from '../autosuggest_emoji';
import ImmutablePureComponent from 'react-immutable-pure-component'
import Textarea from 'react-textarea-autosize'
import { isRtl } from '../../utils/rtl'
import { textAtCursorMatchesToken } from '../../utils/cursor_token_match'
import AutosuggestAccount from '../autosuggest_account'
import AutosuggestEmoji from '../autosuggest_emoji'
import Input from '../input'
const cx = classNames.bind(_s)
@@ -191,12 +192,24 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
}
render () {
const { value, small, suggestions, disabled, placeholder, onKeyUp, autoFocus, children, className, id, maxLength, textarea } = this.props;
const { suggestionsHidden } = this.state;
const style = { direction: 'ltr' };
const {
value,
small,
suggestions,
disabled,
placeholder,
onKeyUp,
autoFocus,
children,
className,
id,
maxLength,
textarea
} = this.props
if (isRtl(value)) {
style.direction = 'rtl';
const { suggestionsHidden } = this.state
const style = {
direction: isRtl(value) ? 'rtl' : 'ltr',
}
const textClasses = cx({
@@ -217,7 +230,7 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
if (textarea) {
return (
<Fragment>
<Fragment>
<div className={[_s.default, _s.flexGrow1].join(' ')}>
<div className={[_s.default, _s.marginLeft5PX].join(' ')}>
<Textarea
@@ -249,11 +262,11 @@ export default class AutosuggestTextbox extends ImmutablePureComponent {
}
return (
<div className='autosuggest-input'>
<label>
<div className={[_s.default, _s.flexGrow1].join(' ')}>
<label className={[_s.default].join(' ')}>
<span style={{ display: 'none' }}>{placeholder}</span>
<input
<Input
type='text'
ref={this.setTextbox}
disabled={disabled}

View File

@@ -79,11 +79,14 @@ export default class Button extends PureComponent {
...otherProps
} = this.props
const theIcon = !!icon ? <Icon id={icon} width={iconWidth} height={iconWidth} className={iconClassName} /> : undefined
if (backgroundColor === 'tertiary') {
console.log("className:", className)
}
const theIcon = !!icon ? (
<Icon
id={icon}
width={iconWidth}
height={iconWidth}
className={iconClassName}
/>
) : undefined
// : todo :
const classes = cx(className, {
@@ -93,6 +96,7 @@ export default class Button extends PureComponent {
cursorPointer: 1,
textAlignCenter: 1,
outlineNone: 1,
flexRow: !!children && !!icon,
backgroundColorPrimary: backgroundColor === COLORS.white,
backgroundColorBrand: backgroundColor === COLORS.brand,
@@ -143,6 +147,7 @@ export default class Button extends PureComponent {
to: to || undefined,
href: href || undefined,
onClick: this.handleClick || undefined,
...otherProps,
}
if (tagName === 'NavLink' && !!to) {

View File

@@ -1,67 +1,53 @@
import { injectIntl, defineMessages } from 'react-intl'
import TabBar from './tab_bar'
import Icon from './icon'
import Button from './button'
import Heading from './heading'
const messages = defineMessages({
show: { id: 'column_header.show_settings', defaultMessage: 'Show settings' },
hide: { id: 'column_header.hide_settings', defaultMessage: 'Hide settings' },
})
export default
@injectIntl
class ColumnHeader extends PureComponent {
export default class ColumnHeader extends PureComponent {
static contextTypes = {
router: PropTypes.object,
}
static propTypes = {
intl: PropTypes.object.isRequired,
title: PropTypes.node,
icon: PropTypes.string,
active: PropTypes.bool,
children: PropTypes.node,
showBackBtn: PropTypes.bool,
actions: PropTypes.array,
tabs: PropTypes.array,
}
state = {
collapsed: true,
}
historyBack = () => {
if (window.history && window.history.length === 1) {
this.context.router.history.push('/home') // homehack
this.context.router.history.push('/home')
} else {
this.context.router.history.goBack()
}
}
handleToggleClick = (e) => {
e.stopPropagation()
this.setState({
collapsed: !this.state.collapsed,
})
}
handleBackClick = () => {
this.historyBack()
}
render() {
const { title, showBackBtn, tabs, icon, active, children, actions, intl: { formatMessage } } = this.props
const { collapsed } = this.state
const {
title,
showBackBtn,
tabs,
actions
} = this.props
return (
<div className={[_s.default, _s.height100PC, _s.flexRow].join(' ')}>
{
showBackBtn &&
<button className={[_s.default, _s.cursorPointer, _s.backgroundTransparent, _s.alignItemsCenter, _s.marginRight10PX, _s.justifyContentCenter].join(' ')}>
<Icon className={[_s.marginRight5PX, _s.fillColorPrimary].join(' ')} id='back' width='20px' height='20px' />
</button>
<Button
backgroundColor='none'
className={[_s.alignItemsCenter, _s.paddingLeft0, _s.justifyContentCenter].join(' ')}
icon='back'
iconWidth='20px'
iconHeight='20px'
iconClassName={[_s.marginRight5PX, _s.fillColorPrimary].join(' ')}
onClick={this.handleBackClick}
/>
}
<div className={[_s.default, _s.height100PC, _s.justifyContentCenter, _s.marginRight10PX].join(' ')}>
@@ -80,13 +66,17 @@ class ColumnHeader extends PureComponent {
<div className={[_s.default, _s.backgroundTransparent, _s.flexRow, _s.alignItemsCenter, _s.justifyContentCenter, _s.marginLeftAuto].join(' ')}>
{
actions.map((action, i) => (
<button
<Button
radiusSmall
backgroundColor='tertiary'
onClick={() => action.onClick()}
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' />
</button>
className={[_s.marginLeft5PX, _s.paddingHorizontal10PX].join(' ')}
iconClassName={_s.fillColorSecondary}
icon={action.icon}
iconWidth='20px'
iconHeight='20px'
/>
))
}
</div>

View File

@@ -45,6 +45,8 @@ export default class Icon extends PureComponent {
return <I.GlobeIcon {...options} />
case 'group':
return <I.GroupIcon {...options} />
case 'happy':
return <I.HappyIcon {...options} />
case 'home':
return <I.HomeIcon {...options} />
case 'like':

View File

@@ -1,3 +1,4 @@
import { Fragment } from 'react'
import classNames from 'classnames/bind'
import Icon from './icon'
import Text from './text'
@@ -16,6 +17,7 @@ export default class Input extends PureComponent {
onBlur: PropTypes.func,
onClear: PropTypes.func,
title: PropTypes.string,
small: PropTypes.bool,
}
render() {
@@ -29,7 +31,8 @@ export default class Input extends PureComponent {
onFocus,
onBlur,
onClear,
title
title,
small
} = this.props
const inputClasses = cx({
@@ -48,7 +51,7 @@ export default class Input extends PureComponent {
})
return (
<div>
<Fragment>
{
!!title &&
<div className={[_s.default, _s.marginBottom10PX, _s.paddingLeft15PX].join(' ')}>
@@ -81,7 +84,7 @@ export default class Input extends PureComponent {
</div>
}
</div>
</div>
</Fragment>
)
}
}

View File

@@ -3,14 +3,18 @@ import ImmutablePureComponent from 'react-immutable-pure-component'
import { NavLink } from 'react-router-dom'
import { decode } from 'blurhash'
import { autoPlayGif, displayMedia } from '../initial_state'
import classNames from 'classnames/bind'
import Icon from './icon'
import Image from './image'
import Text from './text'
const cx = classNames.bind(_s)
export default class MediaItem extends ImmutablePureComponent {
static propTypes = {
attachment: ImmutablePropTypes.map.isRequired,
small: PropTypes.bool
}
state = {
@@ -55,7 +59,7 @@ export default class MediaItem extends ImmutablePureComponent {
}
render() {
const { attachment } = this.props
const { attachment, small } = this.props
const { visible, loaded } = this.state
const status = attachment.get('status')
@@ -71,13 +75,33 @@ export default class MediaItem extends ImmutablePureComponent {
badge = 'GIF'
}
const containerClasses = cx({
default: 1,
positionAbsolute: 1,
top0: 1,
height100PC: 1,
width100PC: 1,
paddingVertical5PX: !small,
paddingHorizontal5PX: !small,
})
const linkClasses = cx({
default: 1,
width100PC: 1,
height100PC: 1,
overflowHidden: 1,
border1PX: 1,
borderColorSecondary: !small,
borderColorWhite: small,
})
return (
<div className={[_s.default, _s.width25PC, _s.paddingTop25PC].join(' ')}>
<div className={[_s.default, _s.positionAbsolute, _s.top0, _s.height100PC, _s.width100PC, _s.paddingVertical5PX, _s.paddingHorizontal5PX].join(' ')}>
<div className={containerClasses}>
<NavLink
to={status.get('url')} /* : todo : */
title={title}
className={[_s.default, _s.width100PC, _s.height100PC, _s.border1PX, _s.borderColorSecondary, _s.overflowHidden].join(' ')}
className={linkClasses}
>
{
(!loaded || !visible) &&

View File

@@ -29,7 +29,7 @@ class ComposeModal extends ImmutablePureComponent {
};
onClickClose = () => {
const {composeText, dispatch, onClose, intl} = this.props;
const { composeText, dispatch, onClose, intl } = this.props;
if (composeText) {
dispatch(openModal('CONFIRM', {
@@ -44,12 +44,15 @@ class ComposeModal extends ImmutablePureComponent {
}
};
render () {
render() {
const { intl } = this.props;
return (
<ModalLayout title={intl.formatMessage(messages.title)} onClose={this.onClickClose}>
<TimelineComposeBlock />
<ModalLayout
noPadding
title={intl.formatMessage(messages.title)} onClose={this.onClickClose}
>
<TimelineComposeBlock modal />
</ModalLayout>
);
}

View File

@@ -130,7 +130,7 @@ class ModalBase extends PureComponent {
<Fragment>
<div
role='presentation'
className={[_s.default, _s.backgroundColorPrimaryOpaque, _s.positionFixed, _s.z3, _s.top0, _s.right0, _s.bottom0, _s.left0].join(' ')}
className={[_s.default, _s.backgroundColorOpaque, _s.positionFixed, _s.z3, _s.top0, _s.right0, _s.bottom0, _s.left0].join(' ')}
onClick={this.handleOnClose}
/>
<div

View File

@@ -17,7 +17,12 @@ class ModalLayout extends PureComponent {
}
render() {
const { title, children, intl, onClose } = this.props
const {
title,
children,
intl,
onClose,
} = this.props
return (
<div className={[_s.width645PX].join(' ')}>
@@ -27,12 +32,13 @@ class ModalLayout extends PureComponent {
{title}
</Heading>
<Button
className=''
backgroundColor='none'
title={intl.formatMessage(messages.close)}
className={_s.marginLeftAuto}
onClick={onClose}
icon='times'
iconWidth='20px'
iconWidth='20px'
icon='close'
iconWidth='10px'
iconWidth='10px'
/>
</div>
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>

View File

@@ -1,52 +1,82 @@
import { defineMessages, injectIntl } from 'react-intl'
import { fetchSuggestions, dismissSuggestion } from '../../actions/suggestions'
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import AccountContainer from '../../containers/account_container'
import { expandAccountMediaTimeline } from '../../actions/timelines'
import { getAccountGallery } from '../../selectors'
import PanelLayout from './panel_layout'
import MediaItem from '../media_item'
const messages = defineMessages({
title: { id: 'media_gallery_panel.title', defaultMessage: 'Media' },
show_all: { id: 'media_gallery_panel.all', defaultMessage: 'Show all' },
})
const mapStateToProps = state => ({
suggestions: state.getIn(['suggestions', 'items']),
})
const mapStateToProps = (state, { account }) => {
const accountId = !!account ? account.get('id') : -1
const mapDispatchToProps = dispatch => {
return {
fetchSuggestions: () => dispatch(fetchSuggestions()),
dismissSuggestion: account => dispatch(dismissSuggestion(account.get('id'))),
accountId,
attachments: getAccountGallery(state, accountId),
}
}
export default
@connect(mapStateToProps, mapDispatchToProps)
@connect(mapStateToProps)
@injectIntl
class MediaGalleryPanel extends ImmutablePureComponent {
static propTypes = {
dispatch: PropTypes.func.isRequired,
accountId: PropTypes.string,
account: ImmutablePropTypes.map.isRequired,
attachments: ImmutablePropTypes.list.isRequired,
intl: PropTypes.object.isRequired,
}
componentDidMount() {
// this.props.fetchSuggestions()
const { accountId } = this.props
if (accountId) {
this.props.dispatch(expandAccountMediaTimeline(accountId, {limit: 8}))
}
}
componentWillReceiveProps(nextProps) {
if (nextProps.accountId && nextProps.accountId !== this.props.accountId) {
this.props.dispatch(expandAccountMediaTimeline(nextProps.accountId, {limit: 8}))
}
}
render() {
const { intl, account } = this.props
const {
intl,
account,
attachments
} = this.props
console.log("account:", account)
console.log("account, attachments:", account, attachments)
if (!account || !attachments) return null
if (attachments.size === 0) return null
return (
<PanelLayout
noPadding
title={intl.formatMessage(messages.title)}
headerButtonTitle={intl.formatMessage(messages.show_all)}
headerButtonTo='/explore'
headerButtonTo={`/${account.get('acct')}/media`}
>
<div className={[_s.default, _s.flexRow, _s.flexWrap, _s.paddingHorizontal10PX, _s.paddingVertical10PX].join(' ')}>
{
attachments.slice(0, 16).map((attachment) => (
<MediaItem
small
key={attachment.get('id')}
attachment={attachment}
/>
))
}
</div>
</PanelLayout>
)
}

View File

@@ -25,7 +25,6 @@ const mapStateToProps = (state, { account }) => {
}
}
const mapDispatchToProps = dispatch => {
return {
fetchSuggestions: () => dispatch(fetchSuggestions()),
@@ -119,7 +118,7 @@ class ProfileInfoPanel extends ImmutablePureComponent {
<Divider small />
<dl className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')} key={`profile-field-${i}`}>
<dt
className={[_s.text, _s.dangerousContent].join('')}
className={[_s.text, _s.dangerousContent].join(' ')}
dangerouslySetInnerHTML={{ __html: pair.get('name_emojified') }}
title={pair.get('name')}
/>

View File

@@ -1,29 +1,34 @@
import ImmutablePropTypes from 'react-immutable-proptypes';
import ImmutablePureComponent from 'react-immutable-pure-component';
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl';
import classNames from 'classnames';
import escapeTextContentForBrowser from 'escape-html';
import spring from 'react-motion/lib/spring';
import Motion from '../../features/ui/util/optional_motion';
import { vote, fetchPoll } from '../../actions/polls';
import emojify from '../emoji/emoji';
import RelativeTimestamp from '../relative_timestamp';
import Button from '../button';
import { Fragment } from 'react'
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
import { defineMessages, injectIntl, FormattedMessage } from 'react-intl'
import classNames from 'classnames/bind'
import escapeTextContentForBrowser from 'escape-html'
import spring from 'react-motion/lib/spring'
import Motion from '../../features/ui/util/optional_motion'
import { vote } from '../../actions/polls'
import emojify from '../emoji/emoji'
import RelativeTimestamp from '../relative_timestamp'
import Button from '../button'
import DotTextSeperator from '../dot_text_seperator'
import Text from '../text'
const cx = classNames.bind(_s)
const mapStateToProps = (state, { pollId }) => ({
poll: state.getIn(['polls', pollId]),
});
})
const messages = defineMessages({
closed: { id: 'poll.closed', defaultMessage: 'Closed' },
vote: { id: 'poll.vote', defaultMessage: 'Vote' },
refresh: { id: 'poll.refresh', defaultMessage: 'Refresh' },
});
})
const makeEmojiMap = record => record.get('emojis').reduce((obj, emoji) => {
obj[`:${emoji.get('shortcode')}:`] = emoji.toJS();
return obj;
}, {});
obj[`:${emoji.get('shortcode')}:`] = emoji.toJS()
return obj
}, {})
export default
@connect(mapStateToProps)
@@ -35,71 +40,91 @@ class Poll extends ImmutablePureComponent {
intl: PropTypes.object.isRequired,
dispatch: PropTypes.func,
disabled: PropTypes.bool,
};
}
state = {
selected: {},
};
}
handleOptionChange = e => {
const { target: { value } } = e;
const { target: { value } } = e
if (this.props.poll.get('multiple')) {
const tmp = { ...this.state.selected };
const tmp = { ...this.state.selected }
if (tmp[value]) {
delete tmp[value];
delete tmp[value]
} else {
tmp[value] = true;
tmp[value] = true
}
this.setState({ selected: tmp });
this.setState({ selected: tmp })
} else {
const tmp = {};
tmp[value] = true;
this.setState({ selected: tmp });
const tmp = {}
tmp[value] = true
this.setState({ selected: tmp })
}
};
}
handleVote = () => {
if (this.props.disabled) return;
if (this.props.disabled) return
this.props.dispatch(vote(this.props.poll.get('id'), Object.keys(this.state.selected)));
};
this.props.dispatch(vote(this.props.poll.get('id'), Object.keys(this.state.selected)))
}
handleRefresh = () => {
if (this.props.disabled) return;
renderOption(option, optionIndex) {
const { poll, disabled } = this.props
const { selected } = this.state
const percent = poll.get('votes_count') === 0 ? 0 : (option.get('votes_count') / poll.get('votes_count')) * 100
const leading = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') > other.get('votes_count'))
const active = !!selected[`${optionIndex}`]
const showResults = poll.get('voted') || poll.get('expired')
const multiple = poll.get('multiple')
this.props.dispatch(fetchPoll(this.props.poll.get('id')));
};
renderOption (option, optionIndex) {
const { poll, disabled } = this.props;
const percent = poll.get('votes_count') === 0 ? 0 : (option.get('votes_count') / poll.get('votes_count')) * 100;
const leading = poll.get('options').filterNot(other => other.get('title') === option.get('title')).every(other => option.get('votes_count') > other.get('votes_count'));
const active = !!this.state.selected[`${optionIndex}`];
const showResults = poll.get('voted') || poll.get('expired');
const multiple = poll.get('multiple');
let titleEmojified = option.get('title_emojified');
let titleEmojified = option.get('title_emojified')
if (!titleEmojified) {
const emojiMap = makeEmojiMap(poll);
titleEmojified = emojify(escapeTextContentForBrowser(option.get('title')), emojiMap);
const emojiMap = makeEmojiMap(poll)
titleEmojified = emojify(escapeTextContentForBrowser(option.get('title')), emojiMap)
}
const chartClasses = classNames('poll__chart', {
'poll__chart--leading': leading,
});
const chartClasses = cx({
default: 1,
positionAbsolute: 1,
top0: 1,
left0: 1,
radiusSmall: 1,
height100PC: 1,
backgroundSubtle2: !leading,
backgroundColorBrandLight: leading,
})
const textClasses = classNames('poll__text', {
selectable: !showResults,
});
const inputClasses = classNames('poll__input', {
const inputClasses = cx('poll__input', {
'poll__input--checkbox': multiple,
'poll__input--active': active,
});
})
const listItemClasses = cx({
default: 1,
flexRow: 1,
paddingVertical10PX: showResults,
marginBottom10PX: 1,
border1PX: !showResults,
borderColorSecondary: !showResults,
circle: !showResults,
cursorPointer: !showResults,
backgroundSubtle_onHover: !showResults,
backgroundSubtle: !showResults && active,
})
const textContainerClasses = cx({
default: 1,
width100PC: 1,
paddingHorizontal15PX: 1,
paddingVertical10PX: !showResults,
cursorPointer: !showResults,
alignItemsCenter: !showResults,
})
return (
<li className='poll-item' key={option.get('title')}>
<li className={listItemClasses} key={option.get('title')}>
{
showResults && (
<Motion defaultStyle={{ width: 0 }} style={{ width: spring(percent, { stiffness: 180, damping: 12 }) }}>
@@ -110,72 +135,99 @@ class Poll extends ImmutablePureComponent {
)
}
<label className={textClasses}>
<input
name='vote-options'
type={multiple ? 'checkbox' : 'radio'}
value={optionIndex}
checked={active}
onChange={this.handleOptionChange}
disabled={disabled}
/>
<label className={textContainerClasses}>
<Text
size='medium'
color='primary'
weight={leading ? 'bold' : 'normal'}
className={[_s.displayFlex, _s.flexRow, _s.width100PC, _s.alignItemsCenter].join(' ')}
>
{
!showResults &&
<input
name='vote-options'
type={multiple ? 'checkbox' : 'radio'}
value={optionIndex}
checked={active}
onChange={this.handleOptionChange}
disabled={disabled}
className={[_s.default, _s.marginRight10PX].join(' ')}
/>
}
{!showResults && <span className={inputClasses} />}
{showResults && <span className='poll-item__number'>{Math.round(percent)}%</span>}
{
!showResults && <span className={inputClasses} />
}
<span className='poll-item__text' dangerouslySetInnerHTML={{ __html: titleEmojified }} />
<span dangerouslySetInnerHTML={{ __html: titleEmojified }} />
{
showResults &&
<span className={_s.marginLeftAuto}>
{Math.round(percent)}%
</span>
}
</Text>
</label>
</li>
);
)
}
render () {
const { poll, intl } = this.props;
render() {
const { poll, intl } = this.props
if (!poll) return null;
if (!poll) return null
const timeRemaining = poll.get('expired') ?
intl.formatMessage(messages.closed)
: <RelativeTimestamp timestamp={poll.get('expires_at')} futureDate />;
const showResults = poll.get('voted') || poll.get('expired');
const disabled = this.props.disabled || Object.entries(this.state.selected).every(item => !item);
: <RelativeTimestamp timestamp={poll.get('expires_at')} futureDate />
const showResults = poll.get('voted') || poll.get('expired')
const disabled = this.props.disabled || Object.entries(this.state.selected).every(item => !item)
return (
<div className='poll'>
<ul className='poll__list'>
{poll.get('options').map((option, i) => this.renderOption(option, i))}
<div className={[_s.default, _s.paddingHorizontal15PX, _s.paddingVertical10PX].join(' ')}>
<ul className={[_s.default, _s.listStyleNone].join(' ')}>
{
poll.get('options').map((option, i) => this.renderOption(option, i))
}
</ul>
<div className='poll__footer'>
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter].join(' ')}>
{
!showResults &&
<Button className='poll__button' disabled={disabled} onClick={this.handleVote} secondary>
{intl.formatMessage(messages.vote)}
<Button
narrow
className={_s.marginRight10PX}
disabled={disabled}
onClick={this.handleVote}
>
<Text color='inherit' size='small' className={_s.paddingHorizontal10PX}>
{intl.formatMessage(messages.vote)}
</Text>
</Button>
}
{
showResults && !this.props.disabled &&
<span>
<button className='poll__link' onClick={this.handleRefresh}>
{intl.formatMessage(messages.refresh)}
</button>
&nbsp;·&nbsp;
</span>
}
<FormattedMessage
id='poll.total_votes'
defaultMessage='{count, plural, one {# vote} other {# votes}}'
values={{
count: poll.get('votes_count'),
}}
/>
{
poll.get('expires_at') &&
<span> · {timeRemaining}</span>
}
<Text color='secondary'>
<FormattedMessage
id='poll.total_votes'
defaultMessage='{count, plural, one {# vote} other {# votes}}'
values={{
count: poll.get('votes_count'),
}}
/>
{
poll.get('expires_at') &&
<Fragment>
<DotTextSeperator />
<Text color='secondary' className={_s.marginLeft5PX}>
{timeRemaining}
</Text>
</Fragment>
}
</Text>
</div>
</div>
);
)
}
}

View File

@@ -0,0 +1,41 @@
import ImmutablePropTypes from 'react-immutable-proptypes'
import ImmutablePureComponent from 'react-immutable-pure-component'
export default class Select extends ImmutablePureComponent {
static propTypes = {
options: PropTypes.oneOf([
ImmutablePropTypes.map,
PropTypes.object,
]),
value: PropTypes.string,
onChange: PropTypes.func,
}
render() {
const {
value,
options,
onChange
} = this.props
return (
<div className={_s.default}>
<select
className={[_s.default, _s.outlineNone, _s.text, _s.border1PX, _s.borderColorSecondary, _s.paddingHorizontal15PX, _s.select].join(' ')}
value={value}
onChange={onChange}
>
{
options.map(option => (
<option key={`option-${option.value}`} value={option.value}>
{option.title}
</option>
))
}
</select>
</div>
)
}
}

View File

@@ -165,8 +165,8 @@ class Status extends ImmutablePureComponent {
}
handleToggleMediaVisibility = () => {
this.setState({ showMedia: !this.state.showMedia });
};
this.setState({ showMedia: !this.state.showMedia })
}
handleClick = () => {
if (this.props.onClick) {
@@ -178,8 +178,8 @@ class Status extends ImmutablePureComponent {
this.context.router.history.push(
`/${this._properStatus().getIn(['account', 'acct'])}/posts/${this._properStatus().get('id')}`
);
};
)
}
handleExpandClick = e => {
if (e.button === 0) {
@@ -271,10 +271,18 @@ class Status extends ImmutablePureComponent {
}
render() {
let media = null;
let prepend, rebloggedByText, reblogContent;
const {
intl,
hidden,
featured,
unread,
showThread,
group,
promoted
} = this.props
const { intl, hidden, featured, unread, showThread, group, promoted } = this.props;
let media = null
let prepend, rebloggedByText, reblogContent
// console.log("replies:", this.props.replies)
@@ -321,7 +329,7 @@ class Status extends ImmutablePureComponent {
prepend = (
<div className={[_s.default, _s.flexRow, _s.alignItemsCenter, _s.borderBottom1PX, _s.borderColorSecondary, _s.paddingVertical5PX, _s.paddingHorizontal15PX].join(' ')}>
<Icon
id='star'
id='pin'
width='10px'
height='10px'
className={_s.fillColorSecondary}
@@ -460,12 +468,7 @@ class Status extends ImmutablePureComponent {
{prepend}
<div
className={classNames('status', `status-${status.get('visibility')}`, {
muted: this.props.muted,
})}
data-id={status.get('id')}
>
<div data-id={status.get('id')}>
<StatusHeader status={status} />
@@ -493,6 +496,7 @@ class Status extends ImmutablePureComponent {
) */ }
<StatusActionBar status={status} account={account} {...other} />
{ /* : todo : comment bar, comments */ }
</div>
</Block>
</div>

View File

@@ -5,7 +5,7 @@ import classNames from 'classnames/bind'
import { openModal } from '../actions/modal'
import { me, isStaff } from '../initial_state'
import ComposeFormContainer from '../features/compose/containers/compose_form_container'
import Icon from './icon'
import Text from './text'
import StatusActionBarItem from './status_action_bar_item'
const messages = defineMessages({
@@ -117,6 +117,10 @@ class StatusActionBar extends ImmutablePureComponent {
}
}
handleShareClick = () => {
//
}
render() {
const { status, intl: { formatMessage } } = this.props
@@ -135,34 +139,6 @@ class StatusActionBar extends ImmutablePureComponent {
<IconButton className='status-action-bar-button' title={formatMessage(messages.share)} icon='share-alt' onClick={this.handleShareClick} />
)
const items = [
{
title: formatMessage(messages.like),
icon: 'like',
active: !!status.get('favorited'),
onClick: this.handleFavoriteClick,
},
{
title: formatMessage(messages.comment),
icon: 'comment',
active: false,
onClick: this.handleReplyClick,
},
{
title: repostTitle,
icon: (status.get('visibility') === 'private') ? 'lock' : 'repost',
disabled: !publicStatus,
active: !!status.get('reblogged'),
onClick: this.handleRepostClick,
},
{
title: formatMessage(messages.share),
icon: 'share',
active: false,
onClick: this.handleFavoriteClick,
},
]
const hasInteractions = favoriteCount > 0 || replyCount > 0 || repostCount > 0
const shouldCondense = (!!status.get('card') || status.get('media_attachments').size > 0) && !hasInteractions
@@ -186,9 +162,7 @@ class StatusActionBar extends ImmutablePureComponent {
const interactionBtnClasses = cx({
default: 1,
text: 1,
colorSecondary: 1,
cursorPointer: 1,
fontSize15PX: 1,
fontWeightNormal: 1,
marginRight10PX: 1,
paddingVertical5PX: 1,
@@ -199,37 +173,64 @@ class StatusActionBar extends ImmutablePureComponent {
{
hasInteractions &&
<div className={[_s.default, _s.flexRow, _s.paddingHorizontal5PX].join(' ')}>
{favoriteCount > 0 &&
{
favoriteCount > 0 &&
<button className={interactionBtnClasses}>
{favoriteCount}
&nbsp;Likes
<Text color='secondary'>
{favoriteCount}
&nbsp;Likes
</Text>
</button>
}
{replyCount > 0 &&
{
replyCount > 0 &&
<button className={interactionBtnClasses}>
{replyCount}
&nbsp;Comments
<Text color='secondary'>
{replyCount}
&nbsp;Comments
</Text>
</button>
}
{repostCount > 0 &&
{
repostCount > 0 &&
<button className={interactionBtnClasses}>
{repostCount}
&nbsp;Reposts
<Text color='secondary'>
{repostCount}
&nbsp;Reposts
</Text>
</button>
}
</div>
}
<div className={innerContainerClasses}>
<div className={[_s.default, _s.flexRow, _s.paddingVertical2PX, _s.width100PC].join(' ')}>
{
items.map((item, i) => (
<StatusActionBarItem key={`status-action-bar-item-${i}`} {...item} />
))
}
<StatusActionBarItem
title={formatMessage(messages.like)}
icon='like'
active={!!status.get('favorited')}
onClick={this.handleFavoriteClick}
/>
<StatusActionBarItem
title={formatMessage(messages.comment)}
icon='comment'
onClick={this.handleReplyClick}
/>
<StatusActionBarItem
title={repostTitle}
icon={(status.get('visibility') === 'private') ? 'lock' : 'repost'}
disabled={!publicStatus}
active={!!status.get('reblogged')}
onClick={this.handleRepostClick}
/>
<StatusActionBarItem
title={formatMessage(messages.share)}
icon='share'
onClick={this.handleShareClick}
/>
</div>
</div>
<div className={[_s.default, _s.borderTop1PX, _s.borderColorSecondary, _s.paddingTop10PX, _s.marginBottom10PX].join(' ')}>
{ /* <ComposeFormContainer statusId={status.get('id')} shouldCondense /> */ }
{ /* <ComposeFormContainer statusId={status.get('id')} shouldCondense /> */}
</div>
</div>
)

View File

@@ -29,7 +29,7 @@ export default class StatusActionBarItem extends PureComponent {
paddingHorizontal10PX: 1,
width100PC: 1,
radiusSmall: 1,
outlineFocusBrand: 1,
outlineNone: 1,
backgroundTransparent: 1,
backgroundSubtle_onHover: 1,
colorSecondary: 1,

View File

@@ -1,11 +1,14 @@
import ImmutablePureComponent from 'react-immutable-pure-component'
import ImmutablePropTypes from 'react-immutable-proptypes'
import { injectIntl, defineMessages } from 'react-intl'
import classNames from 'classnames/bind'
import { me } from '../initial_state'
import ComposeFormContainer from '../features/compose/containers/compose_form_container'
import Block from './block'
import Heading from './heading'
const cx = classNames.bind(_s)
const messages = defineMessages({
createPost: { id: 'column_header.create_post', defaultMessage: 'Create Post' },
})
@@ -25,6 +28,7 @@ class TimelineComposeBlock extends ImmutablePureComponent {
intl: PropTypes.object.isRequired,
account: ImmutablePropTypes.map.isRequired,
size: PropTypes.number,
modal: PropTypes.bool,
}
static defaultProps = {
@@ -32,7 +36,23 @@ class TimelineComposeBlock extends ImmutablePureComponent {
}
render() {
const { account, size, intl, ...rest } = this.props
const {
account,
size,
intl,
modal,
...rest
} = this.props
if (modal) {
return (
<section className={_s.default}>
<div className={[_s.default, _s.flexRow].join(' ')}>
<ComposeFormContainer {...rest} />
</div>
</section>
)
}
return (
<section className={[_s.default, _s.marginBottom15PX].join(' ')}>
@@ -42,7 +62,7 @@ class TimelineComposeBlock extends ImmutablePureComponent {
{intl.formatMessage(messages.createPost)}
</Heading>
</div>
<div className={[_s.default, _s.flexRow, _s.paddingVertical15PX, _s.paddingHorizontal15PX].join(' ')}>
<div className={[_s.default, _s.flexRow, _s.paddingHorizontal15PX, _s.paddingVertical15PX].join(' ')}>
<ComposeFormContainer {...rest} />
</div>
</Block>