import { createSignal, Show, onMount, type ParentComponent, createRenderEffect, } 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"; import TimelinePanel from "./TimelinePanel"; import { useSessions } from "../masto/clients"; const Home: ParentComponent = (props) => { let panelList: HTMLDivElement; useDocumentTitle("Timelines"); const now = createTimeSource(); const settings$ = useStore($settings); const profiles = useSessions(); const client = () => { const all = profiles(); return all?.[0]?.client; }; const prefetching = () => !settings$().prefetchTootsDisabled; const [focusRange, setFocusRange] = createSignal([0, 0] as readonly [ number, number, ]); let scrollEventLockReleased = true; const recalculateTabIndicator = () => { scrollEventLockReleased = false; try { const { x: panelX, width: panelWidth } = panelList.getBoundingClientRect(); let minIdx = +Infinity, maxIdx = -Infinity; const items = panelList.querySelectorAll(".tab-panel"); const ranges = Array.from(items).map((x) => { const rect = x.getBoundingClientRect(); const inlineStart = rect.x - panelX; const inlineEnd = rect.width + inlineStart; return [inlineStart, inlineEnd] as const; }); for (let i = 0; i < items.length; i++) { const e = items.item(i); const [inlineStart, inlineEnd] = ranges[i]; if (inlineStart >= 0 && inlineEnd <= panelWidth) { minIdx = Math.min(minIdx, i); maxIdx = Math.max(maxIdx, i); e.classList.add("active"); } else { e.classList.remove("active"); } } if (isFinite(minIdx) && isFinite(maxIdx)) { setFocusRange([minIdx, maxIdx]); } } finally { scrollEventLockReleased = true; } }; const requestRecalculateTabIndicator = () => { if (scrollEventLockReleased) { requestAnimationFrame(recalculateTabIndicator); } }; createRenderEffect(() => { makeEventListener(window, "resize", requestRecalculateTabIndicator); }); onMount(() => { requestAnimationFrame(recalculateTabIndicator); }); const isTabFocus = (idx: number) => { const [start, end] = focusRange(); if (!isFinite(start) || !isFinite(end)) return false; return idx >= start && idx <= end; }; const onTabClick = (idx: number) => { const items = panelList.querySelectorAll(".tab-panel"); if (items.length > idx) { items.item(idx).scrollIntoView({ block: "start", behavior: "smooth" }); } if (isTabFocus(idx)) { items.item(idx).scrollTo({ top: 0, behavior: "smooth", }); } }; css` .tab-panel { overflow: visible auto; max-width: 560px; height: 100%; padding: 0 16px; scroll-snap-align: center; overscroll-behavior-block: none; contain: strict; contain-intrinsic-size: auto 560px auto 100vh; contain-intrinsic-size: auto 560px auto 100dvh; @media (max-width: 600px) { padding: 0; } } .panel-list { display: grid; grid-auto-columns: 560px; grid-auto-flow: column; overflow: auto hidden; scroll-snap-type: x mandatory; scroll-snap-stop: always; height: calc(100vh - var(--scaffold-topbar-height, 0px)); height: calc(100dvh - var(--scaffold-topbar-height, 0px)); padding-left: var(--safe-area-inset-left, 0); padding-right: var(--safe-area-inset-right, 0); & > * { content-visibility: auto; } @media (max-width: 600px) { grid-auto-columns: 100%; } } `; return ( <> Home Trending Public $settings.setKey( "prefetchTootsDisabled", !$settings.get().prefetchTootsDisabled, ) } > Prefetch Toots } >
); }; export default Home;