import { msg } from "@lingui/macro"
import { ActionIcon, AppShell, Box, Center, Group, ScrollArea, Title, useMantineTheme } from "@mantine/core"
import { Constants } from "app/constants"
import { redirectToAdd, redirectToRootCategory } from "app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "app/store"
import { setMobileMenuOpen } from "app/tree/slice"
import { reloadTree } from "app/tree/thunks"
import { setSidebarWidth } from "app/user/slice"
import { reloadProfile, reloadSettings, reloadTags } from "app/user/thunks"
import { ActionButton } from "components/ActionButton"
import { AnnouncementDialog } from "components/AnnouncementDialog"
import { Loader } from "components/Loader"
import { Logo } from "components/Logo"
import { OnDesktop } from "components/responsive/OnDesktop"
import { OnMobile } from "components/responsive/OnMobile"
import { useAppLoading } from "hooks/useAppLoading"
import { useBrowserExtension } from "hooks/useBrowserExtension"
import { useMobile } from "hooks/useMobile"
import { useWebSocket } from "hooks/useWebSocket"
import { LoadingPage } from "pages/LoadingPage"
import { type ReactNode, Suspense, useEffect, useRef } from "react"
import Draggable from "react-draggable"
import { TbMenu2, TbPlus, TbX } from "react-icons/tb"
import { Outlet } from "react-router-dom"
import { useSwipeable } from "react-swipeable"
import { tss } from "tss"
interface LayoutProps {
sidebar: ReactNode
sidebarVisible: boolean
header: ReactNode
}
function LogoAndTitle() {
const dispatch = useAppDispatch()
return (
await dispatch(redirectToRootCategory())} style={{ cursor: "pointer" }}>
CommaFeed
)
}
const useStyles = tss
.withParams<{
sidebarWidth: number
sidebarPadding: string
sidebarRightBorderWidth: string
}>()
.create(({ sidebarWidth, sidebarPadding, sidebarRightBorderWidth }) => {
return {
sidebarContent: {
maxWidth: `calc(${sidebarWidth}px - ${sidebarPadding} * 2 - ${sidebarRightBorderWidth})`,
[`@media (max-width: ${Constants.layout.mobileBreakpoint}px)`]: {
maxWidth: `calc(100vw - ${sidebarPadding} * 2 - ${sidebarRightBorderWidth})`,
},
},
}
})
export default function Layout(props: LayoutProps) {
const theme = useMantineTheme()
const mobile = useMobile()
const { isBrowserExtensionPopup } = useBrowserExtension()
const draggableSeparator = useRef(null)
const { loading } = useAppLoading()
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
const treeReloadInterval = useAppSelector(state => state.server.serverInfos?.treeReloadInterval)
const mobileFooter = useAppSelector(state => state.user.settings?.mobileFooter)
const sidebarWidth = useAppSelector(state => state.user.localSettings.sidebarWidth)
const headerInFooter = mobile && !isBrowserExtensionPopup && mobileFooter
const dispatch = useAppDispatch()
useWebSocket()
const sidebarPadding = theme.spacing.xs
const { classes } = useStyles({
sidebarWidth,
sidebarPadding,
sidebarRightBorderWidth: "1px",
})
useEffect(() => {
// load initial data
dispatch(reloadSettings())
dispatch(reloadProfile())
dispatch(reloadTree())
dispatch(reloadTags())
}, [dispatch])
useEffect(() => {
let timer: number | undefined
if (!webSocketConnected && treeReloadInterval) {
// reload tree periodically if not receiving websocket events
timer = window.setInterval(async () => await dispatch(reloadTree()), treeReloadInterval)
}
return () => clearInterval(timer)
}, [dispatch, webSocketConnected, treeReloadInterval])
const burger = (
: }
onClick={() => dispatch(setMobileMenuOpen(!mobileMenuOpen))}
/>
)
const addButton = (
await dispatch(redirectToAdd())}
aria-label="Subscribe"
>
)
const header = (
<>
{mobileMenuOpen && (
{burger}
{addButton}
)}
{!mobileMenuOpen && (
{burger}
{props.header}
)}
{addButton}
{props.header}
>
)
const swipeHandlers = useSwipeable({
onSwiping: e => {
const threshold = document.documentElement.clientWidth / 6
if (e.absX > threshold) {
dispatch(setMobileMenuOpen(e.dir === "Right"))
}
},
})
if (loading) return
return (
{
dispatch(setSidebarWidth(data.x))
return
}}
>
}>
)
}