From 31406cb0f21233aea78f3be87c303d1266ba9b65 Mon Sep 17 00:00:00 2001 From: Essem Date: Sat, 27 Jan 2024 00:22:11 -0600 Subject: [PATCH] Add emoji overlay to avatars in reaction list I swear there has to be a better way to do this --- .../flavours/glitch/actions/interactions.js | 12 ++-- .../flavours/glitch/api_types/reaction.ts | 5 ++ .../flavours/glitch/components/account.jsx | 14 ++++- .../glitch/components/avatar_overlay.tsx | 43 ++++++++++---- .../glitch/features/reactions/index.jsx | 17 +++--- .../flavours/glitch/models/reaction.ts | 13 +++++ .../flavours/glitch/reducers/index.ts | 2 + .../glitch/reducers/status_reactions.js | 57 +++++++++++++++++++ .../flavours/glitch/reducers/user_lists.js | 17 ------ 9 files changed, 138 insertions(+), 42 deletions(-) create mode 100644 app/javascript/flavours/glitch/api_types/reaction.ts create mode 100644 app/javascript/flavours/glitch/models/reaction.ts create mode 100644 app/javascript/flavours/glitch/reducers/status_reactions.js diff --git a/app/javascript/flavours/glitch/actions/interactions.js b/app/javascript/flavours/glitch/actions/interactions.js index b9713e3215..c837e144c0 100644 --- a/app/javascript/flavours/glitch/actions/interactions.js +++ b/app/javascript/flavours/glitch/actions/interactions.js @@ -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)); @@ -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, }; } @@ -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))); }; @@ -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, }; } diff --git a/app/javascript/flavours/glitch/api_types/reaction.ts b/app/javascript/flavours/glitch/api_types/reaction.ts new file mode 100644 index 0000000000..c644246ef1 --- /dev/null +++ b/app/javascript/flavours/glitch/api_types/reaction.ts @@ -0,0 +1,5 @@ +export interface ApiStatusReactionJSON { + name: string; + static_url?: string | undefined; + url?: string | undefined; +} diff --git a/app/javascript/flavours/glitch/components/account.jsx b/app/javascript/flavours/glitch/components/account.jsx index 7e5209653e..f1be32eba0 100644 --- a/app/javascript/flavours/glitch/components/account.jsx +++ b/app/javascript/flavours/glitch/components/account.jsx @@ -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'; @@ -42,6 +43,7 @@ class Account extends ImmutablePureComponent { onMute: PropTypes.func, onMuteNotifications: PropTypes.func, intl: PropTypes.object.isRequired, + overlayEmoji: PropTypes.object, hidden: PropTypes.bool, minimal: PropTypes.bool, defaultAction: PropTypes.string, @@ -50,6 +52,7 @@ class Account extends ImmutablePureComponent { static defaultProps = { size: 46, + overlayEmoji: { name: null } }; handleFollow = () => { @@ -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 ; @@ -138,12 +141,19 @@ class Account extends ImmutablePureComponent { verification = ; } + let statusAvatar; + if (!overlayEmoji.name) { + statusAvatar = ; + } else { + statusAvatar = ; + } + return (
- + {statusAvatar}
diff --git a/app/javascript/flavours/glitch/components/avatar_overlay.tsx b/app/javascript/flavours/glitch/components/avatar_overlay.tsx index cddca43d5b..1f806f7fb1 100644 --- a/app/javascript/flavours/glitch/components/avatar_overlay.tsx +++ b/app/javascript/flavours/glitch/components/avatar_overlay.tsx @@ -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; @@ -14,6 +18,7 @@ interface Props { export const AvatarOverlay: React.FC = ({ account, friend, + emoji, size = 46, baseSize = 36, overlaySize = 24, @@ -27,6 +32,32 @@ export const AvatarOverlay: React.FC = ({ ? friend?.get('avatar') : friend?.get('avatar_static'); + let overlayElement; + if (friendSrc) { + overlayElement = ( +
+ {friendSrc && {friend?.get('acct')}} +
+ ); + } else { + overlayElement = ( +
+ {emoji && ( + + )} +
+ ); + } + return (
= ({ {accountSrc && {account?.get('acct')}}
-
-
- {friendSrc && {friend?.get('acct')}} -
-
+
{overlayElement}
); }; diff --git a/app/javascript/flavours/glitch/features/reactions/index.jsx b/app/javascript/flavours/glitch/features/reactions/index.jsx index 5a849e70af..1ef01ad114 100644 --- a/app/javascript/flavours/glitch/features/reactions/index.jsx +++ b/app/javascript/flavours/glitch/features/reactions/index.jsx @@ -27,9 +27,9 @@ 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 { @@ -37,7 +37,7 @@ 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, @@ -67,9 +67,9 @@ 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 ( @@ -77,6 +77,9 @@ class Reactions extends ImmutablePureComponent { ); } + const accountIds = reactions.map(v => v.account); + const reactionsByAccount = new Map(reactions.map(v => [v.account, v])); + const emptyMessage = ; return ( @@ -102,7 +105,7 @@ class Reactions extends ImmutablePureComponent { bindToDocument={!multiColumn} > {accountIds.map(id => - , + , )} diff --git a/app/javascript/flavours/glitch/models/reaction.ts b/app/javascript/flavours/glitch/models/reaction.ts new file mode 100644 index 0000000000..7c02b43ee9 --- /dev/null +++ b/app/javascript/flavours/glitch/models/reaction.ts @@ -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; +export type StatusReaction = RecordOf; + +export const CustomEmojiFactory = Record({ + name: '', + static_url: '', + url: '', +}); diff --git a/app/javascript/flavours/glitch/reducers/index.ts b/app/javascript/flavours/glitch/reducers/index.ts index c67349e51f..6b2ddee99c 100644 --- a/app/javascript/flavours/glitch/reducers/index.ts +++ b/app/javascript/flavours/glitch/reducers/index.ts @@ -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'; @@ -86,6 +87,7 @@ const reducers = { history, tags, followed_tags, + status_reactions, notificationPolicy: notificationPolicyReducer, notificationRequests: notificationRequestsReducer, }; diff --git a/app/javascript/flavours/glitch/reducers/status_reactions.js b/app/javascript/flavours/glitch/reducers/status_reactions.js new file mode 100644 index 0000000000..3ba6a83b39 --- /dev/null +++ b/app/javascript/flavours/glitch/reducers/status_reactions.js @@ -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; + } +} diff --git a/app/javascript/flavours/glitch/reducers/user_lists.js b/app/javascript/flavours/glitch/reducers/user_lists.js index 4eee00d49d..3eb80da437 100644 --- a/app/javascript/flavours/glitch/reducers/user_lists.js +++ b/app/javascript/flavours/glitch/reducers/user_lists.js @@ -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, @@ -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, @@ -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: