2024-07-14 12:28:44 +00:00
|
|
|
import { Route, Router } from "@solidjs/router";
|
|
|
|
import { ThemeProvider } from "@suid/material";
|
2024-07-15 05:59:10 +00:00
|
|
|
import {
|
|
|
|
Component,
|
2024-10-11 08:39:42 +00:00
|
|
|
createEffect,
|
|
|
|
createMemo,
|
2024-07-15 05:59:10 +00:00
|
|
|
createRenderEffect,
|
2024-10-15 12:30:08 +00:00
|
|
|
createSignal,
|
2024-07-15 05:59:10 +00:00
|
|
|
ErrorBoundary,
|
|
|
|
lazy,
|
2024-09-26 09:23:40 +00:00
|
|
|
onCleanup,
|
2024-07-15 05:59:10 +00:00
|
|
|
} from "solid-js";
|
2024-10-24 14:21:37 +00:00
|
|
|
import { useRootTheme } from "./material/theme.js";
|
2024-07-15 05:59:10 +00:00
|
|
|
import {
|
|
|
|
Provider as ClientProvider,
|
|
|
|
createMastoClientFor,
|
|
|
|
} from "./masto/clients.js";
|
2024-10-11 08:39:42 +00:00
|
|
|
import { $accounts, updateAcctInf } from "./accounts/stores.js";
|
2024-07-15 05:59:10 +00:00
|
|
|
import { useStore } from "@nanostores/solid";
|
2024-09-26 09:23:40 +00:00
|
|
|
import { DateFnScope, useLanguage } from "./platform/i18n.jsx";
|
2024-10-15 12:30:08 +00:00
|
|
|
import { useRegisterSW } from "virtual:pwa-register/solid";
|
|
|
|
import {
|
|
|
|
isJSONRPCResult,
|
|
|
|
ResultDispatcher,
|
|
|
|
type JSONRPC,
|
2024-10-16 14:42:25 +00:00
|
|
|
} from "./serviceworker/workerrpc.js";
|
2024-10-18 11:15:35 +00:00
|
|
|
import { Service } from "./serviceworker/services.js";
|
2024-10-15 12:30:08 +00:00
|
|
|
import { makeEventListener } from "@solid-primitives/event-listener";
|
|
|
|
import { ServiceWorkerProvider } from "./platform/host.js";
|
2024-11-16 12:04:55 +00:00
|
|
|
import StackedRouter from "./platform/StackedRouter.js";
|
2024-07-14 12:28:44 +00:00
|
|
|
|
|
|
|
const AccountSignIn = lazy(() => import("./accounts/SignIn.js"));
|
2024-07-15 05:59:10 +00:00
|
|
|
const AccountMastodonOAuth2Callback = lazy(
|
|
|
|
() => import("./accounts/MastodonOAuth2Callback.js"),
|
|
|
|
);
|
|
|
|
const TimelineHome = lazy(() => import("./timelines/Home.js"));
|
2024-07-22 13:57:04 +00:00
|
|
|
const Settings = lazy(() => import("./settings/Settings.js"));
|
2024-08-12 09:25:03 +00:00
|
|
|
const TootBottomSheet = lazy(() => import("./timelines/TootBottomSheet.js"));
|
2024-10-09 10:45:19 +00:00
|
|
|
const MotionSettings = lazy(() => import("./settings/Motions.js"));
|
2024-10-09 15:27:20 +00:00
|
|
|
const LanguageSettings = lazy(() => import("./settings/Language.js"));
|
2024-11-16 12:04:55 +00:00
|
|
|
const RegionSettings = lazy(() => import("./settings/Region.js"));
|
2024-10-11 08:39:42 +00:00
|
|
|
const UnexpectedError = lazy(() => import("./UnexpectedError.js"));
|
2024-10-18 11:15:35 +00:00
|
|
|
const Profile = lazy(() => import("./profiles/Profile.js"));
|
2024-07-14 12:28:44 +00:00
|
|
|
|
|
|
|
const Routing: Component = () => {
|
|
|
|
return (
|
2024-11-16 12:04:55 +00:00
|
|
|
<StackedRouter>
|
|
|
|
<Route path="/" component={TimelineHome} />
|
|
|
|
<Route path="/settings" component={Settings} />
|
|
|
|
<Route path="/settings/language" component={LanguageSettings} />
|
|
|
|
<Route path="/settings/region" component={RegionSettings} />
|
|
|
|
<Route path="/motions" component={MotionSettings} />
|
|
|
|
<Route path="/:acct/toot/:id" component={TootBottomSheet} />
|
|
|
|
<Route path="/:acct/profile/:id" component={Profile} />
|
|
|
|
|
2024-07-14 12:28:44 +00:00
|
|
|
<Route path={"/accounts"}>
|
|
|
|
<Route path={"/sign-in"} component={AccountSignIn} />
|
2024-07-15 05:59:10 +00:00
|
|
|
<Route
|
|
|
|
path={"/oauth2/mastodon"}
|
|
|
|
component={AccountMastodonOAuth2Callback}
|
|
|
|
/>
|
2024-07-14 12:28:44 +00:00
|
|
|
</Route>
|
2024-11-16 12:04:55 +00:00
|
|
|
</StackedRouter>
|
2024-07-14 12:28:44 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
const App: Component = () => {
|
|
|
|
const theme = useRootTheme();
|
2024-07-15 05:59:10 +00:00
|
|
|
const accts = useStore($accounts);
|
2024-09-26 09:23:40 +00:00
|
|
|
const lang = useLanguage();
|
2024-11-16 12:04:55 +00:00
|
|
|
const [serviceWorker, setServiceWorker] = createSignal<
|
|
|
|
ServiceWorker | undefined
|
|
|
|
>(undefined, { name: "serviceWorker" });
|
2024-10-15 12:30:08 +00:00
|
|
|
const dispatcher = new ResultDispatcher();
|
|
|
|
|
|
|
|
let checkAge = 0;
|
|
|
|
const untilServiceWorkerAlive = async (
|
|
|
|
worker: ServiceWorker,
|
|
|
|
expectedAge: number,
|
|
|
|
) => {
|
2024-10-16 14:42:25 +00:00
|
|
|
const [call, ret] = dispatcher.createTypedCall<Service>("ping");
|
2024-10-15 12:30:08 +00:00
|
|
|
worker.postMessage(await call);
|
|
|
|
const result = await ret;
|
|
|
|
console.assert(!result.error, result);
|
|
|
|
if (expectedAge === checkAge) {
|
|
|
|
setServiceWorker(worker);
|
|
|
|
}
|
|
|
|
};
|
|
|
|
|
|
|
|
makeEventListener(window, "message", (event: MessageEvent<JSONRPC>) => {
|
|
|
|
if (isJSONRPCResult(event.data)) {
|
|
|
|
dispatcher.dispatch(event.data.id, event.data);
|
|
|
|
}
|
|
|
|
});
|
|
|
|
|
|
|
|
const {
|
|
|
|
needRefresh: [needRefresh],
|
|
|
|
offlineReady: [offlineReady],
|
|
|
|
} = useRegisterSW({
|
|
|
|
onRegisteredSW(scriptUrl, reg) {
|
|
|
|
console.info("service worker is registered from %s", scriptUrl);
|
|
|
|
const active = reg?.active;
|
|
|
|
if (!active) {
|
|
|
|
console.warn("No service is in activating or activated");
|
|
|
|
return;
|
|
|
|
}
|
|
|
|
untilServiceWorkerAlive(active, checkAge++);
|
|
|
|
},
|
|
|
|
});
|
2024-07-15 05:59:10 +00:00
|
|
|
|
2024-10-11 08:39:42 +00:00
|
|
|
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);
|
|
|
|
},
|
|
|
|
);
|
|
|
|
}
|
2024-07-15 05:59:10 +00:00
|
|
|
});
|
|
|
|
|
2024-09-26 09:23:40 +00:00
|
|
|
createRenderEffect(() => {
|
|
|
|
const root = document.querySelector(":root")!;
|
|
|
|
root.setAttribute("lang", lang());
|
|
|
|
});
|
|
|
|
|
|
|
|
onCleanup(() => {
|
|
|
|
const root = document.querySelector(":root")!;
|
|
|
|
root.removeAttribute("lang");
|
|
|
|
});
|
|
|
|
|
2024-07-14 12:28:44 +00:00
|
|
|
return (
|
2024-07-15 05:59:10 +00:00
|
|
|
<ErrorBoundary
|
|
|
|
fallback={(err, reset) => {
|
|
|
|
console.error(err);
|
2024-08-05 08:24:34 +00:00
|
|
|
return <UnexpectedError error={err} />;
|
2024-07-15 05:59:10 +00:00
|
|
|
}}
|
|
|
|
>
|
2024-10-24 15:47:44 +00:00
|
|
|
<ThemeProvider theme={theme}>
|
2024-09-26 09:23:40 +00:00
|
|
|
<DateFnScope>
|
2024-10-11 08:39:42 +00:00
|
|
|
<ClientProvider value={clients}>
|
2024-10-15 12:30:08 +00:00
|
|
|
<ServiceWorkerProvider
|
|
|
|
value={{
|
|
|
|
needRefresh,
|
|
|
|
offlineReady,
|
|
|
|
serviceWorker,
|
|
|
|
}}
|
|
|
|
>
|
|
|
|
<Routing />
|
|
|
|
</ServiceWorkerProvider>
|
2024-09-26 09:23:40 +00:00
|
|
|
</ClientProvider>
|
|
|
|
</DateFnScope>
|
2024-07-15 05:59:10 +00:00
|
|
|
</ThemeProvider>
|
|
|
|
</ErrorBoundary>
|
2024-07-14 12:28:44 +00:00
|
|
|
);
|
|
|
|
};
|
|
|
|
|
|
|
|
export default App;
|