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