Add emoji overlay to avatars in reaction list
I swear there has to be a better way to do this
This commit is contained in:
parent
a4bfa67151
commit
31406cb0f2
9 changed files with 138 additions and 42 deletions
|
@ -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,
|
||||
};
|
||||
}
|
||||
|
|
5
app/javascript/flavours/glitch/api_types/reaction.ts
Normal file
5
app/javascript/flavours/glitch/api_types/reaction.ts
Normal file
|
@ -0,0 +1,5 @@
|
|||
export interface ApiStatusReactionJSON {
|
||||
name: string;
|
||||
static_url?: string | undefined;
|
||||
url?: string | undefined;
|
||||
}
|
|
@ -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 <EmptyAccount size={size} minimal={minimal} />;
|
||||
|
@ -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'>
|
||||
|
|
|
@ -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<Props> = ({
|
||||
account,
|
||||
friend,
|
||||
emoji,
|
||||
size = 46,
|
||||
baseSize = 36,
|
||||
overlaySize = 24,
|
||||
|
@ -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'
|
||||
|
@ -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>
|
||||
);
|
||||
};
|
||||
|
|
|
@ -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 (
|
||||
<Column>
|
||||
<LoadingIndicator />
|
||||
|
@ -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 = <FormattedMessage id='status.reactions.empty' defaultMessage='No one has reacted to this post yet. When someone does, they will show up here.' />;
|
||||
|
||||
return (
|
||||
|
@ -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>
|
||||
|
||||
|
|
13
app/javascript/flavours/glitch/models/reaction.ts
Normal file
13
app/javascript/flavours/glitch/models/reaction.ts
Normal file
|
@ -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: '',
|
||||
});
|
|
@ -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,
|
||||
};
|
||||
|
|
57
app/javascript/flavours/glitch/reducers/status_reactions.js
Normal file
57
app/javascript/flavours/glitch/reducers/status_reactions.js
Normal file
|
@ -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;
|
||||
}
|
||||
}
|
|
@ -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:
|
||||
|
|
Loading…
Reference in a new issue