wildebeest/frontend/src/routes/(frontend)/about/index.tsx

158 wiersze
5.4 KiB
TypeScript

import { component$ } from '@builder.io/qwik'
import { DocumentHead, loader$ } from '@builder.io/qwik-city'
import { getDatabase } from 'wildebeest/backend/src/database'
import { getDomain } from 'wildebeest/backend/src/utils/getDomain'
import { handleRequestGet as settingsHandleRequestGet } from 'wildebeest/functions/api/wb/settings/server/server'
import { handleRequestGet as rulesHandleRequestGet } from 'wildebeest/functions/api/v1/instance/rules'
import { Accordion } from '~/components/Accordion/Accordion'
// import { AccountCard } from '~/components/AccountCard/AccountCard'
import { HtmlContent } from '~/components/HtmlContent/HtmlContent'
import { ServerSettingsData } from '~/routes/(admin)/settings/server-settings/layout'
import { Account } from '~/types'
import { getDocumentHead } from '~/utils/getDocumentHead'
import { instanceLoader } from '../layout'
import { getAdmins } from 'wildebeest/functions/api/wb/settings/server/admins'
import { emailSymbol } from 'wildebeest/backend/src/activitypub/actors'
import { loadLocalMastodonAccount } from 'wildebeest/backend/src/mastodon/account'
import { AccountCard } from '~/components/AccountCard/AccountCard'
import { getNotFoundHtml } from '~/utils/getNotFoundHtml/getNotFoundHtml'
type AboutInfo = {
image: string
domain: string
admin: { account: Account | null; email: string }
rules: { id: number; text: string }[]
extended_description: {
content: string
}
}
export const aboutInfoLoader = loader$<Promise<AboutInfo>>(async ({ resolveValue, request, platform, html }) => {
throw html(404, getNotFoundHtml())
// TODO: fetching the instance for the thumbnail, but that should be part of the settings
const instance = await resolveValue(instanceLoader)
const database = await getDatabase(platform)
const brandingDataResp = await settingsHandleRequestGet(database)
let brandingData: ServerSettingsData | null
try {
brandingData = await brandingDataResp.json()
} catch {
brandingData = null
}
const rulesResp = await rulesHandleRequestGet(database)
let rules: { id: number; text: string }[] = []
try {
rules = await rulesResp.json()
} catch {
rules = []
}
const admins = await getAdmins(database)
let adminAccount: Account | null = null
const adminPerson = admins.find((admin) => admin[emailSymbol] === platform.ADMIN_EMAIL)
if (adminPerson) {
try {
adminAccount = (await loadLocalMastodonAccount(database, adminPerson)) as Account
} catch {
/* empty */
}
}
return {
image: instance.thumbnail,
domain: getDomain(request.url),
admin: { account: JSON.parse(JSON.stringify(adminAccount)), email: platform.ADMIN_EMAIL },
rules: JSON.parse(JSON.stringify(rules.sort(({ id: idA }, { id: idB }) => idA - idB))),
extended_description: {
content: brandingData?.['extended description'] ?? '',
},
}
})
export default component$(() => {
const aboutInfo = aboutInfoLoader().value
return (
<>
<div class="bg-wildebeest-900 sticky top-[3.9rem] xl:top-0 xl:pt-2.5 z-10">
<div class="flex flex-col items-center bg-wildebeest-600 xl:rounded-t overflow-hidden p-5">
<img class="rounded w-full aspect-[1.9] mb-5" src={aboutInfo.image} alt="" />
<h2 data-testid="domain-text" class="my-4 text-2xl font-semibold">
{aboutInfo.domain}
</h2>
<p data-testid="social-text" class="mb-6 text-wildebeest-500">
<span>
Decentralised social media powered by{' '}
<a href="https://joinmastodon.org" class="no-underline text-wildebeest-200 font-semibold" target="_blank">
Mastodon
</a>
</span>
</p>
<div
class="rounded bg-wildebeest-700 flex flex-col md:flex-row p-2 w-full my-5 overflow-auto"
data-testid="contact"
>
{!!aboutInfo.admin.account && (
<div class="flex-1 p-4 border-wildebeest-500 border-solid border-b md:border-b-0 md:border-r">
<span class="block uppercase text-wildebeest-500 font-semibold mb-5">Administered by:</span>
<AccountCard account={aboutInfo.admin.account} subText="username" />
</div>
)}
<div class="flex-1 p-4 pt-6 md:pt-4 md:pl-6 min-w-max">
<span class="block uppercase text-wildebeest-500 font-semibold mb-5">Contact:</span>
<span>{aboutInfo.admin.email}</span>
</div>
</div>
<div class="flex flex-col w-full my-5">
<div class="my-1">
<Accordion title="About">
<div class="p-6">
<HtmlContent html={aboutInfo.extended_description.content} />
</div>
</Accordion>
</div>
<div class="my-1">
<Accordion title="Server rules">
<ol class="list-none flex flex-col gap-1 my-5 px-6">
{aboutInfo.rules.map(({ id, text }) => (
<li key={id} class="flex items-center border-wildebeest-700 border-b last-of-type:border-b-0 py-2">
<span class="bg-wildebeest-vibrant-400 text-wildebeest-900 mr-4 my-1 p-4 rounded-full w-5 h-5 grid place-content-center">
{id}
</span>
<span>{text}</span>
</li>
))}
</ol>
</Accordion>
</div>
</div>
</div>
</div>
</>
)
})
export const head: DocumentHead = ({ resolveValue, head }) => {
const instance = resolveValue(instanceLoader)
return getDocumentHead(
{
title: `About - ${instance.title}`,
description: `About page for the ${instance.title} Mastodon instance`,
og: {
type: 'website',
image: instance.thumbnail,
},
},
head
)
}