mirror of
https://github.com/cheeaun/phanpy.git
synced 2025-01-22 08:36:21 +01:00
Super MVP-ish annual report page
This commit is contained in:
parent
ab6a977aeb
commit
4270304a28
5 changed files with 258 additions and 19 deletions
|
@ -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 }) {
|
|||
<Route path="/fh" element={<FollowedHashtags />} />
|
||||
<Route path="/ft" element={<Filters />} />
|
||||
<Route path="/catchup" element={<Catchup />} />
|
||||
<Route path="/annual_report/:year" element={<AnnualReport />} />
|
||||
</>
|
||||
)}
|
||||
<Route path="/:instance?/t/:hashtag" element={<Hashtag />} />
|
||||
|
|
|
@ -261,6 +261,9 @@ const contentText = {
|
|||
),
|
||||
emoji_reaction: emojiText,
|
||||
'pleroma:emoji_reaction': emojiText,
|
||||
annual_report: ({ year }) => (
|
||||
<Trans>Your {year} #Wrapstodon is here!</Trans>
|
||||
),
|
||||
};
|
||||
|
||||
// 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({
|
|||
</a>
|
||||
</div>
|
||||
)}
|
||||
{type === 'annual_report' && (
|
||||
<div>
|
||||
<Link to={`/annual_report/${annualReport?.year}`}><Trans>View #Wrapstodon</Trans></Link>
|
||||
</div>
|
||||
)}
|
||||
</>
|
||||
)}
|
||||
{_accounts?.length > 1 && (
|
||||
|
|
48
src/locales/en.po
generated
48
src/locales/en.po
generated
|
@ -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}</0> has suspended <1>{targetName}</1>, 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}</0> has blocked <1>{targetName}</1>. Affected followers: {followersCount}, followings: {followingCount}."
|
||||
msgstr ""
|
||||
|
||||
#: src/components/notification.jsx:281
|
||||
#: src/components/notification.jsx:284
|
||||
msgid "You have blocked <0>{targetName}</0>. 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"
|
||||
|
|
77
src/pages/annual-report.css
Normal file
77
src/pages/annual-report.css
Normal file
|
@ -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);
|
||||
}
|
||||
}
|
||||
|
||||
}
|
||||
}
|
||||
}
|
137
src/pages/annual-report.jsx
Normal file
137
src/pages/annual-report.jsx
Normal file
|
@ -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 (
|
||||
<div id="annual-report-page" class="deck-container" tabIndex="-1">
|
||||
<div class="report">
|
||||
<h1>{year} #Wrapstodon</h1>
|
||||
{uiState === 'loading' && (
|
||||
<p>
|
||||
<Loader abrupt /> <Trans>Loading…</Trans>
|
||||
</p>
|
||||
)}
|
||||
{!!report && (
|
||||
<dl>
|
||||
{Object.entries(report).map(([key, value]) => (
|
||||
<>
|
||||
<dt>{key}</dt>
|
||||
<dd class={`report-${key}`}>
|
||||
{Array.isArray(value) ? (
|
||||
<table>
|
||||
<thead>
|
||||
<tr>
|
||||
{Object.keys(value[0]).map((key) => (
|
||||
<th>{key}</th>
|
||||
))}
|
||||
</tr>
|
||||
</thead>
|
||||
<tbody>
|
||||
{value.map((item) => (
|
||||
<tr>
|
||||
{Object.entries(item).map(([k, value]) => (
|
||||
<td>
|
||||
{value && /(accountId)/i.test(k) &&
|
||||
/^(mostRebloggedAccounts|commonlyInteractedWithAccounts)$/i.test(
|
||||
key,
|
||||
) ? (
|
||||
<NameText
|
||||
account={accounts?.find(
|
||||
(a) => a.id === value,
|
||||
)}
|
||||
showAvatar
|
||||
/>
|
||||
) : (
|
||||
value
|
||||
)}
|
||||
</td>
|
||||
))}
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
) : typeof value === 'object' ? (
|
||||
/^(topStatuses)$/i.test(key) ? (
|
||||
<dl>
|
||||
{Object.entries(value).map(([k, value]) => (
|
||||
<>
|
||||
<dt>{k}</dt>
|
||||
<dd>
|
||||
{value &&
|
||||
<Link to={`/${instance}/s/${value}`}>
|
||||
<Status
|
||||
status={statuses?.find((s) => s.id === value)}
|
||||
size="s"
|
||||
readOnly
|
||||
/>
|
||||
</Link>}
|
||||
</dd>
|
||||
</>
|
||||
))}
|
||||
</dl>
|
||||
) : (
|
||||
<table>
|
||||
<tbody>
|
||||
{Object.entries(value).map(([k, value]) => (
|
||||
<tr>
|
||||
<th>{k}</th>
|
||||
<td>{value}</td>
|
||||
</tr>
|
||||
))}
|
||||
</tbody>
|
||||
</table>
|
||||
)
|
||||
) : typeof value === 'string' ? (
|
||||
value
|
||||
) : (
|
||||
// Last resort
|
||||
JSON.stringify(value, null, 2)
|
||||
)}
|
||||
</dd>
|
||||
</>
|
||||
))}
|
||||
</dl>
|
||||
)}
|
||||
</div>
|
||||
<hr />
|
||||
<p style={{ textAlign: 'center' }}>
|
||||
<Link to="/">
|
||||
<Trans>Go home</Trans>
|
||||
</Link>
|
||||
</p>
|
||||
</div>
|
||||
);
|
||||
}
|
Loading…
Reference in a new issue