diff --git a/src/app.jsx b/src/app.jsx index 8d95cc34..e4b89603 100644 --- a/src/app.jsx +++ b/src/app.jsx @@ -46,6 +46,7 @@ import Search from './pages/search'; import StatusRoute from './pages/status-route'; import Trending from './pages/trending'; import Welcome from './pages/welcome'; +import AnnualReport from './pages/annual-report'; import { api, hasInstance, @@ -546,6 +547,7 @@ function SecondaryRoutes({ isLoggedIn }) { } /> } /> } /> + } /> )} } /> diff --git a/src/components/notification.jsx b/src/components/notification.jsx index dc864bbf..6c497643 100644 --- a/src/components/notification.jsx +++ b/src/components/notification.jsx @@ -261,6 +261,9 @@ const contentText = { ), emoji_reaction: emojiText, 'pleroma:emoji_reaction': emojiText, + annual_report: ({ year }) => ( + Your {year} #Wrapstodon is here! + ), }; // account_suspension, domain_block, user_domain_block @@ -312,6 +315,7 @@ function Notification({ report, event, moderation_warning, + annualReport, // Client-side grouped notification _ids, _accounts, @@ -409,6 +413,10 @@ function Notification({ emoji: notification.emoji, emojiURL, }); + } else if (type === 'annual_report') { + text = text({ + ...notification.annualReport, + }); } else { text = text({ account: account ? ( @@ -527,6 +535,11 @@ function Notification({ )} + {type === 'annual_report' && ( +
+ View #Wrapstodon +
+ )} )} {_accounts?.length > 1 && ( diff --git a/src/locales/en.po b/src/locales/en.po index 238dc806..06003a63 100644 --- a/src/locales/en.po +++ b/src/locales/en.po @@ -963,7 +963,7 @@ msgid "Nothing to show" msgstr "" #: src/components/generic-accounts.jsx:145 -#: src/components/notification.jsx:438 +#: src/components/notification.jsx:446 #: src/pages/accounts.jsx:41 #: src/pages/search.jsx:317 #: src/pages/search.jsx:350 @@ -1472,74 +1472,82 @@ msgstr "" msgid "Moderation warning" msgstr "" -#: src/components/notification.jsx:269 +#: src/components/notification.jsx:265 +msgid "Your {year} #Wrapstodon is here!" +msgstr "Your {year} #Wrapstodon is here!" + +#: src/components/notification.jsx:272 msgid "An admin from <0>{from} has suspended <1>{targetName}, which means you can no longer receive updates from them or interact with them." msgstr "" -#: src/components/notification.jsx:275 +#: src/components/notification.jsx:278 msgid "An admin from <0>{from} has blocked <1>{targetName}. Affected followers: {followersCount}, followings: {followingCount}." msgstr "" -#: src/components/notification.jsx:281 +#: src/components/notification.jsx:284 msgid "You have blocked <0>{targetName}. Removed followers: {followersCount}, followings: {followingCount}." msgstr "" -#: src/components/notification.jsx:289 +#: src/components/notification.jsx:292 msgid "Your account has received a moderation warning." msgstr "" -#: src/components/notification.jsx:290 +#: src/components/notification.jsx:293 msgid "Your account has been disabled." msgstr "" -#: src/components/notification.jsx:291 +#: src/components/notification.jsx:294 msgid "Some of your posts have been marked as sensitive." msgstr "" -#: src/components/notification.jsx:292 +#: src/components/notification.jsx:295 msgid "Some of your posts have been deleted." msgstr "" -#: src/components/notification.jsx:293 +#: src/components/notification.jsx:296 msgid "Your posts will be marked as sensitive from now on." msgstr "" -#: src/components/notification.jsx:294 +#: src/components/notification.jsx:297 msgid "Your account has been limited." msgstr "" -#: src/components/notification.jsx:295 +#: src/components/notification.jsx:298 msgid "Your account has been suspended." msgstr "" -#: src/components/notification.jsx:369 +#: src/components/notification.jsx:373 msgid "[Unknown notification type: {type}]" msgstr "" -#: src/components/notification.jsx:434 +#: src/components/notification.jsx:442 #: src/components/status.jsx:1036 #: src/components/status.jsx:1046 msgid "Boosted/Liked by…" msgstr "" -#: src/components/notification.jsx:435 +#: src/components/notification.jsx:443 msgid "Liked by…" msgstr "" -#: src/components/notification.jsx:436 +#: src/components/notification.jsx:444 msgid "Boosted by…" msgstr "" -#: src/components/notification.jsx:437 +#: src/components/notification.jsx:445 msgid "Followed by…" msgstr "" -#: src/components/notification.jsx:508 -#: src/components/notification.jsx:524 +#: src/components/notification.jsx:516 +#: src/components/notification.jsx:532 msgid "Learn more <0/>" msgstr "" -#: src/components/notification.jsx:756 +#: src/components/notification.jsx:540 +msgid "View #Wrapstodon" +msgstr "View #Wrapstodon" + +#: src/components/notification.jsx:769 #: src/components/status.jsx:267 msgid "Read more →" msgstr "" @@ -2260,6 +2268,7 @@ msgid "Failed to load history" msgstr "" #: src/components/status.jsx:3006 +#: src/pages/annual-report.jsx:44 msgid "Loading…" msgstr "" @@ -2401,6 +2410,7 @@ msgid "Login required." msgstr "Login required." #: src/compose.jsx:90 +#: src/pages/annual-report.jsx:132 #: src/pages/http-route.jsx:91 #: src/pages/login.jsx:270 msgid "Go home" diff --git a/src/pages/annual-report.css b/src/pages/annual-report.css new file mode 100644 index 00000000..be765301 --- /dev/null +++ b/src/pages/annual-report.css @@ -0,0 +1,77 @@ +#annual-report-page { + .report { + background-color: var(--bg-color); + border: 16px ridge var(--bg-faded-color); + box-shadow: 0 0 0 2px var(--bg-color); + padding: 16px; + margin: 80px auto; + max-width: var(--main-width); + font-family: var(--monospace-font); + font-variant-numeric: slashed-zero; + font-feature-settings: 'ss01'; + font-variant-numeric: tabular-nums; + min-height: 80vh; + + h1 { + margin: 0; + padding: 0; + } + + dt { + font-weight: bold; + font-size: larger; + } + + dd { + margin: 0 0 2em; + padding: 0; + overflow: auto; + } + + table { + width: 100%; + + td, th { + vertical-align: top; + } + + th { + font-weight: normal; + text-align: start; + color: var(--text-insignificant-color); + text-transform: uppercase; + } + + tr > * { + border-top: 1px dashed var(--outline-color); + } + } + + .report-topStatuses { + dt { + font-size: var(--text-size); + } + + dd { + margin-block-end: 1em; + + > a { + display: block; + color: inherit; + text-decoration: none; + border: 2px dashed var(--outline-stronger-color); + + &:is(:hover, :focus) { + border-color: var(--text-color); + } + } + + .status { + pointer-events: none; + font-size: calc(var(--text-size) * .8); + } + } + + } + } +} \ No newline at end of file diff --git a/src/pages/annual-report.jsx b/src/pages/annual-report.jsx new file mode 100644 index 00000000..ab7d1c75 --- /dev/null +++ b/src/pages/annual-report.jsx @@ -0,0 +1,137 @@ +import { t, Trans } from '@lingui/macro'; + +import './annual-report.css'; + +import { useEffect, useState } from 'preact/hooks'; +import { useParams } from 'react-router-dom'; + +import Link from '../components/link'; +import Loader from '../components/loader'; +import NameText from '../components/name-text'; +import Status from '../components/status'; +import { api } from '../utils/api'; +import useTitle from '../utils/useTitle'; + +export default function AnnualReport() { + const params = useParams(); + const { year } = params; + useTitle(year ? `Annual Report: ${year}` : 'Annual Report'); + const { masto, instance } = api(); + const [results, setResults] = useState(null); + const [uiState, setUIState] = useState('default'); + + useEffect(() => { + if (year) { + (async () => { + setUIState('loading'); + const results = await masto.v1.annualReports.$select(year).fetch(); + console.log('REPORT', results); + setResults(results); + setUIState('default'); + })(); + } + }, [year]); + + const { accounts, annualReports, statuses } = results || {}; + const report = annualReports?.find((report) => report.year == year)?.data; + + return ( +
+
+

{year} #Wrapstodon

+ {uiState === 'loading' && ( +

+ Loading… +

+ )} + {!!report && ( +
+ {Object.entries(report).map(([key, value]) => ( + <> +
{key}
+
+ {Array.isArray(value) ? ( + + + + {Object.keys(value[0]).map((key) => ( + + ))} + + + + {value.map((item) => ( + + {Object.entries(item).map(([k, value]) => ( + + ))} + + ))} + +
{key}
+ {value && /(accountId)/i.test(k) && + /^(mostRebloggedAccounts|commonlyInteractedWithAccounts)$/i.test( + key, + ) ? ( + a.id === value, + )} + showAvatar + /> + ) : ( + value + )} +
+ ) : typeof value === 'object' ? ( + /^(topStatuses)$/i.test(key) ? ( +
+ {Object.entries(value).map(([k, value]) => ( + <> +
{k}
+
+ {value && + + s.id === value)} + size="s" + readOnly + /> + } +
+ + ))} +
+ ) : ( + + + {Object.entries(value).map(([k, value]) => ( + + + + + ))} + +
{k}{value}
+ ) + ) : typeof value === 'string' ? ( + value + ) : ( + // Last resort + JSON.stringify(value, null, 2) + )} +
+ + ))} +
+ )} +
+
+

+ + Go home + +

+
+ ); +}