From 0710aaf4f30a6e95ac63a814fae73d8867bd39d6 Mon Sep 17 00:00:00 2001 From: thislight Date: Sat, 16 Nov 2024 20:04:55 +0800 Subject: [PATCH] first prototype of StackedRouter --- src/App.tsx | 30 ++-- src/accounts/MastodonOAuth2Callback.tsx | 5 +- src/masto/clients.ts | 7 +- src/material/BottomSheet.css | 16 -- src/material/Scaffold.css | 28 ++- src/material/Scaffold.tsx | 48 ++--- src/platform/A.tsx | 19 ++ src/platform/BackButton.tsx | 24 +++ src/platform/StackedRouter.css | 52 ++++++ src/platform/StackedRouter.tsx | 228 ++++++++++++++++++++++++ src/profiles/Profile.css | 2 + src/profiles/Profile.tsx | 11 +- src/settings/Language.tsx | 19 +- src/settings/Motions.tsx | 6 +- src/settings/Region.tsx | 6 +- src/settings/Settings.tsx | 9 +- src/timelines/Home.tsx | 8 +- src/timelines/ProfileMenuButton.tsx | 2 +- src/timelines/TootBottomSheet.css | 3 +- src/timelines/TootBottomSheet.tsx | 21 +-- src/timelines/TootList.tsx | 7 +- 21 files changed, 442 insertions(+), 109 deletions(-) create mode 100644 src/platform/A.tsx create mode 100644 src/platform/BackButton.tsx create mode 100644 src/platform/StackedRouter.css create mode 100644 src/platform/StackedRouter.tsx diff --git a/src/App.tsx b/src/App.tsx index abdff31..5fdb0c8 100644 --- a/src/App.tsx +++ b/src/App.tsx @@ -27,6 +27,7 @@ import { import { Service } from "./serviceworker/services.js"; import { makeEventListener } from "@solid-primitives/event-listener"; import { ServiceWorkerProvider } from "./platform/host.js"; +import StackedRouter from "./platform/StackedRouter.js"; const AccountSignIn = lazy(() => import("./accounts/SignIn.js")); const AccountMastodonOAuth2Callback = lazy( @@ -37,24 +38,21 @@ const Settings = lazy(() => import("./settings/Settings.js")); 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 RegionSettings = lazy(() => import("./settings/Region.js")); const UnexpectedError = lazy(() => import("./UnexpectedError.js")); const Profile = lazy(() => import("./profiles/Profile.js")); const Routing: Component = () => { return ( - - - - - - - - - - - - + + + + + + + + + { component={AccountMastodonOAuth2Callback} /> - + ); }; @@ -70,7 +68,9 @@ const App: Component = () => { const theme = useRootTheme(); const accts = useStore($accounts); const lang = useLanguage(); - const [serviceWorker, setServiceWorker] = createSignal(); + const [serviceWorker, setServiceWorker] = createSignal< + ServiceWorker | undefined + >(undefined, { name: "serviceWorker" }); const dispatcher = new ResultDispatcher(); let checkAge = 0; diff --git a/src/accounts/MastodonOAuth2Callback.tsx b/src/accounts/MastodonOAuth2Callback.tsx index 249c58f..0714984 100644 --- a/src/accounts/MastodonOAuth2Callback.tsx +++ b/src/accounts/MastodonOAuth2Callback.tsx @@ -1,4 +1,4 @@ -import { useNavigate, useSearchParams } from "@solidjs/router"; +import { useSearchParams } from "@solidjs/router"; import { Component, Show, @@ -14,6 +14,7 @@ import { LinearProgress } from "@suid/material"; import Img from "../material/Img"; import { createRestAPIClient } from "masto"; import { Title } from "../material/typography"; +import { useNavigator } from "../platform/StackedRouter"; type OAuth2CallbackParams = { code?: string; @@ -25,7 +26,7 @@ const MastodonOAuth2Callback: Component = () => { const progressId = createUniqueId(); const titleId = createUniqueId(); const [params] = useSearchParams(); - const navigate = useNavigate(); + const { push: navigate } = useNavigator(); const setDocumentTitle = useDocumentTitle("Back from Mastodon..."); const [siteImg, setSiteImg] = createSignal<{ src: string; diff --git a/src/masto/clients.ts b/src/masto/clients.ts index 95c9b0f..d11c7c9 100644 --- a/src/masto/clients.ts +++ b/src/masto/clients.ts @@ -8,7 +8,8 @@ import { } from "solid-js"; import { Account } from "../accounts/stores"; import { createRestAPIClient, mastodon } from "masto"; -import { useLocation, useNavigate } from "@solidjs/router"; +import { useLocation } from "@solidjs/router"; +import { useNavigator } from "../platform/StackedRouter"; const restfulCache: Record = {}; @@ -56,12 +57,12 @@ export const Provider = Context.Provider; export function useSessions() { const sessions = useSessionsRaw(); - const navigate = useNavigate(); + const {push} = useNavigator(); const location = useLocation(); createRenderEffect(() => { if (sessions().length > 0) return; - navigate( + push( "/accounts/sign-in?back=" + encodeURIComponent(location.pathname), { replace: true }, ); diff --git a/src/material/BottomSheet.css b/src/material/BottomSheet.css index c0f2803..049ca32 100644 --- a/src/material/BottomSheet.css +++ b/src/material/BottomSheet.css @@ -25,22 +25,6 @@ box-shadow: var(--tutu-shadow-e16); - .MuiToolbar-root { - >.MuiButtonBase-root { - - &:first-child { - margin-left: -0.5em; - margin-right: 24px; - } - - &:last-child { - margin-right: -0.5em; - margin-left: 24px; - } - - } - } - @media (max-width: 560px) { & { left: 0; diff --git a/src/material/Scaffold.css b/src/material/Scaffold.css index fdcf2bd..56b2cd0 100644 --- a/src/material/Scaffold.css +++ b/src/material/Scaffold.css @@ -1,22 +1,42 @@ - -.Scaffold__topbar { +.Scaffold>.topbar { position: sticky; top: 0px; z-index: var(--tutu-zidx-nav, auto); + + .MuiToolbar-root { + >.MuiButtonBase-root { + &:first-child { + margin-left: -0.5em; + margin-right: 24px; + } + + &:last-child { + margin-right: -0.5em; + margin-left: 24px; + } + + } + } } -.Scaffold__fab-dock { +.Scaffold>.fab-dock { position: fixed; bottom: 40px; right: 40px; z-index: var(--tutu-zidx-nav, auto); } -.Scaffold__bottom-dock { +.Scaffold>.bottom-dock { position: sticky; bottom: 0; left: 0; right: 0; z-index: var(--tutu-zidx-nav, auto); padding-bottom: var(--safe-area-inset-bottom, 0); +} + +.Scaffold { + height: 100%; + width: 100%; + background-color: var(--tutu-color-surface); } \ No newline at end of file diff --git a/src/material/Scaffold.tsx b/src/material/Scaffold.tsx index 693793f..4e905c3 100644 --- a/src/material/Scaffold.tsx +++ b/src/material/Scaffold.tsx @@ -28,42 +28,48 @@ const Scaffold: Component = (props) => { "bottom", "children", "ref", + "class", ]); const [topbarElement, setTopbarElement] = createSignal(); const topbarSize = createElementSize(topbarElement); return ( - <> +
{ + createRenderEffect(() => { + e.style.setProperty( + "--scaffold-topbar-height", + (topbarSize.height?.toString() ?? 0) + "px", + ); + }); + + if (managed.ref) { + (managed.ref as (val: typeof e) => void)(e); + } + }} + {...rest} + > - ); }; diff --git a/src/platform/A.tsx b/src/platform/A.tsx new file mode 100644 index 0000000..b655dc9 --- /dev/null +++ b/src/platform/A.tsx @@ -0,0 +1,19 @@ +import { type JSX } from "solid-js"; +import { useNavigator } from "./StackedRouter"; + +function handleClick( + push: (name: string, state: unknown) => void, + event: MouseEvent & { currentTarget: HTMLAnchorElement }, +) { + const target = event.currentTarget; + event.preventDefault(); + event.stopPropagation(); + push(target.href, { state: target.getAttribute("state") || undefined }); +} + +const A = (oprops: JSX.HTMLElementTags["a"]) => { + const { push } = useNavigator(); + return ; +}; + +export default A; diff --git a/src/platform/BackButton.tsx b/src/platform/BackButton.tsx new file mode 100644 index 0000000..0c59876 --- /dev/null +++ b/src/platform/BackButton.tsx @@ -0,0 +1,24 @@ +import type { IconButtonProps } from "@suid/material/IconButton"; +import IconButton from "@suid/material/IconButton"; +import { Show, type Component } from "solid-js"; +import { useCurrentFrame, useNavigator } from "./StackedRouter"; +import { ArrowBack, Close } from "@suid/icons-material"; + +export type BackButtonProps = Omit; + +const BackButton: Component = (props) => { + const currentFrame = useCurrentFrame(); + const { pop } = useNavigator(); + + const hasPrevSubPage = () => currentFrame().index > 1; + + return ( + + }> + + + + ); +}; + +export default BackButton; diff --git a/src/platform/StackedRouter.css b/src/platform/StackedRouter.css new file mode 100644 index 0000000..714893c --- /dev/null +++ b/src/platform/StackedRouter.css @@ -0,0 +1,52 @@ +.StackedPage { + container: StackedPage / size; + display: contents; + max-width: 100vw; + max-width: 100dvw; +} + +dialog.StackedPage { + border: none; + position: fixed; + padding: 0; + overscroll-behavior: none; + width: 560px; + max-height: 100vh; + max-height: 100dvh; + background: none; + display: none; + + contain: strict; + contain-intrinsic-size: auto 560px auto 100vh; + contain-intrinsic-size: auto 560px auto 100dvh; + content-visibility: auto; + + box-shadow: var(--tutu-shadow-e16); + + @media (min-width: 560px) { + & { + left: 50%; + transform: translateX(-50%); + } + } + + @media (max-width: 560px) { + & { + width: 100vw; + width: 100dvw; + height: 100vh; + height: 100dvh; + contain-intrinsic-size: 100vw 100vh; + contain-intrinsic-size: 100dvw 100dvh; + } + + } + + &[open] { + display: contents; + } + + &::backdrop { + background: none; + } +} \ No newline at end of file diff --git a/src/platform/StackedRouter.tsx b/src/platform/StackedRouter.tsx new file mode 100644 index 0000000..8d489ae --- /dev/null +++ b/src/platform/StackedRouter.tsx @@ -0,0 +1,228 @@ +import { StaticRouter, type RouterProps } from "@solidjs/router"; +import { + Component, + createContext, + createEffect, + createRenderEffect, + createUniqueId, + Index, + onMount, + Show, + untrack, + useContext, + type Accessor, +} from "solid-js"; +import { createStore, unwrap } from "solid-js/store"; +import { insert, render } from "solid-js/web"; +import "./StackedRouter.css"; +import { animateSlideInFromRight } from "./anim"; +import { ANIM_CURVE_DECELERATION, ANIM_CURVE_STD } from "../material/theme"; + +export type StackedRouterProps = Omit; + +export type StackFrame = { + path: string; + rootId: string; + state: unknown; + beforeShow?: (element: HTMLElement) => void; +}; + +export type NewFrameOptions = (T extends undefined + ? { + state?: T; + } + : { state: T }) & { + replace?: boolean; +}; + +export type FramePusher = T[K] extends + | undefined + | any + ? (path: K, state?: Readonly>) => Readonly + : (path: K, state: Readonly>) => Readonly; + +export type Navigator> = { + frames: readonly StackFrame[]; + push: FramePusher; + pop: (depth?: number) => void; +}; + +const NavigatorContext = /* @__PURE__ */ createContext(); + +/** + * Get the navigator of the {@link StackedRouter}. + * + * This function returns a {@link Navigator} without available + * push guide. Push guide is a record type contains available + * path and its state. If you need push guide, you may want to + * define your own function (like `useAppNavigator`) and cast the + * navigator to the type you need. + */ +export function useNavigator() { + const navigator = useContext(NavigatorContext); + + if (!navigator) { + throw new TypeError("not in available scope of StackedRouter"); + } + + return navigator; +} + +export type CurrentFrame = { + index: number; + frame: Readonly; +}; + +const CurrentFrameContext = + /* @__PURE__ */ createContext>>(); + +export function useCurrentFrame() { + const frame = useContext(CurrentFrameContext); + + if (!frame) { + throw new TypeError("not in available scope of StackedRouter"); + } + + return frame; +} + +function onDialogClick( + onClose: () => void, + event: MouseEvent & { currentTarget: HTMLDialogElement }, +) { + if (event.target !== event.currentTarget) return; + const rect = event.currentTarget.getBoundingClientRect(); + const isNotInDialog = + event.clientY < rect.top || + event.clientY > rect.bottom || + event.clientX < rect.left || + event.clientX > rect.right; + if (isNotInDialog) { + onClose(); + } +} + +/** + * The router that stacks the pages. + */ +const StackedRouter: Component = (oprops) => { + const [stack, mutStack] = createStore([] as StackFrame[], { name: "stack" }); + + const pushFrame = (path: string, opts?: Readonly>) => + untrack(() => { + const frame = { + path, + state: opts?.state, + rootId: createUniqueId(), + }; + mutStack(opts?.replace ? stack.length - 1 : stack.length, frame); + return frame; + }); + + const popFrame = (depth: number = 1) => + untrack(() => { + if (import.meta.env.DEV) { + if (depth < 0) { + console.warn("the depth to pop should not < 0, now is", depth); + } + } + if (stack.length > 1) { + const lastFrame = stack[stack.length - 1]; + const element = document.getElementById(lastFrame.rootId)!; + requestAnimationFrame(() => { + const animation = element.animate( + { + opacity: [0.5, 0], + }, + { easing: ANIM_CURVE_STD, duration: 220 }, + ); + animation.addEventListener("finish", () => + mutStack((o) => o.toSpliced(o.length - depth, depth)), + ); + }); + } else { + mutStack((o) => { + return o.toSpliced(o.length - depth, depth); + }); + } + }); + + /* createEffect(() => { + const length = stack.length; + console.debug("stack is changed", length, unwrap(stack)); + }); */ + + createRenderEffect(() => { + if (stack.length === 0) { + pushFrame("/", undefined); + } + }); + + const onBeforeDialogMount = (element: HTMLDialogElement) => { + createEffect(() => { + requestAnimationFrame(() => { + element.showModal(); + if (window.innerWidth <= 560) { + animateSlideInFromRight(element, { easing: ANIM_CURVE_DECELERATION }); + } else { + element.animate( + { + opacity: [0.5, 1], + }, + { easing: ANIM_CURVE_STD, duration: 220 }, + ); + } + }); + }); + }; + + return ( + + + {(frame, index) => { + const currentFrame = () => { + return { + index, + frame: frame(), + }; + }; + + return ( + + + +
+ } + > + + + + + + ); + }} + + + ); +}; + +export default StackedRouter; diff --git a/src/profiles/Profile.css b/src/profiles/Profile.css index cce8364..3f0bce8 100644 --- a/src/profiles/Profile.css +++ b/src/profiles/Profile.css @@ -1,4 +1,6 @@ .Profile { + height: 100%; + .intro { background-color: var(--tutu-color-surface-d); color: var(--tutu-color-on-surface); diff --git a/src/profiles/Profile.tsx b/src/profiles/Profile.tsx index caf3cd0..21ce929 100644 --- a/src/profiles/Profile.tsx +++ b/src/profiles/Profile.tsx @@ -45,7 +45,7 @@ import { Verified, } from "@suid/icons-material"; import { Body2, Title } from "../material/typography"; -import { useNavigate, useParams } from "@solidjs/router"; +import { useParams } from "@solidjs/router"; import { useSessionForAcctStr } from "../masto/clients"; import { resolveCustomEmoji } from "../masto/toot"; import { FastAverageColor } from "fast-average-color"; @@ -57,9 +57,10 @@ import TootFilterButton from "./TootFilterButton"; import Menu, { createManagedMenuState } from "../material/Menu"; import { share } from "../platform/share"; import "./Profile.css"; +import { useNavigator } from "../platform/StackedRouter"; const Profile: Component = () => { - const navigate = useNavigate(); + const { pop } = useNavigator(); const params = useParams<{ acct: string; id: string }>(); const acctText = () => decodeURIComponent(params.acct); const session = useSessionForAcctStr(acctText); @@ -209,11 +210,7 @@ const Profile: Component = () => { paddingTop: "var(--safe-area-inset-top)", }} > - + { - const navigate = useNavigate() + const { pop } = useNavigator(); const [t] = createTranslator( () => import("./i18n/lang-names.json"), (code) => @@ -37,9 +37,9 @@ const ChooseLang: Component = () => { }; }>, ); - const settings = useStore($settings) + const settings = useStore($settings); - const code = () => settings().language + const code = () => settings().language; const unsupportedLangCodes = createMemo(() => { return iso639_1.getAllCodes().filter((x) => !["zh", "en"].includes(x)); @@ -48,8 +48,8 @@ const ChooseLang: Component = () => { const matchedLangCode = createMemo(() => autoMatchLangTag()); const onCodeChange = (code?: string) => { - $settings.setKey("language", code) - } + $settings.setKey("language", code); + }; return ( <Scaffold @@ -59,7 +59,7 @@ const ChooseLang: Component = () => { variant="dense" sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }} > - <IconButton color="inherit" onClick={[navigate, -1]} disableRipple> + <IconButton color="inherit" onClick={[pop, 1]} disableRipple> <ArrowBack /> </IconButton> <Title>{t("Choose Language")} @@ -96,7 +96,10 @@ const ChooseLang: Component = () => { {t(`lang.${c}`)} diff --git a/src/settings/Motions.tsx b/src/settings/Motions.tsx index a90bd44..5b8cabb 100644 --- a/src/settings/Motions.tsx +++ b/src/settings/Motions.tsx @@ -13,14 +13,14 @@ import { Toolbar, } from "@suid/material"; import { Title } from "../material/typography"; -import { useNavigate } from "@solidjs/router"; import { ArrowBack } from "@suid/icons-material"; import { createTranslator } from "../platform/i18n"; import { useStore } from "@nanostores/solid"; import { $settings } from "./stores"; +import { useNavigator } from "../platform/StackedRouter"; const Motions: Component = () => { - const navigate = useNavigate(); + const {pop} = useNavigator(); const [t] = createTranslator( (code) => import(`./i18n/${code}.json`) as Promise<{ @@ -36,7 +36,7 @@ const Motions: Component = () => { variant="dense" sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }} > - + {t("motions")} diff --git a/src/settings/Region.tsx b/src/settings/Region.tsx index 130ef1f..c4b7a50 100644 --- a/src/settings/Region.tsx +++ b/src/settings/Region.tsx @@ -20,12 +20,12 @@ import { } from "../platform/i18n"; import { Title } from "../material/typography"; import type { Template } from "@solid-primitives/i18n"; -import { useNavigate } from "@solidjs/router"; import { $settings } from "./stores"; import { useStore } from "@nanostores/solid"; +import { useNavigator } from "../platform/StackedRouter"; const ChooseRegion: Component = () => { - const navigate = useNavigate(); + const {pop} = useNavigator(); const [t] = createTranslator( () => import("./i18n/lang-names.json"), (code) => @@ -54,7 +54,7 @@ const ChooseRegion: Component = () => { variant="dense" sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }} > - + {t("Choose Region")} diff --git a/src/settings/Settings.tsx b/src/settings/Settings.tsx index 34bb4cc..0aec49c 100644 --- a/src/settings/Settings.tsx +++ b/src/settings/Settings.tsx @@ -30,7 +30,7 @@ import { Refresh as RefreshIcon, Translate as TranslateIcon, } from "@suid/icons-material"; -import { A, useNavigate } from "@solidjs/router"; +import A from "../platform/A.js"; import { Title } from "../material/typography.jsx"; import { css } from "solid-styled"; import { signOut, type Account } from "../accounts/stores.js"; @@ -47,6 +47,7 @@ import { type Template } from "@solid-primitives/i18n"; import BottomSheet from "../material/BottomSheet.jsx"; import { useServiceWorker } from "../platform/host.js"; import { useSessions } from "../masto/clients.js"; +import { useNavigator } from "../platform/StackedRouter.jsx"; type Inset = { top?: number; @@ -170,7 +171,7 @@ const Settings: ParentComponent = (props) => { }>, () => import(`./i18n/lang-names.json`), ); - const navigate = useNavigate(); + const {pop} = useNavigator(); const settings$ = useStore($settings); const { needRefresh, offlineReady } = useServiceWorker(); const dateFnLocale = useDateFnLocale(); @@ -200,7 +201,7 @@ const Settings: ParentComponent = (props) => { variant="dense" sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }} > - + {t("Settings")} @@ -208,7 +209,7 @@ const Settings: ParentComponent = (props) => { } > - navigate(-1)}> + pop(1)}> {subpage()} diff --git a/src/timelines/Home.tsx b/src/timelines/Home.tsx index 99045bf..339e157 100644 --- a/src/timelines/Home.tsx +++ b/src/timelines/Home.tsx @@ -29,11 +29,11 @@ import BottomSheet, { import { $settings } from "../settings/stores"; import { useStore } from "@nanostores/solid"; import { HeroSourceProvider, type HeroSource } from "../platform/anim"; -import { useNavigate } from "@solidjs/router"; import { setCache as setTootBottomSheetCache } from "./TootBottomSheet"; import TrendTimelinePanel from "./TrendTimelinePanel"; import TimelinePanel from "./TimelinePanel"; import { useSessions } from "../masto/clients"; +import { useNavigator } from "../platform/StackedRouter"; const Home: ParentComponent = (props) => { let panelList: HTMLDivElement; @@ -53,7 +53,7 @@ const Home: ParentComponent = (props) => { const all = profiles(); return all?.[0]?.client; }; - const navigate = useNavigate(); + const {pop, push} = useNavigator(); const [heroSrc, setHeroSrc] = createSignal({}); const [panelOffset, setPanelOffset] = createSignal(0); @@ -151,7 +151,7 @@ const Home: ParentComponent = (props) => { ); const acct = `${inf.username}@${p.account.site}`; setTootBottomSheetCache(acct, toot); - navigate(`/${encodeURIComponent(acct)}/toot/${toot.id}`, { + push(`/${encodeURIComponent(acct)}/toot/${toot.id}`, { state: reply ? { tootReply: true, @@ -276,7 +276,7 @@ const Home: ParentComponent = (props) => { - navigate(-1)}> + pop(1)}> {child()} diff --git a/src/timelines/ProfileMenuButton.tsx b/src/timelines/ProfileMenuButton.tsx index 0aa088c..c33ba8e 100644 --- a/src/timelines/ProfileMenuButton.tsx +++ b/src/timelines/ProfileMenuButton.tsx @@ -21,7 +21,7 @@ import { Star as LikeIcon, FeaturedPlayList as ListIcon, } from "@suid/icons-material"; -import { A } from "@solidjs/router"; +import A from "../platform/A"; const ProfileMenuButton: ParentComponent<{ profile?: { diff --git a/src/timelines/TootBottomSheet.css b/src/timelines/TootBottomSheet.css index ceb7c17..e6babb0 100644 --- a/src/timelines/TootBottomSheet.css +++ b/src/timelines/TootBottomSheet.css @@ -1,12 +1,11 @@ .TootBottomSheet { overflow: hidden; - height: calc(100% - var(--scaffold-topbar-height, 0px)); .Scrollable { padding-bottom: var(--safe-area-inset-bottom, 0); overflow-y: auto; overscroll-behavior-y: contain; - height: 100%; + height: calc(100% - var(--scaffold-topbar-height, 0px)); } .progress-line { diff --git a/src/timelines/TootBottomSheet.tsx b/src/timelines/TootBottomSheet.tsx index 1c6b111..e82ce00 100644 --- a/src/timelines/TootBottomSheet.tsx +++ b/src/timelines/TootBottomSheet.tsx @@ -1,10 +1,9 @@ -import { useLocation, useNavigate, useParams } from "@solidjs/router"; +import { useLocation, useParams } from "@solidjs/router"; import { catchError, createEffect, createRenderEffect, createResource, - createSignal, Show, type Component, } from "solid-js"; @@ -25,6 +24,8 @@ import { useDocumentTitle } from "../utils"; import { createTimelineControlsForArray } from "../masto/timelines"; import TootList from "./TootList"; import "./TootBottomSheet.css"; +import { useNavigator } from "../platform/StackedRouter"; +import BackButton from "../platform/BackButton"; let cachedEntry: [string, mastodon.v1.Status] | undefined; @@ -43,7 +44,7 @@ const TootBottomSheet: Component = (props) => { const location = useLocation<{ tootReply?: boolean; }>(); - const navigate = useNavigate(); + const { pop, push } = useNavigator(); const time = createTimeSource(); const acctText = () => decodeURIComponent(params.acct); const session = useSessionForAcctStr(acctText); @@ -186,7 +187,7 @@ const TootBottomSheet: Component = (props) => { target.dataset.client || `@${new URL(target.href).origin}`, ); - navigate(`/${acct}/profile/${target.dataset.acctId}`); + push(`/${acct}/profile/${target.dataset.acctId}`); return; } else { @@ -228,9 +229,7 @@ const TootBottomSheet: Component = (props) => { variant="dense" sx={{ paddingTop: "var(--safe-area-inset-top, 0px)" }} > - - - + <span ref={(e: HTMLElement) => @@ -246,9 +245,7 @@ const TootBottomSheet: Component = (props) => { } class="TootBottomSheet" > - <div - class="Scrollable" - > + <div class="Scrollable"> <TimeSourceProvider value={time}> <TootList threads={ancestors.list} @@ -288,9 +285,7 @@ const TootBottomSheet: Component = (props) => { </Show> <Show when={tootContextErrorUncaught.loading}> - <div - class="progress-line" - > + <div class="progress-line"> <CircularProgress style="width: 1.5em; height: 1.5em;" /> </div> </Show> diff --git a/src/timelines/TootList.tsx b/src/timelines/TootList.tsx index 7ef86da..caaf4c3 100644 --- a/src/timelines/TootList.tsx +++ b/src/timelines/TootList.tsx @@ -20,6 +20,7 @@ import RegularToot, { } from "./RegularToot"; import cardStyle from "../material/cards.module.css"; import type { ThreadNode } from "../masto/timelines"; +import { useNavigator } from "../platform/StackedRouter"; function positionTootInThread(index: number, threadLength: number) { if (index === 0) { @@ -40,7 +41,7 @@ const TootList: Component<{ const session = useDefaultSession(); const heroSrc = useHeroSource(); const [expandedThreadId, setExpandedThreadId] = createSignal<string>(); - const navigate = useNavigate(); + const {push} = useNavigator(); const onBookmark = async (status: mastodon.v1.Status) => { const client = session()?.client; @@ -115,7 +116,7 @@ const TootList: Component<{ const acct = `${inf.username}@${p.site}`; setTootBottomSheetCache(acct, toot); - navigate(`/${encodeURIComponent(acct)}/toot/${toot.id}`, { + push(`/${encodeURIComponent(acct)}/toot/${toot.id}`, { state: reply ? { tootReply: true, @@ -146,7 +147,7 @@ const TootList: Component<{ target.dataset.client || `@${new URL(target.href).origin}`, ); - navigate(`/${acct}/profile/${target.dataset.acctId}`); + push(`/${acct}/profile/${target.dataset.acctId}`); return; } else {