diff --git a/src/platform/StackedRouter.tsx b/src/platform/StackedRouter.tsx index df3d522..fbfc713 100644 --- a/src/platform/StackedRouter.tsx +++ b/src/platform/StackedRouter.tsx @@ -15,6 +15,8 @@ import { useContext, onCleanup, type Accessor, + useTransition, + getOwner, } from "solid-js"; import { createStore, unwrap } from "solid-js/store"; import "./StackedRouter.css"; @@ -79,8 +81,8 @@ export type NewFrameOptions = (T extends undefined export type FramePusher = T[K] extends | undefined | any - ? (path: K, state?: Readonly>) => Readonly - : (path: K, state: Readonly>) => Readonly; + ? (path: K, state?: Readonly>) => Promise> + : (path: K, state: Readonly>) => Promise>; export type Navigator> = { frames: readonly StackFrame[]; @@ -465,6 +467,8 @@ const StackedRouter: Component = (oprops) => { const [stack, mutStack] = createStore([] as StackFrame[], { name: "stack" }); const windowSize = useWindowSize(); + const [, startTransition] = useTransition(); + if (import.meta.hot) { const saveStack = () => { import.meta.hot!.data[$StackedRouterSavedStack] = unwrap(stack); @@ -488,8 +492,8 @@ const StackedRouter: Component = (oprops) => { }); } - const pushFrame = (path: string, opts?: Readonly>) => - untrack(() => { + const pushFrame = async (path: string, opts?: Readonly>) => + await untrack(async () => { const frame = { path, state: opts?.state, @@ -499,14 +503,17 @@ const StackedRouter: Component = (oprops) => { }; const replace = opts?.replace; - if (replace === "all" || stack.length === 0) { - mutStack([frame]); - } else if (replace) { - const idx = stack.length - 1; - mutStack(idx, frame); - } else { - mutStack(stack.length, frame); - } + const length = stack.length; + await startTransition(() => { + if (replace === "all" || length === 0) { + mutStack([frame]); + } else if (replace) { + const idx = length - 1; + mutStack(idx, frame); + } else { + mutStack(length, frame); + } + }); const savedStack = serializableStack(stack); @@ -515,6 +522,7 @@ const StackedRouter: Component = (oprops) => { } else { window.history.pushState(savedStack, "", path); } + return frame; }); diff --git a/src/timelines/Home.tsx b/src/timelines/Home.tsx index 5b413ce..3153f52 100644 --- a/src/timelines/Home.tsx +++ b/src/timelines/Home.tsx @@ -1,27 +1,23 @@ import { createSignal, Show, - onMount, type ParentComponent, - createRenderEffect, createEffect, + useTransition, } from "solid-js"; import { useDocumentTitle } from "../utils"; import Scaffold from "~material/Scaffold"; import { - AppBar, ListItemSecondaryAction, ListItemText, MenuItem, Switch, - Toolbar, } from "@suid/material"; import { css } from "solid-styled"; import { TimeSourceProvider, createTimeSource } from "~platform/timesrc"; import ProfileMenuButton from "./ProfileMenuButton"; import Tabs from "~material/Tabs"; import Tab from "~material/Tab"; -import { makeEventListener } from "@solid-primitives/event-listener"; import { $settings } from "../settings/stores"; import { useStore } from "@nanostores/solid"; import TrendTimelinePanel from "./TrendTimelinePanel"; @@ -32,10 +28,20 @@ import { default as ItemSelectionProvider, } from "./toots/ItemSelectionProvider"; import AppTopBar from "~material/AppTopBar"; +import { createTranslator } from "~platform/i18n"; +import { useWindowSize } from "@solid-primitives/resize-observer"; + +type StringRes = Record< + "tabs.home" | "tabs.trending" | "tabs.public" | "set.prefetch-toots", + string +>; const Home: ParentComponent = (props) => { let panelList: HTMLDivElement; useDocumentTitle("Timelines"); + const [t] = createTranslator( + (code) => import(`./i18n/${code}.json`) as Promise<{ default: StringRes }>, + ); const now = createTimeSource(); const [, selectionState] = createSingluarItemSelection( undefined as string | undefined, @@ -98,10 +104,19 @@ const Home: ParentComponent = (props) => { } }; - createEffect(() => { - makeEventListener(window, "resize", requestRecalculateTabIndicator); + const windowSize = useWindowSize(); + createEffect((last) => { + if (last !== windowSize.width) { + requestRecalculateTabIndicator(); + } + }); - requestAnimationFrame(recalculateTabIndicator); + const [inTransition] = useTransition(); + + createEffect(() => { + if (!inTransition()) { + requestAnimationFrame(recalculateTabIndicator); + } }); const isTabFocus = (idx: number) => { @@ -169,13 +184,13 @@ const Home: ParentComponent = (props) => { - Home + {t("tabs.home")} - Trending + {t("tabs.trending")} - Public + {t("tabs.public")} @@ -187,7 +202,7 @@ const Home: ParentComponent = (props) => { ) } > - Prefetch Toots + {t("set.prefetch-toots")} diff --git a/src/timelines/ProfileMenuButton.tsx b/src/timelines/ProfileMenuButton.tsx index 042b105..da91edf 100644 --- a/src/timelines/ProfileMenuButton.tsx +++ b/src/timelines/ProfileMenuButton.tsx @@ -16,6 +16,12 @@ import { } from "@suid/icons-material"; import A from "~platform/A"; import Menu, { createManagedMenuState } from "~material/Menu"; +import { createTranslator } from "~platform/i18n"; + +type StringRes = Record< + "nav.bookmarks" | "nav.likes" | "nav.lists" | "nav.settings", + string +>; const ProfileMenuButton: ParentComponent<{ profile?: { @@ -32,6 +38,10 @@ const ProfileMenuButton: ParentComponent<{ }> = (props) => { const menuId = createUniqueId(); const buttonId = createUniqueId(); + const [t] = createTranslator( + async (code) => + (await import(`./i18n/${code}.json`)) as { default: StringRes }, + ); const [open, state] = createManagedMenuState(); @@ -84,19 +94,19 @@ const ProfileMenuButton: ParentComponent<{ - Bookmarks + {t("nav.bookmarks")} - Likes + {t("nav.likes")} - Lists + {t("nav.lists")} @@ -107,7 +117,7 @@ const ProfileMenuButton: ParentComponent<{ - Settings + {t("nav.settings")} diff --git a/src/timelines/i18n/en.json b/src/timelines/i18n/en.json index f9bed6a..a359dac 100644 --- a/src/timelines/i18n/en.json +++ b/src/timelines/i18n/en.json @@ -1,3 +1,15 @@ { - "Choose Language": "Choose Language" + "Choose Language": "Choose Language", + + "tabs.home": "Home", + "tabs.trending": "Trending", + "tabs.public": "Public", + + "set.prefetch-toots": "Prefetch Toots", + + "nav.bookmarks": "Bookmarks", + "nav.likes": "Likes", + "nav.lists": "Lists", + "nav.settings": "Settings" + } \ No newline at end of file diff --git a/src/timelines/i18n/zh-Hans.json b/src/timelines/i18n/zh-Hans.json index 06d6bfb..e6ad5ff 100644 --- a/src/timelines/i18n/zh-Hans.json +++ b/src/timelines/i18n/zh-Hans.json @@ -1,3 +1,14 @@ { - "Choose Language": "选择语言" + "Choose Language": "选择语言", + + "tabs.home": "主页", + "tabs.trending": "当下热门", + "tabs.public": "公开", + + "set.prefetch-toots": "提前下载嘟文", + + "nav.bookmarks": "所有书签", + "nav.likes": "喜欢的嘟文", + "nav.lists": "所有列表", + "nav.settings": "设置" } \ No newline at end of file