import { component$, Slot, useStyles$ } from '@builder.io/qwik' import { type DocumentHead, loader$, useLocation, Link } from '@builder.io/qwik-city' import { MastodonAccount } from 'wildebeest/backend/src/types' import StickyHeader from '~/components/StickyHeader/StickyHeader' import { formatDateTime } from '~/utils/dateTime' import { formatRoundedNumber } from '~/utils/numbers' import styles from '../../../utils/innerHtmlContent.scss?inline' import { getAccount } from 'wildebeest/backend/src/accounts/getAccount' import { getNotFoundHtml } from '~/utils/getNotFoundHtml/getNotFoundHtml' import { getErrorHtml } from '~/utils/getErrorHtml/getErrorHtml' import { getDocumentHead } from '~/utils/getDocumentHead' import * as statusAPI from 'wildebeest/functions/api/v1/statuses/[id]' import { useAccountUrl } from '~/utils/useAccountUrl' import { getDatabase } from 'wildebeest/backend/src/database' import { Person } from 'wildebeest/backend/src/activitypub/actors' export const accountPageLoader = loader$< Promise<{ account: MastodonAccount; accountHandle: string; isValidStatus: boolean }> >(async ({ platform, params, request, html }) => { let isValidStatus = false let account: MastodonAccount | null = null try { const url = new URL(request.url) const domain = url.hostname const accountId = url.pathname.split('/')[1] try { const statusResponse = await statusAPI.handleRequestGet( await getDatabase(platform), params.statusId, domain, null as unknown as Person ) const statusText = await statusResponse.text() isValidStatus = !!statusText } catch { isValidStatus = false } account = await getAccount(domain, accountId, await getDatabase(platform)) } catch { throw html( 500, getErrorHtml(`An error happened when trying to retrieve the account's details, please try again later`) ) } if (!account) { throw html(404, getNotFoundHtml()) } const accountDomain = getAccountDomain(account) const accountHandle = `@${account.acct}${accountDomain ? `@${accountDomain}` : ''}` return { account, accountHandle, isValidStatus } }) export default component$(() => { useStyles$(styles) const pageDetails = accountPageLoader().value const showAccountInfo = !pageDetails.isValidStatus const location = useLocation() const currentPath = location.pathname.replace(/\/$/, '') const fields = [ { name: 'Joined', value: formatDateTime(pageDetails.account.created_at, false), }, ...pageDetails.account.fields, ] const stats = [ { name: 'Posts', value: formatRoundedNumber(pageDetails.account.statuses_count), }, { name: 'Following', value: formatRoundedNumber(pageDetails.account.following_count), }, { name: 'Followers', value: formatRoundedNumber(pageDetails.account.followers_count), }, ] const accountUrl = useAccountUrl(pageDetails.account) const tabLinks = [ { text: 'Posts', href: accountUrl, }, { text: 'Posts and replies', href: `${accountUrl}/with_replies`, }, ] return (
{showAccountInfo && ( <>
{`Header {`Avatar

{pageDetails.account.display_name}

{pageDetails.accountHandle}
{fields.map(({ name, value }) => (
{name}
))}
{stats.map(({ name, value }) => (
{value} {name}
))}
{tabLinks.map(({ text, href }) => (
{text}
))}
)}
) }) export function getAccountDomain(account: MastodonAccount): string | null { try { const url = new URL(account.url) return url.hostname || null } catch { return null } } export const head: DocumentHead = ({ resolveValue, head }) => { const { account, accountHandle } = resolveValue(accountPageLoader) return getDocumentHead( { title: `${account.display_name} (${accountHandle}) - Wildebeest`, description: `${account.display_name} account page - Wildebeest`, og: { url: account.url, type: 'article', image: account.avatar, }, }, head ) } export const activeClasses = [ 'relative', 'before:block', 'before:content-[""]', 'before:absolute', 'before:w-0', 'before:h-0', 'before:bottom-[-1px]', 'before:left-1/2', 'before:translate-x-[-50%]', 'before:border-solid', 'before:border-t-0', 'before:border-x-[0.7rem]', 'before:border-b-[0.7rem]', 'before:border-x-transparent', 'before:border-b-wildebeest-500', 'after:block', 'after:content-[""]', 'after:absolute', 'after:w-0', 'after:h-0', 'after:bottom-[-1px]', 'after:left-1/2', 'after:translate-x-[-50%]', 'after:border-solid', 'after:border-t-0', 'after:border-x-[0.7rem]', 'after:border-b-[0.7rem]', 'after:border-x-transparent', 'after:border-b-wildebeest-600', ].join(' ')