diff --git a/docs/devnotes.md b/docs/devnotes.md index bd97c7b..100207d 100644 --- a/docs/devnotes.md +++ b/docs/devnotes.md @@ -13,31 +13,3 @@ You can debug on the Safari on iOS only if you have mac (and run macOS). The cer ## Hero Animation won't work (after hot reload) That's a known issue. Hot reload won't refresh the module sets the hero cache. Refresh the whole page and it should work. - -## The components don't react to the change as I setting the store, until the page reloaded - -The `WritableAtom.set` might do an equals check. You must set a different object to ensure the atom sending a notify. - -The code below may not notify the change: - -```ts -export function updateAcctInf(idx: number) { - const o = $accounts.get(); - // ... - o[idx].inf = inf; - $accounts.set(o); -} -``` - -Instead, set a new object: - -```ts -export function updateAcctInf(idx: number) { - const o = $accounts.get(); - // ... - o[idx] = Object.assign({}, o[idx], { inf }); - $accounts.set(Array.from(o)); -} -``` - -Ja, the code is weird, but that's the best we know. Anyway, you need new object on the path of your changed value. diff --git a/src/App.tsx b/src/App.tsx index f532882..cd6fe4f 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -2,9 +2,8 @@ import { Route, Router } from "@solidjs/router"; import { ThemeProvider } from "@suid/material"; import { Component, - createEffect, - createMemo, createRenderEffect, + createSignal, ErrorBoundary, lazy, onCleanup, @@ -13,8 +12,9 @@ import { useRootTheme } from "./material/mui.js"; import { Provider as ClientProvider, createMastoClientFor, + type Session, } from "./masto/clients.js"; -import { $accounts, updateAcctInf } from "./accounts/stores.js"; +import { $accounts } from "./accounts/stores.js"; import { useStore } from "@nanostores/solid"; import { DateFnScope, useLanguage } from "./platform/i18n.jsx"; @@ -28,7 +28,6 @@ const TootBottomSheet = lazy(() => import("./timelines/TootBottomSheet.js")); const MotionSettings = lazy(() => import("./settings/Motions.js")); const LanguageSettings = lazy(() => import("./settings/Language.js")); const RegionSettings = lazy(() => import("./settings/Region.jsx")); -const UnexpectedError = lazy(() => import("./UnexpectedError.js")); const Routing: Component = () => { return ( @@ -57,30 +56,14 @@ const Routing: Component = () => { const App: Component = () => { const theme = useRootTheme(); const accts = useStore($accounts); + const clientStore = createSignal([]); const lang = useLanguage(); - const clients = createMemo(() => { - return accts().map((x) => ({ - account: x, - client: createMastoClientFor(x), - })); - }); - - createEffect(() => { - const neededUpdates = accts() - .map((x, i) => [i, x] as const) - .filter(([, x]) => !x.inf); - if (neededUpdates.length > 0) { - // FIXME: we might need some kind of concurrent control - Promise.all(neededUpdates.map(([i]) => updateAcctInf(i))).then( - (x) => { - console.info("acct info updated for %d acct(s)", x.length); - }, - (reason) => { - console.error("acct info update is fail", reason); - }, - ); - } + createRenderEffect(() => { + const [, setClients] = clientStore; + setClients( + accts().map((x) => ({ account: x, client: createMastoClientFor(x) })), + ); }); createRenderEffect(() => { @@ -93,6 +76,8 @@ const App: Component = () => { root.removeAttribute("lang"); }); + const UnexpectedError = lazy(() => import("./UnexpectedError.js")); + return ( { @@ -102,7 +87,7 @@ const App: Component = () => { > - + diff --git a/src/accounts/stores.ts b/src/accounts/stores.ts index 9b66924..9d7a270 100644 --- a/src/accounts/stores.ts +++ b/src/accounts/stores.ts @@ -59,32 +59,32 @@ async function oauth2TokenViaAuthCode(app: RegisteredApp, authCode: string) { export async function acceptAccountViaAuthCode(site: string, authCode: string) { const $store = $accounts; const app = $registeredApps.get()[site]; - if (!app) { - throw TypeError("application not found"); - } - const token = await oauth2TokenViaAuthCode(app, authCode); + if (!app) { + throw TypeError("application not found"); + } + const token = await oauth2TokenViaAuthCode(app, authCode); - const acct = { - site: app.site, - accessToken: token.access_token, - tokenType: token.token_type, - scope: token.scope, - createdAt: token.created_at * 1000, - }; + const acct = { + site: app.site, + accessToken: token.access_token, + tokenType: token.token_type, + scope: token.scope, + createdAt: token.created_at * 1000, + }; - const all = [...$store.get(), acct]; - $store.set(all); + const all = [...$store.get(), acct]; + $store.set(all); - return acct; + return acct; } export async function updateAcctInf(idx: number) { const o = $accounts.get(); - const client = createMastoClientFor(o[idx]); - const inf = await client.v1.accounts.verifyCredentials(); - o[idx] = Object.assign({}, o[idx], { inf }); - $accounts.set(Array.from(o)); - return inf; + const client = createMastoClientFor(o[idx]); + const inf = await client.v1.accounts.verifyCredentials(); + o[idx].inf = inf; + $accounts.set(o); + return inf; } export function signOut(predicate: (acct: Account) => boolean) { @@ -131,57 +131,57 @@ async function getAppAccessToken(app: RegisteredApp) { export async function getOrRegisterApp(site: string, redirectUrl: string) { const $store = $registeredApps; const all = $store.get(); - const savedApp = all[site]; - if (savedApp && savedApp.redirectUrl === redirectUrl) { - const appAccessToken = await getAppAccessToken(savedApp); - if (appAccessToken) { - const client = createRestAPIClient({ - url: site, - accessToken: appAccessToken, - }); - try { - const verify = await client.v1.apps.verifyCredentials(); - Object.assign(savedApp, { - vapidKey: verify.vapidKey, - }); - const oauthClient = createOAuthAPIClient({ + const savedApp = all[site]; + if (savedApp && savedApp.redirectUrl === redirectUrl) { + const appAccessToken = await getAppAccessToken(savedApp); + if (appAccessToken) { + const client = createRestAPIClient({ url: site, accessToken: appAccessToken, }); try { - await oauthClient.revoke({ - clientId: savedApp.clientId, - clientSecret: savedApp.clientSecret, - token: appAccessToken, + const verify = await client.v1.apps.verifyCredentials(); + Object.assign(savedApp, { + vapidKey: verify.vapidKey, }); - } catch {} - return savedApp; - } finally { - $store.set(all); + const oauthClient = createOAuthAPIClient({ + url: site, + accessToken: appAccessToken, + }); + try { + await oauthClient.revoke({ + clientId: savedApp.clientId, + clientSecret: savedApp.clientSecret, + token: appAccessToken, + }); + } catch {} + return savedApp; + } finally { + $store.set(all); + } } } - } - const client = createRestAPIClient({ - url: site, - }); - const app = await client.v1.apps.create({ - clientName: "TuTu", - website: "https://github.com/thislight/tutu", - redirectUris: redirectUrl, - scopes: "read write push", - }); - if (!app.clientId || !app.clientSecret) { - return null; - } - all[site] = { - site, - clientId: app.clientId, - clientSecret: app.clientSecret, - vapidKey: app.vapidKey ?? undefined, - redirectUrl: redirectUrl, - scope: "read write push", - }; - $store.set(all); - return all[site]; + const client = createRestAPIClient({ + url: site, + }); + const app = await client.v1.apps.create({ + clientName: "TuTu", + website: "https://github.com/thislight/tutu", + redirectUris: redirectUrl, + scopes: "read write push", + }); + if (!app.clientId || !app.clientSecret) { + return null; + } + all[site] = { + site, + clientId: app.clientId, + clientSecret: app.clientSecret, + vapidKey: app.vapidKey ?? undefined, + redirectUrl: redirectUrl, + scope: "read write push", + }; + $store.set(all); + return all[site]; } diff --git a/src/masto/clients.ts b/src/masto/clients.ts index 4ed109b..f100779 100644 --- a/src/masto/clients.ts +++ b/src/masto/clients.ts @@ -49,12 +49,12 @@ export type Session = { client: mastodon.rest.Client; }; -const Context = /* @__PURE__ */ createContext[]>>(); +const Context = /* @__PURE__ */ createContext>(); export const Provider = Context.Provider; export function useSessions() { - const sessions = useSessionsRaw(); + const [sessions] = useSessionsRw(); const navigate = useNavigate(); const location = useLocation(); @@ -69,7 +69,7 @@ export function useSessions() { return sessions; } -function useSessionsRaw() { +function useSessionsRw() { const store = useContext(Context); if (!store) { throw new TypeError("sessions are not provided"); diff --git a/src/material/BottomSheet.tsx b/src/material/BottomSheet.tsx index 747126c..270b1c0 100644 --- a/src/material/BottomSheet.tsx +++ b/src/material/BottomSheet.tsx @@ -118,12 +118,12 @@ const BottomSheet: ParentComponent = (props) => { animation = element.animate( { - top: [`${rect.top}px`, `${rect.top}px`], + top: [rect.top, rect.top], left: reserve ? [`${rect.left}px`, `${window.innerWidth}px`] : [`${window.innerWidth}px`, `${rect.left}px`], - width: [`${rect.width}px`, `${rect.width}px`], - height: [`${rect.height}px`, `${rect.height}px`], + width: [rect.width, rect.width], + height: [rect.height, rect.height], }, { easing, duration }, ); @@ -151,12 +151,12 @@ const BottomSheet: ParentComponent = (props) => { animation = element.animate( { - left: [`${rect.left}px`, `${rect.left}px`], + left: [rect.left, rect.left], top: reserve ? [`${rect.top}px`, `${window.innerHeight}px`] : [`${window.innerHeight}px`, `${rect.top}px`], - width: [`${rect.width}px`, `${rect.width}px`], - height: [`${rect.height}px`, `${rect.height}px`], + width: [rect.width, rect.width], + height: [rect.height, rect.height], }, { easing, duration }, );