2026-05-20 14:06:38 +00:00

271 lines
6.8 KiB
TypeScript
Raw Blame History

This file contains ambiguous Unicode characters

This file contains Unicode characters that might be confused with other characters. If you think that this is intentional, you can safely ignore this warning. Use the Escape button to reveal them.

import { useCallback, useMemo } from 'react';
import type { FC } from 'react';
import { defineMessage, FormattedMessage, useIntl } from 'react-intl';
import AnniversaryImage from '@/images/anniversary.svg?react';
import { focusCompose, resetCompose } from '@/mastodon/actions/compose';
import { closeModal } from '@/mastodon/actions/modal';
import { Button } from '@/mastodon/components/button';
import { DisplayNameSimple } from '@/mastodon/components/display_name/simple';
import { FormattedDateWrapper } from '@/mastodon/components/formatted_date';
import { IconButton } from '@/mastodon/components/icon_button';
import { ModalShell, ModalShellBody } from '@/mastodon/components/modal_shell';
import { useAccount } from '@/mastodon/hooks/useAccount';
import { useCurrentAccountId } from '@/mastodon/hooks/useAccountId';
import {
createAppSelector,
useAppDispatch,
useAppSelector,
} from '@/mastodon/store';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import classes from './styles.module.scss';
const closeMessage = defineMessage({
id: 'lightbox.close',
defaultMessage: 'Close',
});
const selectServerName = createAppSelector(
[
(state) => state.accounts,
(_, accountId: string) => accountId,
(state) => state.server.server.item?.domain,
],
(accounts, accountId, serverDomain) => {
const acct = accounts.getIn([accountId, 'acct']) as string | undefined;
if (!acct) {
return undefined;
}
const domain = acct.split('@').at(1);
if (domain) {
return domain;
}
return serverDomain;
},
);
export const AccountJoinModal: FC<{
accountId: string;
onClose: () => void;
}> = ({ accountId, onClose }) => {
const intl = useIntl();
const account = useAccount(accountId);
const currentId = useCurrentAccountId();
const isMe = accountId === currentId;
const createdAtStr = account?.created_at;
const anniversary = useMemo(() => {
if (!createdAtStr) {
return null;
}
const now = new Date();
const createdAt = new Date(createdAtStr);
if (
now.getMonth() === createdAt.getMonth() &&
now.getDate() === createdAt.getDate()
) {
return now.getFullYear() - createdAt.getFullYear();
}
return null;
}, [createdAtStr]);
const domain = useAppSelector((state) => selectServerName(state, accountId));
const dispatch = useAppDispatch();
const handle = account?.acct;
const handleShare = useCallback(() => {
if (anniversary === null) {
return;
}
let shareText = '#Fediversary';
if (anniversary === 0) {
shareText = isMe ? '#firstday' : '#welcome';
}
if (!isMe && handle) {
shareText = `@${handle} ${shareText}`;
}
dispatch(resetCompose());
dispatch(focusCompose(`\n\n${shareText}`, true));
dispatch(closeModal({ modalType: 'ACCOUNT_JOIN_DATE', ignoreFocus: true }));
}, [anniversary, handle, dispatch, isMe]);
return (
<ModalShell as='div' className={classes.joinShell}>
<ModalShellBody className={classes.joinWrapper}>
<AccountAnniversaryImage anniversary={anniversary} />
<div>
<AccountJoinMessage
name={<DisplayNameSimple account={account} />}
isMe={isMe}
serverName={domain}
anniversary={anniversary}
/>
<h1>
<FormattedDateWrapper
value={account?.created_at}
month='short'
day='numeric'
year='numeric'
/>
</h1>
</div>
<AccountAnniversaryShare
anniversary={anniversary}
onShare={handleShare}
isMe={isMe}
/>
<IconButton
iconComponent={CloseIcon}
icon='times'
onClick={onClose}
title={intl.formatMessage(closeMessage)}
className={classes.joinClose}
/>
</ModalShellBody>
</ModalShell>
);
};
const AccountJoinMessage: FC<{
name: React.JSX.Element;
isMe: boolean;
serverName?: string;
anniversary: number | null;
}> = ({ name, isMe, serverName, anniversary }) => {
if (anniversary === 0) {
if (isMe) {
return (
<FormattedMessage
id='account.join_modal.me_today'
defaultMessage='Its your first day on {server}!'
tagName='p'
values={{
server: serverName,
}}
/>
);
}
return (
<FormattedMessage
id='account.join_modal.other_today'
defaultMessage='Its {name}s first day on {server}!'
tagName='p'
values={{
name,
server: serverName,
}}
/>
);
}
if (isMe) {
if (anniversary !== null && anniversary > 0) {
return (
<FormattedMessage
id='account.join_modal.me_anniversary'
defaultMessage='Happy Fediversary! You joined {server} on'
tagName='p'
values={{
server: serverName,
}}
/>
);
}
return (
<FormattedMessage
id='account.join_modal.me'
defaultMessage='You joined {server} on'
tagName='p'
values={{
server: serverName,
}}
/>
);
}
return (
<FormattedMessage
id='account.join_modal.other'
defaultMessage='{name} joined {server} on'
tagName='p'
values={{
name,
server: serverName,
}}
/>
);
};
const AccountAnniversaryImage: FC<{ anniversary: number | null }> = ({
anniversary,
}) => {
if (anniversary === null) {
return null;
}
return (
<div className={classes.joinBanner}>
<AnniversaryImage role='presentation' />
<h2>{anniversary || 1}</h2>
{anniversary === 0 && (
<FormattedMessage
id='account.join_modal.day'
defaultMessage='Day'
tagName='h3'
/>
)}
{anniversary > 0 && (
<FormattedMessage
id='account.join_modal.years'
defaultMessage='{number, plural, one {year} other {years}}'
values={{ number: anniversary }}
tagName='h3'
/>
)}
</div>
);
};
const AccountAnniversaryShare: FC<{
anniversary: number | null;
onShare: () => void;
isMe: boolean;
}> = ({ anniversary, onShare, isMe }) => {
if (anniversary === null) {
return null;
}
return (
<Button onClick={onShare}>
{anniversary === 0 && isMe && (
<FormattedMessage
id='account.join_modal.share.intro'
defaultMessage='Share an intro post'
/>
)}
{anniversary === 0 && !isMe && (
<FormattedMessage
id='account.join_modal.share.welcome'
defaultMessage='Share a welcome post'
/>
)}
{anniversary > 0 && (
<FormattedMessage
id='account.join_modal.share.celebrate'
defaultMessage='Share a celebratory post'
/>
)}
</Button>
);
};