From f8fcce54b038012e1581b883f6adb541d4205e46 Mon Sep 17 00:00:00 2001 From: thecodrr Date: Mon, 2 Mar 2020 15:24:12 +0500 Subject: [PATCH 1/7] fix: #44 --- apps/web/src/components/list-item/index.js | 187 +++++++++++---------- apps/web/src/stores/notebook-store.js | 4 +- 2 files changed, 97 insertions(+), 94 deletions(-) diff --git a/apps/web/src/components/list-item/index.js b/apps/web/src/components/list-item/index.js index f009fb0b1..7b81ab3b0 100644 --- a/apps/web/src/components/list-item/index.js +++ b/apps/web/src/components/list-item/index.js @@ -23,100 +23,99 @@ const ListItem = props => { `contextMenu${props.index}` ); return ( - - {props.pinned && ( - - - - )} - { - //e.stopPropagation(); - if (props.onClick) { - props.onClick(); - } - }} + + - - - {props.title} - - - + + + )} + { + //e.stopPropagation(); + if (props.onClick) { + props.onClick(); + } + }} sx={{ - marginBottom: 1, + flex: "1 1 auto", + paddingTop: props.pinned ? 4 : 0, ":hover": { cursor: "pointer" - }, - flexWrap: "wrap" + } }} > - {props.body} - - {props.subBody && props.subBody} - - {props.info} - - - {props.menuItems && props.dropdownRefs && ( - <> - {" "} + + + {props.title} + + + + {props.body} + + {props.subBody && props.subBody} + + {props.info} + + + {props.menuItems && props.dropdownRefs && ( (props.dropdownRefs[props.index] = ref)} @@ -134,16 +133,18 @@ const ListItem = props => { - - + )} + + {props.menuItems && props.dropdownRefs && ( + )} ); diff --git a/apps/web/src/stores/notebook-store.js b/apps/web/src/stores/notebook-store.js index 3fa473d14..136d135e0 100644 --- a/apps/web/src/stores/notebook-store.js +++ b/apps/web/src/stores/notebook-store.js @@ -28,7 +28,9 @@ function notebookStore(set) { update: function() {}, pin: async function(notebook, index) { await db.notebooks.notebook(notebook).pin(); - set(state => (state.notebooks[index].pinned = !notebook.pinned)); + set(state => { + state.notebooks[index].pinned = !notebook.pinned; + }); } }; } From 914cae225f7123dabb35220a0eb3ad3852c5724f Mon Sep 17 00:00:00 2001 From: thecodrr Date: Mon, 2 Mar 2020 15:48:53 +0500 Subject: [PATCH 2/7] fix: #45 --- apps/web/src/stores/notebook-store.js | 3 ++- 1 file changed, 2 insertions(+), 1 deletion(-) diff --git a/apps/web/src/stores/notebook-store.js b/apps/web/src/stores/notebook-store.js index 136d135e0..2310b6c4d 100644 --- a/apps/web/src/stores/notebook-store.js +++ b/apps/web/src/stores/notebook-store.js @@ -14,7 +14,8 @@ function notebookStore(set) { let notebook = await db.notebooks.add(nb); if (notebook) { set(state => { - state.notebooks.push(nb); + //TODO investigate ways to improve perf + state.notebooks = db.notebooks.all; }); } }, From 32d19ca0674db75d4e873145d1012a72ec8b7fd0 Mon Sep 17 00:00:00 2001 From: thecodrr Date: Mon, 2 Mar 2020 15:51:53 +0500 Subject: [PATCH 3/7] fix: #49 --- apps/web/src/navigation/navigators/rootnavigator.js | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/apps/web/src/navigation/navigators/rootnavigator.js b/apps/web/src/navigation/navigators/rootnavigator.js index 5b8b07257..ab89e785b 100644 --- a/apps/web/src/navigation/navigators/rootnavigator.js +++ b/apps/web/src/navigation/navigators/rootnavigator.js @@ -47,7 +47,7 @@ const invisibleRoutes = { const RootNavigator = new Navigator( "RootNavigator", - { ...routes, ...invisibleRoutes }, + { ...routes, ...bottomRoutes, ...invisibleRoutes }, { backButtonEnabled: false } From 8b6026816fa68739cc1d47a493c56459b0a527d2 Mon Sep 17 00:00:00 2001 From: thecodrr Date: Mon, 2 Mar 2020 15:54:32 +0500 Subject: [PATCH 4/7] fix: #50 --- apps/web/src/components/list-item/index.js | 8 ++++++-- apps/web/src/components/menu/index.js | 4 ++-- 2 files changed, 8 insertions(+), 4 deletions(-) diff --git a/apps/web/src/components/list-item/index.js b/apps/web/src/components/list-item/index.js index 7b81ab3b0..c682d4dc2 100644 --- a/apps/web/src/components/list-item/index.js +++ b/apps/web/src/components/list-item/index.js @@ -9,10 +9,10 @@ import useContextMenu from "../../utils/useContextMenu"; const ActionsMenu = props => ( ); @@ -130,7 +130,10 @@ const ListItem = props => { - + props.dropdownRefs[props.index].hide()} + /> )} @@ -144,6 +147,7 @@ const ListItem = props => { display: "none", zIndex: 999 }} + closeMenu={() => closeContextMenu()} /> )} diff --git a/apps/web/src/components/menu/index.js b/apps/web/src/components/menu/index.js index fbb16a547..21b0df188 100644 --- a/apps/web/src/components/menu/index.js +++ b/apps/web/src/components/menu/index.js @@ -34,8 +34,8 @@ function Menu(props) { onClick={e => { e.stopPropagation(); Dropdown.closeLastOpened(); - if (props.dropdownRef) { - props.dropdownRef.hide(); + if (props.closeMenu) { + props.closeMenu(); } if (item.onClick) { item.onClick(props.data); From 251f9af40afa844d8c8326802cf63c3beb117dfc Mon Sep 17 00:00:00 2001 From: thecodrr Date: Mon, 2 Mar 2020 19:36:02 +0500 Subject: [PATCH 5/7] fix: highlight selected route --- apps/web/src/app.js | 20 ++++++++++---------- apps/web/src/utils/hooks.js | 11 ++++++++++- 2 files changed, 20 insertions(+), 11 deletions(-) diff --git a/apps/web/src/app.js b/apps/web/src/app.js index 515ab46d8..9f1747bf7 100644 --- a/apps/web/src/app.js +++ b/apps/web/src/app.js @@ -77,9 +77,9 @@ const NavMenuItem = props => { }; function App() { - const [selectedIndex, setSelectedIndex] = usePersistentState( - "navSelectedIndex", - 0 + const [selectedKey, setSelectedKey] = usePersistentState( + "navSelectedKey", + "home" ); const [show, setShow] = usePersistentState("navContainerState", true); @@ -87,9 +87,8 @@ function App() { const refreshColors = useStore(store => store.refreshColors); const setSelectedContext = useNotesStore(store => store.setSelectedContext); useEffect(() => { - RootNavigator.navigate(Object.keys(RootNavigator.routes)[selectedIndex]); + RootNavigator.navigate(selectedKey); refreshColors(); - console.log(colors); // eslint-disable-next-line react-hooks/exhaustive-deps }, []); const colors = useStore(store => store.colors); @@ -129,24 +128,24 @@ function App() { {Object.values(routes).map((item, index) => ( { - if (selectedIndex === index) { + if (selectedKey === item.key) { setShow(!show); return; } if (RootNavigator.navigate(item.key)) { - setSelectedIndex(index); + setSelectedKey(item.key); } }} key={item.key} item={item} - selected={selectedIndex === index} + selected={selectedKey === item.key} /> ))} {colors.map(color => { return ( { - setSelectedIndex(-1); + setSelectedKey(undefined); setSelectedContext({ type: "color", value: color.title }); RootNavigator.navigate("color", { title: toTitleCase(color.title), @@ -172,11 +171,12 @@ function App() { return item.onClick(); } if (RootNavigator.navigate(item.key)) { - setSelectedIndex(index); + setSelectedKey(item.key); } }} key={item.key} item={item} + selected={selectedKey === item.key} /> ))} diff --git a/apps/web/src/utils/hooks.js b/apps/web/src/utils/hooks.js index 7aec1d3f1..a454cc291 100644 --- a/apps/web/src/utils/hooks.js +++ b/apps/web/src/utils/hooks.js @@ -1,7 +1,8 @@ import { useState } from "react"; export const usePersistentState = (key, def) => { - const defState = JSON.parse(window.localStorage.getItem(key) || def); + let value = window.localStorage.getItem(key) || def; + const defState = tryParse(value); const [k, setKey] = useState(defState); const _setKey = s => { setKey(s); @@ -9,3 +10,11 @@ export const usePersistentState = (key, def) => { }; return [k, _setKey]; }; + +function tryParse(val) { + try { + return JSON.parse(val); + } catch (e) { + return val; + } +} From 09f93c3d85902405f2e530f58537afb2d18bce4a Mon Sep 17 00:00:00 2001 From: thecodrr Date: Mon, 2 Mar 2020 23:40:27 +0500 Subject: [PATCH 6/7] feat: implement status info in editor --- apps/web/src/components/editor/index.js | 36 ++++++++++++- apps/web/src/components/editor/titlebox.js | 8 +-- apps/web/src/stores/editor-store.js | 7 +++ apps/web/src/utils/string.js | 10 ++++ apps/web/src/utils/time.js | 63 ++++++++++++++++++++++ 5 files changed, 119 insertions(+), 5 deletions(-) create mode 100644 apps/web/src/utils/time.js diff --git a/apps/web/src/components/editor/index.js b/apps/web/src/components/editor/index.js index e23f5484d..64771e342 100644 --- a/apps/web/src/components/editor/index.js +++ b/apps/web/src/components/editor/index.js @@ -1,13 +1,29 @@ import React, { useEffect } from "react"; import "./editor.css"; import ReactQuill from "./react-quill"; -import { Flex, Box } from "rebass"; +import { Flex, Box, Text } from "rebass"; import TitleBox from "./title-box"; +import * as Icon from "react-feather"; import Properties from "../properties"; import { useStore, SESSION_STATES } from "../../stores/editor-store"; +import { timeConverter } from "../../utils/time"; +import { countWords } from "../../utils/string"; +import { useTheme } from "emotion-theming"; + +const TextSeperator = props => { + const theme = useTheme(); + return ( + + + + ); +}; function Editor() { const title = useStore(store => store.session.title); + const dateEdited = useStore(store => store.session.dateEdited); + const text = useStore(store => store.session.content.text); + const isSaving = useStore(store => store.session.isSaving); const delta = useStore(store => store.session.content.delta); const sessionState = useStore(store => store.session.state); const setSession = useStore(store => store.setSession); @@ -32,7 +48,25 @@ function Editor() { state.session.title = title; }) } + sx={{ + paddingTop: 2, + paddingBottom: dateEdited > 0 ? 0 : 2 + }} /> + {dateEdited > 0 && ( + + {timeConverter(dateEdited)} + + {countWords(text) + " words"} + + {isSaving ? "Saving" : "Saved"} + + )} (this.inputRef = ref)} @@ -25,12 +25,12 @@ export default class TitleBox extends React.Component { fontWeight="heading" fontSize="heading" display={["none", "flex", "flex"]} + px={2} sx={{ borderWidth: 0, - ":focus": { outline: "none" } + ":focus": { outline: "none" }, + ...sx }} - py={2} - px={2} value={title} onChange={e => { setTitle(e.target.value); diff --git a/apps/web/src/stores/editor-store.js b/apps/web/src/stores/editor-store.js index 9a7c0abc8..fcc039d0c 100644 --- a/apps/web/src/stores/editor-store.js +++ b/apps/web/src/stores/editor-store.js @@ -10,6 +10,7 @@ const SESSION_STATES = { const DEFAULT_SESSION = { state: "", + isSaving: false, title: "", timeout: 0, id: "", @@ -17,6 +18,7 @@ const DEFAULT_SESSION = { favorite: false, tags: [], colors: [], + dateEdited: 0, content: { text: "", delta: { @@ -44,12 +46,16 @@ function editorStore(set, get) { favorite: note.favorite, colors: note.colors, tags: note.tags, + dateEdited: note.dateEdited, content, state: SESSION_STATES.new }; }); }, saveSession: function(oldSession) { + set(state => { + state.session.isSaving = true; + }); const { session } = get(); const { title, id, content, pinned, favorite, tags, colors } = session; let note = { @@ -72,6 +78,7 @@ function editorStore(set, get) { noteStore.getState().setSelectedNote(id); } state.session.id = id; + state.session.isSaving = false; noteStore.getState().refresh(); // we update favorites only if favorite has changed diff --git a/apps/web/src/utils/string.js b/apps/web/src/utils/string.js index 9953d7717..d2e7f4752 100644 --- a/apps/web/src/utils/string.js +++ b/apps/web/src/utils/string.js @@ -1,3 +1,13 @@ export function toTitleCase(str) { return str[0].toUpperCase() + str.substring(1); } + +/** + * + * @param {String} str + */ +export function countWords(str) { + str = str.trim(); + if (!str.length) return 0; + return str.split(/\W+\S/).length + (str[str.length] === " " ? 0 : 1); +} diff --git a/apps/web/src/utils/time.js b/apps/web/src/utils/time.js new file mode 100644 index 000000000..b6550ff22 --- /dev/null +++ b/apps/web/src/utils/time.js @@ -0,0 +1,63 @@ +const days = [ + "Sunday", + "Monday", + "Tuesday", + "Wednesday", + "Thursday", + "Friday", + "Saturday" +]; +const months = [ + "Jan", + "Feb", + "Mar", + "Apr", + "May", + "Jun", + "Jul", + "Aug", + "Sep", + "Oct", + "Nov", + "Dec" +]; +export const timeConverter = timestamp => { + if (!timestamp) return; + var d = new Date(timestamp), // Convert the passed timestamp to milliseconds + yyyy = d.getFullYear(), + dd = ("0" + d.getDate()).slice(-2), // Add leading 0. + currentDay = d.getDay(), + hh = d.getHours(), + h = hh, + min = ("0" + d.getMinutes()).slice(-2), // Add leading 0. + ampm = "AM", + time; + + if (hh > 12) { + h = hh - 12; + ampm = "PM"; + } else if (hh === 12) { + h = 12; + ampm = "PM"; + } else if (hh === 0) { + h = 12; + } + + // ie: 2013-02-18, 8:35 AM + time = + days[currentDay] + + " " + + dd + + " " + + months[d.getMonth()] + + ", " + + yyyy + + ", " + + h + + ":" + + min + + " " + + ampm; + + return time; +}; From 2b4fcfaed686a59ff98662fe49e11525f5558da6 Mon Sep 17 00:00:00 2001 From: thecodrr Date: Tue, 3 Mar 2020 00:00:32 +0500 Subject: [PATCH 7/7] feat: persist last opened session --- apps/web/src/app.js | 1 + apps/web/src/components/editor/index.js | 6 ++++ apps/web/src/stores/editor-store.js | 40 +++++++++++++++++-------- 3 files changed, 35 insertions(+), 12 deletions(-) diff --git a/apps/web/src/app.js b/apps/web/src/app.js index 9f1747bf7..18baf2c94 100644 --- a/apps/web/src/app.js +++ b/apps/web/src/app.js @@ -86,6 +86,7 @@ function App() { const isSideMenuOpen = useStore(store => store.isSideMenuOpen); const refreshColors = useStore(store => store.refreshColors); const setSelectedContext = useNotesStore(store => store.setSelectedContext); + useEffect(() => { RootNavigator.navigate(selectedKey); refreshColors(); diff --git a/apps/web/src/components/editor/index.js b/apps/web/src/components/editor/index.js index 64771e342..f8e3c936c 100644 --- a/apps/web/src/components/editor/index.js +++ b/apps/web/src/components/editor/index.js @@ -28,6 +28,8 @@ function Editor() { const sessionState = useStore(store => store.session.state); const setSession = useStore(store => store.setSession); const saveSession = useStore(store => store.saveSession); + const reopenLastSession = useStore(store => store.reopenLastSession); + useEffect(() => { // move the toolbar outside (easiest way) const toolbar = document.querySelector(".ql-toolbar.ql-snow"); @@ -37,6 +39,10 @@ function Editor() { } }, []); + useEffect(() => { + reopenLastSession(); + }, [reopenLastSession]); + return ( diff --git a/apps/web/src/stores/editor-store.js b/apps/web/src/stores/editor-store.js index fcc039d0c..eadb2348d 100644 --- a/apps/web/src/stores/editor-store.js +++ b/apps/web/src/stores/editor-store.js @@ -27,17 +27,27 @@ const DEFAULT_SESSION = { } }; +function saveLastOpenedNote(id) { + if (!id) return localStorage.removeItem("lastOpenedNote"); + localStorage.setItem("lastOpenedNote", id); +} + function editorStore(set, get) { return { session: DEFAULT_SESSION, + reopenLastSession: function() { + const id = localStorage.getItem("lastOpenedNote"); + if (!id) return; + get().openSession(db.notes.note(id).data); + }, openSession: async function(note) { + clearTimeout(get().session.timeout); const content = { text: note.content.text, delta: await db.notes.note(note).delta() }; noteStore.getState().setSelectedNote(note.id); set(state => { - clearTimeout(state.session.timeout); state.session = { ...DEFAULT_SESSION, id: note.id, @@ -51,6 +61,7 @@ function editorStore(set, get) { state: SESSION_STATES.new }; }); + saveLastOpenedNote(note.id); }, saveSession: function(oldSession) { set(state => { @@ -73,41 +84,46 @@ function editorStore(set, get) { updateContext("colors", colors); appStore.getState().refreshColors(); } + let notesState = noteStore.getState(); + if (get().session.id === "") { + noteStore.getState().setSelectedNote(id); + } + set(state => { - if (state.session.id === "") { - noteStore.getState().setSelectedNote(id); - } state.session.id = id; state.session.isSaving = false; - noteStore.getState().refresh(); - - // we update favorites only if favorite has changed - if (!oldSession || oldSession.favorite !== session.favorite) { - noteStore.getState().refreshList(LIST_TYPES.fav); - } }); + + notesState.refresh(); + saveLastOpenedNote(id); + + // we update favorites only if favorite has changed + if (!oldSession || oldSession.favorite !== session.favorite) { + notesState.refreshList(LIST_TYPES.fav); + } }); }, setSession: function(session) { const oldSession = get().session; + clearTimeout(oldSession.timeout); set(state => { state.session.state = SESSION_STATES.stale; session(state); - clearTimeout(state.session.timeout); state.session.timeout = setTimeout(() => { get().saveSession(oldSession); }, 500); }); }, newSession: function(context = {}) { + clearTimeout(get().session.timeout); set(state => { - clearTimeout(state.session.timeout); state.session = { ...DEFAULT_SESSION, ...context, state: SESSION_STATES.new }; }); + saveLastOpenedNote(); }, setColor: function(color) { setTagOrColor(get().session, "colors", color, "color", get().setSession);