initial commit
This commit is contained in:
commit
5449e361d5
46 changed files with 8309 additions and 0 deletions
12
src/masto/acct.ts
Normal file
12
src/masto/acct.ts
Normal 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
41
src/masto/clients.ts
Normal 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
75
src/masto/timelines.ts
Normal 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
35
src/masto/toot.ts
Normal 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);
|
||||
}
|
Loading…
Add table
Add a link
Reference in a new issue