diff --git a/apps/web/.env-cmdrc.js b/apps/web/.env-cmdrc.js index ccda0d189..9cbcd3b19 100644 --- a/apps/web/.env-cmdrc.js +++ b/apps/web/.env-cmdrc.js @@ -7,8 +7,8 @@ const gitHash = execSync("git rev-parse --short HEAD").toString().trim(); module.exports = { all: { UV_THREADPOOL_SIZE: IS_CI ? NUM_CPUS : 2, - GENERATE_SOURCEMAP: false, - INLINE_RUNTIME_CHUNK: false, + // GENERATE_SOURCEMAP: false, + // INLINE_RUNTIME_CHUNK: false, DISABLE_ESLINT_PLUGIN: true, REACT_APP_GIT_HASH: gitHash, }, diff --git a/apps/web/__e2e__/editor.test.js b/apps/web/__e2e__/editor.test.js index 3fd04426e..df809f9d0 100644 --- a/apps/web/__e2e__/editor.test.js +++ b/apps/web/__e2e__/editor.test.js @@ -5,10 +5,11 @@ const { createNote, NOTE, getTestId } = require("./utils"); */ var page = null; global.page = null; -test.beforeEach(async ({ page: _page }) => { +test.beforeEach(async ({ page: _page, baseURL }) => { global.page = _page; page = _page; - await page.goto("http://localhost:3000/"); + await page.goto(baseURL); + await page.waitForSelector(getTestId("routeHeader")); }); test("focus mode", async () => { diff --git a/apps/web/__e2e__/navigation.test.js b/apps/web/__e2e__/navigation.test.js index 1b7d2a2c5..2fb2f289b 100644 --- a/apps/web/__e2e__/navigation.test.js +++ b/apps/web/__e2e__/navigation.test.js @@ -5,6 +5,7 @@ const { getTestId } = require("./utils"); test.beforeEach(async ({ page, baseURL }) => { await page.goto(baseURL); + await page.waitForSelector(getTestId("routeHeader")); }); function createRoute(key, header) { diff --git a/apps/web/__e2e__/notebooks.test.js b/apps/web/__e2e__/notebooks.test.js index 1629d7994..67bcdd888 100644 --- a/apps/web/__e2e__/notebooks.test.js +++ b/apps/web/__e2e__/notebooks.test.js @@ -17,9 +17,10 @@ const { checkNotePresence, isPresent } = require("./utils/conditions"); */ global.page = null; -test.beforeEach(async ({ page: _page }) => { +test.beforeEach(async ({ page: _page, baseURL }) => { global.page = _page; - await page.goto("http://localhost:3000/"); + await page.goto(baseURL); + await page.waitForSelector(getTestId("routeHeader")); }); async function fillNotebookDialog(notebook) { diff --git a/apps/web/__e2e__/notes.test.js b/apps/web/__e2e__/notes.test.js index 09a6a4612..710685603 100644 --- a/apps/web/__e2e__/notes.test.js +++ b/apps/web/__e2e__/notes.test.js @@ -203,9 +203,10 @@ test.describe("run tests independently", () => { * @type {Page} */ global.page = null; - test.beforeEach(async ({ page: _page }) => { + test.beforeEach(async ({ page: _page, baseURL }) => { global.page = _page; - await page.goto("http://localhost:3000/"); + await page.goto(baseURL); + await page.waitForSelector(getTestId("routeHeader")); }); test("create a note", async () => { diff --git a/apps/web/__e2e__/user.test.js b/apps/web/__e2e__/user.test.js index b3755e03b..3b6efbee3 100644 --- a/apps/web/__e2e__/user.test.js +++ b/apps/web/__e2e__/user.test.js @@ -15,6 +15,7 @@ test.beforeEach(async ({ page: _page, baseURL }) => { global.page = _page; page = _page; await page.goto(baseURL); + await page.waitForSelector(getTestId("routeHeader")); }); const USER = { diff --git a/apps/web/desktop/autoupdate.js b/apps/web/desktop/autoupdate.js index c3e4dacf3..89de0b389 100644 --- a/apps/web/desktop/autoupdate.js +++ b/apps/web/desktop/autoupdate.js @@ -14,7 +14,6 @@ async function configureAutoUpdater() { sendMessageToRenderer(EVENTS.updateAvailable, info); }); autoUpdater.addListener("download-progress", (progress) => { - console.log("Downloading", progress); sendMessageToRenderer(EVENTS.updateDownloadProgress, progress); }); autoUpdater.addListener("update-downloaded", (info) => { diff --git a/apps/web/package.json b/apps/web/package.json index 3f0df8b0d..b590e7e8f 100644 --- a/apps/web/package.json +++ b/apps/web/package.json @@ -23,7 +23,7 @@ "emotion-theming": "^10.0.19", "fast-sort": "^2.1.1", "file-saver": "^2.0.5", - "framer-motion": "^3.3.0", + "framer-motion": "^4.1.17", "hash-wasm": "^4.9.0", "hotkeys-js": "^3.8.3", "immer": "^9.0.6", @@ -77,6 +77,7 @@ "patch-package": "^6.4.7", "source-map-explorer": "^2.5.2", "typescript": "^4.1.5", + "webpack-bundle-analyzer": "^4.5.0", "worker-loader": "^3.0.8", "zx": "^2.0.0" }, diff --git a/apps/web/public/fonts.css b/apps/web/public/fonts.css deleted file mode 100644 index ed043415c..000000000 --- a/apps/web/public/fonts.css +++ /dev/null @@ -1,68 +0,0 @@ -/* open-sans-regular - latin */ -@font-face { - font-family: "Open Sans"; - font-display: swap; - font-style: normal; - font-weight: 400; - src: local(""), - url("/fonts/open-sans-v20-latin-regular.woff2") format("woff2"), - /* Chrome 26+, Opera 23+, Firefox 39+ */ - url("/fonts/open-sans-v20-latin-regular.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - -/* open-sans-italic - latin */ -@font-face { - font-family: "Open Sans"; - font-display: swap; - font-style: italic; - font-weight: 400; - src: local(""), url("/fonts/open-sans-v20-latin-italic.woff2") format("woff2"), - /* Chrome 26+, Opera 23+, Firefox 39+ */ - url("/fonts/open-sans-v20-latin-italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - -/* open-sans-600 - latin */ -@font-face { - font-family: "Open Sans"; - font-display: swap; - font-style: normal; - font-weight: 600; - src: local(""), url("/fonts/open-sans-v20-latin-600.woff2") format("woff2"), - /* Chrome 26+, Opera 23+, Firefox 39+ */ - url("/fonts/open-sans-v20-latin-600.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - -/* open-sans-600italic - latin */ -@font-face { - font-family: "Open Sans"; - font-display: swap; - font-style: italic; - font-weight: 600; - src: local(""), - url("/fonts/open-sans-v20-latin-600italic.woff2") format("woff2"), - /* Chrome 26+, Opera 23+, Firefox 39+ */ - url("/fonts/open-sans-v20-latin-600italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - -/* open-sans-700 - latin */ -@font-face { - font-family: "Open Sans"; - font-display: swap; - font-style: normal; - font-weight: 700; - src: local(""), url("/fonts/open-sans-v20-latin-700.woff2") format("woff2"), - /* Chrome 26+, Opera 23+, Firefox 39+ */ - url("/fonts/open-sans-v20-latin-700.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} - -/* open-sans-700italic - latin */ -@font-face { - font-family: "Open Sans"; - font-display: swap; - font-style: italic; - font-weight: 700; - src: local(""), - url("/fonts/open-sans-v20-latin-700italic.woff2") format("woff2"), - /* Chrome 26+, Opera 23+, Firefox 39+ */ - url("/fonts/open-sans-v20-latin-700italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ -} diff --git a/apps/web/public/index.html b/apps/web/public/index.html index 8db866bbe..ec4dcf5ef 100644 --- a/apps/web/public/index.html +++ b/apps/web/public/index.html @@ -17,7 +17,7 @@ rel="icon" type="image/png" sizes="32x32" - href="%PUBLIC_URL%/favicon-32x32.png" + href="data:image/png;base64,iVBORw0KGgoAAAANSUhEUgAAACAAAAAgCAMAAABEpIrGAAAABGdBTUEAALGPC/xhBQAAAAFzUkdCAK7OHOkAAAHUUExURUxpcf////////////////////////////////////////////////////////////////////////////////////39/f39/f////////////////////////////////z8/O3s7P7+/vTz8wAAAPz7++/v7/Py8u/u7vDv7+7t7dfX1/79/ezr66Kiovv6+vf29vX09OTj4w0NDfv7++Ti4vr6+vT09Ovq6vn5+f39/fb19dnX1/b29tvb29/f3xMTE/j4+Pn4+O7s7MnJyerq6uno6M/Pz8jIyN3d3dnZ2d7e3kZGRvj39/38/Pf39+zs7PDw8OPi4ubl5cvLy+rp6czMzPLy8vPz887Ozujo6MrKyuDf3+fn59PT0+bm5uXl5c3NzfX19evr6wsLC+/t7ePj4/Ty8u3r6/Lx8eXk5OPh4dDQ0Nza2tjX19vZ2fHw8Nva2tLR0djW1tbW1tra2tLS0uHh4evp6eLi4tXV1djY2L+/vysrK7q6utHR0aGhoa6urrm5uQICAigoKCEhIZCQkFxcXBgYGFRUVBISEm1tbQ4ODklJSbu7uyAgID09PZqamkVFRXh4eLGxsTg4OOTk5FFRUQEBAQoKCnx8fBcXF7/+rP8AAAAfdFJOUwBOqO2h6KT9Mtyr5+w3Str+6TTZOjXb31M24O8BOd0PapSsAAABxElEQVQ4y2NgYOBgYWWXxwLYBdmEGICAjx9Z1NJJVQfB42QE6hdG0RaoaaCqhKSCl4EF1VxNVAXyTAzcaArUUBXwMLBjKDBBViDAII+uwBZFgTy6AjU1awIKNKz1aa0g1VBfB58CDS08CuIb40AKzLAqsJKXN2tXacChwKS+rTtB3tJHRU9ewxmbFZ6tKiqTrSAKdIO1LDEUaPeoqMzsUwAp0NIt8Uj0ijJXQFVgnDJVZcK0XrACXcXY0sjoHM8AfyUkBbOn9KvMmgRSYFEW4hUVqJagHeaShqTAwX7ijJQusAkW5VXRHu6eruFqaigKkqarqAAVKFbYxWiYq1XGedijuMHBWz6xo7m2Rl7RwtvdJaw6NjzCFl2BVVNdshJQgbqBsqtdpIu7nasjXIFXS2cyRL2CW6i6JTCgFCLitUPs4QrM7cDmKZi7FRYVm6o5QeLbDGICLNEqJAVk59rk2djY5Me4FThawQOKFSzrrxmaZWpqBAamQUGmwRaakNTNzMAm7+ekrKmlqOjsrIgAGcAA1ci0NVPiYhAV8zVURgbp6nBg7isuwsDAKKfkp4AEVOHASklKApS9JZmY5bECaS5ZGQYA3vqpy6NoYh0AAABXelRYdFJhdyBwcm9maWxlIHR5cGUgaXB0YwAAeJzj8gwIcVYoKMpPy8xJ5VIAAyMLLmMLEyMTS5MUAxMgRIA0w2QDI7NUIMvY1MjEzMQcxAfLgEigSi4A6hcRdPJCNZUAAAAASUVORK5CYII=" /> - + + - diff --git a/apps/web/src/app-effects.js b/apps/web/src/app-effects.js index 24d051997..53d85f9d1 100644 --- a/apps/web/src/app-effects.js +++ b/apps/web/src/app-effects.js @@ -8,16 +8,21 @@ import { db } from "./common/db"; import { CHECK_IDS, EV, EVENTS } from "notes-core/common"; import { registerKeyMap } from "./common/key-map"; import { isUserPremium } from "./hooks/use-is-user-premium"; +import { loadTrackerScript } from "./utils/analytics"; +import Modal from "react-modal"; -function AppEffects({ isMobile, isTablet, setShow, slideToIndex }) { +Modal.setAppElement("#root"); +if (process.env.NODE_ENV === "production") { + loadTrackerScript(); + console.log = () => {}; +} + +export default function AppEffects({ setShow }) { const refreshColors = useStore((store) => store.refreshColors); const refreshMenuPins = useStore((store) => store.refreshMenuPins); const updateLastSynced = useStore((store) => store.updateLastSynced); const setProcessingStatus = useStore((store) => store.setProcessingStatus); const isFocusMode = useStore((store) => store.isFocusMode); - const isEditorOpen = useStore((store) => store.isEditorOpen); - const toggleSideMenu = useStore((store) => store.toggleSideMenu); - const isSideMenuOpen = useStore((store) => store.isSideMenuOpen); const addReminder = useStore((store) => store.addReminder); const initUser = useUserStore((store) => store.init); const initNotes = useNotesStore((store) => store.init); @@ -115,25 +120,8 @@ function AppEffects({ isMobile, isTablet, setShow, slideToIndex }) { }; }, []); - useEffect(() => { - if (!isMobile) return; - slideToIndex(isSideMenuOpen ? 0 : 1); - }, [isMobile, slideToIndex, isSideMenuOpen]); - - useEffect(() => { - if (!isMobile) return; - slideToIndex(isEditorOpen ? 2 : 1); - }, [isMobile, slideToIndex, isEditorOpen]); - - useEffect(() => { - toggleSideMenu(!isMobile); - if (!isMobile && !isTablet && !isFocusMode) setShow(true); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, [isMobile, isTablet, isFocusMode, toggleSideMenu]); - return ; } -export default AppEffects; function getProcessingStatusFromType(type) { switch (type) { diff --git a/apps/web/src/app-effects.mobile.js b/apps/web/src/app-effects.mobile.js new file mode 100644 index 000000000..08b99c5d7 --- /dev/null +++ b/apps/web/src/app-effects.mobile.js @@ -0,0 +1,64 @@ +import { useEffect } from "react"; +import { useStore } from "./stores/app-store"; +import useSlider from "./hooks/use-slider"; +import useMobile from "./utils/use-mobile"; +import useTablet from "./utils/use-tablet"; + +export default function MobileAppEffects({ sliderId, overlayId, setShow }) { + const isMobile = useMobile(); + const isTablet = useTablet(); + const toggleSideMenu = useStore((store) => store.toggleSideMenu); + const setIsEditorOpen = useStore((store) => store.setIsEditorOpen); + const isEditorOpen = useStore((store) => store.isEditorOpen); + const isSideMenuOpen = useStore((store) => store.isSideMenuOpen); + const isFocusMode = useStore((store) => store.isFocusMode); + + const [slideToIndex] = useSlider(sliderId, { + onSliding: (e, { lastSlide, position, lastPosition }) => { + if (!isMobile) return; + const offset = 70; + const width = 180; + + const percent = offset - (position / width) * offset; + const overlay = document.getElementById("overlay"); + if (percent > 0) { + overlay.style.opacity = `${percent}%`; + overlay.style.pointerEvents = "all"; + } else { + overlay.style.pointerEvents = "none"; + } + }, + onChange: (e, { slide, lastSlide }) => { + if (!lastSlide || !isMobile) return; + toggleSideMenu(slide?.index === 0 ? true : false); + setIsEditorOpen(slide?.index === 2 ? true : false); + }, + }); + + useEffect(() => { + if (!isMobile) return; + slideToIndex(isSideMenuOpen ? 0 : 1); + }, [isMobile, slideToIndex, isSideMenuOpen]); + + useEffect(() => { + if (!isMobile) return; + slideToIndex(isEditorOpen ? 2 : 1); + }, [isMobile, slideToIndex, isEditorOpen]); + + useEffect(() => { + toggleSideMenu(!isMobile); + if (!isMobile && !isTablet && !isFocusMode) setShow(true); + // eslint-disable-next-line react-hooks/exhaustive-deps + }, [isMobile, isTablet, isFocusMode, toggleSideMenu]); + + useEffect(() => { + if (!overlayId) return; + const overlay = document.getElementById(overlayId); + overlay.onclick = () => toggleSideMenu(false); + return () => { + overlay.onclick = null; + }; + }, [overlayId, toggleSideMenu]); + + return null; +} diff --git a/apps/web/src/app.css b/apps/web/src/app.css index cc8ce7f6d..268d8c101 100644 --- a/apps/web/src/app.css +++ b/apps/web/src/app.css @@ -1,3 +1,75 @@ +/* open-sans-regular - latin */ +@font-face { + font-family: "Open Sans"; + font-display: swap; + font-style: normal; + font-weight: 400; + src: local(""), + url("./assets/fonts/open-sans-v20-latin-regular.woff2") format("woff2"), + /* Chrome 26+, Opera 23+, Firefox 39+ */ + url("./assets/fonts/open-sans-v20-latin-regular.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ +} + +/* open-sans-italic - latin */ +@font-face { + font-family: "Open Sans"; + font-display: swap; + font-style: italic; + font-weight: 400; + src: local(""), + url("./assets/fonts/open-sans-v20-latin-italic.woff2") format("woff2"), + /* Chrome 26+, Opera 23+, Firefox 39+ */ + url("./assets/fonts/open-sans-v20-latin-italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ +} + +/* open-sans-600 - latin */ +@font-face { + font-family: "Open Sans"; + font-display: swap; + font-style: normal; + font-weight: 600; + src: local(""), + url("./assets/fonts/open-sans-v20-latin-600.woff2") format("woff2"), + /* Chrome 26+, Opera 23+, Firefox 39+ */ + url("./assets/fonts/open-sans-v20-latin-600.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ +} + +/* open-sans-600italic - latin */ +@font-face { + font-family: "Open Sans"; + font-display: swap; + font-style: italic; + font-weight: 600; + src: local(""), + url("./assets/fonts/open-sans-v20-latin-600italic.woff2") format("woff2"), + /* Chrome 26+, Opera 23+, Firefox 39+ */ + url("./assets/fonts/open-sans-v20-latin-600italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ +} + +/* open-sans-700 - latin */ +@font-face { + font-family: "Open Sans"; + font-display: swap; + font-style: normal; + font-weight: 700; + src: local(""), + url("./assets/fonts/open-sans-v20-latin-700.woff2") format("woff2"), + /* Chrome 26+, Opera 23+, Firefox 39+ */ + url("./assets/fonts/open-sans-v20-latin-700.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ +} + +/* open-sans-700italic - latin */ +@font-face { + font-family: "Open Sans"; + font-display: swap; + font-style: italic; + font-weight: 700; + src: local(""), + url("./assets/fonts/open-sans-v20-latin-700italic.woff2") format("woff2"), + /* Chrome 26+, Opera 23+, Firefox 39+ */ + url("./assets/fonts/open-sans-v20-latin-700italic.woff") format("woff"); /* Chrome 6+, Firefox 3.6+, IE 9+, Safari 5.1+ */ +} + .unselectable { -webkit-touch-callout: none; -webkit-user-select: none; diff --git a/apps/web/src/app.js b/apps/web/src/app.js index 779daafcd..2c62e964c 100644 --- a/apps/web/src/app.js +++ b/apps/web/src/app.js @@ -1,81 +1,48 @@ -import React, { useState, useEffect } from "react"; -import "./app.css"; -import { Box, Flex } from "rebass"; -import { MotionConfig, AnimationFeature, GesturesFeature } from "framer-motion"; +import React, { useState, Suspense } from "react"; +import { Box, Flex, Text } from "rebass"; import ThemeProvider from "./components/theme-provider"; -import StatusBar from "./components/statusbar"; -import Animated from "./components/animated"; -import NavigationMenu from "./components/navigationmenu"; -import GlobalMenuWrapper from "./components/globalmenuwrapper"; -import { NavigationEvents } from "./navigation"; -import rootroutes from "./navigation/rootroutes"; -import { useStore } from "./stores/app-store"; -import { Suspense } from "react"; +import { AnimatedFlex } from "./components/animated"; +import NavigationMenuPlaceholder from "./components/navigationmenu/index.lite"; +import StatusBarPlaceholder from "./components/statusbar/index.lite"; import useMobile from "./utils/use-mobile"; import useTablet from "./utils/use-tablet"; -import HashRouter from "./components/hashrouter"; -import useSlider from "./hooks/use-slider"; -import useRoutes from "./utils/use-routes"; -import { clearRouteCache } from "./components/cachedrouter"; +import { Loading } from "./components/icons"; +import { LazyMotion, domAnimation } from "framer-motion"; +import useDatabase from "./hooks/use-database"; +import Loader from "./components/loader"; +const GlobalMenuWrapper = React.lazy(() => + import("./components/global-menu-wrapper") +); const AppEffects = React.lazy(() => import("./app-effects")); +const MobileAppEffects = React.lazy(() => import("./app-effects.mobile")); const CachedRouter = React.lazy(() => import("./components/cached-router")); +const HashRouter = React.lazy(() => import("./components/hash-router")); +const NavigationMenu = React.lazy(() => import("./components/navigation-menu")); +const StatusBar = React.lazy(() => import("./components/status-bar")); function App() { const [show, setShow] = useState(true); const isMobile = useMobile(); const isTablet = useTablet(); - const [isAppLoaded, setIsAppLoaded] = useState(false); - const toggleSideMenu = useStore((store) => store.toggleSideMenu); - const setIsEditorOpen = useStore((store) => store.setIsEditorOpen); - const [sliderRef, slideToIndex] = useSlider({ - onSliding: (e, { lastSlide, position, lastPosition }) => { - if (!isMobile) return; - const offset = 70; - const width = 180; - - const percent = offset - (position / width) * offset; - const overlay = document.getElementById("overlay"); - if (percent > 0) { - overlay.style.opacity = `${percent}%`; - overlay.style.pointerEvents = "all"; - } else { - overlay.style.pointerEvents = "none"; - } - }, - onChange: (e, { slide, lastSlide }) => { - if (!lastSlide || !isMobile) return; - toggleSideMenu(slide?.index === 0 ? true : false); - setIsEditorOpen(slide?.index === 2 ? true : false); - }, - }); - - useEffect(() => { - function onNavigate() { - NavigationEvents.unsubscribe("onNavigate", onNavigate); - setIsAppLoaded(true); - } - NavigationEvents.subscribe("onNavigate", onNavigate); - return () => { - NavigationEvents.unsubscribe("onNavigate", onNavigate); - }; - }, []); + const [isAppLoaded] = useDatabase(); return ( - + {isAppLoaded && ( }> - + + + {isMobile && ( + + )} )} - - - { - if (!isMobile) setShow(state || !show); + { + if (!isMobile) setShow(state || !show); + }, }} + fallback={} /> - - }> - - + + } + /> {isMobile && ( { - toggleSideMenu(false); - }} /> )} - + - + + } + component={HashRouter} + condition={isAppLoaded} + /> - + } + component={StatusBar} + condition={isAppLoaded} + /> - + ); } -function Root() { - const route = useRoutes(rootroutes); - if (route) clearRouteCache(); - return route?.component || ; -} +export default App; -export default Root; +function SuspenseLoader({ condition, props, component: Component, fallback }) { + if (!condition) return fallback; + + return ( + + + + ); +} diff --git a/apps/web/public/fonts/open-sans-v20-latin-600.woff b/apps/web/src/assets/fonts/open-sans-v20-latin-600.woff similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-600.woff rename to apps/web/src/assets/fonts/open-sans-v20-latin-600.woff diff --git a/apps/web/public/fonts/open-sans-v20-latin-600.woff2 b/apps/web/src/assets/fonts/open-sans-v20-latin-600.woff2 similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-600.woff2 rename to apps/web/src/assets/fonts/open-sans-v20-latin-600.woff2 diff --git a/apps/web/public/fonts/open-sans-v20-latin-600italic.woff b/apps/web/src/assets/fonts/open-sans-v20-latin-600italic.woff similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-600italic.woff rename to apps/web/src/assets/fonts/open-sans-v20-latin-600italic.woff diff --git a/apps/web/public/fonts/open-sans-v20-latin-600italic.woff2 b/apps/web/src/assets/fonts/open-sans-v20-latin-600italic.woff2 similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-600italic.woff2 rename to apps/web/src/assets/fonts/open-sans-v20-latin-600italic.woff2 diff --git a/apps/web/public/fonts/open-sans-v20-latin-700.woff b/apps/web/src/assets/fonts/open-sans-v20-latin-700.woff similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-700.woff rename to apps/web/src/assets/fonts/open-sans-v20-latin-700.woff diff --git a/apps/web/public/fonts/open-sans-v20-latin-700.woff2 b/apps/web/src/assets/fonts/open-sans-v20-latin-700.woff2 similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-700.woff2 rename to apps/web/src/assets/fonts/open-sans-v20-latin-700.woff2 diff --git a/apps/web/public/fonts/open-sans-v20-latin-700italic.woff b/apps/web/src/assets/fonts/open-sans-v20-latin-700italic.woff similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-700italic.woff rename to apps/web/src/assets/fonts/open-sans-v20-latin-700italic.woff diff --git a/apps/web/public/fonts/open-sans-v20-latin-700italic.woff2 b/apps/web/src/assets/fonts/open-sans-v20-latin-700italic.woff2 similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-700italic.woff2 rename to apps/web/src/assets/fonts/open-sans-v20-latin-700italic.woff2 diff --git a/apps/web/public/fonts/open-sans-v20-latin-italic.woff b/apps/web/src/assets/fonts/open-sans-v20-latin-italic.woff similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-italic.woff rename to apps/web/src/assets/fonts/open-sans-v20-latin-italic.woff diff --git a/apps/web/public/fonts/open-sans-v20-latin-italic.woff2 b/apps/web/src/assets/fonts/open-sans-v20-latin-italic.woff2 similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-italic.woff2 rename to apps/web/src/assets/fonts/open-sans-v20-latin-italic.woff2 diff --git a/apps/web/public/fonts/open-sans-v20-latin-regular.woff b/apps/web/src/assets/fonts/open-sans-v20-latin-regular.woff similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-regular.woff rename to apps/web/src/assets/fonts/open-sans-v20-latin-regular.woff diff --git a/apps/web/public/fonts/open-sans-v20-latin-regular.woff2 b/apps/web/src/assets/fonts/open-sans-v20-latin-regular.woff2 similarity index 100% rename from apps/web/public/fonts/open-sans-v20-latin-regular.woff2 rename to apps/web/src/assets/fonts/open-sans-v20-latin-regular.woff2 diff --git a/apps/web/src/common/db.js b/apps/web/src/common/db.js index 7eb54d5ca..c4b7758a2 100644 --- a/apps/web/src/common/db.js +++ b/apps/web/src/common/db.js @@ -1,11 +1,3 @@ -import StorageInterface from "../interfaces/storage"; -import FS from "../interfaces/fs"; -import Config from "../utils/config"; -import http from "notes-core/utils/http"; -import { EV, EVENTS } from "notes-core/common"; -import { getCurrentHash, hashNavigate } from "../navigation"; -import { isTesting } from "../utils/platform"; - global.HTMLParser = new DOMParser().parseFromString( "", "text/html" @@ -16,67 +8,31 @@ global.HTMLParser = new DOMParser().parseFromString( var db; async function initializeDatabase() { const { default: Database } = await import("notes-core/api"); - db = new Database(StorageInterface, EventSource, FS); + const { default: Storage } = await import("../interfaces/storage"); + const { default: FS } = await import("../interfaces/fs"); + db = new Database(Storage, EventSource, FS); - if (isTesting()) { - db.host({ - API_HOST: "https://api.notesnook.com", - AUTH_HOST: "https://auth.streetwriters.co", - SSE_HOST: "https://events.streetwriters.co", - }); - } else { - db.host({ - API_HOST: "http://localhost:5264", - AUTH_HOST: "http://localhost:8264", - SSE_HOST: "http://localhost:7264", - }); - // db.host({ - // API_HOST: "http://192.168.10.29:5264", - // AUTH_HOST: "http://192.168.10.29:8264", - // SSE_HOST: "http://192.168.10.29:7264", - // }); - } + // if (isTesting()) { + // db.host({ + // API_HOST: "https://api.notesnook.com", + // AUTH_HOST: "https://auth.streetwriters.co", + // SSE_HOST: "https://events.streetwriters.co", + // }); + // } else { + // db.host({ + // API_HOST: "http://localhost:5264", + // AUTH_HOST: "http://localhost:8264", + // SSE_HOST: "http://localhost:7264", + // }); + // // db.host({ + // // API_HOST: "http://192.168.10.29:5264", + // // AUTH_HOST: "http://192.168.10.29:8264", + // // SSE_HOST: "http://192.168.10.29:7264", + // // }); + // } await db.init(); - - if (!isAppHydrated() && !isTesting()) { - try { - loadDefaultNotes(db); - } catch (e) {} - } return db; } export { db, initializeDatabase }; - -function isAppHydrated() { - return Config.get("hydrated", false); -} - -function setAppHydrated() { - return Config.set("hydrated", true); -} - -async function loadDefaultNotes(db) { - const notes = await http.get("/notes/index_v14.json"); - if (!notes) return; - let autoOpenId; - const hash = getCurrentHash().replaceAll("#", ""); - for (let note of notes) { - const content = await http.get(note.webContent); - let id = await db.notes.add({ - title: note.title, - headline: note.headline, - localOnly: true, - content: { type: "tiny", data: content }, - }); - if (note.autoOpen) autoOpenId = id; - } - - if (autoOpenId) { - hashNavigate(`/notes/${autoOpenId}/edit`); - if (hash) setTimeout(() => hashNavigate(hash), 100); - } - setAppHydrated(); - EV.publish(EVENTS.appRefreshRequested); -} diff --git a/apps/web/src/common/height-calculator.js b/apps/web/src/common/height-calculator.js index 6432c13e5..0d2a223e2 100644 --- a/apps/web/src/common/height-calculator.js +++ b/apps/web/src/common/height-calculator.js @@ -1,6 +1,6 @@ const SINGLE_LINE_HEIGHT = 1.4; const DEFAULT_FONT_SIZE = document.getElementById("p").clientHeight - 1; -console.log("HEIGHT", DEFAULT_FONT_SIZE); + const MAX_HEIGHTS = { note: SINGLE_LINE_HEIGHT * 7 * DEFAULT_FONT_SIZE, notebook: SINGLE_LINE_HEIGHT * 7 * DEFAULT_FONT_SIZE, diff --git a/apps/web/src/components/animated/index.js b/apps/web/src/components/animated/index.js index 6ffd4f160..481f8b468 100644 --- a/apps/web/src/components/animated/index.js +++ b/apps/web/src/components/animated/index.js @@ -1,12 +1,9 @@ -import { m as motion } from "framer-motion"; +import { m } from "framer-motion"; import { Flex, Box, Image, Text } from "rebass"; import { Input } from "@rebass/forms"; -const Animated = { - Flex: motion.custom(Flex), - Box: motion.custom(Box), - Image: motion.custom(Image), - Text: motion.custom(Text), - Input: motion.custom(Input), -}; -export default Animated; +export const AnimatedFlex = m(Flex); +export const AnimatedBox = m(Box); +export const AnimatedImage = m(Image); +export const AnimatedText = m(Text); +export const AnimatedInput = m(Input); diff --git a/apps/web/src/components/cached-router/index.js b/apps/web/src/components/cached-router/index.js index 7f628757a..e81b43795 100644 --- a/apps/web/src/components/cached-router/index.js +++ b/apps/web/src/components/cached-router/index.js @@ -1,6 +1,6 @@ import React, { useEffect } from "react"; import ReactDOM from "react-dom"; -import { NavigationEvents } from "../../navigation"; +import { getHomeRoute, NavigationEvents } from "../../navigation"; import useRoutes from "../../utils/use-routes"; import RouteContainer from "../route-container"; import ThemeProvider from "../theme-provider"; @@ -8,10 +8,12 @@ import routes from "../../navigation/routes"; var cache = {}; function CachedRouter() { - const RouteResult = useRoutes(routes, { fallbackRoute: "/" }); + const [RouteResult, location] = useRoutes(routes, { + fallbackRoute: getHomeRoute(), + }); useEffect(() => { if (!RouteResult) return; - NavigationEvents.publish("onNavigate", RouteResult); + NavigationEvents.publish("onNavigate", RouteResult, location); window.currentViewType = RouteResult.type; window.currentViewKey = RouteResult.key; @@ -45,7 +47,7 @@ function CachedRouter() { route ); } - }, [RouteResult]); + }, [RouteResult, location]); return ( - ) : null} - + diff --git a/apps/web/src/components/editor/toolbar.js b/apps/web/src/components/editor/toolbar.js index 764ca1b63..5af9d2c14 100644 --- a/apps/web/src/components/editor/toolbar.js +++ b/apps/web/src/components/editor/toolbar.js @@ -6,7 +6,7 @@ import { useStore as useAppStore } from "../../stores/app-store"; import { useStore as useThemeStore } from "../../stores/theme-store"; import { useStore, store } from "../../stores/editor-store"; import { showToast } from "../../utils/toast"; -import Animated from "../animated"; +import { AnimatedInput } from "../animated"; import { showPublishView } from "../publish-view"; import { db } from "../../common/db"; @@ -163,7 +163,7 @@ function Toolbar(props) { clearSession(); }} /> - { if (groups.length <= 0) return; @@ -165,7 +165,7 @@ function GroupHeader(props) { )} )} - + ); } export default GroupHeader; diff --git a/apps/web/src/components/icons/index.js b/apps/web/src/components/icons/index.js index ecf5eb6de..a9fc14e95 100644 --- a/apps/web/src/components/icons/index.js +++ b/apps/web/src/components/icons/index.js @@ -1,8 +1,101 @@ import React, { useState } from "react"; import MDIIcon from "@mdi/react"; -import * as Icons from "@mdi/js"; +import { + mdiPlus, + mdiHomeVariantOutline, + mdiMinus, + mdiBookOutline, + mdiNotebookOutline, + mdiArrowLeft, + mdiArrowRight, + mdiArrowDown, + mdiBookPlusMultipleOutline, + mdiBookmarkOutline, + mdiAlert, + mdiShieldOutline, + mdiLockOpenOutline, + mdiLockOutline, + mdiStar, + mdiStarOutline, + mdiCircle, + mdiCircleOutline, + mdiUpdate, + mdiCheck, + mdiClose, + mdiDotsVertical, + mdiTrashCanOutline, + mdiBookRemoveOutline, + mdiMagnify, + mdiMenu, + mdiLoginVariant, + mdiEmailAlertOutline, + mdiAccountOutline, + mdiLogoutVariant, + mdiSunglasses, + mdiGlasses, + mdiCogOutline, + mdiHomeOutline, + mdiRecycle, + mdiSync, + mdiLoading, + mdiExportVariant, + mdiArrowExpandDown, + mdiArrowTopRightThick, + mdiChevronLeft, + mdiChevronRight, + mdiPound, + mdiPinOutline, + mdiPin, + mdiWeatherNight, + mdiWeatherSunny, + mdiThemeLightDark, + mdiCheckCircle, + mdiLanguageMarkdownOutline, + mdiFilePdfOutline, + mdiLanguageHtml5, + mdiFormatTitle, + mdiAlertCircle, + mdiInformation, + mdiToggleSwitchOffOutline, + mdiToggleSwitchOutline, + mdiBackupRestore, + mdiCurrencyUsdCircleOutline, + mdiPencil, + mdiUndoVariant, + mdiRedoVariant, + mdiTune, + mdiChevronDown, + mdiChevronUp, + mdiSortAscending, + mdiSortDescending, + mdiEye, + mdiEyeOff, + mdiFullscreen, + mdiFullscreenExit, + mdiBullhorn, + mdiCloudUploadOutline, + mdiCloudCheckOutline, + mdiContentCopy, + mdiCheckboxMultipleMarkedCircleOutline, + mdiBookEditOutline, + mdiDeleteForeverOutline, + mdiTextBoxMultipleOutline, + mdiRocketLaunchOutline, + mdiShareVariantOutline, + mdiFormTextboxPassword, + mdiBomb, + mdiViewHeadline, + mdiViewSequentialOutline, + mdiEmailCheckOutline, + mdiDiscord, + mdiTwitter, + mdiReddit, + mdiFileOutline, + mdiImage, + mdiNoteOutline, +} from "@mdi/js"; import { useTheme } from "emotion-theming"; -import Animated from "../animated"; +import { AnimatedFlex } from "../animated"; function Icon({ title, name, size = 24, color = "icon", stroke, rotate }) { const theme = useTheme(); @@ -25,7 +118,7 @@ function createIcon(name, rotate = false) { return function (props) { const [isHovering, setIsHovering] = useState(); return ( - - + ); }; } -export const Plus = createIcon(Icons.mdiPlus); -export const Note = createIcon(Icons.mdiHomeVariantOutline); -export const Minus = createIcon(Icons.mdiMinus); -export const Notebook = createIcon(Icons.mdiBookOutline); -export const Notebook2 = createIcon(Icons.mdiNotebookOutline); -export const ArrowLeft = createIcon(Icons.mdiArrowLeft); -export const ArrowRight = createIcon(Icons.mdiArrowRight); -export const ArrowDown = createIcon(Icons.mdiArrowDown); -export const Move = createIcon(Icons.mdiBookPlusMultipleOutline); -export const Topic = createIcon(Icons.mdiBookmarkOutline); -export const Alert = createIcon(Icons.mdiAlert); -export const Vault = createIcon(Icons.mdiShieldOutline); -export const Unlock = createIcon(Icons.mdiLockOpenOutline); -export const Lock = createIcon(Icons.mdiLockOutline); -export const Star = createIcon(Icons.mdiStar); -export const StarOutline = createIcon(Icons.mdiStarOutline); -export const Circle = createIcon(Icons.mdiCircle); -export const CircleEmpty = createIcon(Icons.mdiCircleOutline); -export const Update = createIcon(Icons.mdiUpdate); -export const Check = createIcon(Icons.mdiCheck); -export const Cross = createIcon(Icons.mdiClose); -export const MoreVertical = createIcon(Icons.mdiDotsVertical); -export const Trash = createIcon(Icons.mdiTrashCanOutline); -export const TopicRemove = createIcon(Icons.mdiBookRemoveOutline); -export const Search = createIcon(Icons.mdiMagnify); -export const Menu = createIcon(Icons.mdiMenu); -export const Login = createIcon(Icons.mdiLoginVariant); -export const Email = createIcon(Icons.mdiEmailAlertOutline); -export const Signup = createIcon(Icons.mdiAccountOutline); -export const Logout = createIcon(Icons.mdiLogoutVariant); -export const FocusMode = createIcon(Icons.mdiSunglasses); -export const NormalMode = createIcon(Icons.mdiGlasses); -export const Settings = createIcon(Icons.mdiCogOutline); -export const Home = createIcon(Icons.mdiHomeOutline); -export const Restore = createIcon(Icons.mdiRecycle); -export const Sync = createIcon(Icons.mdiSync); -export const Loading = createIcon(Icons.mdiLoading, true); -export const Export = createIcon(Icons.mdiExportVariant); -export const AddToNotebook = createIcon(Icons.mdiBookPlusMultipleOutline); -export const Expand = createIcon(Icons.mdiArrowExpandDown); -export const Shortcut = createIcon(Icons.mdiArrowTopRightThick); - -/** Properties Icons */ -export const ChevronLeft = createIcon(Icons.mdiChevronLeft); -export const ChevronRight = createIcon(Icons.mdiChevronRight); -export const Close = createIcon(Icons.mdiClose); -export const Tag = createIcon(Icons.mdiPound); -export const Tag2 = createIcon(Icons.mdiPound); -export const Pin = createIcon(Icons.mdiPinOutline); -export const PinFilled = createIcon(Icons.mdiPin); - -/** Settings Icons */ -export const User = createIcon(Icons.mdiAccountOutline); -export const DarkMode = createIcon(Icons.mdiWeatherNight); -export const LightMode = createIcon(Icons.mdiWeatherSunny); -export const Theme = createIcon(Icons.mdiThemeLightDark); -export const Checkmark = createIcon(Icons.mdiCheck); -export const CheckCircle = createIcon(Icons.mdiCheckCircle); - -export const Properties = createIcon(Icons.mdiDotsVertical); - -// FORMATS - -export const Markdown = createIcon(Icons.mdiLanguageMarkdownOutline); -export const PDF = createIcon(Icons.mdiFilePdfOutline); -export const HTML = createIcon(Icons.mdiLanguageHtml5); -export const Text = createIcon(Icons.mdiFormatTitle); - -// TOAST -export const Success = createIcon(Icons.mdiCheckCircle); -export const Error = createIcon(Icons.mdiAlertCircle); -export const Warn = createIcon(Icons.mdiAlert); -export const Info = createIcon(Icons.mdiInformation); - -export const ToggleUnchecked = createIcon(Icons.mdiToggleSwitchOffOutline); -export const ToggleChecked = createIcon(Icons.mdiToggleSwitchOutline); - -export const Backup = createIcon(Icons.mdiBackupRestore); -export const Buy = createIcon(Icons.mdiCurrencyUsdCircleOutline); - -export const Edit = createIcon(Icons.mdiPencil); - -export const Undo = createIcon(Icons.mdiUndoVariant); -export const Redo = createIcon(Icons.mdiRedoVariant); - -export const Filter = createIcon(Icons.mdiTune); - -export const ChevronDown = createIcon(Icons.mdiChevronDown); -export const ChevronUp = createIcon(Icons.mdiChevronUp); - -export const SortAsc = createIcon(Icons.mdiSortAscending); -export const SortDesc = createIcon(Icons.mdiSortDescending); - -export const PasswordInvisible = createIcon(Icons.mdiEye); -export const PasswordVisible = createIcon(Icons.mdiEyeOff); - -export const Fullscreen = createIcon(Icons.mdiFullscreen); -export const ExitFullscreen = createIcon(Icons.mdiFullscreenExit); - -export const Announcement = createIcon(Icons.mdiBullhorn); -export const Publish = createIcon(Icons.mdiCloudUploadOutline); -export const Published = createIcon(Icons.mdiCloudCheckOutline); - -export const Copy = createIcon(Icons.mdiContentCopy); - -export const Select = createIcon(Icons.mdiCheckboxMultipleMarkedCircleOutline); -export const NotebookEdit = createIcon(Icons.mdiBookEditOutline); -export const DeleteForver = createIcon(Icons.mdiDeleteForeverOutline); - -export const Monographs = createIcon(Icons.mdiTextBoxMultipleOutline); - -export const Rocket = createIcon(Icons.mdiRocketLaunchOutline); -export const Share = createIcon(Icons.mdiShareVariantOutline); -export const Password = createIcon(Icons.mdiFormTextboxPassword); -export const Destruct = createIcon(Icons.mdiBomb); - -export const CompactView = createIcon(Icons.mdiViewHeadline); -export const DetailedView = createIcon(Icons.mdiViewSequentialOutline); - -export const MailCheck = createIcon(Icons.mdiEmailCheckOutline); - -export const Discord = createIcon(Icons.mdiDiscord); -export const Twitter = createIcon(Icons.mdiTwitter); -export const Reddit = createIcon(Icons.mdiReddit); - -export const Dismiss = createIcon(Icons.mdiClose); - -export const File = createIcon(Icons.mdiFileOutline); -export const Download = createIcon(Icons.mdiArrowDown); -export const ImageDownload = createIcon(Icons.mdiImage); +export const Plus = createIcon(mdiPlus); +export const Note = createIcon(mdiNoteOutline); +export const Minus = createIcon(mdiMinus); +export const Notebook = createIcon(mdiBookOutline); +export const Notebook2 = createIcon(mdiNotebookOutline); +export const ArrowLeft = createIcon(mdiArrowLeft); +export const ArrowRight = createIcon(mdiArrowRight); +export const ArrowDown = createIcon(mdiArrowDown); +export const Move = createIcon(mdiBookPlusMultipleOutline); +export const Topic = createIcon(mdiBookmarkOutline); +export const Alert = createIcon(mdiAlert); +export const Vault = createIcon(mdiShieldOutline); +export const Unlock = createIcon(mdiLockOpenOutline); +export const Lock = createIcon(mdiLockOutline); +export const Star = createIcon(mdiStar); +export const StarOutline = createIcon(mdiStarOutline); +export const Circle = createIcon(mdiCircle); +export const CircleEmpty = createIcon(mdiCircleOutline); +export const Update = createIcon(mdiUpdate); +export const Check = createIcon(mdiCheck); +export const Cross = createIcon(mdiClose); +export const MoreVertical = createIcon(mdiDotsVertical); +export const Trash = createIcon(mdiTrashCanOutline); +export const TopicRemove = createIcon(mdiBookRemoveOutline); +export const Search = createIcon(mdiMagnify); +export const Menu = createIcon(mdiMenu); +export const Login = createIcon(mdiLoginVariant); +export const Email = createIcon(mdiEmailAlertOutline); +export const Signup = createIcon(mdiAccountOutline); +export const Logout = createIcon(mdiLogoutVariant); +export const FocusMode = createIcon(mdiSunglasses); +export const NormalMode = createIcon(mdiGlasses); +export const Settings = createIcon(mdiCogOutline); +export const Home = createIcon(mdiHomeOutline); +export const Restore = createIcon(mdiRecycle); +export const Sync = createIcon(mdiSync); +export const Loading = createIcon(mdiLoading, true); +export const Export = createIcon(mdiExportVariant); +export const AddToNotebook = createIcon(mdiBookPlusMultipleOutline); +export const Expand = createIcon(mdiArrowExpandDown); +export const Shortcut = createIcon(mdiArrowTopRightThick); +export const ChevronLeft = createIcon(mdiChevronLeft); +export const ChevronRight = createIcon(mdiChevronRight); +export const Close = createIcon(mdiClose); +export const Tag = createIcon(mdiPound); +export const Tag2 = createIcon(mdiPound); +export const Pin = createIcon(mdiPinOutline); +export const PinFilled = createIcon(mdiPin); +export const User = createIcon(mdiAccountOutline); +export const DarkMode = createIcon(mdiWeatherNight); +export const LightMode = createIcon(mdiWeatherSunny); +export const Theme = createIcon(mdiThemeLightDark); +export const Checkmark = createIcon(mdiCheck); +export const CheckCircle = createIcon(mdiCheckCircle); +export const Properties = createIcon(mdiDotsVertical); +export const Markdown = createIcon(mdiLanguageMarkdownOutline); +export const PDF = createIcon(mdiFilePdfOutline); +export const HTML = createIcon(mdiLanguageHtml5); +export const Text = createIcon(mdiFormatTitle); +export const Success = createIcon(mdiCheckCircle); +export const Error = createIcon(mdiAlertCircle); +export const Warn = createIcon(mdiAlert); +export const Info = createIcon(mdiInformation); +export const ToggleUnchecked = createIcon(mdiToggleSwitchOffOutline); +export const ToggleChecked = createIcon(mdiToggleSwitchOutline); +export const Backup = createIcon(mdiBackupRestore); +export const Buy = createIcon(mdiCurrencyUsdCircleOutline); +export const Edit = createIcon(mdiPencil); +export const Undo = createIcon(mdiUndoVariant); +export const Redo = createIcon(mdiRedoVariant); +export const Filter = createIcon(mdiTune); +export const ChevronDown = createIcon(mdiChevronDown); +export const ChevronUp = createIcon(mdiChevronUp); +export const SortAsc = createIcon(mdiSortAscending); +export const SortDesc = createIcon(mdiSortDescending); +export const PasswordInvisible = createIcon(mdiEye); +export const PasswordVisible = createIcon(mdiEyeOff); +export const Fullscreen = createIcon(mdiFullscreen); +export const ExitFullscreen = createIcon(mdiFullscreenExit); +export const Announcement = createIcon(mdiBullhorn); +export const Publish = createIcon(mdiCloudUploadOutline); +export const Published = createIcon(mdiCloudCheckOutline); +export const Copy = createIcon(mdiContentCopy); +export const Select = createIcon(mdiCheckboxMultipleMarkedCircleOutline); +export const NotebookEdit = createIcon(mdiBookEditOutline); +export const DeleteForver = createIcon(mdiDeleteForeverOutline); +export const Monographs = createIcon(mdiTextBoxMultipleOutline); +export const Rocket = createIcon(mdiRocketLaunchOutline); +export const Share = createIcon(mdiShareVariantOutline); +export const Password = createIcon(mdiFormTextboxPassword); +export const Destruct = createIcon(mdiBomb); +export const CompactView = createIcon(mdiViewHeadline); +export const DetailedView = createIcon(mdiViewSequentialOutline); +export const MailCheck = createIcon(mdiEmailCheckOutline); +export const Discord = createIcon(mdiDiscord); +export const Twitter = createIcon(mdiTwitter); +export const Reddit = createIcon(mdiReddit); +export const Dismiss = createIcon(mdiClose); +export const File = createIcon(mdiFileOutline); +export const Download = createIcon(mdiArrowDown); +export const ImageDownload = createIcon(mdiImage); diff --git a/apps/web/src/components/loader/index.js b/apps/web/src/components/loader/index.js new file mode 100644 index 000000000..c6c5052ae --- /dev/null +++ b/apps/web/src/components/loader/index.js @@ -0,0 +1,21 @@ +import { Flex, Text } from "rebass"; +import { Loading } from "../icons"; + +export default function Loader({ title, text }) { + return ( + + + + {title} + + + {text} + + + ); +} diff --git a/apps/web/src/components/menu/index.js b/apps/web/src/components/menu/index.js index 006f0a1db..314511155 100644 --- a/apps/web/src/components/menu/index.js +++ b/apps/web/src/components/menu/index.js @@ -4,7 +4,7 @@ import React, { useEffect, useMemo } from "react"; import { Flex, Box, Text, Button } from "rebass"; import { useIsUserPremium } from "../../hooks/use-is-user-premium"; import useMobile from "../../utils/use-mobile"; -import Animated from "../animated"; +import { AnimatedFlex } from "../animated"; function Menu(props) { const { menuItems, data, closeMenu, id, style, sx, state } = props; @@ -193,7 +193,7 @@ function MobileMenuContainer({ style, id, state, title, children }) { overflow="hidden" sx={{ position: "relative" }} > - {children} - + ); } diff --git a/apps/web/src/components/navigation-menu/index.js b/apps/web/src/components/navigation-menu/index.js index c75db2fa9..fa8625949 100644 --- a/apps/web/src/components/navigation-menu/index.js +++ b/apps/web/src/components/navigation-menu/index.js @@ -1,16 +1,32 @@ -import React, { useCallback } from "react"; +import { useCallback } from "react"; import { Box, Flex } from "rebass"; -import { useStore as useAppStore } from "../../stores/app-store"; -import * as Icon from "../icons"; -import { useStore as useUserStore } from "../../stores/user-store"; -import { useStore as useThemeStore } from "../../stores/theme-store"; -import Animated from "../animated"; +import { + Note, + Notebook, + StarOutline, + Monographs, + Tag, + Trash, + Settings, + Notebook2, + Tag2, + Topic, + DarkMode, + LightMode, + Sync, + Login, + Circle, +} from "../icons"; +import { AnimatedFlex } from "../animated"; import NavigationItem from "./navigation-item"; import { navigate } from "../../navigation"; import { db } from "../../common/db"; import useMobile from "../../utils/use-mobile"; -import { useLocation } from "wouter"; import { showRenameColorDialog } from "../../common/dialog-controller"; +import { useStore as useAppStore } from "../../stores/app-store"; +import { useStore as useUserStore } from "../../stores/user-store"; +import { useStore as useThemeStore } from "../../stores/theme-store"; +import useLocation from "../../hooks/use-location"; function shouldSelectNavItem(route, pin) { if (pin.type === "notebook") { @@ -23,31 +39,31 @@ function shouldSelectNavItem(route, pin) { } const routes = [ - { title: "Notes", path: "/", icon: Icon.Note }, + { title: "Notes", path: "/notes", icon: Note }, { title: "Notebooks", path: "/notebooks", - icon: Icon.Notebook, + icon: Notebook, }, { title: "Favorites", path: "/favorites", - icon: Icon.StarOutline, + icon: StarOutline, }, - { title: "Tags", path: "/tags", icon: Icon.Tag }, + { title: "Tags", path: "/tags", icon: Tag }, { title: "Monographs", path: "/monographs", - icon: Icon.Monographs, + icon: Monographs, }, - { title: "Trash", path: "/trash", icon: Icon.Trash }, + { title: "Trash", path: "/trash", icon: Trash }, ]; const bottomRoutes = [ { title: "Settings", path: "/settings", - icon: Icon.Settings, + icon: Settings, }, ]; @@ -77,7 +93,7 @@ function NavigationMenu(props) { ); return ( - { @@ -181,10 +197,10 @@ function NavigationMenu(props) { }} icon={ pin.type === "notebook" - ? Icon.Notebook2 + ? Notebook2 : pin.type === "tag" - ? Icon.Tag2 - : Icon.Topic + ? Tag2 + : Topic } isShortcut selected={shouldSelectNavItem(location, pin)} @@ -204,13 +220,13 @@ function NavigationMenu(props) { {theme === "light" ? ( ) : ( )} @@ -219,14 +235,14 @@ function NavigationMenu(props) { ) : ( navigate("/login")} /> )} @@ -242,7 +258,7 @@ function NavigationMenu(props) { /> ))} - + ); } export default NavigationMenu; diff --git a/apps/web/src/components/navigation-menu/index.lite.js b/apps/web/src/components/navigation-menu/index.lite.js new file mode 100644 index 000000000..291861668 --- /dev/null +++ b/apps/web/src/components/navigation-menu/index.lite.js @@ -0,0 +1,190 @@ +import { Flex } from "rebass"; +import { useStore as useThemeStore } from "../../stores/theme-store"; +import { Button, Text } from "rebass"; +import useTablet from "../../utils/use-tablet"; +import { + Note, + Notebook, + StarOutline, + Monographs, + Tag, + Trash, + Settings, + DarkMode, + LightMode, + Sync, + Login, +} from "../icons"; +import useLocation from "../../hooks/use-location"; + +const routes = [ + { title: "Notes", path: "/", icon: Note }, + { + title: "Notebooks", + path: "/notebooks", + icon: Notebook, + }, + { + title: "Favorites", + path: "/favorites", + icon: StarOutline, + }, + { title: "Tags", path: "/tags", icon: Tag }, + { + title: "Monographs", + path: "/monographs", + icon: Monographs, + }, + { title: "Trash", path: "/trash", icon: Trash }, +]; + +const bottomRoutes = [ + { + title: "Settings", + path: "/settings", + icon: Settings, + }, +]; + +const NAVIGATION_MENU_WIDTH = "10em"; +const NAVIGATION_MENU_TABLET_WIDTH = "4em"; + +function NavigationMenu() { + const isLoggedIn = false; + const theme = useThemeStore((store) => store.theme); + const toggleNightMode = useThemeStore((store) => store.toggleNightMode); + const [location] = useLocation(); + + return ( + + + {routes.map((item) => ( + + ))} + + + {theme === "light" ? ( + + ) : ( + + )} + {isLoggedIn ? ( + <> + + + ) : ( + + )} + {bottomRoutes.map((item) => ( + + ))} + + + ); +} +export default NavigationMenu; + +function NavigationItem(props) { + const { icon: Icon, color, title, isLoading } = props; + const isTablet = useTablet(); + + return ( + + ); +} diff --git a/apps/web/src/components/placeholders/favorites-placeholder.js b/apps/web/src/components/placeholders/favorites-placeholder.js index c805341af..7e78c9a75 100644 --- a/apps/web/src/components/placeholders/favorites-placeholder.js +++ b/apps/web/src/components/placeholders/favorites-placeholder.js @@ -1,5 +1,5 @@ import React from "react"; -import { ReactComponent as Fav } from "../../assets/fav.svg"; +import Fav from "../../assets/fav.svg"; import Placeholder from "./index"; function FavoritesPlaceholder() { diff --git a/apps/web/src/components/placeholders/index.js b/apps/web/src/components/placeholders/index.js index 6e03278c2..99fcf7f08 100644 --- a/apps/web/src/components/placeholders/index.js +++ b/apps/web/src/components/placeholders/index.js @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { Button, Flex, Text } from "rebass"; +import { Button, Flex, Image, Text } from "rebass"; import { useStore } from "../../stores/theme-store"; import { changeSvgTheme } from "../../utils/css"; @@ -8,7 +8,7 @@ function Placeholder(props) { useEffect(() => { changeSvgTheme(accent); }, [accent]); - const { image: Image, text, callToAction } = props; + const { image: PlaceholderImage, text, callToAction } = props; return ( <> @@ -17,7 +17,7 @@ function Placeholder(props) { alignSelf="stretch" sx={{ position: "relative" }} > - + - - )} - + ); } diff --git a/apps/web/src/components/status-bar/index.js b/apps/web/src/components/status-bar/index.js index ae251be7f..925dedf50 100644 --- a/apps/web/src/components/status-bar/index.js +++ b/apps/web/src/components/status-bar/index.js @@ -1,7 +1,7 @@ import React from "react"; import { Box, Button, Flex, Text } from "rebass"; import EditorFooter from "../editor/footer"; -import * as Icon from "../icons"; +import { Circle, Sync, Loading, Update } from "../icons"; import { useStore as useUserStore } from "../../stores/user-store"; import { useStore as useAppStore } from "../../stores/app-store"; import TimeAgo from "timeago-react"; @@ -43,7 +43,7 @@ function StatusBar() { display="flex" sx={{ alignItems: "center", justifyContent: "center" }} > - @@ -58,7 +58,7 @@ function StatusBar() { onClick={sync} sx={{ alignItems: "center", justifyContent: "center" }} > - + {"Synced "} {lastSynced ? ( @@ -76,7 +76,7 @@ function StatusBar() { onClick={() => navigate("/login")} sx={{ alignItems: "center", justifyContent: "center" }} > - + Not logged in @@ -84,7 +84,7 @@ function StatusBar() { )} {processingStatuses?.map(({ key, status, progress }) => ( - + {progress ? `${progress}% ${status}` : status} @@ -106,7 +106,7 @@ function StatusBar() { }} sx={{ alignItems: "center", justifyContent: "center" }} > - + + + ); +} + +export default StatusBar; diff --git a/apps/web/src/hooks/use-database.js b/apps/web/src/hooks/use-database.js new file mode 100644 index 000000000..d983a951b --- /dev/null +++ b/apps/web/src/hooks/use-database.js @@ -0,0 +1,16 @@ +import { useEffect, useState } from "react"; +import { initializeDatabase } from "../common/db"; + +export default function useDatabase() { + const [isAppLoaded, setIsAppLoaded] = useState(false); + + useEffect(() => { + (async () => { + await import("../app.css"); + await initializeDatabase(); + setIsAppLoaded(true); + })(); + }, []); + + return [isAppLoaded]; +} diff --git a/apps/web/src/hooks/use-location.js b/apps/web/src/hooks/use-location.js new file mode 100644 index 000000000..073f9fce5 --- /dev/null +++ b/apps/web/src/hooks/use-location.js @@ -0,0 +1,18 @@ +const { useState, useEffect } = require("react"); +const { getCurrentPath, NavigationEvents } = require("../navigation"); + +export default function useLocation() { + const [location, setLocation] = useState(getCurrentPath()); + useEffect(() => { + const navigateEvent = NavigationEvents.subscribe( + "onNavigate", + (_, location) => { + setLocation(location); + } + ); + return () => { + navigateEvent.unsubscribe(); + }; + }, []); + return [location]; +} diff --git a/apps/web/src/hooks/use-slider.js b/apps/web/src/hooks/use-slider.js index d1eb61ddb..095a582d8 100644 --- a/apps/web/src/hooks/use-slider.js +++ b/apps/web/src/hooks/use-slider.js @@ -1,7 +1,10 @@ import { useCallback, useEffect, useMemo, useRef } from "react"; -export default function useSlider({ initialIndex, onSliding, onChange }) { - const ref = useRef(); +export default function useSlider( + sliderId, + { initialIndex, onSliding, onChange } +) { + const ref = useRef(document.getElementById(sliderId)); const slides = useMemo(() => [], []); useEffect(() => { @@ -49,5 +52,5 @@ export default function useSlider({ initialIndex, onSliding, onChange }) { [ref, slides] ); - return [ref, slideToIndex]; + return [slideToIndex]; } diff --git a/apps/web/src/index.css b/apps/web/src/index.css deleted file mode 100644 index 77812d68e..000000000 --- a/apps/web/src/index.css +++ /dev/null @@ -1,65 +0,0 @@ -html, -body { - font-size: 16px; -} - -@media only screen and (max-width: 480px) { - html, - body { - font-size: 18px; - background-color: var(--background); - } -} - -:root, -body, -#root { - margin: 0 !important; - height: 100%; - overflow: hidden; -} - -* { - font-family: "Open Sans", -apple-system, BlinkMacSystemFont, "Segoe UI", - Roboto, Oxygen, Ubuntu, Cantarell, "Helvetica Neue", sans-serif; -} - -@keyframes fadeUp { - 0% { - transform: translateY(500px); - opacity: 0; - } - 80% { - transform: translateY(0px); - opacity: 0.7; - } - 100% { - opacity: 1; - } -} - -/* svg { - width: 1.5rem; -} */ - -.ReactModal__Overlay { - opacity: 0; - transition: opacity 200ms ease-in-out; -} - -.ReactModal__Overlay--after-open { - opacity: 1; -} - -.ReactModal__Overlay--before-close { - opacity: 0; -} - -.slide { - background-color: transparent !important; -} - -.carousel.carousel-slider { - display: flex; - flex-direction: row; -} diff --git a/apps/web/src/index.js b/apps/web/src/index.js index ce8f235db..dfe0092b9 100644 --- a/apps/web/src/index.js +++ b/apps/web/src/index.js @@ -1,46 +1,46 @@ -import "./commands"; -import React from "react"; -import { initializeDatabase } from "./common/db"; -import "./index.css"; +import { render } from "react-dom"; +import { getCurrentPath } from "./navigation"; import * as serviceWorker from "./serviceWorkerRegistration"; -import { loadTrackerScript } from "./utils/analytics"; -import Config from "./utils/config"; -import { isTesting } from "./utils/platform"; +if (process.env.REACT_APP_PLATFORM === "desktop") require("./commands"); -if (process.env.NODE_ENV === "production") { - loadTrackerScript(); - console.log = () => {}; +const ROUTES = { + "/account/recovery": { + component: () => import("./views/recovery"), + props: {}, + }, + "/account/verified": { + component: () => import("./views/email-confirmed"), + props: {}, + }, + "/signup": { + component: () => import("./views/auth"), + props: { type: "signup" }, + }, + "/login": { + component: () => import("./views/auth"), + props: { type: "login" }, + }, + "/recover": { + component: () => import("./views/auth"), + props: { type: "recover" }, + }, + default: { component: () => import("./app"), props: {} }, +}; + +function getRoute() { + const path = getCurrentPath(); + return ROUTES[path] || ROUTES.default; } -const HOMEPAGE_ROUTE = { 1: "/notebooks", 2: "/favorites", 3: "/tags" }; - -async function checkRedirects(db) { - const isLoggedIn = !!(await db.user.getUser()); - if (window.location.pathname === "/") { - const skipInitiation = Config.get("skipInitiation", false); - const homepage = Config.get("homepage", 0); - if (!isTesting() && !isLoggedIn && !skipInitiation) - window.location.replace("/signup"); - else if (homepage) { - const route = HOMEPAGE_ROUTE[homepage]; - window.location.replace(route); +const route = getRoute(); +route.component().then(({ default: Component }) => { + render( + , + document.getElementById("root"), + () => { + document.getElementById("splash").remove(); } - } -} - -initializeDatabase().then(async (db) => { - await checkRedirects(db); - - import("react-dom").then(({ render }) => { - import("./App").then(({ default: App }) => { - render(, document.getElementById("root"), async () => { - document.getElementById("splash").remove(); - import("react-modal").then(({ default: Modal }) => { - Modal.setAppElement("#root"); - }); - }); - }); - }); + ); }); // If you want your app to work offline and load faster, you can change diff --git a/apps/web/src/interfaces/fs.js b/apps/web/src/interfaces/fs.js index f4dd4fba6..ad1f4990d 100644 --- a/apps/web/src/interfaces/fs.js +++ b/apps/web/src/interfaces/fs.js @@ -122,7 +122,6 @@ async function hashStream(reader) { } async function readEncrypted(filename, key, cipherData) { - console.log("Reading encrypted file", filename); const fileHandle = await streamablefs.readFile(filename); if (!fileHandle) { console.error(`File not found. Filename: ${filename}`); @@ -159,8 +158,6 @@ async function readEncrypted(filename, key, cipherData) { } async function uploadFile(filename, requestOptions) { - console.log("Request to upload file", filename, requestOptions); - const fileHandle = await streamablefs.readFile(filename); if (!fileHandle) throw new Error(`File stream not found. Filename: ${filename}`); @@ -261,7 +258,6 @@ function reportProgress(ev, { type, hash }) { async function downloadFile(filename, requestOptions) { const { url, headers, cancellationToken } = requestOptions; - console.log("Request to download file", filename, url, headers); if (await streamablefs.exists(filename)) return true; try { @@ -273,7 +269,6 @@ async function downloadFile(filename, requestOptions) { reportProgress(ev, { type: "download", hash: filename }), }); - console.log("File downloaded", filename, url, response); if (!isSuccessStatusCode(response.status)) return false; const distributor = new ChunkDistributor(ENCRYPTED_CHUNK_SIZE); distributor.fill(new Uint8Array(response.data)); @@ -352,7 +347,6 @@ function cancellable(operation) { return { execute: () => operation(filename, requestOptions), cancel: (message) => { - console.log("Canceled", message); source.cancel(message); }, }; diff --git a/apps/web/src/navigation/index.js b/apps/web/src/navigation/index.js index 2b6dae211..4c3d2e9fb 100644 --- a/apps/web/src/navigation/index.js +++ b/apps/web/src/navigation/index.js @@ -1,6 +1,6 @@ import { useState } from "react"; import EventManager from "notes-core/utils/eventmanager"; -// import { useCallback } from "react"; +import Config from "../utils/config"; export function navigate(url, replaceOrQuery, replace) { if (typeof url !== "string") { @@ -81,3 +81,13 @@ export function getCurrentHash() { } export const NavigationEvents = new EventManager(); + +const HOMEPAGE_ROUTE = { + 0: "/notes", + 1: "/notebooks", + 2: "/favorites", + 3: "/tags", +}; +export function getHomeRoute() { + return HOMEPAGE_ROUTE[Config.get("homepage", 0)]; +} diff --git a/apps/web/src/navigation/rootroutes.js b/apps/web/src/navigation/rootroutes.js deleted file mode 100644 index ff1fd5e94..000000000 --- a/apps/web/src/navigation/rootroutes.js +++ /dev/null @@ -1,14 +0,0 @@ -import React from "react"; -import AccountRecovery from "../views/recovery"; -import EmailConfirmed from "../views/emailconfirmed"; -import Auth from "../views/auth"; - -const rootroutes = { - "/account/recovery": () => ({ component: }), - "/account/verified": () => ({ component: }), - "/signup": () => ({ component: }), - "/login": () => ({ component: }), - "/recover": () => ({ component: }), -}; - -export default rootroutes; diff --git a/apps/web/src/navigation/routes.js b/apps/web/src/navigation/routes.js index d69a9ba40..aade74832 100644 --- a/apps/web/src/navigation/routes.js +++ b/apps/web/src/navigation/routes.js @@ -15,7 +15,7 @@ import Monographs from "../views/monographs"; import { showToast } from "../utils/toast"; const routes = { - "/": () => ({ + "/notes": () => ({ key: "home", type: "notes", title: "Notes", diff --git a/apps/web/src/stores/user-store.js b/apps/web/src/stores/user-store.js index 67c61d9e0..ef6d2ace3 100644 --- a/apps/web/src/stores/user-store.js +++ b/apps/web/src/stores/user-store.js @@ -67,7 +67,6 @@ class UserStore extends BaseStore { }); EV.subscribe(EVENTS.databaseSyncRequested, async () => { - console.log("Sync requested."); await appStore.sync(false); }); diff --git a/apps/web/src/utils/use-routes.js b/apps/web/src/utils/use-routes.js index 59d5a30cf..f13cd33cb 100644 --- a/apps/web/src/utils/use-routes.js +++ b/apps/web/src/utils/use-routes.js @@ -1,27 +1,27 @@ import { useLocation } from "wouter"; import makeMatcher from "wouter/matcher"; -import { navigate } from "../navigation"; -import { store as selectionStore } from "../stores/selection-store"; +import { navigate, getHomeRoute } from "../navigation"; export default function useRoutes(routes, options) { const [location] = useLocation(); const matcher = makeMatcher(); - // TODO move this to an extension function - if (selectionStore.get().isSelectionMode) - selectionStore.toggleSelectionMode(false); + if (location === "/") navigate(getHomeRoute()); + + options?.hooks?.beforeNavigate(location); for (var key in routes) { const [match, params] = matcher(key, location); if (match) { const result = routes[key](params); if (!result) break; - return result; + return [result, location]; } } - if (!options) return; + if (!options) return []; const { fallbackRoute } = options; if (fallbackRoute) { navigate(fallbackRoute); } + return []; } diff --git a/apps/web/src/views/auth.js b/apps/web/src/views/auth.js index 00ad77786..410d8e17d 100644 --- a/apps/web/src/views/auth.js +++ b/apps/web/src/views/auth.js @@ -1,12 +1,14 @@ import { useEffect, useState } from "react"; import { Box, Button, Flex, Text } from "rebass"; import ThemeProvider from "../components/theme-provider"; -import * as Icon from "../components/icons"; +import { CheckCircle, Loading, ArrowRight, Error } from "../components/icons"; import Field from "../components/field"; import { getQueryParams, navigate, useQueryParams } from "../navigation"; import { store as userstore } from "../stores/user-store"; import { db } from "../common/db"; import Config from "../utils/config"; +import useDatabase from "../hooks/use-database"; +import Loader from "../components/loader"; const features = [ { @@ -90,7 +92,6 @@ const authTypes = { ), onSubmit: async (form, onError) => { - console.log(form); if (form.password !== form.confirmPassword) { onError("Passwords do not match."); return; @@ -162,6 +163,7 @@ function Auth(props) { const [error, setError] = useState(); const [success, setSuccess] = useState(); const [featureIndex, setFeatureIndex] = useState(0); + const [isAppLoaded] = useDatabase(); const data = authTypes[type]; const feature = features[featureIndex]; @@ -188,7 +190,7 @@ function Auth(props) { setSuccess(); if (authTypes[type].resetOnNavigate) { const form = document.getElementById("authForm"); - form.reset(); + form?.reset(); } }, [type]); @@ -211,7 +213,7 @@ function Auth(props) { navigate("/"); }} > - + Go to app @@ -262,7 +264,7 @@ function Auth(props) { mt={4} alignItems="center" > - + {feature.linkText} @@ -285,151 +287,171 @@ function Auth(props) { - { - e.preventDefault(); - setIsSubmitting(true); - const form = new FormData(e.target); - const obj = Object.fromEntries(form.entries()); - obj.redirect = redirect; - await data.onSubmit( - obj, - (error) => { - setIsSubmitting(false); - setError(error); - }, - setSuccess - ); - setIsSubmitting(false); - }} - > - - {data.title} - - - {data.subtitle.text}{" "} - {data.subtitle.action && ( - - {data.subtitle.action.text} - - )} - - - {data.labels.password && ( - - )} - - {data.confirmPassword && ( - - )} - - {data.supportsPasswordRecovery && ( - - )} - - {data.secondaryAction && ( - - )} - {error && ( - - - - {error} - - - )} - {success && ( - - - - {success} - - - )} - {data.agreementText && ( - - {data.agreementText} + + {data.title} - )} - + + {data.subtitle.text}{" "} + {data.subtitle.action && ( + + {data.subtitle.action.text} + + )} + + + {data.labels.password && ( + + )} + + {data.confirmPassword && ( + + )} + + {data.supportsPasswordRecovery && ( + + )} + + {data.secondaryAction && ( + + )} + {error && ( + + + + {error} + + + )} + {success && ( + + + + {success} + + + )} + {data.agreementText && ( + + {data.agreementText} + + )} + + ) : ( + + )} diff --git a/apps/web/src/views/email-confirmed.js b/apps/web/src/views/email-confirmed.js index 1af9d0755..38e80b2ab 100644 --- a/apps/web/src/views/email-confirmed.js +++ b/apps/web/src/views/email-confirmed.js @@ -1,3 +1,4 @@ +import "../app.css"; import { useEffect } from "react"; import { Flex, Text } from "rebass"; import ThemeProvider from "../components/theme-provider"; diff --git a/apps/web/src/views/recovery.js b/apps/web/src/views/recovery.js index 8e627fe05..c379b6a49 100644 --- a/apps/web/src/views/recovery.js +++ b/apps/web/src/views/recovery.js @@ -10,6 +10,7 @@ import { useCallback } from "react"; import { showRecoveryKeyDialog } from "../common/dialog-controller"; import { createBackup } from "../common"; import Logo from "../assets/logo.svg"; +import useDatabase from "../hooks/use-database"; function navigate(path) { window.location.href = path; @@ -42,7 +43,9 @@ function useRecovery() { } function useAuthenticateUser({ code, userId, performAction }) { + const [isAppLoaded] = useDatabase(); useEffect(() => { + if (!isAppLoaded) return; performAction({ message: "Authenticating. Please wait...", error: "Failed to authenticate. Please try again.", @@ -63,7 +66,7 @@ function useAuthenticateUser({ code, userId, performAction }) { } await db.user.fetchUser(true); } - }, [code, userId, performAction]); + }, [code, userId, performAction, isAppLoaded]); } const steps = [RecoveryKeyStep, BackupDataStep, NewPasswordStep, FinalStep];