diff --git a/commafeed-client/src/App.tsx b/commafeed-client/src/App.tsx
index 10fa065d..fd93c839 100644
--- a/commafeed-client/src/App.tsx
+++ b/commafeed-client/src/App.tsx
@@ -4,21 +4,16 @@ import { MantineProvider, v8CssVariablesResolver } from "@mantine/core"
import { ModalsProvider } from "@mantine/modals"
import { Notifications } from "@mantine/notifications"
import type React from "react"
-import { useEffect, useState } from "react"
+import { useEffect } from "react"
import { HashRouter, Navigate, Route, Routes, useNavigate } from "react-router-dom"
-import Tinycon from "tinycon"
import { Constants } from "@/app/constants"
import { redirectTo } from "@/app/redirect/slice"
import { redirectToInitialSetup } from "@/app/redirect/thunks"
import { reloadServerInfos } from "@/app/server/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
-import { categoryUnreadCount } from "@/app/utils"
-import { DisablePullToRefresh } from "@/components/DisablePullToRefresh"
import { ErrorBoundary } from "@/components/ErrorBoundary"
import { Header } from "@/components/header/Header"
import { Tree } from "@/components/sidebar/Tree"
-import { useAppLoading } from "@/hooks/useAppLoading"
-import { useBrowserExtension } from "@/hooks/useBrowserExtension"
import { useI18n } from "@/i18n"
import { AdminUsersPage } from "@/pages/admin/AdminUsersPage"
import { MetricsPage } from "@/pages/admin/MetricsPage"
@@ -146,83 +141,8 @@ function RedirectHandler() {
return null
}
-function UnreadCountTitleHandler({
- enabled,
-}: Readonly<{
- enabled?: boolean
-}>) {
- const root = useAppSelector(state => state.tree.rootCategory)
- const unreadCount = categoryUnreadCount(root)
- return
{enabled && unreadCount > 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"}
-}
-
-function UnreadCountFaviconHandler({ enabled }: { enabled?: boolean }) {
- const root = useAppSelector(state => state.tree.rootCategory)
- const unreadCount = categoryUnreadCount(root)
- useEffect(() => {
- if (enabled && unreadCount > 0) {
- Tinycon.setBubble(unreadCount)
- } else {
- Tinycon.reset()
- }
- }, [unreadCount, enabled])
-
- return null
-}
-
-function BrowserExtensionBadgeUnreadCountHandler() {
- const root = useAppSelector(state => state.tree.rootCategory)
- const { setBadgeUnreadCount } = useBrowserExtension()
- useEffect(() => {
- if (!root) return
- const unreadCount = categoryUnreadCount(root)
- setBadgeUnreadCount(unreadCount)
- }, [root, setBadgeUnreadCount])
-
- return null
-}
-
-function CustomJsHandler() {
- const [scriptLoaded, setScriptLoaded] = useState(false)
- const { loading } = useAppLoading()
-
- useEffect(() => {
- if (scriptLoaded || loading) {
- return
- }
-
- const script = document.createElement("script")
- script.src = "custom_js.js"
- script.async = true
- document.body.appendChild(script)
-
- setScriptLoaded(true)
-
- return () => script.remove()
- }, [scriptLoaded, loading])
-
- return null
-}
-
-function CustomCssHandler() {
- useEffect(() => {
- const link = document.createElement("link")
- link.rel = "stylesheet"
- link.type = "text/css"
- link.href = "custom_css.css"
- document.head.appendChild(link)
-
- return () => link.remove()
- }, [])
-
- return null
-}
-
export function App() {
useI18n()
- const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
- const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
- const disablePullToRefresh = useAppSelector(state => state.user.settings?.disablePullToRefresh)
const dispatch = useAppDispatch()
useEffect(() => {
@@ -231,13 +151,6 @@ export function App() {
return (
-
-
-
-
-
-
-
diff --git a/commafeed-client/src/pages/app/Layout.tsx b/commafeed-client/src/pages/app/Layout.tsx
index 48846e4d..0b44928d 100644
--- a/commafeed-client/src/pages/app/Layout.tsx
+++ b/commafeed-client/src/pages/app/Layout.tsx
@@ -1,10 +1,11 @@
import { msg } from "@lingui/core/macro"
import { ActionIcon, AppShell, Box, Center, Group, ScrollArea, Title, useMantineTheme } from "@mantine/core"
-import { type ReactNode, type RefObject, Suspense, useEffect, useRef } from "react"
+import { type ReactNode, type RefObject, Suspense, useEffect, useRef, useState } 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 Tinycon from "tinycon"
import { Constants } from "@/app/constants"
import { redirectToAdd, redirectToRootCategory } from "@/app/redirect/thunks"
import { useAppDispatch, useAppSelector } from "@/app/store"
@@ -12,8 +13,10 @@ 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 { categoryUnreadCount } from "@/app/utils"
import { ActionButton } from "@/components/ActionButton"
import { AnnouncementDialog } from "@/components/AnnouncementDialog"
+import { DisablePullToRefresh } from "@/components/DisablePullToRefresh"
import { Loader } from "@/components/Loader"
import { Logo } from "@/components/Logo"
import { MarkAllAsReadConfirmationDialog } from "@/components/MarkAllAsReadConfirmationDialog"
@@ -51,6 +54,78 @@ function LogoAndTitle() {
)
}
+function UnreadCountTitleHandler({
+ enabled,
+}: Readonly<{
+ enabled?: boolean
+}>) {
+ const root = useAppSelector(state => state.tree.rootCategory)
+ const unreadCount = categoryUnreadCount(root)
+ return {enabled && unreadCount > 0 ? `(${unreadCount}) CommaFeed` : "CommaFeed"}
+}
+
+function UnreadCountFaviconHandler({ enabled }: { enabled?: boolean }) {
+ const root = useAppSelector(state => state.tree.rootCategory)
+ const unreadCount = categoryUnreadCount(root)
+ useEffect(() => {
+ if (enabled && unreadCount > 0) {
+ Tinycon.setBubble(unreadCount)
+ } else {
+ Tinycon.reset()
+ }
+ }, [unreadCount, enabled])
+
+ return null
+}
+
+function BrowserExtensionBadgeUnreadCountHandler() {
+ const root = useAppSelector(state => state.tree.rootCategory)
+ const { setBadgeUnreadCount } = useBrowserExtension()
+ useEffect(() => {
+ if (!root) return
+ const unreadCount = categoryUnreadCount(root)
+ setBadgeUnreadCount(unreadCount)
+ }, [root, setBadgeUnreadCount])
+
+ return null
+}
+
+function CustomJsHandler() {
+ const [scriptLoaded, setScriptLoaded] = useState(false)
+ const { loading } = useAppLoading()
+
+ useEffect(() => {
+ if (scriptLoaded || loading) {
+ return
+ }
+
+ const script = document.createElement("script")
+ script.src = "custom_js.js"
+ script.async = true
+ document.body.appendChild(script)
+
+ setScriptLoaded(true)
+
+ return () => script.remove()
+ }, [scriptLoaded, loading])
+
+ return null
+}
+
+function CustomCssHandler() {
+ useEffect(() => {
+ const link = document.createElement("link")
+ link.rel = "stylesheet"
+ link.type = "text/css"
+ link.href = "custom_css.css"
+ document.head.appendChild(link)
+
+ return () => link.remove()
+ }, [])
+
+ return null
+}
+
const useStyles = tss
.withParams<{
sidebarWidth: number
@@ -75,6 +150,9 @@ export default function Layout(props: Readonly) {
const draggableSeparator = useRef(null)
const { loading } = useAppLoading()
+ const unreadCountTitle = useAppSelector(state => state.user.settings?.unreadCountTitle)
+ const unreadCountFavicon = useAppSelector(state => state.user.settings?.unreadCountFavicon)
+ const disablePullToRefresh = useAppSelector(state => state.user.settings?.disablePullToRefresh)
const mobileMenuOpen = useAppSelector(state => state.tree.mobileMenuOpen)
const webSocketConnected = useAppSelector(state => state.server.webSocketConnected)
const treeReloadInterval = useAppSelector(state => state.server.serverInfos?.treeReloadInterval)
@@ -173,61 +251,69 @@ export default function Layout(props: Readonly) {
if (loading) return
return (
-
-
- {!headerInFooter && header}
- {headerInFooter && header}
-
-
- {props.sidebar}
-
-
-
- }
- axis="x"
- defaultPosition={{
- x: sidebarWidth,
- y: 0,
- }}
- bounds={{
- left: 120,
- right: 1000,
- }}
- grid={[30, 30]}
- onDrag={(_e, data) => {
- dispatch(setSidebarWidth(data.x))
- }}
- >
-
+
+
+
+
+
+
+
+
+ {!headerInFooter && header}
+ {headerInFooter && header}
+
+
+ {props.sidebar}
+
+
+
+ }
+ axis="x"
+ defaultPosition={{
+ x: sidebarWidth,
+ y: 0,
}}
- />
-
-
+ bounds={{
+ left: 120,
+ right: 1000,
+ }}
+ grid={[30, 30]}
+ onDrag={(_e, data) => {
+ dispatch(setSidebarWidth(data.x))
+ }}
+ >
+
+
+
-
- }>
-
-
-
-
-
-
-
+
+ }>
+
+
+
+
+
+
+
+ >
)
}