Files
mastodon/app/javascript/flavours/glitch/features/annual_report/index.tsx
2025-12-10 18:13:17 +01:00

118 lines
3.6 KiB
TypeScript

import { useCallback, useEffect, useState } from 'react';
import type { FC } from 'react';
import { defineMessage, useIntl } from 'react-intl';
import { useLocation } from 'react-router';
import classNames from 'classnames/bind';
import { closeModal } from '@/flavours/glitch/actions/modal';
import { IconButton } from '@/flavours/glitch/components/icon_button';
import { LoadingIndicator } from '@/flavours/glitch/components/loading_indicator';
import { me } from '@/flavours/glitch/initial_state';
import { useAppDispatch, useAppSelector } from '@/flavours/glitch/store';
import CloseIcon from '@/material-icons/400-24px/close.svg?react';
import { Archetype } from './archetype';
import { Followers } from './followers';
import { HighlightedPost } from './highlighted_post';
import styles from './index.module.scss';
import { MostUsedHashtag } from './most_used_hashtag';
import { NewPosts } from './new_posts';
const moduleClassNames = classNames.bind(styles);
export const shareMessage = defineMessage({
id: 'annual_report.summary.share_message',
defaultMessage: 'I got the {archetype} archetype!',
});
export const AnnualReport: FC<{ context?: 'modal' | 'standalone' }> = ({
context = 'standalone',
}) => {
const intl = useIntl();
const dispatch = useAppDispatch();
const report = useAppSelector((state) => state.annualReport.report);
const account = useAppSelector((state) => {
if (me) {
return state.accounts.get(me);
}
if (report?.schema_version === 2) {
return state.accounts.get(report.account_id);
}
return undefined;
});
const close = useCallback(() => {
dispatch(closeModal({ modalType: 'ANNUAL_REPORT', ignoreFocus: false }));
}, [dispatch]);
// Close modal when navigating away from within
const { pathname } = useLocation();
const [initialPathname] = useState(pathname);
useEffect(() => {
if (pathname !== initialPathname) {
close();
}
}, [pathname, initialPathname, close]);
if (!report) {
return <LoadingIndicator />;
}
const newPostCount = report.data.time_series.reduce(
(sum, item) => sum + item.statuses,
0,
);
const newFollowerCount =
context === 'modal' &&
report.data.time_series.reduce((sum, item) => sum + item.followers, 0);
const topHashtag = report.data.top_hashtags[0];
return (
<div className={moduleClassNames(styles.wrapper, 'theme-dark')}>
<div className={styles.header}>
<h1>Wrapstodon {report.year}</h1>
{account && <p>@{account.acct}</p>}
{context === 'modal' && (
<IconButton
title={intl.formatMessage({
id: 'annual_report.summary.close',
defaultMessage: 'Close',
})}
className={styles.closeButton}
icon='close'
iconComponent={CloseIcon}
onClick={close}
/>
)}
</div>
<div className={styles.stack}>
<HighlightedPost data={report.data.top_statuses} context={context} />
<div
className={moduleClassNames(styles.statsGrid, {
noHashtag: !topHashtag,
onlyHashtag: !(newFollowerCount && newPostCount),
singleNumber: !!newFollowerCount !== !!newPostCount,
})}
>
{!!newFollowerCount && <Followers count={newFollowerCount} />}
{!!newPostCount && <NewPosts count={newPostCount} />}
{topHashtag && (
<MostUsedHashtag
hashtag={topHashtag}
name={account?.display_name}
context={context}
/>
)}
</div>
<Archetype report={report} account={account} context={context} />
</div>
</div>
);
};