Compare commits

..

3 commits

Author SHA1 Message Date
thislight
b4f7a863a2
start of v1.0.2
All checks were successful
/ depoly (push) Successful in 1m15s
2024-07-15 14:00:58 +08:00
thislight
b8340aecd6
bump version
All checks were successful
/ depoly (push) Successful in 1m16s
2024-07-15 14:00:12 +08:00
thislight
00fa334d42
rewrite client providing & fix sign in redirect 2024-07-15 13:59:10 +08:00
6 changed files with 107 additions and 54 deletions

View file

@ -1,7 +1,7 @@
{ {
"$schema": "https://json.schemastore.org/package", "$schema": "https://json.schemastore.org/package",
"name": "tutu", "name": "tutu",
"version": "1.0.0", "version": "1.0.2",
"description": "", "description": "",
"private": true, "private": true,
"type": "module", "type": "module",

View file

@ -1,12 +1,27 @@
import { Route, Router } from "@solidjs/router"; import { Route, Router } from "@solidjs/router";
import { ThemeProvider } from "@suid/material"; import { ThemeProvider } from "@suid/material";
import { Component, lazy } from "solid-js"; import {
Component,
createRenderEffect,
createSignal,
ErrorBoundary,
lazy,
} from "solid-js";
import { useRootTheme } from "./material/mui.js"; import { useRootTheme } from "./material/mui.js";
import "./App.css" import {
Provider as ClientProvider,
createMastoClientFor,
type Session,
} from "./masto/clients.js";
import "./App.css";
import { $accounts } from "./accounts/stores.js";
import { useStore } from "@nanostores/solid";
const AccountSignIn = lazy(() => import("./accounts/SignIn.js")); const AccountSignIn = lazy(() => import("./accounts/SignIn.js"));
const AccountMastodonOAuth2Callback = lazy(() => import("./accounts/MastodonOAuth2Callback.js")) const AccountMastodonOAuth2Callback = lazy(
const TimelineHome = lazy(() => import("./timelines/Home.js")) () => import("./accounts/MastodonOAuth2Callback.js"),
);
const TimelineHome = lazy(() => import("./timelines/Home.js"));
const Routing: Component = () => { const Routing: Component = () => {
return ( return (
@ -14,7 +29,10 @@ const Routing: Component = () => {
<Route path="/" component={TimelineHome}></Route> <Route path="/" component={TimelineHome}></Route>
<Route path={"/accounts"}> <Route path={"/accounts"}>
<Route path={"/sign-in"} component={AccountSignIn} /> <Route path={"/sign-in"} component={AccountSignIn} />
<Route path={"/oauth2/mastodon"} component={AccountMastodonOAuth2Callback} /> <Route
path={"/oauth2/mastodon"}
component={AccountMastodonOAuth2Callback}
/>
</Route> </Route>
</Router> </Router>
); );
@ -22,10 +40,29 @@ const Routing: Component = () => {
const App: Component = () => { const App: Component = () => {
const theme = useRootTheme(); const theme = useRootTheme();
const accts = useStore($accounts);
const clientStore = createSignal<Session[]>([]);
createRenderEffect(() => {
const [, setClients] = clientStore;
setClients(
accts().map((x) => ({ account: x, client: createMastoClientFor(x) })),
);
});
return ( return (
<ThemeProvider theme={theme()}> <ErrorBoundary
<Routing /> fallback={(err, reset) => {
</ThemeProvider> console.error(err);
return <></>;
}}
>
<ThemeProvider theme={theme()}>
<ClientProvider value={clientStore}>
<Routing />
</ClientProvider>
</ThemeProvider>
</ErrorBoundary>
); );
}; };

View file

@ -1,9 +1,6 @@
import { persistentAtom } from "@nanostores/persistent"; import { persistentAtom } from "@nanostores/persistent";
import { useStore } from "@nanostores/solid";
import { useNavigate } from "@solidjs/router";
import { createOAuthAPIClient, createRestAPIClient } from "masto"; import { createOAuthAPIClient, createRestAPIClient } from "masto";
import { action } from "nanostores"; import { action } from "nanostores";
import { createRenderEffect } from "solid-js";
export type Account = { export type Account = {
site: string; site: string;
@ -175,15 +172,3 @@ export const getOrRegisterApp = action(
return all[site]; return all[site];
}, },
); );
export function useAccts() {
const accts = useStore($accounts);
const naviagte = useNavigate();
createRenderEffect(() => {
if (accts().length > 0) return;
naviagte("/accounts/sign-in");
});
return accts;
}

View file

@ -1,9 +1,7 @@
import { Accessor, createResource } from "solid-js"; import { Accessor, createResource } from "solid-js";
import { Account } from "../accounts/stores"; import type { mastodon } from "masto";
import { useMastoClientFor } from "./clients";
export function useAcctProfile(account: Accessor<Account>) { export function useAcctProfile(client: Accessor<mastodon.rest.Client>) {
const client = useMastoClientFor(account)
return createResource(client, (client) => { return createResource(client, (client) => {
return client.v1.accounts.verifyCredentials() return client.v1.accounts.verifyCredentials()
}, { }, {

View file

@ -1,41 +1,78 @@
import { Accessor, createMemo, createResource } from "solid-js"; import {
Accessor,
createContext,
createRenderEffect,
createResource,
Signal,
useContext,
} from "solid-js";
import { Account } from "../accounts/stores"; import { Account } from "../accounts/stores";
import { createRestAPIClient, mastodon } from "masto"; import { createRestAPIClient, mastodon } from "masto";
import { useLocation, useNavigate } from "@solidjs/router";
const restfulCache: Record<string, mastodon.rest.Client> = {} const restfulCache: Record<string, mastodon.rest.Client> = {};
export function createMastoClientFor(account: Account) { export function createMastoClientFor(account: Account) {
const cacheKey = [account.site, account.accessToken].join('') const cacheKey = [account.site, account.accessToken].join("");
const cache = restfulCache[cacheKey] const cache = restfulCache[cacheKey];
if (cache) return cache; if (cache) return cache;
const client = createRestAPIClient({ const client = createRestAPIClient({
url: account.site, url: account.site,
accessToken: account.accessToken, accessToken: account.accessToken,
}) });
restfulCache[cacheKey] = client restfulCache[cacheKey] = client;
return client return client;
}
export function useMastoClientFor(account: Accessor<Account>) {
return createMemo(() => createMastoClientFor(account()))
} }
export function createUnauthorizedClient(site: string) { export function createUnauthorizedClient(site: string) {
const cache = restfulCache[site] const cache = restfulCache[site];
if (cache) return cache; if (cache) return cache;
const client = createRestAPIClient({ const client = createRestAPIClient({
url: site url: site,
}) });
restfulCache[site] = client restfulCache[site] = client;
return client return client;
} }
export function useInstance(client: Accessor<mastodon.rest.Client>) { export function useInstance(client: Accessor<mastodon.rest.Client>) {
return createResource(client, async (client) => { return createResource(client, async (client) => {
return await client.v2.instance.fetch() return await client.v2.instance.fetch();
}) });
}
export type Session = {
account: Account;
client: mastodon.rest.Client;
};
const Context = /* @__PURE__ */ createContext<Signal<Session[]>>();
export const Provider = Context.Provider;
export function useSessions() {
const [sessions] = useSessionsRw();
const navigate = useNavigate();
const location = useLocation();
createRenderEffect(() => {
if (sessions().length > 0) return;
navigate(
"/accounts/sign-in?back=" + encodeURIComponent(location.pathname),
{ replace: true },
);
});
return sessions;
}
export function useSessionsRw() {
const store = useContext(Context);
if (!store) {
throw new TypeError("sessions are not provided");
}
return store;
} }

View file

@ -3,15 +3,12 @@ import {
For, For,
onCleanup, onCleanup,
createSignal, createSignal,
createEffect,
Show, Show,
untrack, untrack,
onMount, onMount,
} from "solid-js"; } from "solid-js";
import { $accounts, useAccts } from "../accounts/stores";
import { useDocumentTitle } from "../utils"; import { useDocumentTitle } from "../utils";
import { useStore } from "@nanostores/solid"; import { useSessions } from "../masto/clients";
import { useMastoClientFor } from "../masto/clients";
import { type mastodon } from "masto"; import { type mastodon } from "masto";
import Scaffold from "../material/Scaffold"; import Scaffold from "../material/Scaffold";
import { import {
@ -24,7 +21,6 @@ import {
MenuItem, MenuItem,
Switch, Switch,
Toolbar, Toolbar,
Typography,
} from "@suid/material"; } from "@suid/material";
import { css } from "solid-styled"; import { css } from "solid-styled";
import { TimeSourceProvider, createTimeSource } from "../platform/timesrc"; import { TimeSourceProvider, createTimeSource } from "../platform/timesrc";
@ -152,11 +148,11 @@ const TimelinePanel: Component<{
const Home: Component = () => { const Home: Component = () => {
let panelList: HTMLDivElement; let panelList: HTMLDivElement;
useDocumentTitle("Timelines"); useDocumentTitle("Timelines");
const accounts = useAccts();
const now = createTimeSource(); const now = createTimeSource();
const client = useMastoClientFor(() => accounts()[0]); const sessions = useSessions();
const [profile] = useAcctProfile(() => accounts()[0]); const client = () => sessions()[0].client;
const [profile] = useAcctProfile(client);
const [panelOffset, setPanelOffset] = createSignal(0); const [panelOffset, setPanelOffset] = createSignal(0);
const [prefetching, setPrefetching] = createSignal(true); const [prefetching, setPrefetching] = createSignal(true);