Prettier Linting
Some checks failed
Run Tests on Code / test (push) Has been cancelled
Build Docker Image on Pull Request / test (pull_request) Failing after 3m47s

This commit is contained in:
Neshura 2024-01-06 14:02:30 +01:00
parent 7f3eb2888a
commit 4254015cd8
Signed by: Neshura
GPG key ID: B6983AAA6B9A7A6C
11 changed files with 243 additions and 201 deletions

View file

@ -1,173 +1,193 @@
<svelte:options runes={true} /> <svelte:options runes={true} />
<script lang="ts"> <script lang="ts">
import {onMount} from "svelte"; import { onMount } from 'svelte';
import sanitizeHtml from "sanitize-html"; import sanitizeHtml from 'sanitize-html';
import {Skeleton} from "$lib/components/ui/skeleton/index.js"; import { Skeleton } from '$lib/components/ui/skeleton/index.js';
import {DoubleArrowUp} from "radix-icons-svelte"; import { DoubleArrowUp } from 'radix-icons-svelte';
let { account, maxToots, accountId, excludeReplies }: { account: string, maxToots?: number, accountId?: string, excludeReplies: boolean } = $props(); let {
account,
maxToots,
accountId,
excludeReplies
}: { account: string; maxToots?: number; accountId?: string; excludeReplies: boolean } = $props();
let toots: Toot[] = $state([]); let toots: Toot[] = $state([]);
let loading = $state(false); let loading = $state(false);
onMount(() => { onMount(() => {
loading = true; loading = true;
loadToots(account, accountId, maxToots, excludeReplies); loadToots(account, accountId, maxToots, excludeReplies);
}) });
interface Toot { interface Toot {
created_at: string; created_at: string;
in_reply_to_id: string | null; in_reply_to_id: string | null;
content: string; content: string;
url: string; url: string;
account: { account: {
username: string; username: string;
display_name: string; display_name: string;
avatar: string; avatar: string;
url: string; url: string;
}; };
reblog?: Toot; reblog?: Toot;
media_attachments: { media_attachments: {
type: "unknown" | "image" | "gifv" | "video" | "audio"; type: 'unknown' | 'image' | 'gifv' | 'video' | 'audio';
url: string; url: string;
preview_url: string; preview_url: string;
description: string; description: string;
blurhash: string; blurhash: string;
}[]; }[];
} }
export async function getToots( export async function getToots(
userURL: string, userURL: string,
limit: number, limit: number,
excludeReplies: boolean, excludeReplies: boolean,
accountId?: string, accountId?: string
): Promise<Toot[]> { ): Promise<Toot[]> {
const url = new URL(userURL); const url = new URL(userURL);
// Either use the account id specified or look it up based on the username // Either use the account id specified or look it up based on the username
// in the link. // in the link.
const userId: string = const userId: string =
accountId ?? accountId ??
(await (async () => { (await (async () => {
// Extract username from URL. // Extract username from URL.
const parts = /@(\w+)$/.exec(url.pathname); const parts = /@(\w+)$/.exec(url.pathname);
if (!parts) { if (!parts) {
throw "not a Mastodon user URL"; throw 'not a Mastodon user URL';
} }
const username = parts[1]; const username = parts[1];
// Look up user ID from username. // Look up user ID from username.
const lookupURL = Object.assign(new URL(url), { const lookupURL = Object.assign(new URL(url), {
pathname: "/api/v1/accounts/lookup", pathname: '/api/v1/accounts/lookup',
search: `?acct=${username}`, search: `?acct=${username}`
}); });
return (await (await fetch(lookupURL)).json())["id"]; return (await (await fetch(lookupURL)).json())['id'];
})()); })());
// Fetch toots. // Fetch toots.
const tootURL = Object.assign(new URL(url), { const tootURL = Object.assign(new URL(url), {
pathname: `/api/v1/accounts/${userId}/statuses`, pathname: `/api/v1/accounts/${userId}/statuses`,
search: `?limit=${limit ?? 5}&exclude_replies=${!!excludeReplies}`, search: `?limit=${limit ?? 5}&exclude_replies=${!!excludeReplies}`
}); });
return await (await fetch(tootURL)).json(); return await (await fetch(tootURL)).json();
} }
function loadToots() {
function loadToots() { getToots(account, maxToots ?? 5, excludeReplies === true, accountId).then((data) => {
getToots( toots = data;
account, loading = false;
maxToots ?? 5, });
excludeReplies === true, }
accountId,
).then((data) => {
toots = data;
loading = false;
});
}
</script> </script>
{#snippet avatar(toot)} {#snippet avatar(toot)}
<a class="flex flex-row gap-2" href={toot.account.url}> <a class="flex flex-row gap-2" href={toot.account.url}>
<img class="rounded-md" width="48px" height="48px" src={toot.account.avatar} alt="{toot.account.username} avatar"/> <img
<div class="flex flex-col items-start"> class="rounded-md"
<span class="hover:underline font-bold h-6">{toot.account.display_name}</span> width="48px"
<span class="h-4 text-sm text-muted">@{toot.account.username}</span> height="48px"
</div> src={toot.account.avatar}
</a> alt="{toot.account.username} avatar"
/>
<div class="flex flex-col items-start">
<span class="h-6 font-bold hover:underline">{toot.account.display_name}</span>
<span class="h-4 text-sm text-muted">@{toot.account.username}</span>
</div>
</a>
{/snippet} {/snippet}
{#snippet body(toot)} {#snippet body(toot)}
<div> <div>
<div class="[&>p>span>a]:hover:underline"> <div class="[&>p>span>a]:hover:underline">
{@html sanitizeHtml(toot.content)} {@html sanitizeHtml(toot.content)}
</div> </div>
{#each toot.media_attachments.filter((att) => att.type === "image") as image} {#each toot.media_attachments.filter((att) => att.type === 'image') as image}
<a class="block rounded-md aspect-16/9 w-full overflow-hidden" href={image.url} target="_blank" rel="noopener noreferrer"> <a
<img class="h-full w-full object-cover" src={image.preview_url} alt={image.description}/> class="block aspect-16/9 w-full overflow-hidden rounded-md"
</a> href={image.url}
{/each} target="_blank"
</div> rel="noopener noreferrer"
>
<img class="h-full w-full object-cover" src={image.preview_url} alt={image.description} />
</a>
{/each}
</div>
{/snippet} {/snippet}
<ol class="w-full"> <ol class="w-full">
{#if loading} {#if loading}
{#each Array(maxToots ?? 5) as placeholder} {#each Array(maxToots ?? 5) as placeholder}
<li class="flex flex-col gap-3 px-4 py-3"> <li class="flex flex-col gap-3 px-4 py-3">
<div class="flex flex-row justify-between"> <div class="flex flex-row justify-between">
<div class="flex flex-row gap-2"> <div class="flex flex-row gap-2">
<Skeleton class="h-12 w-12 rounded-md"/> <Skeleton class="h-12 w-12 rounded-md" />
<div class="flex flex-col items-start gap-1"> <div class="flex flex-col items-start gap-1">
<Skeleton class="h-6 w-24"></Skeleton> <Skeleton class="h-6 w-24"></Skeleton>
<Skeleton class="h-4 w-20"></Skeleton> <Skeleton class="h-4 w-20"></Skeleton>
</div> </div>
</div> </div>
<Skeleton class="h-10 w-16" /> <Skeleton class="h-10 w-16" />
</div> </div>
<Skeleton class="h-36 w-full"></Skeleton> <Skeleton class="h-36 w-full"></Skeleton>
</li> </li>
{/each} {/each}
{:else} {:else}
{#each toots as toot} {#each toots as toot}
<li class="flex flex-col gap-3 px-4 py-3"> <li class="flex flex-col gap-3 px-4 py-3">
{#if toot.reblog} {#if toot.reblog}
<div class="flex flex-row justify-between"> <div class="flex flex-row justify-between">
<div class="flex flex-col gap-1"> <div class="flex flex-col gap-1">
<a class="items-center flex flex-row gap-1" href={toot.account.url}> <a class="flex flex-row items-center gap-1" href={toot.account.url}>
<DoubleArrowUp /> <DoubleArrowUp />
<img class="rounded-md" width="23px" height="23px" src={toot.account.avatar} alt="{toot.account.username} avatar"/> <img
<span class="hover:underline font-bold h-6">{toot.account.display_name}</span> class="rounded-md"
</a> width="23px"
{@render avatar(toot.reblog)} height="23px"
</div> src={toot.account.avatar}
<a class="flex flex-col items-center hover:underline text-sm text-muted" href={toot.url}> alt="{toot.account.username} avatar"
<time datetime={toot.created_at}> />
{new Date(toot.created_at).toLocaleDateString()} <span class="h-6 font-bold hover:underline">{toot.account.display_name}</span>
</time> </a>
<time datetime={toot.created_at}> {@render avatar(toot.reblog)}
{new Date(toot.created_at).toLocaleTimeString()} </div>
</time> <a
</a> class="flex flex-col items-center text-sm text-muted hover:underline"
</div> href={toot.url}
{@render body(toot.reblog)} >
{:else} <time datetime={toot.created_at}>
<div class="flex flex-row justify-between"> {new Date(toot.created_at).toLocaleDateString()}
{@render avatar(toot)} </time>
<a class="flex flex-col items-center hover:underline text-sm text-muted" href={toot.url}> <time datetime={toot.created_at}>
<time datetime={toot.created_at}> {new Date(toot.created_at).toLocaleTimeString()}
{new Date(toot.created_at).toLocaleDateString()} </time>
</time> </a>
<time datetime={toot.created_at}> </div>
{new Date(toot.created_at).toLocaleTimeString()} {@render body(toot.reblog)}
</time> {:else}
</a> <div class="flex flex-row justify-between">
</div> {@render avatar(toot)}
{@render body(toot)} <a
{/if} class="flex flex-col items-center text-sm text-muted hover:underline"
</li> href={toot.url}
{/each} >
{/if} <time datetime={toot.created_at}>
{new Date(toot.created_at).toLocaleDateString()}
</time>
<time datetime={toot.created_at}>
{new Date(toot.created_at).toLocaleTimeString()}
</time>
</a>
</div>
{@render body(toot)}
{/if}
</li>
{/each}
{/if}
</ol> </ol>

View file

@ -1,4 +1,4 @@
import Root from "./separator.svelte"; import Root from './separator.svelte';
export { export {
Root, Root,

View file

@ -1,19 +1,19 @@
<script lang="ts"> <script lang="ts">
import { Separator as SeparatorPrimitive } from "bits-ui"; import { Separator as SeparatorPrimitive } from 'bits-ui';
import { cn } from "$lib/utils"; import { cn } from '$lib/utils';
type $$Props = SeparatorPrimitive.Props; type $$Props = SeparatorPrimitive.Props;
let className: $$Props["class"] = undefined; let className: $$Props['class'] = undefined;
export let orientation: $$Props["orientation"] = "horizontal"; export let orientation: $$Props['orientation'] = 'horizontal';
export let decorative: $$Props["decorative"] = undefined; export let decorative: $$Props['decorative'] = undefined;
export { className as class }; export { className as class };
</script> </script>
<SeparatorPrimitive.Root <SeparatorPrimitive.Root
class={cn( class={cn(
"shrink-0 bg-border", 'shrink-0 bg-border',
orientation === "horizontal" ? "h-[1px] w-full" : "h-full w-[1px]", orientation === 'horizontal' ? 'h-[1px] w-full' : 'h-full w-[1px]',
className className
)} )}
{orientation} {orientation}

View file

@ -1,4 +1,4 @@
import { writable } from 'svelte/store'; import { writable } from 'svelte/store';
import {io} from "socket.io-client"; import { io } from 'socket.io-client';
export let socketStore = writable(io('https://status.neshweb.net/')); export let socketStore = writable(io('https://status.neshweb.net/'));

View file

@ -1,4 +1,4 @@
import {type Writable, writable} from 'svelte/store'; import { type Writable, writable } from 'svelte/store';
import type {Heartbeat} from "$lib/types/uptime-kuma-types"; import type { Heartbeat } from '$lib/types/uptime-kuma-types';
export let uptimeStore: Writable<Map<number, Heartbeat>> = writable(new Map()); export let uptimeStore: Writable<Map<number, Heartbeat>> = writable(new Map());

View file

@ -3,18 +3,20 @@
<script> <script>
import '../app.pcss'; import '../app.pcss';
import Header from './Header.svelte'; import Header from './Header.svelte';
import { socketStore } from "$lib/stores/socketStore"; import { socketStore } from '$lib/stores/socketStore';
import {beforeNavigate} from "$app/navigation"; import { beforeNavigate } from '$app/navigation';
$effect(() => { $effect(() => {
beforeNavigate((navigation) => { beforeNavigate((navigation) => {
const servers = navigation.to.url.pathname === "/servers" || navigation.from.url.pathname === "/servers" const servers =
const services = navigation.to.url.pathname === "/services" || navigation.from.url.pathname === "/services" navigation.to.url.pathname === '/servers' || navigation.from.url.pathname === '/servers';
if ( ! (servers && services) ) { const services =
$socketStore.close() navigation.to.url.pathname === '/services' || navigation.from.url.pathname === '/services';
if (!(servers && services)) {
$socketStore.close();
} }
}) });
}) });
</script> </script>
<Header /> <Header />

View file

@ -1,7 +1,7 @@
<script lang="ts"> <script lang="ts">
import {Separator} from "$lib/components/ui/separator"; import { Separator } from '$lib/components/ui/separator';
import {OpenInNewWindow} from "radix-icons-svelte"; import { OpenInNewWindow } from 'radix-icons-svelte';
import Emfed from "$lib/components/Emfed.svelte"; import Emfed from '$lib/components/Emfed.svelte';
</script> </script>
<svelte:head> <svelte:head>
@ -9,32 +9,55 @@
<meta name="description" content="Landing Page for neshweb.net" /> <meta name="description" content="Landing Page for neshweb.net" />
</svelte:head> </svelte:head>
<div class="grid max-h-full grid-cols-3 gap-4 justify-center overflow-auto p-8"> <div class="grid max-h-full grid-cols-3 justify-center gap-4 overflow-auto p-8">
<div class="flex flex-col items-center col-span-2"> <div class="col-span-2 flex flex-col items-center">
<div class="flex flex-col border rounded-md p-4 bg-black/55 backdrop-blur-sm gap-y-2"> <div class="flex flex-col gap-y-2 rounded-md border bg-black/55 p-4 backdrop-blur-sm">
<h1 class="text-2xl text-center" >Home Page</h1> <h1 class="text-center text-2xl">Home Page</h1>
<p>I'm not sure what to put here quite yet, maybe I'll think of something eventually. In the meantime I've linked some of my accounts in the sidebar to the right</p> <p>
I'm not sure what to put here quite yet, maybe I'll think of something eventually. In the
meantime I've linked some of my accounts in the sidebar to the right
</p>
</div> </div>
</div> </div>
<div class="flex flex-col gap-y-1 items-center border rounded-md bg-black/55 backdrop-blur-sm py-1"> <div
class="flex flex-col items-center gap-y-1 rounded-md border bg-black/55 py-1 backdrop-blur-sm"
>
<p class="font-bold">Fediverse Accounts</p> <p class="font-bold">Fediverse Accounts</p>
<Separator class="max-w-80" /> <Separator class="max-w-80" />
<a rel="me" href="https://mastodon.neshweb.net/@neshura" target="_blank" class="flex flex-row gap-1 items-center hover:text-secondary"> <a
rel="me"
href="https://mastodon.neshweb.net/@neshura"
target="_blank"
class="flex flex-row items-center gap-1 hover:text-secondary"
>
Mastodon Mastodon
<OpenInNewWindow /> <OpenInNewWindow />
</a> </a>
<a rel="noopener noreferrer" href="https://bookwormstory.social/u/Neshura" target="_blank" class="flex flex-row gap-1 items-center hover:text-secondary"> <a
rel="noopener noreferrer"
href="https://bookwormstory.social/u/Neshura"
target="_blank"
class="flex flex-row items-center gap-1 hover:text-secondary"
>
Lemmy Lemmy
<OpenInNewWindow /> <OpenInNewWindow />
</a> </a>
<a rel="noopener noreferrer" href="https://neshweb.tv/c/neshura_ch/videos" target="_blank" class="flex flex-row gap-1 items-center hover:text-secondary"> <a
rel="noopener noreferrer"
href="https://neshweb.tv/c/neshura_ch/videos"
target="_blank"
class="flex flex-row items-center gap-1 hover:text-secondary"
>
PeerTube PeerTube
<OpenInNewWindow /> <OpenInNewWindow />
</a> </a>
<Separator class="max-w-80" /> <Separator class="max-w-80" />
<p class="font-bold">Mastodon Feed</p> <p class="font-bold">Mastodon Feed</p>
<Separator class="max-w-80" /> <Separator class="max-w-80" />
<Emfed account="https://mastodon.neshweb.net/@neshura" maxToots={4} accountId="109199738141333007"/> <Emfed
account="https://mastodon.neshweb.net/@neshura"
maxToots={4}
accountId="109199738141333007"
/>
</div> </div>
</div> </div>

View file

@ -16,4 +16,3 @@
{version} {version}
</a> </a>
</p> </p>

View file

@ -1,6 +1,6 @@
<svelte:head> <svelte:head>
<title>CSS Test</title> <title>CSS Test</title>
<meta name="description" content="CSS playground" /> <meta name="description" content="CSS playground" />
</svelte:head> </svelte:head>
<p class="text-background">Background</p> <p class="text-background">Background</p>
@ -26,4 +26,4 @@
<p class="text-offline">offline</p> <p class="text-offline">offline</p>
<p class="text-online">online</p> <p class="text-online">online</p>
<p class="text-pending">pending</p> <p class="text-pending">pending</p>
<p class="text-maintenance">maintenance</p> <p class="text-maintenance">maintenance</p>

View file

@ -6,7 +6,7 @@
import type { Heartbeat } from '$lib/types/uptime-kuma-types'; import type { Heartbeat } from '$lib/types/uptime-kuma-types';
import ServerCard from '$lib/components/ServerCard.svelte'; import ServerCard from '$lib/components/ServerCard.svelte';
import { socketStore } from '$lib/stores/socketStore'; import { socketStore } from '$lib/stores/socketStore';
import {uptimeStore} from "$lib/stores/uptimeStore"; import { uptimeStore } from '$lib/stores/uptimeStore';
let { data }: { data: { promise: Promise<string> } } = $props(); let { data }: { data: { promise: Promise<string> } } = $props();
@ -30,12 +30,11 @@
if (token) { if (token) {
if (!socket.connected) { if (!socket.connected) {
socket.connect(); socket.connect();
} } else {
else { console.log('already connected');
console.log("already connected");
} }
socket.on('connect', () => { socket.on('connect', () => {
console.log("logging in") console.log('logging in');
socket.emit('loginByToken', token, () => {}); socket.emit('loginByToken', token, () => {});
}); });

View file

@ -5,9 +5,9 @@
import type { Service } from '$lib/types/data-types'; import type { Service } from '$lib/types/data-types';
import { io } from 'socket.io-client'; import { io } from 'socket.io-client';
import type { Heartbeat } from '$lib/types/uptime-kuma-types'; import type { Heartbeat } from '$lib/types/uptime-kuma-types';
import {socketStore} from "$lib/stores/socketStore"; import { socketStore } from '$lib/stores/socketStore';
import {onDestroy} from "svelte"; import { onDestroy } from 'svelte';
import {uptimeStore} from "$lib/stores/uptimeStore"; import { uptimeStore } from '$lib/stores/uptimeStore';
let { data }: { data: { promise: Promise<string> } } = $props(); let { data }: { data: { promise: Promise<string> } } = $props();
@ -29,12 +29,11 @@
if (token) { if (token) {
if (!socket.connected) { if (!socket.connected) {
socket.connect(); socket.connect();
} } else {
else { console.log('connected');
console.log("connected")
} }
socket.on('connect', () => { socket.on('connect', () => {
console.log("login") console.log('login');
socket.emit('loginByToken', token, () => {}); socket.emit('loginByToken', token, () => {});
}); });