From 7c0b7d3b5ecc75f2d0d60a65b34d06fcfb6d2ec2 Mon Sep 17 00:00:00 2001 From: thecodrr Date: Mon, 20 Apr 2020 11:20:42 +0500 Subject: [PATCH] navigation: simplify nested navigation --- apps/web/src/app.js | 10 +- .../editor/modules/markdown/index.js | 29 ++--- .../src/components/navigation-menu/index.js | 7 +- apps/web/src/components/notebook/index.js | 1 - apps/web/src/navigation/container.js | 10 ++ apps/web/src/navigation/index.js | 121 +++--------------- .../src/navigation/navigators/nbnavigator.js | 12 +- .../navigation/navigators/rootnavigator.js | 31 ++--- apps/web/src/navigation/route.js | 102 +++++++++++++++ apps/web/src/navigation/routes.js | 10 +- apps/web/src/views/Notebooks.js | 21 ++- apps/web/src/views/Settings.js | 15 +-- apps/web/src/views/Tags.js | 15 +-- apps/web/src/views/index.js | 9 +- 14 files changed, 197 insertions(+), 196 deletions(-) create mode 100644 apps/web/src/navigation/container.js create mode 100644 apps/web/src/navigation/route.js diff --git a/apps/web/src/app.js b/apps/web/src/app.js index efcfcafb2..7820905d5 100644 --- a/apps/web/src/app.js +++ b/apps/web/src/app.js @@ -9,6 +9,8 @@ import { useStore as useEditorStore } from "./stores/editor-store"; import { useStore as useUserStore } from "./stores/user-store"; import Animated from "./components/animated"; import NavigationMenu from "./components/navigationmenu"; +import NavigationContainer from "./navigation/container"; +import RootNavigator from "./navigation/navigators/rootnavigator"; function App() { const [show, setShow] = usePersistentState("isContainerVisible", true); @@ -42,7 +44,6 @@ function App() { + > + + diff --git a/apps/web/src/components/editor/modules/markdown/index.js b/apps/web/src/components/editor/modules/markdown/index.js index e8efecd10..2bd55c7e5 100644 --- a/apps/web/src/components/editor/modules/markdown/index.js +++ b/apps/web/src/components/editor/modules/markdown/index.js @@ -50,7 +50,7 @@ class MarkdownShortcuts { this.quill.formatLine(selection.index, 0, "header", size - 1); this.quill.deleteText(selection.index - size, size); }, 0); - } + }, }, { name: "blockquote", @@ -61,7 +61,7 @@ class MarkdownShortcuts { this.quill.formatLine(selection.index, 1, "blockquote", true); this.quill.deleteText(selection.index - 2, 2); }, 0); - } + }, }, { name: "code-block", @@ -72,7 +72,7 @@ class MarkdownShortcuts { this.quill.formatLine(selection.index, 1, "code-block", true); this.quill.deleteText(selection.index - 4, 4); }, 0); - } + }, }, { name: "bolditalic", @@ -90,11 +90,11 @@ class MarkdownShortcuts { this.quill.deleteText(startIndex, annotatedText.length); this.quill.insertText(startIndex, matchedText, { bold: true, - italic: true + italic: true, }); this.quill.format("bold", false); }, 0); - } + }, }, { name: "bold", @@ -113,7 +113,7 @@ class MarkdownShortcuts { this.quill.insertText(startIndex, matchedText, { bold: true }); this.quill.format("bold", false); }, 0); - } + }, }, { name: "italic", @@ -132,7 +132,7 @@ class MarkdownShortcuts { this.quill.insertText(startIndex, matchedText, { italic: true }); this.quill.format("italic", false); }, 0); - } + }, }, { name: "strikethrough", @@ -151,7 +151,7 @@ class MarkdownShortcuts { this.quill.insertText(startIndex, matchedText, { strike: true }); this.quill.format("strike", false); }, 0); - } + }, }, { name: "code", @@ -171,7 +171,7 @@ class MarkdownShortcuts { this.quill.format("code", false); this.quill.insertText(this.quill.getSelection(), " "); }, 0); - } + }, }, { name: "hr", @@ -190,7 +190,7 @@ class MarkdownShortcuts { this.quill.insertText(startIndex + 2, "\n", Quill.sources.SILENT); this.quill.setSelection(startIndex + 2, Quill.sources.SILENT); }, 0); - } + }, }, { name: "asterisk-ul", @@ -199,14 +199,13 @@ class MarkdownShortcuts { setTimeout(() => { let index = selection.index; this.quill.formatLine(index, 1, "list", "unordered"); - console.log(text, selection, pattern); if (text.trim() === "*") { this.quill.deleteText(index, 1); } else if (text.trim() === "+") { this.quill.deleteText(index - 2, 2); } }, 0); - } + }, }, { name: "image", @@ -227,7 +226,7 @@ class MarkdownShortcuts { ); }, 0); } - } + }, }, { name: "link", @@ -249,8 +248,8 @@ class MarkdownShortcuts { ); }, 0); } - } - } + }, + }, ]; // Handler that looks for insert deltas that match specific characters diff --git a/apps/web/src/components/navigation-menu/index.js b/apps/web/src/components/navigation-menu/index.js index 190537c50..d4dcf05fc 100644 --- a/apps/web/src/components/navigation-menu/index.js +++ b/apps/web/src/components/navigation-menu/index.js @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React from "react"; import { Box } from "rebass"; import RootNavigator, { bottomRoutes, @@ -21,11 +21,6 @@ function NavigationMenu(props) { const colors = useStore((store) => store.colors); const isSideMenuOpen = useStore((store) => store.isSideMenuOpen); - useEffect(() => { - RootNavigator.navigate(selectedRoute); - // eslint-disable-next-line react-hooks/exhaustive-deps - }, []); - return ( { + props.navigator.onLoad(); + }, [props.navigator]); + return ; +} +export default NavigationContainer; diff --git a/apps/web/src/navigation/index.js b/apps/web/src/navigation/index.js index ad0b0e7d8..eb9091013 100644 --- a/apps/web/src/navigation/index.js +++ b/apps/web/src/navigation/index.js @@ -1,12 +1,10 @@ import React from "react"; import ReactDOM from "react-dom"; -import { Box, Flex, Heading, Text } from "rebass"; import Animated from "../components/animated"; import { AnimatePresence } from "framer-motion"; -import * as Icon from "../components/icons"; -import ThemeProvider from "../components/theme-provider"; -import { useStore } from "../stores/app-store"; import { store as selectionStore } from "../stores/selection-store"; +import Route from "./route"; +import Config from "../utils/config"; class Navigator { constructor(root, routes, options = {}) { @@ -17,10 +15,24 @@ class Navigator { this.lastRoute = undefined; } + onLoad = () => { + const route = Config.get(this.root, this.getRoute(this.options.default)); + this.navigate(route.key, route.params); + }; + getRoute(key) { return this.routes[key]; } + setLastRoute(route) { + this.lastRoute = route; + // cache the route in localStorage + // NOTE: we delete the navigator key if any so it's always new across refreshes + const copy = { ...route, params: { ...route.params } }; + if (copy.params.navigator) delete copy.params.navigator; + Config.set(this.root, copy); + } + getRoot() { return document.querySelector(`.${this.root}`); } @@ -37,7 +49,7 @@ class Navigator { if (this.lastRoute) { this.history.push(this.lastRoute); } - this.lastRoute = route; + this.setLastRoute(route); return this.renderRoute(route); } @@ -59,7 +71,7 @@ class Navigator { flexDirection="column" flex="1 1 auto" > - store.toggleSideMenu); - const isSelectionMode = useStore((store) => store.isSelectionMode); - const exitSelectionMode = useStore((store) => store.exitSelectionMode); - const selectAll = useStore((store) => store.selectAll); - return ( - - - {(props.route.title || props.route.params.title) && ( - <> - - - {props.canGoBack && ( - - - - )} - - - - - {props.route.title || props.route.params.title} - - - {props.route.options && isSelectionMode && ( - - {props.route.options.map((option) => ( - - - - ))} - - )} - - {props.route.params.subtitle && ( - - {props.route.params.subtitle} - - )} - {isSelectionMode && ( - - selectAll()} - > - Select all - - exitSelectionMode()} - > - Unselect - - - )} - - )} - - {props.route.component && ( - - )} - - ); -} diff --git a/apps/web/src/navigation/navigators/nbnavigator.js b/apps/web/src/navigation/navigators/nbnavigator.js index 4d689bc33..8e3fde29b 100644 --- a/apps/web/src/navigation/navigators/nbnavigator.js +++ b/apps/web/src/navigation/navigators/nbnavigator.js @@ -6,16 +6,18 @@ import SelectionModeOptions from "../../common/selectionoptions"; const routes = { ...createRoute("notebooks", Notebooks, { title: "Notebooks", - options: SelectionModeOptions.NotebooksOptions + options: SelectionModeOptions.NotebooksOptions, }), ...createRoute("topics", Topics, { - options: SelectionModeOptions.TopicOptions + options: SelectionModeOptions.TopicOptions, + }), + ...createRoute("notes", Notes, { + options: SelectionModeOptions.NotesOptions, }), - ...createRoute("notes", Notes, { options: SelectionModeOptions.NotesOptions }) }; const NotebookNavigator = new Navigator("NotebookNavigator", routes, { - backButtonEnabled: true + backButtonEnabled: true, + default: "notebooks", }); - export default NotebookNavigator; diff --git a/apps/web/src/navigation/navigators/rootnavigator.js b/apps/web/src/navigation/navigators/rootnavigator.js index b5abb14be..e6410029b 100644 --- a/apps/web/src/navigation/navigators/rootnavigator.js +++ b/apps/web/src/navigation/navigators/rootnavigator.js @@ -1,20 +1,18 @@ -import { - Home, - SettingsContainer, - Trash, - NotebooksContainer, - TagsContainer, - Notes, - Account, -} from "../../views"; +import { Home, Trash, Notes, Account } from "../../views"; import * as Icon from "../../components/icons"; -import { createRoute, createNormalRoute, createDeadRoute } from "../routes"; +import { + createRoute, + createNavigatorRoute, + createNormalRoute, + createDeadRoute, +} from "../routes"; import Navigator from "../index"; import SelectionModeOptions from "../../common/selectionoptions"; import Search from "../../views/Search"; import { store as userStore } from "../../stores/user-store"; import { store as themeStore } from "../../stores/theme-store"; import { showLogInDialog } from "../../components/dialogs/logindialog"; +import { NotebookNavigator, TagNavigator, SettingsNavigator } from "./index"; export const bottomRoutes = { ...createDeadRoute("nightmode", Icon.Theme, { @@ -32,9 +30,7 @@ export const bottomRoutes = { } else return RootNavigator.navigate("account"); }, }), - ...createRoute("settings", SettingsContainer, { - icon: Icon.Settings, - }), + ...createNavigatorRoute("settings", Icon.Settings, SettingsNavigator), }; export const routes = { @@ -42,9 +38,7 @@ export const routes = { title: "Home", options: SelectionModeOptions.NotesOptions, }), - ...createRoute("notebooks", NotebooksContainer, { - icon: Icon.Notebook, - }), + ...createNavigatorRoute("notebooks", Icon.Notebook, NotebookNavigator), ...createRoute( "favorites", Notes, @@ -61,9 +55,7 @@ export const routes = { title: "Trash", options: SelectionModeOptions.TrashOptions, }), - ...createRoute("tags", TagsContainer, { - icon: Icon.Tag, - }), + ...createNavigatorRoute("tags", Icon.Tag, TagNavigator), }; const invisibleRoutes = { @@ -79,6 +71,7 @@ const RootNavigator = new Navigator( { ...routes, ...bottomRoutes, ...invisibleRoutes }, { backButtonEnabled: false, + default: "home", } ); diff --git a/apps/web/src/navigation/route.js b/apps/web/src/navigation/route.js new file mode 100644 index 000000000..8f1342dae --- /dev/null +++ b/apps/web/src/navigation/route.js @@ -0,0 +1,102 @@ +import React from "react"; +import { Box, Flex, Heading, Text } from "rebass"; +import * as Icon from "../components/icons"; +import ThemeProvider from "../components/theme-provider"; +import { useStore } from "../stores/app-store"; + +function Route(props) { + const toggleSideMenu = useStore((store) => store.toggleSideMenu); + const isSelectionMode = useStore((store) => store.isSelectionMode); + const exitSelectionMode = useStore((store) => store.exitSelectionMode); + const selectAll = useStore((store) => store.selectAll); + const navigator = props.params.navigator || props.navigator; + return ( + + + {(props.route.title || props.route.params.title) && ( + <> + + + {props.canGoBack && ( + + + + )} + + + + + {props.route.title || props.route.params.title} + + + {props.route.options && isSelectionMode && ( + + {props.route.options.map((option) => ( + + + + ))} + + )} + + {props.route.params.subtitle && ( + + {props.route.params.subtitle} + + )} + {isSelectionMode && ( + + selectAll()} + > + Select all + + exitSelectionMode()} + > + Unselect + + + )} + + )} + + {props.route.component && ( + + )} + + ); +} +export default Route; diff --git a/apps/web/src/navigation/routes.js b/apps/web/src/navigation/routes.js index 9e87b8c16..3358f9c01 100644 --- a/apps/web/src/navigation/routes.js +++ b/apps/web/src/navigation/routes.js @@ -1,14 +1,20 @@ +import NavigationContainer from "./container"; + export function createRoute(key, component, props = {}, params = {}) { return { [key]: { key, component, ...props, - params - } + params, + }, }; } +export function createNavigatorRoute(key, icon, navigator) { + return createRoute(key, NavigationContainer, { icon }, { navigator }); +} + export function createNormalRoute(key, component, icon, props = {}) { return createRoute(key, component, { title: component.name, icon, ...props }); } diff --git a/apps/web/src/views/Notebooks.js b/apps/web/src/views/Notebooks.js index cbc79a2b8..d8385084b 100644 --- a/apps/web/src/views/Notebooks.js +++ b/apps/web/src/views/Notebooks.js @@ -1,5 +1,4 @@ import React, { useState, useEffect } from "react"; -import { Flex } from "rebass"; import { db } from "../common"; import Notebook from "../components/notebook"; import AddNotebookDialog from "../components/dialogs/addnotebookdialog"; @@ -64,15 +63,13 @@ function Notebooks(props) { ); } -function NotebooksContainer() { - useEffect(() => { - const NotebookNavigator = require("../navigation/navigators/nbnavigator") - .default; - if (!NotebookNavigator.restore()) { - NotebookNavigator.navigate("notebooks"); - } - }, []); - return ; -} +/* function NotebooksContainer() { + return ( + + ); +} */ -export { NotebooksContainer, Notebooks }; +export default Notebooks; diff --git a/apps/web/src/views/Settings.js b/apps/web/src/views/Settings.js index bbe07af4b..ab9b08b23 100644 --- a/apps/web/src/views/Settings.js +++ b/apps/web/src/views/Settings.js @@ -1,4 +1,4 @@ -import React, { useEffect } from "react"; +import React from "react"; import { Box, Button, Flex, Text } from "rebass"; import * as Icon from "../components/icons"; import { useStore as useUserStore } from "../stores/user-store"; @@ -107,15 +107,4 @@ function Settings(props) { ); } -function SettingsContainer() { - useEffect(() => { - const SettingsNavigator = require("../navigation/navigators/settingnavigator") - .default; - if (!SettingsNavigator.restore()) { - SettingsNavigator.navigate("settings"); - } - }, []); - return ; -} - -export { Settings, SettingsContainer }; +export default Settings; diff --git a/apps/web/src/views/Tags.js b/apps/web/src/views/Tags.js index fccec54c6..b7479c951 100644 --- a/apps/web/src/views/Tags.js +++ b/apps/web/src/views/Tags.js @@ -1,5 +1,5 @@ import React, { useEffect } from "react"; -import { Flex, Text } from "rebass"; +import { Text } from "rebass"; import ListContainer from "../components/list-container"; import ListItem from "../components/list-item"; import { useStore, store } from "../stores/tag-store"; @@ -48,15 +48,4 @@ function Tags(props) { ); } -function TagsContainer() { - useEffect(() => { - const TagNavigator = require("../navigation/navigators/tagnavigator") - .default; - if (!TagNavigator.restore()) { - TagNavigator.navigate("tags"); - } - }, []); - return ; -} - -export { Tags, TagsContainer }; +export default Tags; diff --git a/apps/web/src/views/index.js b/apps/web/src/views/index.js index cc9df9f44..54a0ad7cd 100644 --- a/apps/web/src/views/index.js +++ b/apps/web/src/views/index.js @@ -1,12 +1,9 @@ export const Home = require("./Home").default; -export const NotebooksContainer = require("./Notebooks").NotebooksContainer; -export const Notebooks = require("./Notebooks").Notebooks; +export const Notebooks = require("./Notebooks").default; export const Notes = require("./Notes").default; export const Topics = require("./Topics").default; -export const Settings = require("./Settings").Settings; +export const Settings = require("./Settings").default; export const Trash = require("./Trash").default; export const Account = require("./Account").default; -export const SettingsContainer = require("./Settings").SettingsContainer; -export const Tags = require("./Tags").Tags; -export const TagsContainer = require("./Tags").TagsContainer; +export const Tags = require("./Tags").default; export const Search = require("./Search").default;