this repo has no description
0
fork

Configure Feed

Select the types of activity you want to include in your feed.

Super MVP-ish annual report page

+258 -19
+2
src/app.jsx
··· 46 46 import StatusRoute from './pages/status-route'; 47 47 import Trending from './pages/trending'; 48 48 import Welcome from './pages/welcome'; 49 + import AnnualReport from './pages/annual-report'; 49 50 import { 50 51 api, 51 52 hasInstance, ··· 546 547 <Route path="/fh" element={<FollowedHashtags />} /> 547 548 <Route path="/ft" element={<Filters />} /> 548 549 <Route path="/catchup" element={<Catchup />} /> 550 + <Route path="/annual_report/:year" element={<AnnualReport />} /> 549 551 </> 550 552 )} 551 553 <Route path="/:instance?/t/:hashtag" element={<Hashtag />} />
+13
src/components/notification.jsx
··· 261 261 ), 262 262 emoji_reaction: emojiText, 263 263 'pleroma:emoji_reaction': emojiText, 264 + annual_report: ({ year }) => ( 265 + <Trans>Your {year} #Wrapstodon is here!</Trans> 266 + ), 264 267 }; 265 268 266 269 // account_suspension, domain_block, user_domain_block ··· 312 315 report, 313 316 event, 314 317 moderation_warning, 318 + annualReport, 315 319 // Client-side grouped notification 316 320 _ids, 317 321 _accounts, ··· 409 413 emoji: notification.emoji, 410 414 emojiURL, 411 415 }); 416 + } else if (type === 'annual_report') { 417 + text = text({ 418 + ...notification.annualReport, 419 + }); 412 420 } else { 413 421 text = text({ 414 422 account: account ? ( ··· 525 533 Learn more <Icon icon="external" size="s" /> 526 534 </Trans> 527 535 </a> 536 + </div> 537 + )} 538 + {type === 'annual_report' && ( 539 + <div> 540 + <Link to={`/annual_report/${annualReport?.year}`}><Trans>View #Wrapstodon</Trans></Link> 528 541 </div> 529 542 )} 530 543 </>
+29 -19
src/locales/en.po
··· 963 963 msgstr "" 964 964 965 965 #: src/components/generic-accounts.jsx:145 966 - #: src/components/notification.jsx:438 966 + #: src/components/notification.jsx:446 967 967 #: src/pages/accounts.jsx:41 968 968 #: src/pages/search.jsx:317 969 969 #: src/pages/search.jsx:350 ··· 1472 1472 msgid "Moderation warning" 1473 1473 msgstr "" 1474 1474 1475 - #: src/components/notification.jsx:269 1475 + #: src/components/notification.jsx:265 1476 + msgid "Your {year} #Wrapstodon is here!" 1477 + msgstr "Your {year} #Wrapstodon is here!" 1478 + 1479 + #: src/components/notification.jsx:272 1476 1480 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." 1477 1481 msgstr "" 1478 1482 1479 - #: src/components/notification.jsx:275 1483 + #: src/components/notification.jsx:278 1480 1484 msgid "An admin from <0>{from}</0> has blocked <1>{targetName}</1>. Affected followers: {followersCount}, followings: {followingCount}." 1481 1485 msgstr "" 1482 1486 1483 - #: src/components/notification.jsx:281 1487 + #: src/components/notification.jsx:284 1484 1488 msgid "You have blocked <0>{targetName}</0>. Removed followers: {followersCount}, followings: {followingCount}." 1485 1489 msgstr "" 1486 1490 1487 - #: src/components/notification.jsx:289 1491 + #: src/components/notification.jsx:292 1488 1492 msgid "Your account has received a moderation warning." 1489 1493 msgstr "" 1490 1494 1491 - #: src/components/notification.jsx:290 1495 + #: src/components/notification.jsx:293 1492 1496 msgid "Your account has been disabled." 1493 1497 msgstr "" 1494 1498 1495 - #: src/components/notification.jsx:291 1499 + #: src/components/notification.jsx:294 1496 1500 msgid "Some of your posts have been marked as sensitive." 1497 1501 msgstr "" 1498 1502 1499 - #: src/components/notification.jsx:292 1503 + #: src/components/notification.jsx:295 1500 1504 msgid "Some of your posts have been deleted." 1501 1505 msgstr "" 1502 1506 1503 - #: src/components/notification.jsx:293 1507 + #: src/components/notification.jsx:296 1504 1508 msgid "Your posts will be marked as sensitive from now on." 1505 1509 msgstr "" 1506 1510 1507 - #: src/components/notification.jsx:294 1511 + #: src/components/notification.jsx:297 1508 1512 msgid "Your account has been limited." 1509 1513 msgstr "" 1510 1514 1511 - #: src/components/notification.jsx:295 1515 + #: src/components/notification.jsx:298 1512 1516 msgid "Your account has been suspended." 1513 1517 msgstr "" 1514 1518 1515 - #: src/components/notification.jsx:369 1519 + #: src/components/notification.jsx:373 1516 1520 msgid "[Unknown notification type: {type}]" 1517 1521 msgstr "" 1518 1522 1519 - #: src/components/notification.jsx:434 1523 + #: src/components/notification.jsx:442 1520 1524 #: src/components/status.jsx:1036 1521 1525 #: src/components/status.jsx:1046 1522 1526 msgid "Boosted/Liked by…" 1523 1527 msgstr "" 1524 1528 1525 - #: src/components/notification.jsx:435 1529 + #: src/components/notification.jsx:443 1526 1530 msgid "Liked by…" 1527 1531 msgstr "" 1528 1532 1529 - #: src/components/notification.jsx:436 1533 + #: src/components/notification.jsx:444 1530 1534 msgid "Boosted by…" 1531 1535 msgstr "" 1532 1536 1533 - #: src/components/notification.jsx:437 1537 + #: src/components/notification.jsx:445 1534 1538 msgid "Followed by…" 1535 1539 msgstr "" 1536 1540 1537 - #: src/components/notification.jsx:508 1538 - #: src/components/notification.jsx:524 1541 + #: src/components/notification.jsx:516 1542 + #: src/components/notification.jsx:532 1539 1543 msgid "Learn more <0/>" 1540 1544 msgstr "" 1541 1545 1542 - #: src/components/notification.jsx:756 1546 + #: src/components/notification.jsx:540 1547 + msgid "View #Wrapstodon" 1548 + msgstr "View #Wrapstodon" 1549 + 1550 + #: src/components/notification.jsx:769 1543 1551 #: src/components/status.jsx:267 1544 1552 msgid "Read more →" 1545 1553 msgstr "" ··· 2260 2268 msgstr "" 2261 2269 2262 2270 #: src/components/status.jsx:3006 2271 + #: src/pages/annual-report.jsx:44 2263 2272 msgid "Loading…" 2264 2273 msgstr "" 2265 2274 ··· 2401 2410 msgstr "Login required." 2402 2411 2403 2412 #: src/compose.jsx:90 2413 + #: src/pages/annual-report.jsx:132 2404 2414 #: src/pages/http-route.jsx:91 2405 2415 #: src/pages/login.jsx:270 2406 2416 msgid "Go home"
+77
src/pages/annual-report.css
··· 1 + #annual-report-page { 2 + .report { 3 + background-color: var(--bg-color); 4 + border: 16px ridge var(--bg-faded-color); 5 + box-shadow: 0 0 0 2px var(--bg-color); 6 + padding: 16px; 7 + margin: 80px auto; 8 + max-width: var(--main-width); 9 + font-family: var(--monospace-font); 10 + font-variant-numeric: slashed-zero; 11 + font-feature-settings: 'ss01'; 12 + font-variant-numeric: tabular-nums; 13 + min-height: 80vh; 14 + 15 + h1 { 16 + margin: 0; 17 + padding: 0; 18 + } 19 + 20 + dt { 21 + font-weight: bold; 22 + font-size: larger; 23 + } 24 + 25 + dd { 26 + margin: 0 0 2em; 27 + padding: 0; 28 + overflow: auto; 29 + } 30 + 31 + table { 32 + width: 100%; 33 + 34 + td, th { 35 + vertical-align: top; 36 + } 37 + 38 + th { 39 + font-weight: normal; 40 + text-align: start; 41 + color: var(--text-insignificant-color); 42 + text-transform: uppercase; 43 + } 44 + 45 + tr > * { 46 + border-top: 1px dashed var(--outline-color); 47 + } 48 + } 49 + 50 + .report-topStatuses { 51 + dt { 52 + font-size: var(--text-size); 53 + } 54 + 55 + dd { 56 + margin-block-end: 1em; 57 + 58 + > a { 59 + display: block; 60 + color: inherit; 61 + text-decoration: none; 62 + border: 2px dashed var(--outline-stronger-color); 63 + 64 + &:is(:hover, :focus) { 65 + border-color: var(--text-color); 66 + } 67 + } 68 + 69 + .status { 70 + pointer-events: none; 71 + font-size: calc(var(--text-size) * .8); 72 + } 73 + } 74 + 75 + } 76 + } 77 + }
+137
src/pages/annual-report.jsx
··· 1 + import { t, Trans } from '@lingui/macro'; 2 + 3 + import './annual-report.css'; 4 + 5 + import { useEffect, useState } from 'preact/hooks'; 6 + import { useParams } from 'react-router-dom'; 7 + 8 + import Link from '../components/link'; 9 + import Loader from '../components/loader'; 10 + import NameText from '../components/name-text'; 11 + import Status from '../components/status'; 12 + import { api } from '../utils/api'; 13 + import useTitle from '../utils/useTitle'; 14 + 15 + export default function AnnualReport() { 16 + const params = useParams(); 17 + const { year } = params; 18 + useTitle(year ? `Annual Report: ${year}` : 'Annual Report'); 19 + const { masto, instance } = api(); 20 + const [results, setResults] = useState(null); 21 + const [uiState, setUIState] = useState('default'); 22 + 23 + useEffect(() => { 24 + if (year) { 25 + (async () => { 26 + setUIState('loading'); 27 + const results = await masto.v1.annualReports.$select(year).fetch(); 28 + console.log('REPORT', results); 29 + setResults(results); 30 + setUIState('default'); 31 + })(); 32 + } 33 + }, [year]); 34 + 35 + const { accounts, annualReports, statuses } = results || {}; 36 + const report = annualReports?.find((report) => report.year == year)?.data; 37 + 38 + return ( 39 + <div id="annual-report-page" class="deck-container" tabIndex="-1"> 40 + <div class="report"> 41 + <h1>{year} #Wrapstodon</h1> 42 + {uiState === 'loading' && ( 43 + <p> 44 + <Loader abrupt /> <Trans>Loading…</Trans> 45 + </p> 46 + )} 47 + {!!report && ( 48 + <dl> 49 + {Object.entries(report).map(([key, value]) => ( 50 + <> 51 + <dt>{key}</dt> 52 + <dd class={`report-${key}`}> 53 + {Array.isArray(value) ? ( 54 + <table> 55 + <thead> 56 + <tr> 57 + {Object.keys(value[0]).map((key) => ( 58 + <th>{key}</th> 59 + ))} 60 + </tr> 61 + </thead> 62 + <tbody> 63 + {value.map((item) => ( 64 + <tr> 65 + {Object.entries(item).map(([k, value]) => ( 66 + <td> 67 + {value && /(accountId)/i.test(k) && 68 + /^(mostRebloggedAccounts|commonlyInteractedWithAccounts)$/i.test( 69 + key, 70 + ) ? ( 71 + <NameText 72 + account={accounts?.find( 73 + (a) => a.id === value, 74 + )} 75 + showAvatar 76 + /> 77 + ) : ( 78 + value 79 + )} 80 + </td> 81 + ))} 82 + </tr> 83 + ))} 84 + </tbody> 85 + </table> 86 + ) : typeof value === 'object' ? ( 87 + /^(topStatuses)$/i.test(key) ? ( 88 + <dl> 89 + {Object.entries(value).map(([k, value]) => ( 90 + <> 91 + <dt>{k}</dt> 92 + <dd> 93 + {value && 94 + <Link to={`/${instance}/s/${value}`}> 95 + <Status 96 + status={statuses?.find((s) => s.id === value)} 97 + size="s" 98 + readOnly 99 + /> 100 + </Link>} 101 + </dd> 102 + </> 103 + ))} 104 + </dl> 105 + ) : ( 106 + <table> 107 + <tbody> 108 + {Object.entries(value).map(([k, value]) => ( 109 + <tr> 110 + <th>{k}</th> 111 + <td>{value}</td> 112 + </tr> 113 + ))} 114 + </tbody> 115 + </table> 116 + ) 117 + ) : typeof value === 'string' ? ( 118 + value 119 + ) : ( 120 + // Last resort 121 + JSON.stringify(value, null, 2) 122 + )} 123 + </dd> 124 + </> 125 + ))} 126 + </dl> 127 + )} 128 + </div> 129 + <hr /> 130 + <p style={{ textAlign: 'center' }}> 131 + <Link to="/"> 132 + <Trans>Go home</Trans> 133 + </Link> 134 + </p> 135 + </div> 136 + ); 137 + }