initial commit

This commit is contained in:
thislight 2024-07-14 20:28:44 +08:00
commit 5449e361d5
46 changed files with 8309 additions and 0 deletions

12
src/masto/acct.ts Normal file
View file

@ -0,0 +1,12 @@
import { Accessor, createResource } from "solid-js";
import { Account } from "../accounts/stores";
import { useMastoClientFor } from "./clients";
export function useAcctProfile(account: Accessor<Account>) {
const client = useMastoClientFor(account)
return createResource(client, (client) => {
return client.v1.accounts.verifyCredentials()
}, {
name: "MastodonAccountProfile"
})
}

41
src/masto/clients.ts Normal file
View file

@ -0,0 +1,41 @@
import { Accessor, createMemo, createResource } from "solid-js";
import { Account } from "../accounts/stores";
import { createRestAPIClient, mastodon } from "masto";
const restfulCache: Record<string, mastodon.rest.Client> = {}
export function createMastoClientFor(account: Account) {
const cacheKey = [account.site, account.accessToken].join('')
const cache = restfulCache[cacheKey]
if (cache) return cache;
const client = createRestAPIClient({
url: account.site,
accessToken: account.accessToken,
})
restfulCache[cacheKey] = client
return client
}
export function useMastoClientFor(account: Accessor<Account>) {
return createMemo(() => createMastoClientFor(account()))
}
export function createUnauthorizedClient(site: string) {
const cache = restfulCache[site]
if (cache) return cache;
const client = createRestAPIClient({
url: site
})
restfulCache[site] = client
return client
}
export function useInstance(client: Accessor<mastodon.rest.Client>) {
return createResource(client, async (client) => {
return await client.v2.instance.fetch()
})
}

75
src/masto/timelines.ts Normal file
View file

@ -0,0 +1,75 @@
import { type mastodon } from "masto";
import { Accessor, createResource, createSignal } from "solid-js";
type TimelineFetchTips = {
direction?: "new" | "old";
};
type Timeline = {
list(params: {
maxId?: string;
minId?: string;
}): mastodon.Paginator<mastodon.v1.Status[], unknown>;
};
export function useTimeline(
timeline: Accessor<Timeline>,
) {
let minId: string | undefined;
let maxId: string | undefined;
let otl: Timeline | undefined;
const idSet = new Set<string>();
return createResource<
mastodon.v1.Status[],
[Timeline],
TimelineFetchTips | undefined
>(
() => [timeline()] as const,
async ([tl], info) => {
if (otl !== tl) {
minId = undefined;
maxId = undefined;
idSet.clear();
info.value = [];
otl = tl;
}
const direction =
typeof info.refetching !== "boolean"
? info.refetching?.direction
: "old";
const pager = await tl.list(
direction === "old"
? {
maxId: minId,
}
: {
minId: maxId,
},
);
const old = info.value || [];
const diff = pager.filter((x) => !idSet.has(x.id));
for (const v of diff.map((x) => x.id)) {
idSet.add(v);
}
if (direction === "old") {
minId = pager[pager.length - 1]?.id;
if (!maxId && pager.length > 0) {
maxId = pager[0].id;
}
return [...old, ...diff];
} else {
maxId = pager.length > 0 ? pager[0].id : undefined;
if (!minId && pager.length > 0) {
minId = pager[pager.length - 1]?.id;
}
return [...diff, ...old];
}
},
{
initialValue: [],
storage(init) {
return createSignal(init, { equals: false });
},
},
);
}

35
src/masto/toot.ts Normal file
View file

@ -0,0 +1,35 @@
import type { mastodon } from "masto";
import { createRenderEffect, createResource, type Accessor } from "solid-js";
const CUSTOM_EMOJI_REGEX = /:(\S+):/g;
/**
* Resolve the custom emojis in string to HTML.
*/
export function resolveCustomEmoji(
content: string,
emojis: mastodon.v1.CustomEmoji[],
) {
return content.replace(CUSTOM_EMOJI_REGEX, (original, shortcode: string) => {
const emoji = emojis.find((x) => x.shortcode === shortcode);
if (!emoji) {
return original;
}
return `<img src="${emoji.url}" class="custom-emoji" alt="${shortcode}"/>`;
});
}
export function appliedCustomEmoji(
target: { innerHTML: string },
content: string,
emojis?: mastodon.v1.CustomEmoji[],
) {
createRenderEffect(() => {
const result = emojis ? resolveCustomEmoji(content, emojis) : content;
target.innerHTML = result;
});
}
export function hasCustomEmoji(s: string) {
return CUSTOM_EMOJI_REGEX.test(s);
}