Change to not use the me attribute in emoji reactions

This commit is contained in:
noellabo 2021-09-06 23:37:35 +09:00
parent 1c5d0b8ce1
commit 056f24fdc8
4 changed files with 28 additions and 18 deletions

View file

@ -1,5 +1,6 @@
import api from '../api'; import api from '../api';
import { importFetchedAccounts, importFetchedStatus } from './importer'; import { importFetchedAccounts, importFetchedStatus } from './importer';
import { me } from '../initial_state';
export const REBLOG_REQUEST = 'REBLOG_REQUEST'; export const REBLOG_REQUEST = 'REBLOG_REQUEST';
export const REBLOG_SUCCESS = 'REBLOG_SUCCESS'; export const REBLOG_SUCCESS = 'REBLOG_SUCCESS';
@ -552,7 +553,7 @@ export function emojiReactionFail(status, name, domain, url, static_url, error)
}; };
const findMyEmojiReaction = (status) => { const findMyEmojiReaction = (status) => {
return status.get('emoji_reactions').find(emoji_reaction => emoji_reaction.get('me') === true); return status.get('emoji_reactioned') && status.get('emoji_reactions').find(emoji_reaction => emoji_reaction.get('account_ids').includes(me));
}; };
export function removeEmojiReaction(status) { export function removeEmojiReaction(status) {

View file

@ -7,7 +7,7 @@ import Emoji from './emoji';
import unicodeMapping from 'mastodon/features/emoji/emoji_unicode_mapping_light'; import unicodeMapping from 'mastodon/features/emoji/emoji_unicode_mapping_light';
import TransitionMotion from 'react-motion/lib/TransitionMotion'; import TransitionMotion from 'react-motion/lib/TransitionMotion';
import AnimatedNumber from 'mastodon/components/animated_number'; import AnimatedNumber from 'mastodon/components/animated_number';
import { reduceMotion } from 'mastodon/initial_state'; import { reduceMotion, me } from 'mastodon/initial_state';
import spring from 'react-motion/lib/spring'; import spring from 'react-motion/lib/spring';
class EmojiReaction extends ImmutablePureComponent { class EmojiReaction extends ImmutablePureComponent {
@ -15,6 +15,7 @@ class EmojiReaction extends ImmutablePureComponent {
static propTypes = { static propTypes = {
status: ImmutablePropTypes.map.isRequired, status: ImmutablePropTypes.map.isRequired,
emojiReaction: ImmutablePropTypes.map.isRequired, emojiReaction: ImmutablePropTypes.map.isRequired,
myReaction: PropTypes.bool.isRequired,
addEmojiReaction: PropTypes.func.isRequired, addEmojiReaction: PropTypes.func.isRequired,
removeEmojiReaction: PropTypes.func.isRequired, removeEmojiReaction: PropTypes.func.isRequired,
emojiMap: ImmutablePropTypes.map.isRequired, emojiMap: ImmutablePropTypes.map.isRequired,
@ -26,9 +27,9 @@ class EmojiReaction extends ImmutablePureComponent {
}; };
handleClick = () => { handleClick = () => {
const { emojiReaction, status, addEmojiReaction, removeEmojiReaction } = this.props; const { emojiReaction, status, addEmojiReaction, removeEmojiReaction, myReaction } = this.props;
if (emojiReaction.get('me')) { if (myReaction) {
removeEmojiReaction(status); removeEmojiReaction(status);
} else { } else {
addEmojiReaction(status, emojiReaction.get('name'), emojiReaction.get('domain', null), emojiReaction.get('url', null), emojiReaction.get('static_url', null)); addEmojiReaction(status, emojiReaction.get('name'), emojiReaction.get('domain', null), emojiReaction.get('url', null), emojiReaction.get('static_url', null));
@ -40,7 +41,7 @@ class EmojiReaction extends ImmutablePureComponent {
handleMouseLeave = () => this.setState({ hovered: false }) handleMouseLeave = () => this.setState({ hovered: false })
render () { render () {
const { emojiReaction, status } = this.props; const { emojiReaction, status, myReaction } = this.props;
let shortCode = emojiReaction.get('name'); let shortCode = emojiReaction.get('name');
@ -49,7 +50,7 @@ class EmojiReaction extends ImmutablePureComponent {
} }
return ( return (
<button className={classNames('reactions-bar__item', { active: emojiReaction.get('me') })} disabled={status.get('emoji_reactioned') && !emojiReaction.get('me')} onClick={this.handleClick} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} title={`:${shortCode}:`} style={this.props.style}> <button className={classNames('reactions-bar__item', { active: myReaction })} disabled={status.get('emoji_reactioned') && !myReaction} onClick={this.handleClick} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} title={`:${shortCode}:`} style={this.props.style}>
<span className='reactions-bar__item__emoji'><Emoji hovered={this.state.hovered} emoji={emojiReaction.get('name')} emojiMap={this.props.emojiMap} url={emojiReaction.get('url')} static_url={emojiReaction.get('static_url')} /></span> <span className='reactions-bar__item__emoji'><Emoji hovered={this.state.hovered} emoji={emojiReaction.get('name')} emojiMap={this.props.emojiMap} url={emojiReaction.get('url')} static_url={emojiReaction.get('static_url')} /></span>
<span className='reactions-bar__item__count'><AnimatedNumber value={emojiReaction.get('count')} /></span> <span className='reactions-bar__item__count'><AnimatedNumber value={emojiReaction.get('count')} /></span>
</button> </button>
@ -80,16 +81,23 @@ export default class EmojiReactionsBar extends ImmutablePureComponent {
const emoji_reactions = status.get("emoji_reactions") const emoji_reactions = status.get("emoji_reactions")
const visibleReactions = emoji_reactions.filter(x => x.get('count') > 0); const visibleReactions = emoji_reactions.filter(x => x.get('count') > 0);
const styles = visibleReactions.map(emoji_reaction => ({
key: `${emoji_reaction.get('name')}@${emoji_reaction.get('domain', '')}`,
data: emoji_reaction,
style: { scale: reduceMotion ? 1 : spring(1, { stiffness: 150, damping: 13 }) },
})).toArray();
if (visibleReactions.isEmpty() ) { if (visibleReactions.isEmpty() ) {
return <Fragment></Fragment>; return <Fragment></Fragment>;
} }
const styles = visibleReactions.map(emoji_reaction => {
const domain = emoji_reaction.get('domain', '');
return {
key: `${emoji_reaction.get('name')}${domain ? `@${domain}` : ''}`,
data: {
emojiReaction: emoji_reaction,
myReaction: emoji_reaction.get('account_ids', []).includes(me),
},
style: { scale: reduceMotion ? 1 : spring(1, { stiffness: 150, damping: 13 }) },
};
}).toArray();
return ( return (
<TransitionMotion styles={styles} willEnter={this.willEnter} willLeave={this.willLeave}> <TransitionMotion styles={styles} willEnter={this.willEnter} willLeave={this.willLeave}>
{items => ( {items => (
@ -97,7 +105,8 @@ export default class EmojiReactionsBar extends ImmutablePureComponent {
{items.map(({ key, data, style }) => ( {items.map(({ key, data, style }) => (
<EmojiReaction <EmojiReaction
key={key} key={key}
emojiReaction={data} emojiReaction={data.emojiReaction}
myReaction={data.myReaction}
style={{ transform: `scale(${style.scale})`, position: style.scale < 0.5 ? 'absolute' : 'static' }} style={{ transform: `scale(${style.scale})`, position: style.scale < 0.5 ? 'absolute' : 'static' }}
status={this.props.status} status={this.props.status}
addEmojiReaction={this.props.addEmojiReaction} addEmojiReaction={this.props.addEmojiReaction}
@ -105,7 +114,6 @@ export default class EmojiReactionsBar extends ImmutablePureComponent {
emojiMap={this.props.emojiMap} emojiMap={this.props.emojiMap}
/> />
))} ))}
{visibleReactions.size < 8}
</div> </div>
)} )}
</TransitionMotion> </TransitionMotion>

View file

@ -23,6 +23,7 @@ import {
} from '../actions/statuses'; } from '../actions/statuses';
import { TIMELINE_DELETE } from '../actions/timelines'; import { TIMELINE_DELETE } from '../actions/timelines';
import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer'; import { STATUS_IMPORT, STATUSES_IMPORT } from '../actions/importer';
import { me } from '../initial_state';
import { Map as ImmutableMap, fromJS } from 'immutable'; import { Map as ImmutableMap, fromJS } from 'immutable';
const importStatus = (state, status) => { const importStatus = (state, status) => {
@ -55,15 +56,15 @@ const updateEmojiReaction = (state, id, name, domain, url, static_url, updater)
return emojiReactions.update(idx, emojiReactions => updater(emojiReactions)); return emojiReactions.update(idx, emojiReactions => updater(emojiReactions));
} }
return emojiReactions.push(updater(fromJS({ name, domain, url, static_url, count: 0 }))); return emojiReactions.push(updater(fromJS({ name, domain, url, static_url, count: 0, account_ids: [] })));
}); });
}); });
const updateEmojiReactionCount = (state, emojiReaction) => updateEmojiReaction(state, emojiReaction.status_id, emojiReaction.name, emojiReaction.domain, emojiReaction.url, emojiReaction.static_url, x => x.set('count', emojiReaction.count)); const updateEmojiReactionCount = (state, emojiReaction) => updateEmojiReaction(state, emojiReaction.status_id, emojiReaction.name, emojiReaction.domain, emojiReaction.url, emojiReaction.static_url, x => x.set('count', emojiReaction.count));
const addEmojiReaction = (state, id, name, domain, url, static_url) => updateEmojiReaction(state, id, name, domain, url, static_url, x => x.set('me', true).update('count', y => y + 1)); const addEmojiReaction = (state, id, name, domain, url, static_url) => updateEmojiReaction(state, id, name, domain, url, static_url, x => x.update('count', y => y + 1).update('account_ids', z => z.push(me)));
const removeEmojiReaction = (state, id, name, domain, url, static_url) => updateEmojiReaction(state, id, name, domain, url, static_url, x => x.set('me', false).update('count', y => y - 1)); const removeEmojiReaction = (state, id, name, domain, url, static_url) => updateEmojiReaction(state, id, name, domain, url, static_url, x => x.update('count', y => y - 1).update('account_ids', z => z.deleteIn([me])));
const initialState = ImmutableMap(); const initialState = ImmutableMap();

View file

@ -311,7 +311,7 @@ class Status < ApplicationRecord
def generate_grouped_emoji_reactions def generate_grouped_emoji_reactions
records = emoji_reactions.group(:status_id, :name, :custom_emoji_id).order(Arel.sql('MIN(created_at) ASC')).select('name, custom_emoji_id, count(*) as count, array_agg(account_id::text order by created_at) as account_ids') records = emoji_reactions.group(:status_id, :name, :custom_emoji_id).order(Arel.sql('MIN(created_at) ASC')).select('name, custom_emoji_id, count(*) as count, array_agg(account_id::text order by created_at) as account_ids')
ActiveModelSerializers::SerializableResource.new(records, each_serializer: REST::EmojiReactionSerializer, scope: nil, scope_name: :current_user).to_json Oj.dump(ActiveModelSerializers::SerializableResource.new(records, each_serializer: REST::EmojiReactionSerializer, scope: nil, scope_name: :current_user))
end end
def refresh_grouped_emoji_reactions! def refresh_grouped_emoji_reactions!