diff --git a/src/element/flyout.tsx b/src/element/flyout.tsx
new file mode 100644
index 0000000..1c390d7
--- /dev/null
+++ b/src/element/flyout.tsx
@@ -0,0 +1,40 @@
+import { CSSProperties, ReactNode } from "react";
+import { IconButton } from "./buttons";
+import { createPortal } from "react-dom";
+import classNames from "classnames";
+
+export default function Flyout({
+ show,
+ children,
+ onClose,
+ side,
+}: {
+ show: boolean;
+ children: ReactNode;
+ onClose: () => void;
+ side: "left" | "right";
+}) {
+ const styles = {
+ "--flyout-w": "200px",
+ transition: "all 0.2s ease-in-out",
+ transform:
+ side === "right"
+ ? `translate(${show ? "0" : "var(--flyout-w)"},0)`
+ : `translate(${show ? "0" : "calc(-1 * var(--flyout-w))"},0)`,
+ } as CSSProperties;
+
+ return createPortal(
+
,
+ document.body,
+ ) as React.ReactNode;
+}
diff --git a/src/pages/layout/header.tsx b/src/pages/layout/header.tsx
index 41581cf..3f5e8b7 100644
--- a/src/pages/layout/header.tsx
+++ b/src/pages/layout/header.tsx
@@ -142,7 +142,7 @@ export function HeaderNav() {
{layoutState.leftNav && (
{
layoutState.update(c => {
c.leftNavExpand = !c.leftNavExpand;
diff --git a/src/pages/layout/left-nav.tsx b/src/pages/layout/left-nav.tsx
index 537f4b1..e31b1fc 100644
--- a/src/pages/layout/left-nav.tsx
+++ b/src/pages/layout/left-nav.tsx
@@ -1,41 +1,77 @@
import { useLayout } from "./context";
import { NavLinkIcon } from "./nav-icon";
import { FormattedMessage } from "react-intl";
+import { useMediaQuery } from "usehooks-ts";
+import Flyout from "@/element/flyout";
export function LeftNav() {
const layout = useLayout();
+ const isDesktop = useMediaQuery("(min-width: 1280px)");
+ const expandLabels = !isDesktop || layout.leftNavExpand;
+
+ function hideAfterMobileNav() {
+ if (isDesktop) return;
+ layout.update(c => {
+ c.leftNavExpand = false;
+ return { ...c };
+ });
+ }
if (layout.leftNav === false) return;
- return (
-
-
- {layout.leftNavExpand && (
-
-
-
- )}
-
-
- {layout.leftNavExpand && (
-
-
-
- )}
-
-
- {layout.leftNavExpand && (
-
-
-
- )}
-
-
- {layout.leftNavExpand && (
-
-
-
- )}
-
-
- );
+ function navInner() {
+ return (
+
+
+ {expandLabels && (
+
+
+
+ )}
+
+
+ {expandLabels && (
+
+
+
+ )}
+
+
+ {expandLabels && (
+
+
+
+ )}
+
+
+ {expandLabels && (
+
+
+
+ )}
+
+
+ );
+ }
+
+ if (isDesktop) {
+ return navInner();
+ } else {
+ return (
+ {
+ layout.update(c => {
+ c.leftNavExpand = !c.leftNavExpand;
+ return { ...c };
+ });
+ }}>
+ {navInner()}
+
+ );
+ }
}
diff --git a/src/pages/stream-page.tsx b/src/pages/stream-page.tsx
index c23006c..68c7c1f 100644
--- a/src/pages/stream-page.tsx
+++ b/src/pages/stream-page.tsx
@@ -1,6 +1,6 @@
import { NostrLink, TaggedNostrEvent } from "@snort/system";
import { Helmet } from "react-helmet";
-import { Suspense, lazy, useEffect } from "react";
+import { Suspense, lazy } from "react";
import { useMediaQuery } from "usehooks-ts";
const LiveVideoPlayer = lazy(() => import("@/element/stream/live-video-player"));
@@ -12,7 +12,6 @@ import { ContentWarningOverlay, useContentWarning } from "@/element/nsfw";
import { useCurrentStreamFeed } from "@/hooks/current-stream-feed";
import { StreamState } from "@/const";
import { StreamInfo } from "@/element/stream/stream-info";
-import { useLayout } from "./layout/context";
import { StreamContextProvider } from "@/element/stream/stream-state";
export function StreamPage({ link, evPreload }: { evPreload?: TaggedNostrEvent; link: NostrLink }) {
@@ -33,25 +32,6 @@ export function StreamPage({ link, evPreload }: { evPreload?: TaggedNostrEvent;
const goal = useZapGoal(goalTag);
const isDesktop = useMediaQuery("(min-width: 1280px)");
const isGrownUp = useContentWarning();
- const layout = useLayout();
-
- useEffect(() => {
- if (layout.leftNav) {
- layout.update(c => {
- c.leftNav = false;
- return { ...c };
- });
- }
- }, [layout]);
-
- useEffect(() => {
- return () => {
- layout.update(c => {
- c.leftNav = true;
- return { ...c };
- });
- };
- }, []);
if (contentWarning && !isGrownUp) {
return ;