1 import React
from 'react';
2 import PropTypes
from 'prop-types';
3 import ImmutablePropTypes
from 'react-immutable-proptypes';
4 import ImmutablePureComponent
from 'react-immutable-pure-component';
5 import { defineMessages
, injectIntl
, FormattedMessage
} from 'react-intl';
6 import classNames
from 'classnames';
7 import { vote
, fetchPoll
} from 'mastodon/actions/polls';
8 import Motion
from 'mastodon/features/ui/util/optional_motion';
9 import spring
from 'react-motion/lib/spring';
10 import escapeTextContentForBrowser
from 'escape-html';
11 import emojify
from 'mastodon/features/emoji/emoji';
12 import RelativeTimestamp
from './relative_timestamp';
14 const messages
= defineMessages({
15 closed: { id: 'poll.closed', defaultMessage: 'Closed' },
18 const makeEmojiMap
= record
=> record
.get('emojis').reduce((obj
, emoji
) => {
19 obj
[`:${emoji.get('shortcode')}:`] = emoji
.toJS();
23 export default @injectIntl
24 class Poll
extends ImmutablePureComponent
{
27 poll: ImmutablePropTypes
.map
,
28 intl: PropTypes
.object
.isRequired
,
29 dispatch: PropTypes
.func
,
30 disabled: PropTypes
.bool
,
37 handleOptionChange
= e
=> {
38 const { target: { value
} } = e
;
40 if (this.props
.poll
.get('multiple')) {
41 const tmp
= { ...this.state
.selected
};
47 this.setState({ selected: tmp
});
51 this.setState({ selected: tmp
});
56 if (this.props
.disabled
) {
60 this.props
.dispatch(vote(this.props
.poll
.get('id'), Object
.keys(this.state
.selected
)));
63 handleRefresh
= () => {
64 if (this.props
.disabled
) {
68 this.props
.dispatch(fetchPoll(this.props
.poll
.get('id')));
71 renderOption (option
, optionIndex
) {
72 const { poll
, disabled
} = this.props
;
73 const percent
= poll
.get('votes_count') === 0 ? 0 : (option
.get('votes_count') / poll
.get('votes_count')) * 100;
74 const leading
= poll
.get('options').filterNot(other
=> other
.get('title') === option
.get('title')).every(other
=> option
.get('votes_count') > other
.get('votes_count'));
75 const active
= !!this.state
.selected
[`${optionIndex}`];
76 const showResults
= poll
.get('voted') || poll
.get('expired');
78 let titleEmojified
= option
.get('title_emojified');
79 if (!titleEmojified
) {
80 const emojiMap
= makeEmojiMap(poll
);
81 titleEmojified
= emojify(escapeTextContentForBrowser(option
.get('title')), emojiMap
);
85 <li key
={option
.get('title')}>
87 <Motion defaultStyle
={{ width: 0 }} style
={{ width: spring(percent
, { stiffness: 180, damping: 12 }) }}>
89 <span className
={classNames('poll__chart', { leading
})} style
={{ width: `${width}%` }} />
94 <label className
={classNames('poll__text', { selectable: !showResults
})}>
97 type
={poll
.get('multiple') ? 'checkbox' : 'radio'}
100 onChange
={this.handleOptionChange
}
104 {!showResults
&& <span className
={classNames('poll__input', { checkbox: poll
.get('multiple'), active
})} />}
105 {showResults
&& <span className
='poll__number'>{Math
.round(percent
)}%</span
>}
107 <span dangerouslySetInnerHTML
={{ __html: titleEmojified
}} />
114 const { poll
, intl
} = this.props
;
120 const timeRemaining
= poll
.get('expired') ? intl
.formatMessage(messages
.closed
) : <RelativeTimestamp timestamp
={poll
.get('expires_at')} futureDate
/>;
121 const showResults
= poll
.get('voted') || poll
.get('expired');
122 const disabled
= this.props
.disabled
|| Object
.entries(this.state
.selected
).every(item
=> !item
);
125 <div className
='poll'>
127 {poll
.get('options').map((option
, i
) => this.renderOption(option
, i
))}
130 <div className
='poll__footer'>
131 {!showResults
&& <button className
='button button-secondary' disabled
={disabled
} onClick
={this.handleVote
}><FormattedMessage id
='poll.vote' defaultMessage
='Vote' /></button
>}
132 {showResults
&& !this.props
.disabled
&& <span
><button className
='poll__link' onClick
={this.handleRefresh
}><FormattedMessage id
='poll.refresh' defaultMessage
='Refresh' /></button
> · </span
>}
133 <FormattedMessage id
='poll.total_votes' defaultMessage
='{count, plural, one {# vote} other {# votes}}' values
={{ count: poll
.get('votes_count') }} />
134 {poll
.get('expires_at') && <span
> · {timeRemaining
}</span
>}