mirror of
https://github.com/mastodon/mastodon.git
synced 2025-10-06 09:02:43 +00:00
replace duplicated handler code
Some checks failed
Chromatic / Run Chromatic (push) Has been cancelled
Some checks failed
Chromatic / Run Chromatic (push) Has been cancelled
This commit is contained in:
parent
dd61fed777
commit
f04360c43e
|
@ -61,13 +61,19 @@ export const DisplayName: FC<Props & ComponentPropsWithoutRef<'span'>> = ({
|
||||||
if (simple) {
|
if (simple) {
|
||||||
return (
|
return (
|
||||||
<bdi>
|
<bdi>
|
||||||
<EmojiHTML {...props} htmlString={accountName} shallow as='span' />
|
<EmojiHTML
|
||||||
|
{...props}
|
||||||
|
className={`animate-parent ${className}`}
|
||||||
|
htmlString={accountName}
|
||||||
|
shallow
|
||||||
|
as='span'
|
||||||
|
/>
|
||||||
</bdi>
|
</bdi>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<span {...props} className={`display-name ${className}`}>
|
<span {...props} className={`display-name animate-parent ${className}`}>
|
||||||
<bdi>
|
<bdi>
|
||||||
<EmojiHTML
|
<EmojiHTML
|
||||||
{...props}
|
{...props}
|
||||||
|
|
|
@ -13,8 +13,9 @@ import ChevronRightIcon from '@/material-icons/400-24px/chevron_right.svg?react'
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { Poll } from 'mastodon/components/poll';
|
import { Poll } from 'mastodon/components/poll';
|
||||||
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
import { identityContextPropShape, withIdentity } from 'mastodon/identity_context';
|
||||||
import { autoPlayGif, languages as preloadedLanguages } from 'mastodon/initial_state';
|
import { languages as preloadedLanguages } from 'mastodon/initial_state';
|
||||||
import { EmojiHTML } from '../features/emoji/emoji_html';
|
import { EmojiHTML } from '../features/emoji/emoji_html';
|
||||||
|
import { handleAnimateEnter, handleAnimateLeave } from '../features/emoji/handlers';
|
||||||
import { isModernEmojiEnabled } from '../utils/environment';
|
import { isModernEmojiEnabled } from '../utils/environment';
|
||||||
|
|
||||||
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
const MAX_HEIGHT = 706; // 22px * 32 (+ 2px padding at the top)
|
||||||
|
@ -150,32 +151,6 @@ class StatusContent extends PureComponent {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
handleMouseEnter = ({ currentTarget }) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
|
||||||
|
|
||||||
for (var i = 0; i < emojis.length; i++) {
|
|
||||||
let emoji = emojis[i];
|
|
||||||
emoji.src = emoji.getAttribute('data-original');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleMouseLeave = ({ currentTarget }) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
|
||||||
|
|
||||||
for (var i = 0; i < emojis.length; i++) {
|
|
||||||
let emoji = emojis[i];
|
|
||||||
emoji.src = emoji.getAttribute('data-static');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
componentDidMount () {
|
componentDidMount () {
|
||||||
this._updateStatusLinks();
|
this._updateStatusLinks();
|
||||||
}
|
}
|
||||||
|
@ -245,7 +220,7 @@ class StatusContent extends PureComponent {
|
||||||
|
|
||||||
const content = statusContent ?? getStatusContent(status);
|
const content = statusContent ?? getStatusContent(status);
|
||||||
const language = status.getIn(['translation', 'language']) || status.get('language');
|
const language = status.getIn(['translation', 'language']) || status.get('language');
|
||||||
const classNames = classnames('status__content', {
|
const classNames = classnames('status__content animate-parent', {
|
||||||
'status__content--with-action': this.props.onClick && this.props.history,
|
'status__content--with-action': this.props.onClick && this.props.history,
|
||||||
'status__content--collapsed': renderReadMore,
|
'status__content--collapsed': renderReadMore,
|
||||||
});
|
});
|
||||||
|
@ -267,7 +242,15 @@ class StatusContent extends PureComponent {
|
||||||
if (this.props.onClick) {
|
if (this.props.onClick) {
|
||||||
return (
|
return (
|
||||||
<>
|
<>
|
||||||
<div className={classNames} ref={this.setRef} onMouseDown={this.handleMouseDown} onMouseUp={this.handleMouseUp} key='status-content' onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
<div
|
||||||
|
className={classNames}
|
||||||
|
ref={this.setRef}
|
||||||
|
onMouseDown={this.handleMouseDown}
|
||||||
|
onMouseUp={this.handleMouseUp}
|
||||||
|
key='status-content'
|
||||||
|
onMouseEnter={handleAnimateEnter}
|
||||||
|
onMouseLeave={handleAnimateLeave}
|
||||||
|
>
|
||||||
<EmojiHTML
|
<EmojiHTML
|
||||||
className='status__content__text status__content__text--visible translate'
|
className='status__content__text status__content__text--visible translate'
|
||||||
lang={language}
|
lang={language}
|
||||||
|
@ -284,7 +267,7 @@ class StatusContent extends PureComponent {
|
||||||
);
|
);
|
||||||
} else {
|
} else {
|
||||||
return (
|
return (
|
||||||
<div className={classNames} ref={this.setRef} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave}>
|
<div className={classNames} ref={this.setRef} onMouseEnter={handleAnimateEnter} onMouseLeave={handleAnimateLeave}>
|
||||||
<EmojiHTML
|
<EmojiHTML
|
||||||
className='status__content__text status__content__text--visible translate'
|
className='status__content__text status__content__text--visible translate'
|
||||||
lang={language}
|
lang={language}
|
||||||
|
|
|
@ -8,6 +8,10 @@ import { NavLink } from 'react-router-dom';
|
||||||
|
|
||||||
import { AccountBio } from '@/mastodon/components/account_bio';
|
import { AccountBio } from '@/mastodon/components/account_bio';
|
||||||
import { DisplayName } from '@/mastodon/components/display_name';
|
import { DisplayName } from '@/mastodon/components/display_name';
|
||||||
|
import {
|
||||||
|
handleAnimateEnter,
|
||||||
|
handleAnimateLeave,
|
||||||
|
} from '@/mastodon/features/emoji/handlers';
|
||||||
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
import CheckIcon from '@/material-icons/400-24px/check.svg?react';
|
||||||
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
import LockIcon from '@/material-icons/400-24px/lock.svg?react';
|
||||||
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
import MoreHorizIcon from '@/material-icons/400-24px/more_horiz.svg?react';
|
||||||
|
@ -379,36 +383,6 @@ export const AccountHeader: React.FC<{
|
||||||
});
|
});
|
||||||
}, [account]);
|
}, [account]);
|
||||||
|
|
||||||
const handleMouseEnter = useCallback(
|
|
||||||
({ currentTarget }: React.MouseEvent) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTarget
|
|
||||||
.querySelectorAll<HTMLImageElement>('.custom-emoji')
|
|
||||||
.forEach((emoji) => {
|
|
||||||
emoji.src = emoji.getAttribute('data-original') ?? '';
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMouseLeave = useCallback(
|
|
||||||
({ currentTarget }: React.MouseEvent) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
currentTarget
|
|
||||||
.querySelectorAll<HTMLImageElement>('.custom-emoji')
|
|
||||||
.forEach((emoji) => {
|
|
||||||
emoji.src = emoji.getAttribute('data-static') ?? '';
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const suspended = account?.suspended;
|
const suspended = account?.suspended;
|
||||||
const isRemote = account?.acct !== account?.username;
|
const isRemote = account?.acct !== account?.username;
|
||||||
const remoteDomain = isRemote ? account?.acct.split('@')[1] : null;
|
const remoteDomain = isRemote ? account?.acct.split('@')[1] : null;
|
||||||
|
@ -808,11 +782,11 @@ export const AccountHeader: React.FC<{
|
||||||
)}
|
)}
|
||||||
|
|
||||||
<div
|
<div
|
||||||
className={classNames('account__header', {
|
className={classNames('account__header animate-parent', {
|
||||||
inactive: !!account.moved,
|
inactive: !!account.moved,
|
||||||
})}
|
})}
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleAnimateEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleAnimateLeave}
|
||||||
>
|
>
|
||||||
{!(suspended || hidden || account.moved) &&
|
{!(suspended || hidden || account.moved) &&
|
||||||
relationship?.requested_by && (
|
relationship?.requested_by && (
|
||||||
|
|
|
@ -23,9 +23,9 @@ import { IconButton } from 'mastodon/components/icon_button';
|
||||||
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
import { RelativeTimestamp } from 'mastodon/components/relative_timestamp';
|
||||||
import StatusContent from 'mastodon/components/status_content';
|
import StatusContent from 'mastodon/components/status_content';
|
||||||
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
import { Dropdown } from 'mastodon/components/dropdown_menu';
|
||||||
import { autoPlayGif } from 'mastodon/initial_state';
|
|
||||||
import { makeGetStatus } from 'mastodon/selectors';
|
import { makeGetStatus } from 'mastodon/selectors';
|
||||||
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
import { LinkedDisplayName } from '@/mastodon/components/display_name';
|
||||||
|
import { handleAnimateEnter, handleAnimateLeave } from '@/mastodon/features/emoji/handlers';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
more: { id: 'status.more', defaultMessage: 'More' },
|
more: { id: 'status.more', defaultMessage: 'More' },
|
||||||
|
@ -57,31 +57,8 @@ export const Conversation = ({ conversation, scrollKey, onMoveUp, onMoveDown })
|
||||||
const lastStatus = useSelector(state => getStatus(state, { id: lastStatusId }));
|
const lastStatus = useSelector(state => getStatus(state, { id: lastStatusId }));
|
||||||
const accounts = useSelector(state => getAccounts(state, accountIds));
|
const accounts = useSelector(state => getAccounts(state, accountIds));
|
||||||
|
|
||||||
const handleMouseEnter = useCallback(({ currentTarget }) => {
|
const handleMouseEnter = useCallback(handleAnimateEnter, []);
|
||||||
if (autoPlayGif) {
|
const handleMouseLeave = useCallback(handleAnimateLeave, []);
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
|
||||||
|
|
||||||
for (var i = 0; i < emojis.length; i++) {
|
|
||||||
let emoji = emojis[i];
|
|
||||||
emoji.src = emoji.getAttribute('data-original');
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleMouseLeave = useCallback(({ currentTarget }) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
|
||||||
|
|
||||||
for (var i = 0; i < emojis.length; i++) {
|
|
||||||
let emoji = emojis[i];
|
|
||||||
emoji.src = emoji.getAttribute('data-static');
|
|
||||||
}
|
|
||||||
}, []);
|
|
||||||
|
|
||||||
const handleClick = useCallback(() => {
|
const handleClick = useCallback(() => {
|
||||||
if (unread) {
|
if (unread) {
|
||||||
|
@ -173,7 +150,7 @@ export const Conversation = ({ conversation, scrollKey, onMoveUp, onMoveDown })
|
||||||
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
|
{unread && <span className='conversation__unread' />} <RelativeTimestamp timestamp={lastStatus.get('created_at')} />
|
||||||
</div>
|
</div>
|
||||||
|
|
||||||
<div className='conversation__content__names' onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
<div className='conversation__content__names animate-parent' onMouseEnter={handleMouseEnter} onMouseLeave={handleMouseLeave}>
|
||||||
<FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
|
<FormattedMessage id='conversation.with' defaultMessage='With {names}' values={{ names: <span>{names}</span> }} />
|
||||||
</div>
|
</div>
|
||||||
</div>
|
</div>
|
||||||
|
|
|
@ -1,4 +1,3 @@
|
||||||
import type { MouseEventHandler } from 'react';
|
|
||||||
import { useCallback } from 'react';
|
import { useCallback } from 'react';
|
||||||
|
|
||||||
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||||
|
@ -6,6 +5,10 @@ import { FormattedMessage, defineMessages, useIntl } from 'react-intl';
|
||||||
import classNames from 'classnames';
|
import classNames from 'classnames';
|
||||||
import { Link } from 'react-router-dom';
|
import { Link } from 'react-router-dom';
|
||||||
|
|
||||||
|
import {
|
||||||
|
handleAnimateEnter,
|
||||||
|
handleAnimateLeave,
|
||||||
|
} from '@/mastodon/features/emoji/handlers';
|
||||||
import {
|
import {
|
||||||
followAccount,
|
followAccount,
|
||||||
unblockAccount,
|
unblockAccount,
|
||||||
|
@ -44,38 +47,8 @@ export const AccountCard: React.FC<{ accountId: string }> = ({ accountId }) => {
|
||||||
const account = useAppSelector((s) => getAccount(s, accountId));
|
const account = useAppSelector((s) => getAccount(s, accountId));
|
||||||
const dispatch = useAppDispatch();
|
const dispatch = useAppDispatch();
|
||||||
|
|
||||||
const handleMouseEnter = useCallback<MouseEventHandler>(
|
const handleMouseEnter = useCallback(handleAnimateEnter, []);
|
||||||
({ currentTarget }) => {
|
const handleMouseLeave = useCallback(handleAnimateLeave, []);
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
const emojis =
|
|
||||||
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
|
|
||||||
|
|
||||||
emojis.forEach((emoji) => {
|
|
||||||
const original = emoji.getAttribute('data-original');
|
|
||||||
if (original) emoji.src = original;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMouseLeave = useCallback<MouseEventHandler>(
|
|
||||||
({ currentTarget }) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis =
|
|
||||||
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
|
|
||||||
|
|
||||||
emojis.forEach((emoji) => {
|
|
||||||
const staticUrl = emoji.getAttribute('data-static');
|
|
||||||
if (staticUrl) emoji.src = staticUrl;
|
|
||||||
});
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleFollow = useCallback(() => {
|
const handleFollow = useCallback(() => {
|
||||||
if (!account) return;
|
if (!account) return;
|
||||||
|
@ -185,7 +158,7 @@ export const AccountCard: React.FC<{ accountId: string }> = ({ accountId }) => {
|
||||||
|
|
||||||
{account.get('note').length > 0 && (
|
{account.get('note').length > 0 && (
|
||||||
<div
|
<div
|
||||||
className='account-card__bio translate'
|
className='account-card__bio translate animate-parent'
|
||||||
onMouseEnter={handleMouseEnter}
|
onMouseEnter={handleMouseEnter}
|
||||||
onMouseLeave={handleMouseLeave}
|
onMouseLeave={handleMouseLeave}
|
||||||
dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
|
dangerouslySetInnerHTML={{ __html: account.get('note_emojified') }}
|
||||||
|
|
|
@ -5,22 +5,23 @@ import type { CustomEmojiMapArg } from './types';
|
||||||
|
|
||||||
type EmojiHTMLProps<Element extends ElementType = 'div'> = Omit<
|
type EmojiHTMLProps<Element extends ElementType = 'div'> = Omit<
|
||||||
ComponentPropsWithoutRef<Element>,
|
ComponentPropsWithoutRef<Element>,
|
||||||
'dangerouslySetInnerHTML'
|
'dangerouslySetInnerHTML' | 'className'
|
||||||
> & {
|
> & {
|
||||||
htmlString: string;
|
htmlString: string;
|
||||||
extraEmojis?: CustomEmojiMapArg;
|
extraEmojis?: CustomEmojiMapArg;
|
||||||
as?: Element;
|
as?: Element;
|
||||||
shallow?: boolean;
|
shallow?: boolean;
|
||||||
|
className?: string;
|
||||||
};
|
};
|
||||||
|
|
||||||
export const EmojiHTML = <Element extends ElementType>({
|
export const EmojiHTML = ({
|
||||||
extraEmojis,
|
extraEmojis,
|
||||||
htmlString,
|
htmlString,
|
||||||
as: asElement, // Rename for syntax highlighting
|
as: Wrapper = 'div', // Rename for syntax highlighting
|
||||||
shallow,
|
shallow,
|
||||||
|
className = '',
|
||||||
...props
|
...props
|
||||||
}: EmojiHTMLProps<Element>) => {
|
}: EmojiHTMLProps<ElementType>) => {
|
||||||
const Wrapper = asElement ?? 'div';
|
|
||||||
const emojifiedHtml = useEmojify({
|
const emojifiedHtml = useEmojify({
|
||||||
text: htmlString,
|
text: htmlString,
|
||||||
extraEmojis,
|
extraEmojis,
|
||||||
|
@ -32,6 +33,10 @@ export const EmojiHTML = <Element extends ElementType>({
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<Wrapper {...props} dangerouslySetInnerHTML={{ __html: emojifiedHtml }} />
|
<Wrapper
|
||||||
|
{...props}
|
||||||
|
className={`${className} animate-parent`}
|
||||||
|
dangerouslySetInnerHTML={{ __html: emojifiedHtml }}
|
||||||
|
/>
|
||||||
);
|
);
|
||||||
};
|
};
|
||||||
|
|
78
app/javascript/mastodon/features/emoji/handlers.ts
Normal file
78
app/javascript/mastodon/features/emoji/handlers.ts
Normal file
|
@ -0,0 +1,78 @@
|
||||||
|
import type { MouseEventHandler } from 'react';
|
||||||
|
|
||||||
|
import { autoPlayGif } from '@/mastodon/initial_state';
|
||||||
|
import { isModernEmojiEnabled } from '@/mastodon/utils/environment';
|
||||||
|
|
||||||
|
export const handleAnimateEnter: MouseEventHandler = ({ currentTarget }) => {
|
||||||
|
if (autoPlayGif || isModernEmojiEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTarget
|
||||||
|
.querySelectorAll<HTMLImageElement>('img.custom-emoji')
|
||||||
|
.forEach((emoji) => {
|
||||||
|
toggleAnimatedGif(emoji, true);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
export const handleAnimateLeave: MouseEventHandler = ({ currentTarget }) => {
|
||||||
|
if (autoPlayGif || isModernEmojiEnabled()) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
|
||||||
|
currentTarget
|
||||||
|
.querySelectorAll<HTMLImageElement>('img.custom-emoji')
|
||||||
|
.forEach((emoji) => {
|
||||||
|
toggleAnimatedGif(emoji, false);
|
||||||
|
});
|
||||||
|
};
|
||||||
|
|
||||||
|
const PARENT_MAX_DEPTH = 10;
|
||||||
|
|
||||||
|
export function handleAnimateGif(event: MouseEvent) {
|
||||||
|
const { target, type } = event;
|
||||||
|
const animate = type === 'mouseover';
|
||||||
|
if (target instanceof HTMLImageElement) {
|
||||||
|
toggleAnimatedGif(target, animate);
|
||||||
|
} else if (!(target instanceof HTMLElement) || target === document.body) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
let parent: HTMLElement | null = null;
|
||||||
|
let iter = 0;
|
||||||
|
if (target.classList.contains('animate-parent')) {
|
||||||
|
parent = target;
|
||||||
|
} else {
|
||||||
|
let current: HTMLElement | null = target;
|
||||||
|
while (current) {
|
||||||
|
if (iter >= PARENT_MAX_DEPTH) {
|
||||||
|
return; // We can just exit right now.
|
||||||
|
}
|
||||||
|
current = current.parentElement;
|
||||||
|
if (current?.classList.contains('animate-parent')) {
|
||||||
|
parent = current;
|
||||||
|
break;
|
||||||
|
}
|
||||||
|
iter++;
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
if (parent) {
|
||||||
|
const animatedChildren =
|
||||||
|
parent.querySelectorAll<HTMLImageElement>('img.custom-emoji');
|
||||||
|
for (const child of animatedChildren) {
|
||||||
|
toggleAnimatedGif(child, animate);
|
||||||
|
}
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
||||||
|
function toggleAnimatedGif(image: HTMLImageElement, animate: boolean) {
|
||||||
|
const { classList, dataset } = image;
|
||||||
|
if (
|
||||||
|
!classList.contains('custom-emoji') ||
|
||||||
|
!dataset.static ||
|
||||||
|
!dataset.original
|
||||||
|
) {
|
||||||
|
return;
|
||||||
|
}
|
||||||
|
image.src = animate ? dataset.original : dataset.static;
|
||||||
|
}
|
|
@ -1,6 +1,7 @@
|
||||||
import initialState from '@/mastodon/initial_state';
|
import initialState, { autoPlayGif } from '@/mastodon/initial_state';
|
||||||
import { loadWorker } from '@/mastodon/utils/workers';
|
import { loadWorker } from '@/mastodon/utils/workers';
|
||||||
|
|
||||||
|
import { handleAnimateGif } from './handlers';
|
||||||
import { toSupportedLocale } from './locale';
|
import { toSupportedLocale } from './locale';
|
||||||
import { emojiLogger } from './utils';
|
import { emojiLogger } from './utils';
|
||||||
|
|
||||||
|
@ -22,6 +23,11 @@ export function initializeEmoji() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
if (typeof document !== 'undefined' && !autoPlayGif) {
|
||||||
|
document.addEventListener('mouseover', handleAnimateGif, { passive: true });
|
||||||
|
document.addEventListener('mouseout', handleAnimateGif, { passive: true });
|
||||||
|
}
|
||||||
|
|
||||||
if (worker) {
|
if (worker) {
|
||||||
// Assign worker to const to make TS happy inside the event listener.
|
// Assign worker to const to make TS happy inside the event listener.
|
||||||
const thisWorker = worker;
|
const thisWorker = worker;
|
||||||
|
@ -51,16 +57,6 @@ export function initializeEmoji() {
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
async function fallbackLoad() {
|
|
||||||
log('falling back to main thread for loading');
|
|
||||||
const { importCustomEmojiData } = await import('./loader');
|
|
||||||
await importCustomEmojiData();
|
|
||||||
await loadEmojiLocale(userLocale);
|
|
||||||
if (userLocale !== 'en') {
|
|
||||||
await loadEmojiLocale('en');
|
|
||||||
}
|
|
||||||
}
|
|
||||||
|
|
||||||
export async function loadEmojiLocale(localeString: string) {
|
export async function loadEmojiLocale(localeString: string) {
|
||||||
const locale = toSupportedLocale(localeString);
|
const locale = toSupportedLocale(localeString);
|
||||||
|
|
||||||
|
@ -71,3 +67,13 @@ export async function loadEmojiLocale(localeString: string) {
|
||||||
await importEmojiData(locale);
|
await importEmojiData(locale);
|
||||||
}
|
}
|
||||||
}
|
}
|
||||||
|
|
||||||
|
async function fallbackLoad() {
|
||||||
|
log('falling back to main thread for loading');
|
||||||
|
const { importCustomEmojiData } = await import('./loader');
|
||||||
|
await importCustomEmojiData();
|
||||||
|
await loadEmojiLocale(userLocale);
|
||||||
|
if (userLocale !== 'en') {
|
||||||
|
await loadEmojiLocale('en');
|
||||||
|
}
|
||||||
|
}
|
||||||
|
|
|
@ -24,6 +24,7 @@ import { unicodeMapping } from 'mastodon/features/emoji/emoji_unicode_mapping_li
|
||||||
import { autoPlayGif, reduceMotion, disableSwiping, mascot } from 'mastodon/initial_state';
|
import { autoPlayGif, reduceMotion, disableSwiping, mascot } from 'mastodon/initial_state';
|
||||||
import { assetHost } from 'mastodon/utils/config';
|
import { assetHost } from 'mastodon/utils/config';
|
||||||
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
import { WithRouterPropTypes } from 'mastodon/utils/react_router';
|
||||||
|
import { handleAnimateEnter, handleAnimateLeave } from '../../emoji/handlers';
|
||||||
|
|
||||||
const messages = defineMessages({
|
const messages = defineMessages({
|
||||||
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
close: { id: 'lightbox.close', defaultMessage: 'Close' },
|
||||||
|
@ -111,42 +112,16 @@ class ContentWithRouter extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
};
|
};
|
||||||
|
|
||||||
handleMouseEnter = ({ currentTarget }) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
|
||||||
|
|
||||||
for (var i = 0; i < emojis.length; i++) {
|
|
||||||
let emoji = emojis[i];
|
|
||||||
emoji.src = emoji.getAttribute('data-original');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
handleMouseLeave = ({ currentTarget }) => {
|
|
||||||
if (autoPlayGif) {
|
|
||||||
return;
|
|
||||||
}
|
|
||||||
|
|
||||||
const emojis = currentTarget.querySelectorAll('.custom-emoji');
|
|
||||||
|
|
||||||
for (var i = 0; i < emojis.length; i++) {
|
|
||||||
let emoji = emojis[i];
|
|
||||||
emoji.src = emoji.getAttribute('data-static');
|
|
||||||
}
|
|
||||||
};
|
|
||||||
|
|
||||||
render () {
|
render () {
|
||||||
const { announcement } = this.props;
|
const { announcement } = this.props;
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='announcements__item__content translate'
|
className='announcements__item__content translate animate-parent'
|
||||||
ref={this.setRef}
|
ref={this.setRef}
|
||||||
dangerouslySetInnerHTML={{ __html: announcement.get('contentHtml') }}
|
dangerouslySetInnerHTML={{ __html: announcement.get('contentHtml') }}
|
||||||
onMouseEnter={this.handleMouseEnter}
|
onMouseEnter={handleAnimateEnter}
|
||||||
onMouseLeave={this.handleMouseLeave}
|
onMouseLeave={handleAnimateLeave}
|
||||||
/>
|
/>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
@ -238,9 +213,20 @@ class Reaction extends ImmutablePureComponent {
|
||||||
}
|
}
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<animated.button className={classNames('reactions-bar__item', { active: reaction.get('me') })} onClick={this.handleClick} onMouseEnter={this.handleMouseEnter} onMouseLeave={this.handleMouseLeave} title={`:${shortCode}:`} style={this.props.style}>
|
<animated.button
|
||||||
<span className='reactions-bar__item__emoji'><Emoji hovered={this.state.hovered} emoji={reaction.get('name')} emojiMap={this.props.emojiMap} /></span>
|
className={classNames('reactions-bar__item', { active: reaction.get('me') })}
|
||||||
<span className='reactions-bar__item__count'><AnimatedNumber value={reaction.get('count')} /></span>
|
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={reaction.get('name')} emojiMap={this.props.emojiMap} />
|
||||||
|
</span>
|
||||||
|
<span className='reactions-bar__item__count'>
|
||||||
|
<AnimatedNumber value={reaction.get('count')} />
|
||||||
|
</span>
|
||||||
</animated.button>
|
</animated.button>
|
||||||
);
|
);
|
||||||
}
|
}
|
||||||
|
|
|
@ -15,6 +15,8 @@ import { DisplayName } from 'mastodon/components/display_name';
|
||||||
import { Icon } from 'mastodon/components/icon';
|
import { Icon } from 'mastodon/components/icon';
|
||||||
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
import { useAppSelector, useAppDispatch } from 'mastodon/store';
|
||||||
|
|
||||||
|
import { handleAnimateEnter, handleAnimateLeave } from '../../emoji/handlers';
|
||||||
|
|
||||||
import { EmbeddedStatusContent } from './embedded_status_content';
|
import { EmbeddedStatusContent } from './embedded_status_content';
|
||||||
|
|
||||||
export type Mention = RecordOf<{ url: string; acct: string }>;
|
export type Mention = RecordOf<{ url: string; acct: string }>;
|
||||||
|
@ -76,31 +78,9 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
|
||||||
[clickCoordinatesRef, statusId, account, history],
|
[clickCoordinatesRef, statusId, account, history],
|
||||||
);
|
);
|
||||||
|
|
||||||
const handleMouseEnter = useCallback<React.MouseEventHandler<HTMLDivElement>>(
|
const handleMouseEnter = useCallback(handleAnimateEnter, []);
|
||||||
({ currentTarget }) => {
|
|
||||||
const emojis =
|
|
||||||
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
|
|
||||||
|
|
||||||
for (const emoji of emojis) {
|
const handleMouseLeave = useCallback(handleAnimateLeave, []);
|
||||||
const newSrc = emoji.getAttribute('data-original');
|
|
||||||
if (newSrc) emoji.src = newSrc;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleMouseLeave = useCallback<React.MouseEventHandler<HTMLDivElement>>(
|
|
||||||
({ currentTarget }) => {
|
|
||||||
const emojis =
|
|
||||||
currentTarget.querySelectorAll<HTMLImageElement>('.custom-emoji');
|
|
||||||
|
|
||||||
for (const emoji of emojis) {
|
|
||||||
const newSrc = emoji.getAttribute('data-static');
|
|
||||||
if (newSrc) emoji.src = newSrc;
|
|
||||||
}
|
|
||||||
},
|
|
||||||
[],
|
|
||||||
);
|
|
||||||
|
|
||||||
const handleContentWarningClick = useCallback(() => {
|
const handleContentWarningClick = useCallback(() => {
|
||||||
dispatch(toggleStatusSpoilers(statusId));
|
dispatch(toggleStatusSpoilers(statusId));
|
||||||
|
@ -123,7 +103,7 @@ export const EmbeddedStatus: React.FC<{ statusId: string }> = ({
|
||||||
|
|
||||||
return (
|
return (
|
||||||
<div
|
<div
|
||||||
className='notification-group__embedded-status'
|
className='notification-group__embedded-status animate-parent'
|
||||||
role='button'
|
role='button'
|
||||||
tabIndex={-1}
|
tabIndex={-1}
|
||||||
onMouseDown={handleMouseDown}
|
onMouseDown={handleMouseDown}
|
||||||
|
|
Loading…
Reference in New Issue
Block a user