diff --git a/apps/web/src/app/App.tsx b/apps/web/src/app/App.tsx index 55dc1a6f..ea90c78b 100644 --- a/apps/web/src/app/App.tsx +++ b/apps/web/src/app/App.tsx @@ -1,5 +1,6 @@ import { useEffect } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; +import { Navigate, Route, Routes, useNavigate } from "react-router"; import { api } from "@/lib/api"; import { EvernoteImportGuidePane } from "@/components/EvernoteImportGuidePane"; import { LoginScreen } from "@/components/LoginScreen"; @@ -12,24 +13,26 @@ const AuthLoadingScreen = () => ( ); -export const App = () => { +const EvernoteMigrationRoute = () => { + const navigate = useNavigate(); + + return ( + { + if (window.opener) { + window.close(); + return; + } + + navigate("/"); + }} + /> + ); +}; + +const AuthenticatedWorkspace = () => { const queryClient = useQueryClient(); - if (window.location.pathname === "/evernote-migration") { - return ( - { - if (window.opener) { - window.close(); - return; - } - - window.location.href = "/"; - }} - /> - ); - } - const sessionQuery = useQuery({ queryKey: ["auth", "session"], queryFn: () => api.getSession(), @@ -96,3 +99,12 @@ export const App = () => { /> ); }; + +export const App = () => ( + + } /> + } /> + } /> + } /> + +); diff --git a/apps/web/src/components/WorkspaceApp.tsx b/apps/web/src/components/WorkspaceApp.tsx index 4ffc6d53..3928c5a0 100644 --- a/apps/web/src/components/WorkspaceApp.tsx +++ b/apps/web/src/components/WorkspaceApp.tsx @@ -14,6 +14,7 @@ import { } from "react"; import { useQuery, useMutation, useQueryClient } from "@tanstack/react-query"; import { Home, Search, UserRound, Plus, ChevronDown, ChevronRight, RefreshCw, X } from "lucide-react"; +import { useLocation, useNavigate } from "react-router"; import { Button } from "@/components/ui/button"; import { Drawer, @@ -95,6 +96,8 @@ const NotebookPane = lazy(() => import("./NotebookPane").then((module) => ({ def const TagsDialog = lazy(() => import("./dialogs/TagsDialog").then((module) => ({ default: module.TagsDialog }))); const TemplatesDialog = lazy(() => import("./dialogs/TemplatesDialog").then((module) => ({ default: module.TemplatesDialog }))); +const SETTINGS_PATH = "/settings"; + const emptySyncQueueSummary = (): SyncQueueSummary => ({ total: 0, pending: 0, @@ -451,6 +454,8 @@ export const WorkspaceApp = ({ onLogout: () => void; }) => { const queryClient = useQueryClient(); + const location = useLocation(); + const navigate = useNavigate(); const [activePane, setActivePane] = useState("memos"); const [memoView, setMemoView] = useState("notebook"); const [selectedNotebookId, setSelectedNotebookId] = useState(null); @@ -483,6 +488,7 @@ export const WorkspaceApp = ({ const [pullToRefreshDistance, setPullToRefreshDistance] = useState(0); const [isPullRefreshing, setIsPullRefreshing] = useState(false); const isPullRefreshingRef = useRef(false); + const skipNextHomeRouteSyncRef = useRef(false); const [mobileListActionsOpen, setMobileListActionsOpen] = useState(false); const [mobileMoveOpen, setMobileMoveOpen] = useState(false); @@ -491,6 +497,18 @@ export const WorkspaceApp = ({ const [desktopSortOpen, setDesktopSortOpen] = useState(false); const [desktopActionsOpen, setDesktopActionsOpen] = useState(false); + const navigateWorkspaceHome = () => { + if (location.pathname !== "/") { + navigate("/"); + } + }; + + const navigateWorkspaceSettings = () => { + if (location.pathname !== SETTINGS_PATH) { + navigate(SETTINGS_PATH); + } + }; + const runQueuedSync = useCallback(async () => { if (typeof navigator !== "undefined" && !navigator.onLine) { setIsOnline(false); @@ -638,6 +656,24 @@ export const WorkspaceApp = ({ writeImageCompressionPreference(imageCompressionEnabled); }, [imageCompressionEnabled]); + useEffect(() => { + if (location.pathname === SETTINGS_PATH) { + skipNextHomeRouteSyncRef.current = false; + setRightView("settings"); + setMobileBottomNavActive("settings"); + setActivePane("editor"); + return; + } + + if (skipNextHomeRouteSyncRef.current) { + skipNextHomeRouteSyncRef.current = false; + return; + } + + setRightView("editor"); + setMobileBottomNavActive("home"); + }, [location.pathname]); + useEffect(() => { let unsubscribe: (() => void) | undefined; let active = true; @@ -873,6 +909,7 @@ export const WorkspaceApp = ({ queryClient.invalidateQueries({ queryKey: ["notebooks"] }), ]); queryClient.setQueryData(["memo", data.memo.id], { memo: data.memo }); + navigateWorkspaceHome(); setRightView("editor"); setCreatedMemoEditId(data.memo.id); setSelectedMemoId(data.memo.id); @@ -889,6 +926,7 @@ export const WorkspaceApp = ({ queryClient.invalidateQueries({ queryKey: ["notebooks"] }), ]); queryClient.setQueryData(["memo", data.memo.id], { memo: data.memo }); + navigateWorkspaceHome(); setRightView("editor"); setSelectedMemoId(data.memo.id); setActivePane("editor"); @@ -1015,6 +1053,7 @@ export const WorkspaceApp = ({ ]); queryClient.setQueryData(["memo", data.memo.id], { memo: data.memo }); setSelectedNotebookId(data.memo.notebookId); + navigateWorkspaceHome(); setRightView("editor"); setSelectedMemoId(data.memo.id); setActivePane("editor"); @@ -1269,6 +1308,7 @@ export const WorkspaceApp = ({ }; const handleSelectNotebook = (notebookId: string) => { + navigateWorkspaceHome(); setMemoView("notebook"); setSelectedNotebookId(notebookId); setMobileBottomNavActive("home"); @@ -1278,6 +1318,7 @@ export const WorkspaceApp = ({ }; const handleSelectAllMemos = () => { + navigateWorkspaceHome(); setMemoView("notebook"); setSelectedNotebookId(null); setMobileBottomNavActive("home"); @@ -1287,6 +1328,7 @@ export const WorkspaceApp = ({ }; const handleMobileHome = () => { + navigateWorkspaceHome(); if (memoView === "trash") { setMemoView("notebook"); } @@ -1318,6 +1360,8 @@ export const WorkspaceApp = ({ const handleOpenAssets = () => { clearHiddenMobileSearch(); + skipNextHomeRouteSyncRef.current = location.pathname !== "/"; + navigateWorkspaceHome(); setRightView("assets"); setActivePane("editor"); }; @@ -1335,11 +1379,14 @@ export const WorkspaceApp = ({ const handleOpenSettings = () => { clearHiddenMobileSearch(); + navigateWorkspaceSettings(); setRightView("settings"); + setMobileBottomNavActive("settings"); setActivePane("editor"); }; const handleCloseAssets = () => { + navigateWorkspaceHome(); setRightView("editor"); setMobileBottomNavActive("home"); }; @@ -1350,6 +1397,7 @@ export const WorkspaceApp = ({ }; const handleCloseSettings = () => { + navigateWorkspaceHome(); setRightView("editor"); setMobileBottomNavActive("home"); if (!isDesktopViewport()) { @@ -1723,6 +1771,7 @@ export const WorkspaceApp = ({ canCreateMemo={canCreateMemo} isCreatingMemo={createMemoMutation.isPending} onSelect={(notebookId) => { + navigateWorkspaceHome(); setMemoView("notebook"); setSelectedNotebookId(notebookId); clearMemoSelection(); @@ -1736,6 +1785,7 @@ export const WorkspaceApp = ({ onMoveNotebook={handleMoveNotebook} onMoveMemos={handleMoveDraggedMemos} onBackToList={() => { + navigateWorkspaceHome(); if (memoView === "trash") { setMemoView("notebook"); } @@ -1756,6 +1806,7 @@ export const WorkspaceApp = ({ onOpenTags={handleOpenTags} onOpenSettings={handleOpenSettings} onOpenTrash={() => { + navigateWorkspaceHome(); setMemoView("trash"); setSelectedNotebookId(null); setMobileBottomNavActive("home"); @@ -1806,6 +1857,7 @@ export const WorkspaceApp = ({ onOpenTags={handleOpenTags} onOpenSettings={handleOpenSettings} onOpenTrash={() => { + navigateWorkspaceHome(); setMemoView("trash"); setSelectedNotebookId(null); setMobileBottomNavActive("home"); @@ -1815,6 +1867,7 @@ export const WorkspaceApp = ({ setActivePane("memos"); }} onOpenMemo={(memoId) => { + navigateWorkspaceHome(); setRightView("editor"); setCreatedMemoEditId(null); setSelectedMemoId(memoId); diff --git a/apps/web/src/main.tsx b/apps/web/src/main.tsx index 67022314..de4250d9 100644 --- a/apps/web/src/main.tsx +++ b/apps/web/src/main.tsx @@ -1,6 +1,7 @@ import { QueryClient, QueryClientProvider } from "@tanstack/react-query"; import React from "react"; import { createRoot } from "react-dom/client"; +import { BrowserRouter } from "react-router"; import { registerSW } from "virtual:pwa-register"; import { App } from "./app/App"; import "./styles/globals.css"; @@ -26,7 +27,9 @@ if (!root) { createRoot(root).render( - + + + ); diff --git a/bun.lock b/bun.lock index 491d2904..1556aff2 100644 --- a/bun.lock +++ b/bun.lock @@ -30,6 +30,7 @@ "lucide-react": "latest", "react": "latest", "react-dom": "latest", + "react-router": "^8.1.0", "sharp": "^0.35.2", "tailwind-merge": "latest", "tailwindcss-animate": "^1.0.7", @@ -777,6 +778,8 @@ "cookie": ["cookie@1.1.1", "", {}, "sha512-ei8Aos7ja0weRpFzJnEA9UHJ/7XQmqglbRwnf2ATjcB9Wq874VKH9kfjjirM6UhU2/E5fFYadylyhFldcqSidQ=="], + "cookie-es": ["cookie-es@3.1.1", "", {}, "sha512-UaXxwISYJPTr9hwQxMFYZ7kNhSXboMXP+Z3TRX6f1/NyaGPfuNUZOWP1pUEb75B2HjfklIYLVRfWiFZJyC6Npg=="], + "core-js-compat": ["core-js-compat@3.49.0", "", { "dependencies": { "browserslist": "^4.28.1" } }, "sha512-VQXt1jr9cBz03b331DFDCCP90b3fanciLkgiOoy8SBHy06gNf+vQ1A3WFLqG7I8TipYIKeYK9wxd0tUrvHcOZA=="], "cross-spawn": ["cross-spawn@7.0.6", "", { "dependencies": { "path-key": "^3.1.0", "shebang-command": "^2.0.0", "which": "^2.0.1" } }, "sha512-uV2QOWP2nWzsy2aMp8aRibhi9dlzF5Hgh5SHaB9OiTGEyDTiJJyx0uy51QXdyWbtAHNua4XJzUKca3OzKUd3vA=="], @@ -1173,6 +1176,8 @@ "react-remove-scroll-bar": ["react-remove-scroll-bar@2.3.8", "", { "dependencies": { "react-style-singleton": "^2.2.2", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0" }, "optionalPeers": ["@types/react"] }, "sha512-9r+yi9+mgU33AKcj6IbT9oRCO78WriSj6t/cF8DWBZJ9aOGPOTEDvdUDz1FwKim7QXWwmHqtdHnRJfhAxEG46Q=="], + "react-router": ["react-router@8.1.0", "", { "dependencies": { "cookie-es": "^3.1.1" }, "peerDependencies": { "react": ">=19.2.7", "react-dom": ">=19.2.7" }, "optionalPeers": ["react-dom"] }, "sha512-Mdfi61uObuvWNN9OhChOC0HV6YWOIfKRzEWOvCHRSuQg8IM+Nv10edaM/2HE8ZixBpUTdQbruyWqC3sDkkh9vw=="], + "react-style-singleton": ["react-style-singleton@2.2.3", "", { "dependencies": { "get-nonce": "^1.0.0", "tslib": "^2.0.0" }, "peerDependencies": { "@types/react": "*", "react": "^16.8.0 || ^17.0.0 || ^18.0.0 || ^19.0.0 || ^19.0.0-rc" }, "optionalPeers": ["@types/react"] }, "sha512-b6jSvxvVnyptAiLjbkWLE/lOnR4lfTtDAl+eUC7RZy+QQWc6wRzIV2CE6xBuMmDxc2qIihtDCZD5NPOFl7fRBQ=="], "read-cache": ["read-cache@1.0.0", "", { "dependencies": { "pify": "^2.3.0" } }, "sha512-Owdv/Ft7IjOgm/i0xvNDZ1LrRANRfew4b2prF3OWMQLxLfu3bS8FVhCsrSCMK4lR56Y9ya+AThoTpDCTxCmpRA=="], diff --git a/package.json b/package.json index f587e9cc..9f3706b6 100644 --- a/package.json +++ b/package.json @@ -61,6 +61,7 @@ "lucide-react": "latest", "react": "latest", "react-dom": "latest", + "react-router": "^8.1.0", "sharp": "^0.35.2", "tailwind-merge": "latest", "tailwindcss-animate": "^1.0.7",