Skip to content

Commit

Permalink
Add emoji overlay to avatars in reaction list
Browse files Browse the repository at this point in the history
I swear there has to be a better way to do this
  • Loading branch information
TheEssem committed Feb 8, 2024
1 parent 73172ec commit 04ddcc4
Show file tree
Hide file tree
Showing 9 changed files with 138 additions and 42 deletions.
12 changes: 6 additions & 6 deletions app/javascript/flavours/glitch/actions/interactions.js
Original file line number Diff line number Diff line change
Expand Up @@ -303,7 +303,7 @@ export function fetchReactions(id) {
const next = getLinks(response).refs.find(link => link.rel === 'next');
const accounts = response.data.map(item => item.account);
dispatch(importFetchedAccounts(accounts));
dispatch(fetchReactionsSuccess(id, accounts, next ? next.uri : null));
dispatch(fetchReactionsSuccess(id, response.data, next ? next.uri : null));
dispatch(fetchRelationships(accounts.map(item => item.id)));
}).catch(error => {
dispatch(fetchReactionsFail(id, error));
Expand All @@ -318,11 +318,11 @@ export function fetchReactionsRequest(id) {
};
}

export function fetchReactionsSuccess(id, accounts, next) {
export function fetchReactionsSuccess(id, reactions, next) {
return {
type: REACTIONS_FETCH_SUCCESS,
id,
accounts,
reactions,
next,
};
}
Expand All @@ -349,7 +349,7 @@ export function expandReactions(id) {
const accounts = response.data.map(item => item.account);

dispatch(importFetchedAccounts(accounts));
dispatch(expandReactionsSuccess(id, accounts, next ? next.uri : null));
dispatch(expandReactionsSuccess(id, response.data, next ? next.uri : null));
dispatch(fetchRelationships(accounts.map(item => item.id)));
}).catch(error => dispatch(expandReactionsFail(id, error)));
};
Expand All @@ -362,11 +362,11 @@ export function expandReactionsRequest(id) {
};
}

export function expandReactionsSuccess(id, accounts, next) {
export function expandReactionsSuccess(id, reactions, next) {
return {
type: REACTIONS_EXPAND_SUCCESS,
id,
accounts,
reactions,
next,
};
}
Expand Down
5 changes: 5 additions & 0 deletions app/javascript/flavours/glitch/api_types/reaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
export interface ApiStatusReactionJSON {
name: string;
static_url?: string | undefined;
url?: string | undefined;
}
14 changes: 12 additions & 2 deletions app/javascript/flavours/glitch/components/account.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -14,6 +14,7 @@ import { VerifiedBadge } from 'flavours/glitch/components/verified_badge';
import { me } from '../initial_state';

import { Avatar } from './avatar';
import { AvatarOverlay } from './avatar_overlay';
import { Button } from './button';
import { FollowersCounter } from './counters';
import { DisplayName } from './display_name';
Expand Down Expand Up @@ -42,6 +43,7 @@ class Account extends ImmutablePureComponent {
onMute: PropTypes.func.isRequired,
onMuteNotifications: PropTypes.func.isRequired,
intl: PropTypes.object.isRequired,
overlayEmoji: PropTypes.object,
hidden: PropTypes.bool,
minimal: PropTypes.bool,
defaultAction: PropTypes.string,
Expand All @@ -50,6 +52,7 @@ class Account extends ImmutablePureComponent {

static defaultProps = {
size: 46,
overlayEmoji: { name: null }
};

handleFollow = () => {
Expand All @@ -73,7 +76,7 @@ class Account extends ImmutablePureComponent {
};

render () {
const { account, intl, hidden, withBio, defaultAction, size, minimal } = this.props;
const { account, intl, hidden, withBio, defaultAction, overlayEmoji, size, minimal } = this.props;

if (!account) {
return <EmptyAccount size={size} minimal={minimal} />;
Expand Down Expand Up @@ -138,12 +141,19 @@ class Account extends ImmutablePureComponent {
verification = <VerifiedBadge link={firstVerifiedField.get('value')} />;
}

let statusAvatar;
if (!overlayEmoji.name) {
statusAvatar = <Avatar account={account} size={size} />;
} else {
statusAvatar = <AvatarOverlay account={account} emoji={overlayEmoji} baseSize={size} />;
}

return (
<div className={classNames('account', { 'account--minimal': minimal })}>
<div className='account__wrapper'>
<Permalink key={account.get('id')} className='account__display-name' title={account.get('acct')} href={account.get('url')} to={`/@${account.get('acct')}`}>
<div className='account__avatar-wrapper'>
<Avatar account={account} size={size} />
{statusAvatar}
</div>

<div className='account__contents'>
Expand Down
43 changes: 33 additions & 10 deletions app/javascript/flavours/glitch/components/avatar_overlay.tsx
Original file line number Diff line number Diff line change
@@ -1,11 +1,15 @@
import type { Account } from 'flavours/glitch/models/account';
import type { StatusReaction } from 'flavours/glitch/models/reaction';

import { useHovering } from '../hooks/useHovering';
import { autoPlayGif } from '../initial_state';

import { Emoji } from './status_reactions';

interface Props {
account: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
friend: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
friend?: Account | undefined; // FIXME: remove `undefined` once we know for sure its always there
emoji?: StatusReaction | undefined;
size?: number;
baseSize?: number;
overlaySize?: number;
Expand All @@ -14,6 +18,7 @@ interface Props {
export const AvatarOverlay: React.FC<Props> = ({
account,
friend,
emoji,
size = 46,
baseSize = 36,
overlaySize = 24,
Expand All @@ -27,6 +32,32 @@ export const AvatarOverlay: React.FC<Props> = ({
? friend?.get('avatar')
: friend?.get('avatar_static');

let overlayElement;
if (friendSrc) {
overlayElement = (
<div
className='account__avatar'
style={{ width: `${overlaySize}px`, height: `${overlaySize}px` }}
data-avatar-of={`@${friend?.get('acct')}`}
>
{friendSrc && <img src={friendSrc} alt={friend?.get('acct')} />}
</div>
);
} else {
overlayElement = (
<div className='account__emoji' data-emoji-name={emoji?.name}>
{emoji && (
<Emoji
emoji={emoji.name}
hovered={hovering}
url={emoji.url}
staticUrl={emoji.static_url}
/>
)}
</div>
);
}

return (
<div
className='account__avatar-overlay'
Expand All @@ -43,15 +74,7 @@ export const AvatarOverlay: React.FC<Props> = ({
{accountSrc && <img src={accountSrc} alt={account?.get('acct')} />}
</div>
</div>
<div className='account__avatar-overlay-overlay'>
<div
className='account__avatar'
style={{ width: `${overlaySize}px`, height: `${overlaySize}px` }}
data-avatar-of={`@${friend?.get('acct')}`}
>
{friendSrc && <img src={friendSrc} alt={friend?.get('acct')} />}
</div>
</div>
<div className='account__avatar-overlay-overlay'>{overlayElement}</div>
</div>
);
};
17 changes: 10 additions & 7 deletions app/javascript/flavours/glitch/features/reactions/index.jsx
Original file line number Diff line number Diff line change
Expand Up @@ -27,17 +27,17 @@ const messages = defineMessages({
});

const mapStateToProps = (state, props) => ({
accountIds: state.getIn(['user_lists', 'reactions', props.params.statusId, 'items']),
hasMore: !!state.getIn(['user_lists', 'reactions', props.params.statusId, 'next']),
isLoading: state.getIn(['user_lists', 'reactions', props.params.statusId, 'isLoading'], true),
reactions: state.getIn(['status_reactions', 'reactions', props.params.statusId, 'items']),
hasMore: !!state.getIn(['status_reactions', 'reactions', props.params.statusId, 'next']),
isLoading: state.getIn(['status_reactions', 'reactions', props.params.statusId, 'isLoading'], true),
});

class Reactions extends ImmutablePureComponent {

static propTypes = {
params: PropTypes.object.isRequired,
dispatch: PropTypes.func.isRequired,
accountIds: ImmutablePropTypes.list,
reactions: ImmutablePropTypes.orderedSet,
hasMore: PropTypes.bool,
isLoading: PropTypes.bool,
multiColumn: PropTypes.bool,
Expand Down Expand Up @@ -67,16 +67,19 @@ class Reactions extends ImmutablePureComponent {
}, 300, { leading: true });

render () {
const { intl, accountIds, hasMore, isLoading, multiColumn } = this.props;
const { intl, reactions, hasMore, isLoading, multiColumn } = this.props;

if (!accountIds) {
if (!reactions) {
return (
<Column>
<LoadingIndicator />
</Column>
);
}

const accountIds = reactions.map(v => v.account);
const reactionsByAccount = new Map(reactions.map(v => [v.account, v]));

const emptyMessage = <FormattedMessage id='status.reactions.empty' defaultMessage='No one has reacted to this post yet. When someone does, they will show up here.' />;

return (
Expand All @@ -102,7 +105,7 @@ class Reactions extends ImmutablePureComponent {
bindToDocument={!multiColumn}
>
{accountIds.map(id =>
<AccountContainer key={id} id={id} withNote={false} />,
<AccountContainer key={id} id={id} withNote={false} overlayEmoji={reactionsByAccount.get(id)} />,
)}
</ScrollableList>

Expand Down
13 changes: 13 additions & 0 deletions app/javascript/flavours/glitch/models/reaction.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import type { RecordOf } from 'immutable';
import { Record } from 'immutable';

import type { ApiStatusReactionJSON } from 'flavours/glitch/api_types/reaction';

type StatusReactionShape = Required<ApiStatusReactionJSON>;
export type StatusReaction = RecordOf<StatusReactionShape>;

export const CustomEmojiFactory = Record<StatusReactionShape>({
name: '',
static_url: '',
url: '',
});
2 changes: 2 additions & 0 deletions app/javascript/flavours/glitch/reducers/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,6 +38,7 @@ import search from './search';
import server from './server';
import settings from './settings';
import status_lists from './status_lists';
import status_reactions from './status_reactions';
import statuses from './statuses';
import suggestions from './suggestions';
import tags from './tags';
Expand Down Expand Up @@ -88,6 +89,7 @@ const reducers = {
history,
tags,
followed_tags,
status_reactions,
};

// We want the root state to be an ImmutableRecord, which is an object with a defined list of keys,
Expand Down
57 changes: 57 additions & 0 deletions app/javascript/flavours/glitch/reducers/status_reactions.js
Original file line number Diff line number Diff line change
@@ -0,0 +1,57 @@
import { Map as ImmutableMap, OrderedSet as ImmutableOrderedSet } from 'immutable';

import {
REACTIONS_FETCH_SUCCESS,
REACTIONS_EXPAND_SUCCESS,
REACTIONS_FETCH_REQUEST,
REACTIONS_EXPAND_REQUEST,
REACTIONS_FETCH_FAIL,
REACTIONS_EXPAND_FAIL,
} from '../actions/interactions';

const initialState = ImmutableMap({
reactions: ImmutableMap({
next: null,
isLoading: false,
items: ImmutableOrderedSet(),
}),
});

const normalizeList = (state, path, reactions, next) => {
const filteredReactions = reactions.map(v => {
v.account = v.account.id;
return v;
});
return state.setIn(path, ImmutableMap({
next,
items: ImmutableOrderedSet(filteredReactions),
isLoading: false,
}));
};

const appendToList = (state, path, reactions, next) => {
const filteredReactions = reactions.map(v => {
v.account = v.account.id;
return v;
});
return state.updateIn(path, map => {
return map.set('next', next).set('isLoading', false).update('items', list => list.concat(filteredReactions));
});
};

export default function statusReactions(state = initialState, action) {
switch(action.type) {
case REACTIONS_FETCH_SUCCESS:
return normalizeList(state, ['reactions', action.id], action.reactions, action.next);
case REACTIONS_EXPAND_SUCCESS:
return appendToList(state, ['reactions', action.id], action.reactions, action.next);
case REACTIONS_FETCH_REQUEST:
case REACTIONS_EXPAND_REQUEST:
return state.setIn(['reactions', action.id, 'isLoading'], true);
case REACTIONS_FETCH_FAIL:
case REACTIONS_EXPAND_FAIL:
return state.setIn(['reactions', action.id, 'isLoading'], false);
default:
return state;
}
}
17 changes: 0 additions & 17 deletions app/javascript/flavours/glitch/reducers/user_lists.js
Original file line number Diff line number Diff line change
Expand Up @@ -57,12 +57,6 @@ import {
FAVOURITES_EXPAND_REQUEST,
FAVOURITES_EXPAND_SUCCESS,
FAVOURITES_EXPAND_FAIL,
REACTIONS_FETCH_SUCCESS,
REACTIONS_EXPAND_SUCCESS,
REACTIONS_FETCH_REQUEST,
REACTIONS_EXPAND_REQUEST,
REACTIONS_FETCH_FAIL,
REACTIONS_EXPAND_FAIL,
} from '../actions/interactions';
import {
MUTES_FETCH_REQUEST,
Expand All @@ -83,7 +77,6 @@ const initialListState = ImmutableMap({
const initialState = ImmutableMap({
followers: initialListState,
following: initialListState,
reactions: initialListState,
reblogged_by: initialListState,
favourited_by: initialListState,
follow_requests: initialListState,
Expand Down Expand Up @@ -146,16 +139,6 @@ export default function userLists(state = initialState, action) {
case FOLLOWING_FETCH_FAIL:
case FOLLOWING_EXPAND_FAIL:
return state.setIn(['following', action.id, 'isLoading'], false);
case REACTIONS_FETCH_SUCCESS:
return normalizeList(state, ['reactions', action.id], action.accounts, action.next);
case REACTIONS_EXPAND_SUCCESS:
return appendToList(state, ['reactions', action.id], action.accounts, action.next);
case REACTIONS_FETCH_REQUEST:
case REACTIONS_EXPAND_REQUEST:
return state.setIn(['reactions', action.id, 'isLoading'], true);
case REACTIONS_FETCH_FAIL:
case REACTIONS_EXPAND_FAIL:
return state.setIn(['reactions', action.id, 'isLoading'], false);
case REBLOGS_FETCH_SUCCESS:
return normalizeList(state, ['reblogged_by', action.id], action.accounts, action.next);
case REBLOGS_EXPAND_SUCCESS:
Expand Down

0 comments on commit 04ddcc4

Please sign in to comment.