From 4a009bd24c9c7e16c8311bf4f1ded1ab1cfd650e Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 12:22:32 +0900 Subject: [PATCH 01/13] updated preferences --- .prettierignore | 1 + package-lock.json | 138 ++++++++++-------- package.json | 1 + .../PreferencesModal/GeneralTab.tsx | 24 ++- src/lib/i18n/enUS.ts | 1 + src/lib/i18n/ja.ts | 1 + src/lib/preferences/store.ts | 1 + src/lib/preferences/types.ts | 2 + webpack.config.ts | 8 + 9 files changed, 112 insertions(+), 65 deletions(-) create mode 100644 .prettierignore diff --git a/.prettierignore b/.prettierignore new file mode 100644 index 0000000000..16e3dce618 --- /dev/null +++ b/.prettierignore @@ -0,0 +1 @@ +/src/lib/tutorials/files \ No newline at end of file diff --git a/package-lock.json b/package-lock.json index a51eb476d6..4552846076 100644 --- a/package-lock.json +++ b/package-lock.json @@ -2762,7 +2762,7 @@ }, "util": { "version": "0.10.3", - "resolved": "http://registry.npmjs.org/util/-/util-0.10.3.tgz", + "resolved": "https://registry.npmjs.org/util/-/util-0.10.3.tgz", "integrity": "sha1-evsa/lCAUkZInj23/g7TeTNqwPk=", "dev": true, "requires": { @@ -6159,8 +6159,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -6181,14 +6180,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -6203,20 +6200,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -6333,8 +6327,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -6346,7 +6339,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -6361,7 +6353,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -6369,14 +6360,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -6395,7 +6384,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -6476,8 +6464,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -6489,7 +6476,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -6575,8 +6561,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -6612,7 +6597,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -6632,7 +6616,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -6676,14 +6659,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -8271,8 +8252,7 @@ "ansi-regex": { "version": "2.1.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "aproba": { "version": "1.2.0", @@ -8293,14 +8273,12 @@ "balanced-match": { "version": "1.0.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "brace-expansion": { "version": "1.1.11", "bundled": true, "dev": true, - "optional": true, "requires": { "balanced-match": "^1.0.0", "concat-map": "0.0.1" @@ -8315,20 +8293,17 @@ "code-point-at": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "concat-map": { "version": "0.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "console-control-strings": { "version": "1.1.0", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "core-util-is": { "version": "1.0.2", @@ -8445,8 +8420,7 @@ "inherits": { "version": "2.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "ini": { "version": "1.3.5", @@ -8458,7 +8432,6 @@ "version": "1.0.0", "bundled": true, "dev": true, - "optional": true, "requires": { "number-is-nan": "^1.0.0" } @@ -8473,7 +8446,6 @@ "version": "3.0.4", "bundled": true, "dev": true, - "optional": true, "requires": { "brace-expansion": "^1.1.7" } @@ -8481,14 +8453,12 @@ "minimist": { "version": "0.0.8", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "minipass": { "version": "2.3.5", "bundled": true, "dev": true, - "optional": true, "requires": { "safe-buffer": "^5.1.2", "yallist": "^3.0.0" @@ -8507,7 +8477,6 @@ "version": "0.5.1", "bundled": true, "dev": true, - "optional": true, "requires": { "minimist": "0.0.8" } @@ -8588,8 +8557,7 @@ "number-is-nan": { "version": "1.0.1", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "object-assign": { "version": "4.1.1", @@ -8601,7 +8569,6 @@ "version": "1.4.0", "bundled": true, "dev": true, - "optional": true, "requires": { "wrappy": "1" } @@ -8687,8 +8654,7 @@ "safe-buffer": { "version": "5.1.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "safer-buffer": { "version": "2.1.2", @@ -8724,7 +8690,6 @@ "version": "1.0.2", "bundled": true, "dev": true, - "optional": true, "requires": { "code-point-at": "^1.0.0", "is-fullwidth-code-point": "^1.0.0", @@ -8744,7 +8709,6 @@ "version": "3.0.1", "bundled": true, "dev": true, - "optional": true, "requires": { "ansi-regex": "^2.0.0" } @@ -8788,14 +8752,12 @@ "wrappy": { "version": "1.0.2", "bundled": true, - "dev": true, - "optional": true + "dev": true }, "yallist": { "version": "3.0.3", "bundled": true, - "dev": true, - "optional": true + "dev": true } } }, @@ -9843,7 +9805,7 @@ }, "minimist": { "version": "0.0.8", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-0.0.8.tgz", "integrity": "sha1-hX/Kv8M5fSYluCKCYuhqp6ARsF0=" }, "mississippi": { @@ -11614,6 +11576,54 @@ } } }, + "raw-loader": { + "version": "4.0.0", + "resolved": "https://registry.npmjs.org/raw-loader/-/raw-loader-4.0.0.tgz", + "integrity": "sha512-iINUOYvl1cGEmfoaLjnZXt4bKfT2LJnZZib5N/LLyAphC+Dd11vNP9CNVb38j+SAJpFI1uo8j9frmih53ASy7Q==", + "requires": { + "loader-utils": "^1.2.3", + "schema-utils": "^2.5.0" + }, + "dependencies": { + "big.js": { + "version": "5.2.2", + "resolved": "https://registry.npmjs.org/big.js/-/big.js-5.2.2.tgz", + "integrity": "sha512-vyL2OymJxmarO8gxMr0mhChsO9QGwhynfuu4+MHTAW6czfq9humCB7rKpUjDd9YUiDPU4mzpyupFSvOClAwbmQ==" + }, + "json5": { + "version": "1.0.1", + "resolved": "https://registry.npmjs.org/json5/-/json5-1.0.1.tgz", + "integrity": "sha512-aKS4WQjPenRxiQsC93MNfjx+nbF4PAdYzmd/1JIj8HYzqfbu86beTuNgXDzPknWk0n0uARlyewZo4s++ES36Ow==", + "requires": { + "minimist": "^1.2.0" + } + }, + "loader-utils": { + "version": "1.2.3", + "resolved": "https://registry.npmjs.org/loader-utils/-/loader-utils-1.2.3.tgz", + "integrity": "sha512-fkpz8ejdnEMG3s37wGL07iSBDg99O9D5yflE9RGNH3hRdx9SOwYfnGYdZOUIZitN8E+E2vkq3MUMYMvPYl5ZZA==", + "requires": { + "big.js": "^5.2.2", + "emojis-list": "^2.0.0", + "json5": "^1.0.1" + } + }, + "minimist": { + "version": "1.2.0", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=" + }, + "schema-utils": { + "version": "2.6.1", + "resolved": "https://registry.npmjs.org/schema-utils/-/schema-utils-2.6.1.tgz", + "integrity": "sha512-0WXHDs1VDJyo+Zqs9TKLKyD/h7yDpHUhEFsM2CzkICFdoX1av+GBq/J2xRTFfsQO5kBfhZzANf2VcIm84jqDbg==", + "requires": { + "ajv": "^6.10.2", + "ajv-keywords": "^3.4.1" + } + } + } + }, "react": { "version": "16.9.0", "resolved": "https://registry.npmjs.org/react/-/react-16.9.0.tgz", @@ -12522,7 +12532,7 @@ "dependencies": { "minimist": { "version": "1.2.0", - "resolved": "http://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", + "resolved": "https://registry.npmjs.org/minimist/-/minimist-1.2.0.tgz", "integrity": "sha1-o1AIsg9BOD7sH7kU9M1d95omQoQ=", "dev": true } @@ -13509,7 +13519,7 @@ }, "readable-stream": { "version": "1.0.33", - "resolved": "http://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", + "resolved": "https://registry.npmjs.org/readable-stream/-/readable-stream-1.0.33.tgz", "integrity": "sha1-OjYN1mwbHX/UcFOJhg7aHQ9hEmw=", "requires": { "core-util-is": "~1.0.0", diff --git a/package.json b/package.json index 177b29ba24..407d6c2c90 100644 --- a/package.json +++ b/package.json @@ -114,6 +114,7 @@ "pouchdb-mapreduce": "^7.1.1", "pouchdb-replication": "^7.1.1", "ramda": "^0.26.1", + "raw-loader": "^4.0.0", "react": "^16.9.0", "react-dom": "^16.9.0", "react-i18next": "^11.0.1", diff --git a/src/components/PreferencesModal/GeneralTab.tsx b/src/components/PreferencesModal/GeneralTab.tsx index dfa8f35a3c..a4d1855631 100644 --- a/src/components/PreferencesModal/GeneralTab.tsx +++ b/src/components/PreferencesModal/GeneralTab.tsx @@ -12,7 +12,8 @@ import { usePreferences, GeneralThemeOptions, GeneralLanguageOptions, - GeneralNoteSortingOptions + GeneralNoteSortingOptions, + GeneralTutorialsOptions } from '../../lib/preferences' import { useTranslation } from 'react-i18next' import { SelectChangeEventHandler } from '../../lib/events' @@ -51,6 +52,15 @@ const GeneralTab = () => { [setPreferences] ) + const selectTutorialsDisplay: SelectChangeEventHandler = useCallback( + event => { + setPreferences({ + 'general.tutorials': event.target.value as GeneralTutorialsOptions + }) + }, + [setPreferences] + ) + const { t } = useTranslation() return ( @@ -126,6 +136,18 @@ const GeneralTab = () => { +
+ {t('preferences.displayTutorialsLabel')} + + + + + + +
) } diff --git a/src/lib/i18n/enUS.ts b/src/lib/i18n/enUS.ts index 9bf71046f8..f4462b7021 100644 --- a/src/lib/i18n/enUS.ts +++ b/src/lib/i18n/enUS.ts @@ -21,6 +21,7 @@ export default { 'preferences.analyticsDescription2': 'You can choose to enable or disable this option.', 'preferences.analyticsLabel': 'Enable analytics to help improve Boostnote', + 'preferences.displayTutorialsLabel': 'Tutorials and FAQ', // Preferences EditorTab 'preferences.editorTheme': 'Editor Theme', diff --git a/src/lib/i18n/ja.ts b/src/lib/i18n/ja.ts index a2cbcc51fd..951fb17c77 100644 --- a/src/lib/i18n/ja.ts +++ b/src/lib/i18n/ja.ts @@ -21,6 +21,7 @@ export default { 'preferences.analyticsDescription2': 'You can choose to enable or disable this option.', 'preferences.analyticsLabel': 'Enable analytics to help improve Boostnote', + 'preferences.displayTutorialsLabel': 'チュートリアルを表示', // Preferences EditorTab 'preferences.editorTheme': 'エディターテーマ', diff --git a/src/lib/preferences/store.ts b/src/lib/preferences/store.ts index 6967ab1202..463a8de626 100644 --- a/src/lib/preferences/store.ts +++ b/src/lib/preferences/store.ts @@ -30,6 +30,7 @@ const basePreferences: Preferences = { 'general.theme': 'dark', 'general.noteSorting': 'date-updated', 'general.enableAnalytics': true, + 'general.tutorials': 'display', // Editor 'editor.theme': 'default', diff --git a/src/lib/preferences/types.ts b/src/lib/preferences/types.ts index c6cbc3e941..ab6a660eb1 100644 --- a/src/lib/preferences/types.ts +++ b/src/lib/preferences/types.ts @@ -6,6 +6,7 @@ export type GeneralNoteSortingOptions = | 'date-updated' | 'date-created' | 'title' +export type GeneralTutorialsOptions = 'display' | 'hide' export type EditorIndentTypeOptions = 'tab' | 'spaces' export type EditorIndentSizeOptions = 2 | 4 | 8 @@ -18,6 +19,7 @@ export interface Preferences { 'general.theme': GeneralThemeOptions 'general.noteSorting': GeneralNoteSortingOptions 'general.enableAnalytics': boolean + 'general.tutorials': GeneralTutorialsOptions // Editor 'editor.theme': string diff --git a/webpack.config.ts b/webpack.config.ts index 335075d7c6..e47d49ccb7 100644 --- a/webpack.config.ts +++ b/webpack.config.ts @@ -51,6 +51,14 @@ module.exports = { test: /\.tsx?$/, use: [{ loader: 'ts-loader' }], exclude: /node_modules/ + }, + { + test: /\.md$/, + use: [ + { + loader: 'raw-loader' + } + ] } ] }, From 9ab039199106dcfebde22138afdc15c3f60b8eb6 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 14:57:08 +0900 Subject: [PATCH 02/13] code editor props --- src/components/atoms/CodeEditor.tsx | 4 +++- 1 file changed, 3 insertions(+), 1 deletion(-) diff --git a/src/components/atoms/CodeEditor.tsx b/src/components/atoms/CodeEditor.tsx index c9c642d9c5..85cc9a46fd 100644 --- a/src/components/atoms/CodeEditor.tsx +++ b/src/components/atoms/CodeEditor.tsx @@ -33,6 +33,7 @@ interface CodeEditorProps { indentSize?: EditorIndentSizeOptions keyMap?: EditorKeyMapOptions mode?: string + readonly?: boolean } class CodeEditor extends React.Component { @@ -52,7 +53,8 @@ class CodeEditor extends React.Component { indentUnit: indentSize, tabSize: indentSize, keyMap, - mode: this.props.mode || 'markdown' + mode: this.props.mode || 'markdown', + readOnly: this.props.readonly === true }) this.codeMirror.on('change', this.handleCodeMirrorChange) window.addEventListener('codemirror-mode-load', this.reloadMode) From cce99bd1549b76a0546c88e36a40cfb817496e99 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 14:57:37 +0900 Subject: [PATCH 03/13] customizedCodeEditor change --- src/components/atoms/CustomizedCodeEditor.tsx | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/components/atoms/CustomizedCodeEditor.tsx b/src/components/atoms/CustomizedCodeEditor.tsx index 0a498b6cfe..143ae3848e 100644 --- a/src/components/atoms/CustomizedCodeEditor.tsx +++ b/src/components/atoms/CustomizedCodeEditor.tsx @@ -11,6 +11,7 @@ interface CustomizedCodeEditor { codeMirrorRef?: (codeMirror: CodeMirror.EditorFromTextArea) => void className?: string mode?: string + readonly?: boolean } const CustomizedCodeEditor = ({ @@ -18,7 +19,8 @@ const CustomizedCodeEditor = ({ value, codeMirrorRef, className, - mode + mode, + readonly }: CustomizedCodeEditor) => { const { preferences } = usePreferences() return ( @@ -34,6 +36,7 @@ const CustomizedCodeEditor = ({ indentSize={preferences['editor.indentSize']} keyMap={preferences['editor.keyMap']} mode={mode} + readonly={readonly} /> ) } From c5421e2b185d07a721075cb7260f3ac24c1d41a7 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 15:02:46 +0900 Subject: [PATCH 04/13] md files --- .../WelcomePack/Guides/AboutOurCommunity.md | 21 +++ .../WelcomePack/Guides/KeyboardShortcuts.md | 140 ++++++++++++++++++ .../WelcomePack/Guides/StorageGuide.md | 18 +++ .../WelcomePack/Playground/GetStarted.md | 92 ++++++++++++ .../WelcomePack/Playground/TodaysTask.md | 11 ++ .../WelcomePack/Templates/Brainstorm.md | 29 ++++ .../WelcomePack/Templates/BugfixReport.md | 21 +++ .../WelcomePack/Templates/MeetingNotes.md | 33 +++++ .../WelcomePack/Templates/WeeklyPlanner.md | 55 +++++++ 9 files changed, 420 insertions(+) create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Guides/AboutOurCommunity.md create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Guides/KeyboardShortcuts.md create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Guides/StorageGuide.md create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Playground/GetStarted.md create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Playground/TodaysTask.md create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Templates/Brainstorm.md create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Templates/BugfixReport.md create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Templates/MeetingNotes.md create mode 100644 src/lib/tutorials/files/tutorials/WelcomePack/Templates/WeeklyPlanner.md diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Guides/AboutOurCommunity.md b/src/lib/tutorials/files/tutorials/WelcomePack/Guides/AboutOurCommunity.md new file mode 100644 index 0000000000..76c8646f01 --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Guides/AboutOurCommunity.md @@ -0,0 +1,21 @@ +# About Our Community +Here is some additional info on our community! We are always welcoming newcomers, so don't hesitate to say hello ;) + +## Connect with Us +- [Slack Group](https://join.slack.com/t/boostnote-group/shared_invite/enQtMzkxOTk4ODkyNzc0LWQxZTQwNjBlMDI4YjkyYjg2MTRiZGJhNzA1YjQ5ODA5M2M0M2NlMjI5YjhiYWQzNzgzYmU0MDMwOTlmZmZmMGE) +- [Facebook Group](https://www.facebook.com/groups/boostnote/) +- [Twitter](https://twitter.com/boostnoteapp) +- [Blog](https://medium.com/boostnote) +- [Reddit](https://www.reddit.com/r/Boostnote/) + +## Supporting Boostnote +Boostnote is an open source project. It's an independent project with its ongoing development made possible thanks to the support by our amazing backers. + +Issues on Boostnote can be funded by anyone and the money will be distributed to contributors and maintainers. If you use Boostnote please consider becoming a backer: + +[![Let's fund issues in this repository](https://issuehunt.io/static/embed/issuehunt-button-v1.svg)](https://issuehunt.io/repos/53266139) + +## More Information +* [Website](https://boostnote.io) +* [Newsletters](https://boostnote.io/#subscribe) +* [Development Configurations](https://github.com/BoostIO/Boostnote/blob/master/docs/build.md) \ No newline at end of file diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Guides/KeyboardShortcuts.md b/src/lib/tutorials/files/tutorials/WelcomePack/Guides/KeyboardShortcuts.md new file mode 100644 index 0000000000..a6603e0811 --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Guides/KeyboardShortcuts.md @@ -0,0 +1,140 @@ +# Keyboard Shortcuts + + +## Basic Shortcuts +Here are other keyboard shortcuts which can be useful for you :) + + +### Preferences + +* Open Preference +-> `Command + ,` + +--- + +* Close Preference +-> `Esc` + + +### Notes + +* New Note +-> `Command + N` + +--- + +* Clone Note +-> `Command + D` + +--- + +* Next Note +-> `Command + ]` + +--- + +* Previous Note +-> `Command + [` + + +### Adding Stuff + +* Add Tag +-> `Command + Shift + T` + +--- + +* Add Emoji & Symbol +-> `Command + Control + Space` + +--- + +* Generate/Update Markdown TOC +-> `Control + Shift + T` + + +### Toggle Views + +* Toggle Full Screen +-> `Command + Control + F` + +--- + +* Toggle Sidebar +-> `Command + B` + +--- + +* Toggle Note Info +-> `Command + Option + I` + + +### Zoom + +* Zoom In +-> `Command + =` + +--- + +* Zoom Out +-> `Command + -` + +--- + +* Actual Size +-> `Command + 0` + + +### Other Useful Shortcuts + +* Focus Search +-> `Command + Shift + L` + +--- + +* Reload +-> `Command + R` + + +## Hotkeys +Hotkeys are set like below by default, but you can change them in the preference modal. + +--- + +* Show/Hide Boostnote +-> `Command + Alt + L` + +--- + +* Show/Hide Menu Bar +-> `Alt` + +--- + +* Toggle Editor Mode +-> `Command + Alt + M` + +--- + +* Delete Note +-> `Command + Shift + Delete` + +--- + +* Paste HTML +-> `Command + Shift + V` + +--- + +* Prettify Markdown +-> `Command + Shift + F` + +--- + +* Insert Current Date +-> `Command + /` + +--- + +* Insert Current Date and Time +-> `Command + Alt + /` \ No newline at end of file diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Guides/StorageGuide.md b/src/lib/tutorials/files/tutorials/WelcomePack/Guides/StorageGuide.md new file mode 100644 index 0000000000..0e424d1e1b --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Guides/StorageGuide.md @@ -0,0 +1,18 @@ +# Storage guide +Storages and folders can help you to organize your notes. Check this guide and start organizing to make your life easier! + +## Adding a Storage +One of the first thing you want to do in order to use Boostnote is to create a storage. In order to do so, you can click on the link in the sidebar. + +## Storage Can Have Folders +Each storage can have as many folders as you want, and you can create even subfolders inside there. + +You can check the Sample Storage on the sidebar and play around with it so you can get some ideas on how you want to organize with this feature! + +## Local Storage & Cloud Storage +This feature has two storage types, local and cloud. There are no limitations for local storage number (like older version Boostnote), but if you want to create more than two cloud storage, you need to upgrade your plan to do so. [Follow this link](https://boostnote.io/) to upgrade the plan :) + +## Sync Your Cloud Storage +The merit of cloud storage is syncing your notes on every platform. Even when you are offline, Boostnote will start syncing once it became online. + +If there is something problem and couldn't sync your notes, this app will tell you how to solve it. \ No newline at end of file diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Playground/GetStarted.md b/src/lib/tutorials/files/tutorials/WelcomePack/Playground/GetStarted.md new file mode 100644 index 0000000000..0eee0f6b01 --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Playground/GetStarted.md @@ -0,0 +1,92 @@ +# Get Started +Welcome to Boostnote :) This is a page for you to play around with. + +## 👨‍💻 Markdown Cheat Sheet 👩‍💻 + +--- +### 1️⃣ Headings +--- + +#### Heading 1 +`# H1` + +#### Heading 2 +`## H2` + +#### Heading 3 +`### H3` + +#### Heading 4 +`#### H4` + +#### Heading 5 +`##### H5` + +#### Heading 6 +`###### H6` + +--- +### 2️⃣ Text Decoration +--- + +#### Bold +`**bold**` + +#### Italic +`*italicized text*` + +#### Line Through +`~~line through~~` + +--- +### 3️⃣ List +--- + +#### Ordered List +``` +1. First Item +2. Second Item +3. Third Item +``` + +#### Unordered List +``` +* First Item +* Second Item +* Third Item +``` + +--- +### 4️⃣ Code Decoration +--- + +#### Code +`code` + +#### Code Block + +```html +Hello World! + +``` + +--- +### 5️⃣ Others +--- + +#### Checkbox +``` +* [x] First Item +* [ ] Second item +``` + +#### Horizontal Rule +`---` + +#### Link +`[Boostnote](https://boostnote.io/)` + +#### Quote +`> This is a quote from somewhere!` + +--- \ No newline at end of file diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Playground/TodaysTask.md b/src/lib/tutorials/files/tutorials/WelcomePack/Playground/TodaysTask.md new file mode 100644 index 0000000000..6c5a12cc23 --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Playground/TodaysTask.md @@ -0,0 +1,11 @@ +# Today's Task +Let's write your today's tasks to get used to Boostnote! + +## Sample Tasks +* [x] Download Boostnote +* [ ] Write down today's task +* [ ] Get a cup of coffee +* [ ] Turn on my favorite music + +## Write Your Own :) +* [ ] Your first tasks is...? \ No newline at end of file diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Templates/Brainstorm.md b/src/lib/tutorials/files/tutorials/WelcomePack/Templates/Brainstorm.md new file mode 100644 index 0000000000..90cbc47f8a --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Templates/Brainstorm.md @@ -0,0 +1,29 @@ +# Template 4: Brainstorm + +## Goals/Issues + +* Point 1 +* Point 2 +* Point 3 + +## Research + +### [Boostnote](https://boostnote.io/) + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus turpis nunc, tempor et purus tempus, condimentum accumsan massa. + +## Suggestions/Ideas + +* Idea 1 +* Idea 2 +* Idea 3 + +## Final Decision + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus turpis nunc, tempor et purus tempus, condimentum accumsan massa. + +## Action Items + +* [ ] Item 1 +* [ ] Item 2 +* [ ] Item 3 \ No newline at end of file diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Templates/BugfixReport.md b/src/lib/tutorials/files/tutorials/WelcomePack/Templates/BugfixReport.md new file mode 100644 index 0000000000..f46408386d --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Templates/BugfixReport.md @@ -0,0 +1,21 @@ +# Template 3: Bugfix Report + +## Summary of the Bug + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus turpis nunc, tempor et purus tempus, condimentum accumsan massa. + +## Why Did It Happen? + +* Lorem ipsum dolor sit amet, consectetur adipiscing elit. +* Lorem ipsum dolor sit amet, consectetur adipiscing elit. +* Lorem ipsum dolor sit amet, consectetur adipiscing elit. + +## How Did I Fix? + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus turpis nunc, tempor et purus tempus, condimentum accumsan massa. + +## How to Prevent Next Time? + +* [ ] Idea 1 +* [ ] Idea 2 +* [ ] Idea 3 \ No newline at end of file diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Templates/MeetingNotes.md b/src/lib/tutorials/files/tutorials/WelcomePack/Templates/MeetingNotes.md new file mode 100644 index 0000000000..d588ed5ded --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Templates/MeetingNotes.md @@ -0,0 +1,33 @@ +# Template 1: Meeting Notes + +## Basic Info + +* Project Name +* Date, Time, Location +* Goal of This Meeting +* Attendees + +## Background + +Lorem ipsum dolor sit amet, consectetur adipiscing elit. Phasellus turpis nunc, tempor et purus tempus, condimentum accumsan massa. + +## Agenda + +* Topic 1 +* Topic 2 +* Topic 3 + +## Decisions + +* Decision 1 +* Decision 2 +* Decision 3 + +## Next Actions + +* [x] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Closure +Summary & Takeaways diff --git a/src/lib/tutorials/files/tutorials/WelcomePack/Templates/WeeklyPlanner.md b/src/lib/tutorials/files/tutorials/WelcomePack/Templates/WeeklyPlanner.md new file mode 100644 index 0000000000..b4bba4cfc1 --- /dev/null +++ b/src/lib/tutorials/files/tutorials/WelcomePack/Templates/WeeklyPlanner.md @@ -0,0 +1,55 @@ +# Template 2: Weekly Planner + +## This Week's Goal + +* [ ] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Monday + +* [ ] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Tuesday + +* [ ] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Wednesday + +* [ ] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Thursday + +* [ ] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Friday + +* [ ] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Saturday + +* [ ] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Sunday + +* [ ] Task 1 +* [ ] Task 2 +* [ ] Task 3 + +## Memo + +* I learned ... +* Memo 2 +* Memo 3 \ No newline at end of file From f41f27d462bf9b951a7e688d2f12c0fa3dd29f0c Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 15:03:52 +0900 Subject: [PATCH 05/13] tutorials tree --- src/lib/tutorials/index.tsx | 1 + src/lib/tutorials/tree.ts | 113 ++++++++++++++++++++++++++++++++++++ 2 files changed, 114 insertions(+) create mode 100644 src/lib/tutorials/index.tsx create mode 100644 src/lib/tutorials/tree.ts diff --git a/src/lib/tutorials/index.tsx b/src/lib/tutorials/index.tsx new file mode 100644 index 0000000000..aec59884a9 --- /dev/null +++ b/src/lib/tutorials/index.tsx @@ -0,0 +1 @@ +export * from './tree' diff --git a/src/lib/tutorials/tree.ts b/src/lib/tutorials/tree.ts new file mode 100644 index 0000000000..03d64c94ae --- /dev/null +++ b/src/lib/tutorials/tree.ts @@ -0,0 +1,113 @@ +export type TutorialsNavigatorTreeItem = { + label: string + slug: string + absolutePath: string + type: 'folder' | 'note' | 'storage' + children: TutorialsNavigatorTreeItem[] +} + +export const tutorialsTree: TutorialsNavigatorTreeItem[] = [ + { + label: 'Tutorials', + absolutePath: 'tutorials', + slug: 'tutorials', + type: 'storage', + children: [ + { + label: 'Welcome Pack', + absolutePath: 'WelcomePack', + slug: 'welcome-pack', + type: 'folder', + children: [ + { + label: 'Playground', + absolutePath: 'Playground', + slug: 'playground', + type: 'folder', + children: [ + { + label: 'Get Started!', + absolutePath: 'GetStarted.md', + slug: 'get-started', + type: 'note', + children: [] + }, + { + label: "Today's Task", + absolutePath: 'TodaysTask.md', + slug: 'daily-task', + type: 'note', + children: [] + } + ] + }, + { + label: 'Guides', + absolutePath: 'Guides', + slug: 'guides', + type: 'folder', + children: [ + { + label: 'About our community', + absolutePath: 'AboutOurCommunity.md', + slug: 'community', + type: 'note', + children: [] + }, + { + label: 'Keyboard shortcuts', + absolutePath: 'KeyboardShortcuts.md', + slug: 'keyboard-shortcuts', + type: 'note', + children: [] + }, + { + label: 'Storage guide', + absolutePath: 'StorageGuide.md', + slug: 'storage-guide', + type: 'note', + children: [] + } + ] + }, + { + label: 'Templates', + absolutePath: 'Templates', + slug: 'templates', + type: 'folder', + children: [ + { + label: 'Brainstorm', + absolutePath: 'Brainstorm.md', + slug: 'brainstorm', + type: 'note', + children: [] + }, + { + label: 'Bugfix Report', + absolutePath: 'BugfixReport.md', + slug: 'bugfix-report', + type: 'note', + children: [] + }, + { + label: 'Meeting Notes', + absolutePath: 'MeetingNotes.md', + slug: 'meeting-notes', + type: 'note', + children: [] + }, + { + label: 'Weekly Planner', + absolutePath: 'WeeklyPlanner.md', + slug: 'weekly-planner', + type: 'note', + children: [] + } + ] + } + ] + } + ] + } +] From 474f633e1bb615b9082a2876bf7bd0abeb9669f4 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 15:04:42 +0900 Subject: [PATCH 06/13] markdown declaration --- typings/markdowns.d.ts | 1 + 1 file changed, 1 insertion(+) create mode 100644 typings/markdowns.d.ts diff --git a/typings/markdowns.d.ts b/typings/markdowns.d.ts new file mode 100644 index 0000000000..d8024e92b1 --- /dev/null +++ b/typings/markdowns.d.ts @@ -0,0 +1 @@ +declare module '*.md' From 2e2c86eca40711c9dfee4c971c9be151ceaae72a Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 15:45:24 +0900 Subject: [PATCH 07/13] tutorials note components --- .../Tutorials/TutorialsNoteDetail.tsx | 192 ++++++++++++ .../Tutorials/TutorialsNoteItem.tsx | 69 +++++ .../Tutorials/TutorialsNoteList.tsx | 93 ++++++ src/components/Tutorials/TutorialsPage.tsx | 278 ++++++++++++++++++ 4 files changed, 632 insertions(+) create mode 100644 src/components/Tutorials/TutorialsNoteDetail.tsx create mode 100644 src/components/Tutorials/TutorialsNoteItem.tsx create mode 100644 src/components/Tutorials/TutorialsNoteList.tsx create mode 100644 src/components/Tutorials/TutorialsPage.tsx diff --git a/src/components/Tutorials/TutorialsNoteDetail.tsx b/src/components/Tutorials/TutorialsNoteDetail.tsx new file mode 100644 index 0000000000..ff07e578c8 --- /dev/null +++ b/src/components/Tutorials/TutorialsNoteDetail.tsx @@ -0,0 +1,192 @@ +import React from 'react' +import styled from '../../lib/styled' +import CustomizedCodeEditor from '../atoms/CustomizedCodeEditor' +import CustomizedMarkdownPreviewer from '../atoms/CustomizedMarkdownPreviewer' +import { mdiEyeOutline, mdiArrowSplitVertical } from '@mdi/js' +import ToolbarIconButton from '../atoms/ToolbarIconButton' +import Toolbar from '../atoms/Toolbar' +import ToolbarSeparator from '../atoms/ToolbarSeparator' +import { + secondaryBackgroundColor, + textColor, + borderBottom, + borderRight +} from '../../lib/styled/styleFunctions' +import { TutorialsNavigatorTreeItem } from '../../lib/tutorials' + +const StyledTutorialsNoteDetailContainer = styled.div` + ${secondaryBackgroundColor} + display: flex; + flex-direction: column; + height: 100%; + .titleSection { + display: flex; + height: 50px; + border-width: 0 0 1px; + ${borderBottom} + + input { + font-size: 24px; + border: none; + height: 100%; + padding: 0 12px; + flex: 1; + background-color: transparent; + ${textColor} + } + } + + .contentSection { + flex: 1; + overflow: hidden; + position: relative; + .editor .CodeMirror { + position: absolute; + top: 0; + bottom: 0; + width: 100%; + height: 100%; + } + .MarkdownPreviewer { + position: absolute; + top: 0; + bottom: 0; + width: 100%; + height: 100%; + overflow: auto; + padding: 0 10px; + box-sizing: border-box; + } + .splitLeft { + position: absolute; + width: 50%; + height: 100%; + ${borderRight} + } + .splitRight { + position: absolute; + left: 50%; + width: 50%; + height: 100%; + } + } +` + +type TutorialsNoteDetailProps = { + note: TutorialsNavigatorTreeItem + splitMode: boolean + previewMode: boolean + toggleSplitMode: () => void + togglePreviewMode: () => void +} + +type TutorialsNoteDetailState = { + noteComponent: string + noteContent: string +} + +export default class TutorialsNoteDetail extends React.Component< + TutorialsNoteDetailProps +> { + state: TutorialsNoteDetailState = { + noteComponent: this.props.note.slug, + noteContent: '' + } + + async componentDidMount() { + this.setState({ noteContent: await this.fetchNoteContentFromTreeItem() }) + } + + async fetchNoteContentFromTreeItem() { + try { + const doc = await import( + `../../lib/tutorials/files${this.props.note.absolutePath}` + ) + return doc.default + } catch (error) { + console.log(error) + return `Could not load the file` + } + } + + async componentDidUpdate( + _prevProps: TutorialsNoteDetailProps, + prevState: TutorialsNoteDetailState + ) { + const { note } = this.props + if (note.absolutePath !== prevState.noteComponent) { + this.setState({ + noteComponent: note.absolutePath, + noteContent: await this.fetchNoteContentFromTreeItem() + }) + } + } + + codeMirror?: CodeMirror.EditorFromTextArea + codeMirrorRef = (codeMirror: CodeMirror.EditorFromTextArea) => { + this.codeMirror = codeMirror + } + + render() { + const { + note, + splitMode, + previewMode, + toggleSplitMode, + togglePreviewMode + } = this.props + + const codeEditor = ( + {}} + readonly={true} + /> + ) + const markdownPreviewer = ( + + ) + + return ( + + {note == null ? ( +

No note is selected

+ ) : ( + <> +
+ +
+
+ {previewMode ? ( + markdownPreviewer + ) : splitMode ? ( + <> +
{codeEditor}
+
{markdownPreviewer}
+ + ) : ( + codeEditor + )} +
+ + + + + + + )} +
+ ) + } +} diff --git a/src/components/Tutorials/TutorialsNoteItem.tsx b/src/components/Tutorials/TutorialsNoteItem.tsx new file mode 100644 index 0000000000..ae3b7703a2 --- /dev/null +++ b/src/components/Tutorials/TutorialsNoteItem.tsx @@ -0,0 +1,69 @@ +import React from 'react' +import { Link } from '../../lib/router' +import styled from '../../lib/styled/styled' +import { + borderBottom, + uiTextColor, + secondaryBackgroundColor +} from '../../lib/styled/styleFunctions' +import cc from 'classcat' +import { TutorialsNavigatorTreeItem } from '../../lib/tutorials' + +const StyledNoteListItem = styled.div` + margin: 0; + border-left: 2px solid transparent; + ${uiTextColor} + &.active, + &:active, + &:focus, + &:hover { + ${secondaryBackgroundColor} + } + &.active { + border-left: 2px solid ${({ theme }) => theme.primaryColor}; + } + user-select: none; + ${borderBottom} + + transition: 200ms background-color; + + a { + text-decoration: none; + } + + .container { + padding: 8px; + } + + .title { + font-weight: bold; + font-size: 15px; + margin-bottom: 4px; + } +` + +type TutorialsNoteItemProps = { + note: TutorialsNavigatorTreeItem + active: boolean + basePathname: string + focusList: () => void +} + +export default ({ + note, + active, + basePathname, + focusList +}: TutorialsNoteItemProps) => { + const href = `${basePathname}/notes/note:${note.slug}` + + return ( + + +
+
{note.label}
+
+ +
+ ) +} diff --git a/src/components/Tutorials/TutorialsNoteList.tsx b/src/components/Tutorials/TutorialsNoteList.tsx new file mode 100644 index 0000000000..c7a92c7cee --- /dev/null +++ b/src/components/Tutorials/TutorialsNoteList.tsx @@ -0,0 +1,93 @@ +import React, { useCallback, KeyboardEvent, useRef } from 'react' +import { TutorialsNavigatorTreeItem } from '../../lib/tutorials' +import styled from '../../lib/styled' +import TutorialsNoteItem from './TutorialsNoteItem' + +const StyledContainer = styled.div` + display: flex; + flex-direction: column; + overflow: hidden; + height: 100%; + outline: none; + & > ul { + flex: 1; + margin: 0; + padding: 0; + list-style: none; + overflow-y: auto; + + li.empty { + color: ${({ theme }) => theme.uiTextColor}; + } + } +` + +type TutorialsNoteListProps = { + currentTree: TutorialsNavigatorTreeItem + basePathname: string + parentTree?: TutorialsNavigatorTreeItem + selectedNote?: TutorialsNavigatorTreeItem + navigateUp: () => void + navigateDown: () => void +} + +const TutorialsNoteList = ({ + currentTree, + parentTree, + navigateUp, + navigateDown, + basePathname, + selectedNote +}: TutorialsNoteListProps) => { + const handleListKeyDown = useCallback( + (event: KeyboardEvent) => { + switch (event.key) { + case 'ArrowDown': + navigateDown() + break + case 'ArrowUp': + navigateUp() + break + } + }, + [navigateUp, navigateDown] + ) + + const listRef = useRef(null) + + const focusList = useCallback(() => { + listRef.current!.focus() + }, []) + + const notes = + currentTree.type !== 'note' + ? currentTree.children.filter(child => child.type === 'note') + : parentTree == null + ? [] + : parentTree.children.filter(child => child.type === 'note') + + return ( + +
    + {notes.map(note => { + const noteIsCurrentNote = + selectedNote != null && + note.absolutePath === selectedNote.absolutePath + return ( +
  • + +
  • + ) + })} + {notes.length === 0 &&
  • No notes
  • } +
+
+ ) +} + +export default TutorialsNoteList diff --git a/src/components/Tutorials/TutorialsPage.tsx b/src/components/Tutorials/TutorialsPage.tsx new file mode 100644 index 0000000000..067283ea83 --- /dev/null +++ b/src/components/Tutorials/TutorialsPage.tsx @@ -0,0 +1,278 @@ +import React, { useEffect, useCallback, useState } from 'react' +import { tutorialsTree, TutorialsNavigatorTreeItem } from '../../lib/tutorials' +import TwoPaneLayout from '../atoms/TwoPaneLayout' +import { useGeneralStatus } from '../../lib/generalStatus' +import { useRouter } from '../../lib/router' +import TutorialsNoteList from './TutorialsNoteList' +import TutorialsNoteDetail from './TutorialsNoteDetail' + +interface TutorialsPageProps { + pathname: string +} + +type TutoriasPagePicker = { + parentTree?: TutorialsNavigatorTreeItem + currentTree: TutorialsNavigatorTreeItem +} + +export default ({ pathname }: TutorialsPageProps) => { + const [ + currentTutorialBranch, + setCurrentTutorialBranch + ] = useState(getCurrentNodeFromTutorialTrees) + + const [currentFolderPathname, setCurrentFolderPathname] = useState( + getCurrentFolderPathname + ) + const [selectedNote, setSelectedNote] = useState< + TutorialsNavigatorTreeItem | undefined + >() + + const { generalStatus, setGeneralStatus } = useGeneralStatus() + const router = useRouter() + const updateNoteListWidth = useCallback( + (leftWidth: number) => { + setGeneralStatus({ + noteListWidth: leftWidth + }) + }, + [setGeneralStatus] + ) + + const toggleSplitMode = useCallback(() => { + setGeneralStatus(prevState => ({ + noteSplitMode: !prevState.noteSplitMode + })) + }, [setGeneralStatus]) + + const togglePreviewMode = useCallback(() => { + setGeneralStatus(prevState => ({ + notePreviewMode: !prevState.notePreviewMode + })) + }, [setGeneralStatus]) + + useEffect(() => { + setCurrentTutorialBranch(getCurrentNodeFromTutorialTrees) + setCurrentFolderPathname(getCurrentFolderPathname) + }, [pathname, tutorialsTree]) + + useEffect(() => { + setSelectedNote(getCurrentNote) + }, [currentTutorialBranch]) + + function getCurrentNote(): TutorialsNavigatorTreeItem | undefined { + if (currentTutorialBranch == null) { + return undefined + } + + if (currentTutorialBranch.currentTree.type === 'note') { + return currentTutorialBranch.currentTree + } + + const notesChildren = currentTutorialBranch.currentTree.children.filter( + node => node.type === 'note' + ) + if (notesChildren.length > 0) { + return notesChildren[0] + } + return undefined + } + + function getCurrentFolderPathname() { + return pathname.split('/notes')[0] + } + + function getCurrentNodeFromTutorialTrees() { + let match = null + for (let i = 0; i < tutorialsTree.length; i++) { + match = searchThroughTreeForIdenticalNode( + pathname, + '/app', + '', + tutorialsTree[i] + ) + if (match != null) { + break + } + } + return match + } + + function searchThroughTreeForIdenticalNode( + pathToSearch: string, + parentDepthPath: string, + parentAbsolutePath: string, + tree: TutorialsNavigatorTreeItem, + parentTree?: TutorialsNavigatorTreeItem + ): TutoriasPagePicker | null { + let match = null + const currentDepthPath = `${parentDepthPath}/${ + tree.type === 'note' ? 'notes/note:' : '' + }${tree.slug}` + + const currentAbsolutePath = parentAbsolutePath + '/' + tree.absolutePath + if (currentDepthPath === pathToSearch) { + const currentTreeWithDepthAbsolutePath = { + ...tree, + absolutePath: currentAbsolutePath, + children: Object.entries(tree.children).map(obj => { + return { + ...obj[1], + absolutePath: currentAbsolutePath + '/' + obj[1].absolutePath + } + }) as TutorialsNavigatorTreeItem[] + } + + const parentTreeWithDepthAbsolutePath = + parentTree != null + ? { + ...parentTree, + absolutePath: parentAbsolutePath, + children: Object.entries(parentTree.children).map(obj => { + return { + ...obj[1], + absolutePath: parentAbsolutePath + '/' + obj[1].absolutePath + } + }) + } + : undefined + + return { + currentTree: currentTreeWithDepthAbsolutePath, + parentTree: parentTreeWithDepthAbsolutePath + } + } + + for (let i = 0; i < tree.children.length; i++) { + match = searchThroughTreeForIdenticalNode( + pathToSearch, + currentDepthPath, + currentAbsolutePath, + tree.children[i], + tree + ) + if (match != null) { + break + } + } + + return match + } + + const navigateUp = useCallback(() => { + if (currentTutorialBranch == null) { + return + } + + if (selectedNote == null) { + return + } + + const notes = + currentTutorialBranch.currentTree.type !== 'note' + ? currentTutorialBranch.currentTree.children.filter( + node => node.type === 'note' + ) + : currentTutorialBranch.parentTree != null + ? currentTutorialBranch.parentTree.children.filter( + node => node.type === 'note' + ) + : [] + + if (notes.length < 1) { + return + } + + let currentNoteIndex = 0 + for (let i = 0; i < notes.length; i++) { + if (selectedNote.absolutePath === notes[i].absolutePath) { + currentNoteIndex = i + break + } + } + + if (currentNoteIndex - 1 >= 0) { + router.push( + currentFolderPathname + + '/notes/note:' + + notes[currentNoteIndex - 1].slug + ) + } + return + }, [selectedNote]) + + const navigateDown = useCallback(() => { + if (currentTutorialBranch == null) { + return + } + + if (selectedNote == null) { + return + } + + const notes = + currentTutorialBranch.currentTree.type !== 'note' + ? currentTutorialBranch.currentTree.children.filter( + node => node.type === 'note' + ) + : currentTutorialBranch.parentTree != null + ? currentTutorialBranch.parentTree.children.filter( + node => node.type === 'note' + ) + : [] + + if (notes.length < 1) { + return + } + + let currentNoteIndex = 0 + for (let i = 0; i < notes.length; i++) { + if (selectedNote.absolutePath === notes[i].absolutePath) { + currentNoteIndex = i + break + } + } + + if (currentNoteIndex + 1 >= 0 && currentNoteIndex + 1 < notes.length) { + router.push( + currentFolderPathname + + '/notes/note:' + + notes[currentNoteIndex + 1].slug + ) + } + return + }, [selectedNote]) + + return ( + + ) + } + right={ + selectedNote == null ? ( +
No note selected
+ ) : ( + + ) + } + onResizeEnd={updateNoteListWidth} + /> + ) +} From 99a5667d9a5b5cb959e993a3754115daf2e72c83 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 18:22:17 +0900 Subject: [PATCH 08/13] fix nav --- .../NotePage/NoteDetail/NoteDetail.tsx | 2 +- src/components/NotePage/NoteList/NoteItem.tsx | 2 +- src/components/NotePage/NoteList/NoteList.tsx | 6 +- src/components/Router.tsx | 7 + .../SideNavigator/SideNavigator.tsx | 6 +- .../Tutorials/TutorialsNavigator.tsx | 140 ++++++++++++++++++ .../Tutorials/TutorialsNoteDetail.tsx | 70 +-------- .../Tutorials/TutorialsNoteItem.tsx | 40 +---- .../Tutorials/TutorialsNoteList.tsx | 25 +--- src/lib/router/index.tsx | 1 + src/lib/router/redirect.ts | 21 +++ src/lib/router/store.ts | 3 + src/lib/router/utils.ts | 24 +++ 13 files changed, 213 insertions(+), 134 deletions(-) create mode 100644 src/components/Tutorials/TutorialsNavigator.tsx create mode 100644 src/lib/router/redirect.ts diff --git a/src/components/NotePage/NoteDetail/NoteDetail.tsx b/src/components/NotePage/NoteDetail/NoteDetail.tsx index 85e7f4c318..93778b03f2 100644 --- a/src/components/NotePage/NoteDetail/NoteDetail.tsx +++ b/src/components/NotePage/NoteDetail/NoteDetail.tsx @@ -24,7 +24,7 @@ import { } from '../../../lib/styled/styleFunctions' import ToolbarExportButton from '../../atoms/ToolbarExportButton' -const StyledNoteDetailContainer = styled.div` +export const StyledNoteDetailContainer = styled.div` ${secondaryBackgroundColor} display: flex; flex-direction: column; diff --git a/src/components/NotePage/NoteList/NoteItem.tsx b/src/components/NotePage/NoteList/NoteItem.tsx index d2d51d8bd4..25e1ace762 100644 --- a/src/components/NotePage/NoteList/NoteItem.tsx +++ b/src/components/NotePage/NoteList/NoteItem.tsx @@ -12,7 +12,7 @@ import { import cc from 'classcat' import { setTransferrableNoteData } from '../../../lib/dnd' -const StyledNoteListItem = styled.div` +export const StyledNoteListItem = styled.div` margin: 0; border-left: 2px solid transparent; ${uiTextColor} diff --git a/src/components/NotePage/NoteList/NoteList.tsx b/src/components/NotePage/NoteList/NoteList.tsx index 449a03aaf0..a2ffccc55d 100644 --- a/src/components/NotePage/NoteList/NoteList.tsx +++ b/src/components/NotePage/NoteList/NoteList.tsx @@ -10,7 +10,7 @@ import { uiTextColor } from '../../../lib/styled/styleFunctions' -const StyledContainer = styled.div` +export const StyledNoteListContainer = styled.div` display: flex; flex-direction: column; overflow: hidden; @@ -107,7 +107,7 @@ const NoteList = ({ }, []) return ( - +
No notes} - + ) } diff --git a/src/components/Router.tsx b/src/components/Router.tsx index ad9337dedc..d4f02092c4 100644 --- a/src/components/Router.tsx +++ b/src/components/Router.tsx @@ -4,10 +4,14 @@ import { useRouteParams } from '../lib/router' import { StyledNotFoundPage } from './styled' import { StorageEdit, StorageCreate } from './Storage' import { useDb } from '../lib/db' +import TutorialsPage from './Tutorials/TutorialsPage' +import useRedirectHandler from '../lib/router/redirect' export default () => { const routeParams = useRouteParams() const db = useDb() + useRedirectHandler() + switch (routeParams.name) { case 'storages.allNotes': case 'storages.notes': @@ -16,6 +20,9 @@ export default () => { return case 'storages.create': return + case 'tutorials.show': + return + break case 'storages.edit': const storage = db.storageMap[routeParams.storageId] if (storage != null) { diff --git a/src/components/SideNavigator/SideNavigator.tsx b/src/components/SideNavigator/SideNavigator.tsx index 42d7366333..3f43dd3e5a 100644 --- a/src/components/SideNavigator/SideNavigator.tsx +++ b/src/components/SideNavigator/SideNavigator.tsx @@ -19,6 +19,7 @@ import { useGeneralStatus } from '../../lib/generalStatus' import ControlButton from './ControlButton' import FolderListFragment from './FolderListFragment' import TagListFragment from './TagListFragment' +import TutorialsNavigator from '../Tutorials/TutorialsNavigator' const StyledSideNavContainer = styled.nav` display: flex; @@ -126,7 +127,7 @@ export default () => { [popup, prompt, createStorage] ) - const { toggleClosed } = usePreferences() + const { toggleClosed, preferences } = usePreferences() const { toggleSideNavOpenedItem, sideNavOpenedItemSet, @@ -260,6 +261,9 @@ export default () => { {storageEntries.length === 0 && (
No storages
)} + {preferences['general.tutorials'] === 'display' && ( + + )}
{ + const routeParams = useRouteParams() + const { push } = useRouter() + const currentHref = currentTutorialPathname() + const { toggleSideNavOpenedItem, sideNavOpenedItemSet } = useGeneralStatus() + + const tutorials = tutorialsTree + + function getNavigatorNodesFromTreeItem( + tree: TutorialsNavigatorTreeItem, + currentDepth: number, + parentNode?: NavigatorNode, + parentComponentPathname?: string + ): NavigatorNode | undefined { + if (tree.type === 'note') { + return + } + + const componentPathname = `${parentComponentPathname != null && + parentComponentPathname}/${tree.absolutePath}` + const nodeHref = `${parentNode != null ? parentNode.href : '/app'}/${ + tree.slug + }` + const folderIsActive = + currentHref == null + ? false + : currentHref.split('/notes/note:')[0] === nodeHref + + const notesUnderCurrentNode = tree.children.filter( + child => child.type === 'note' + ) + + const nodeId = `TF-${nodeHref.split('/app')[1]}` + const currentNode = { + id: nodeId, + name: `${tree.label} ${ + notesUnderCurrentNode.length > 0 + ? `(${notesUnderCurrentNode.length})` + : '' + }`, + iconPath: + tree.type === 'folder' + ? folderIsActive + ? mdiFolderOpenOutline + : mdiFolderOutline + : mdiHelpCircleOutline, + href: nodeHref, + active: folderIsActive, + depth: currentDepth, + opened: sideNavOpenedItemSet.has(nodeId) + } + + const childrenNodes = + tree.children.length === 0 + ? [] + : (tree.children + .map(childrenTree => + getNavigatorNodesFromTreeItem( + childrenTree, + currentDepth + 1, + currentNode, + componentPathname + ) + ) + .filter(node => node != null) as NavigatorNode[]) + return { + ...currentNode, + children: childrenNodes + } + } + + const nodes = useMemo(() => { + return tutorials + .map(tutorial => getNavigatorNodesFromTreeItem(tutorial, 0)) + .filter(node => node != null) as NavigatorNode[] + }, [routeParams, tutorials, toggleSideNavOpenedItem]) + + const redirectToTutorial = (node: NavigatorNode) => { + push(node.href) + } + + const renderNode = ( + node: NavigatorNode, + parentNodeIsOpened: boolean + ): JSX.Element | null => { + if (node.depth > 0 && !parentNodeIsOpened) { + return null + } + return ( + + redirectToTutorial(node)} + onFoldButtonClick={() => toggleSideNavOpenedItem(node.id)} + folded={ + node.children == null || node.children.length === 0 + ? undefined + : !node.opened + } + /> + {node.children != null && + node.children.map(child => renderNode(child, node.opened))} + + ) + } + + return <>{nodes.map(node => renderNode(node, true))} +} + +export default TutorialsNavigator diff --git a/src/components/Tutorials/TutorialsNoteDetail.tsx b/src/components/Tutorials/TutorialsNoteDetail.tsx index ff07e578c8..aadfd227e2 100644 --- a/src/components/Tutorials/TutorialsNoteDetail.tsx +++ b/src/components/Tutorials/TutorialsNoteDetail.tsx @@ -1,76 +1,12 @@ import React from 'react' -import styled from '../../lib/styled' import CustomizedCodeEditor from '../atoms/CustomizedCodeEditor' import CustomizedMarkdownPreviewer from '../atoms/CustomizedMarkdownPreviewer' import { mdiEyeOutline, mdiArrowSplitVertical } from '@mdi/js' import ToolbarIconButton from '../atoms/ToolbarIconButton' import Toolbar from '../atoms/Toolbar' import ToolbarSeparator from '../atoms/ToolbarSeparator' -import { - secondaryBackgroundColor, - textColor, - borderBottom, - borderRight -} from '../../lib/styled/styleFunctions' import { TutorialsNavigatorTreeItem } from '../../lib/tutorials' - -const StyledTutorialsNoteDetailContainer = styled.div` - ${secondaryBackgroundColor} - display: flex; - flex-direction: column; - height: 100%; - .titleSection { - display: flex; - height: 50px; - border-width: 0 0 1px; - ${borderBottom} - - input { - font-size: 24px; - border: none; - height: 100%; - padding: 0 12px; - flex: 1; - background-color: transparent; - ${textColor} - } - } - - .contentSection { - flex: 1; - overflow: hidden; - position: relative; - .editor .CodeMirror { - position: absolute; - top: 0; - bottom: 0; - width: 100%; - height: 100%; - } - .MarkdownPreviewer { - position: absolute; - top: 0; - bottom: 0; - width: 100%; - height: 100%; - overflow: auto; - padding: 0 10px; - box-sizing: border-box; - } - .splitLeft { - position: absolute; - width: 50%; - height: 100%; - ${borderRight} - } - .splitRight { - position: absolute; - left: 50%; - width: 50%; - height: 100%; - } - } -` +import { StyledNoteDetailContainer } from '../NotePage/NoteDetail/NoteDetail' type TutorialsNoteDetailProps = { note: TutorialsNavigatorTreeItem @@ -151,7 +87,7 @@ export default class TutorialsNoteDetail extends React.Component< ) return ( - + {note == null ? (

No note is selected

) : ( @@ -186,7 +122,7 @@ export default class TutorialsNoteDetail extends React.Component< )} -
+ ) } } diff --git a/src/components/Tutorials/TutorialsNoteItem.tsx b/src/components/Tutorials/TutorialsNoteItem.tsx index ae3b7703a2..bb88e107bb 100644 --- a/src/components/Tutorials/TutorialsNoteItem.tsx +++ b/src/components/Tutorials/TutorialsNoteItem.tsx @@ -1,46 +1,8 @@ import React from 'react' import { Link } from '../../lib/router' -import styled from '../../lib/styled/styled' -import { - borderBottom, - uiTextColor, - secondaryBackgroundColor -} from '../../lib/styled/styleFunctions' import cc from 'classcat' import { TutorialsNavigatorTreeItem } from '../../lib/tutorials' - -const StyledNoteListItem = styled.div` - margin: 0; - border-left: 2px solid transparent; - ${uiTextColor} - &.active, - &:active, - &:focus, - &:hover { - ${secondaryBackgroundColor} - } - &.active { - border-left: 2px solid ${({ theme }) => theme.primaryColor}; - } - user-select: none; - ${borderBottom} - - transition: 200ms background-color; - - a { - text-decoration: none; - } - - .container { - padding: 8px; - } - - .title { - font-weight: bold; - font-size: 15px; - margin-bottom: 4px; - } -` +import { StyledNoteListItem } from '../NotePage/NoteList/NoteItem' type TutorialsNoteItemProps = { note: TutorialsNavigatorTreeItem diff --git a/src/components/Tutorials/TutorialsNoteList.tsx b/src/components/Tutorials/TutorialsNoteList.tsx index c7a92c7cee..623398cf57 100644 --- a/src/components/Tutorials/TutorialsNoteList.tsx +++ b/src/components/Tutorials/TutorialsNoteList.tsx @@ -1,26 +1,7 @@ import React, { useCallback, KeyboardEvent, useRef } from 'react' import { TutorialsNavigatorTreeItem } from '../../lib/tutorials' -import styled from '../../lib/styled' import TutorialsNoteItem from './TutorialsNoteItem' - -const StyledContainer = styled.div` - display: flex; - flex-direction: column; - overflow: hidden; - height: 100%; - outline: none; - & > ul { - flex: 1; - margin: 0; - padding: 0; - list-style: none; - overflow-y: auto; - - li.empty { - color: ${({ theme }) => theme.uiTextColor}; - } - } -` +import { StyledNoteListContainer } from '../NotePage/NoteList/NoteList' type TutorialsNoteListProps = { currentTree: TutorialsNavigatorTreeItem @@ -67,7 +48,7 @@ const TutorialsNoteList = ({ : parentTree.children.filter(child => child.type === 'note') return ( - +
    {notes.map(note => { const noteIsCurrentNote = @@ -86,7 +67,7 @@ const TutorialsNoteList = ({ })} {notes.length === 0 &&
  • No notes
  • }
-
+ ) } diff --git a/src/lib/router/index.tsx b/src/lib/router/index.tsx index 35b09ace34..418afe3e52 100644 --- a/src/lib/router/index.tsx +++ b/src/lib/router/index.tsx @@ -2,3 +2,4 @@ export * from './types' export * from './store' export * from './utils' export * from './Link' +export * from './redirect' diff --git a/src/lib/router/redirect.ts b/src/lib/router/redirect.ts new file mode 100644 index 0000000000..134f0dac0b --- /dev/null +++ b/src/lib/router/redirect.ts @@ -0,0 +1,21 @@ +import { useDb } from '../db' +import { useRouter } from './store' +import { useEffect } from 'react' +import { usePreferences } from '../preferences' + +export default function useRedirectHandler() { + const db = useDb() + const { preferences } = usePreferences() + const { push, pathname } = useRouter() + + useEffect(() => { + if ( + pathname === '/app' && + preferences['general.tutorials'] === 'display' && + (db.storageMap == null || Object.keys(db.storageMap).length === 0) + ) { + push('/app/tutorials/welcome-pack/guides/notes/note:storage-guide') + } + return + }, [pathname, db.initialized, preferences['general.displayTutorials']]) +} diff --git a/src/lib/router/store.ts b/src/lib/router/store.ts index e34f0eaa09..0408d745b7 100644 --- a/src/lib/router/store.ts +++ b/src/lib/router/store.ts @@ -22,15 +22,18 @@ function useRouteStore(): RouterStore { setLocation(location) history.pushState(null, document.title, urlStr) }, []) + const replace = useCallback((urlStr: string) => { const location = parseUrl(urlStr) setLocation(location) history.pushState(null, document.title, urlStr) }, []) + const go = useCallback((count: number) => { history.go(count) }, []) + const goBack = useCallback(() => go(-1), [go]) const goForward = useCallback(() => go(1), [go]) diff --git a/src/lib/router/utils.ts b/src/lib/router/utils.ts index 4855cef4aa..d881c01b09 100644 --- a/src/lib/router/utils.ts +++ b/src/lib/router/utils.ts @@ -77,6 +77,11 @@ export interface StorageTagsRouteParams extends BaseRouteParams { noteId?: string } +export interface TutorialsRouteParams extends BaseRouteParams { + name: 'tutorials.show' + path: string +} + export interface UnknownRouteparams extends BaseRouteParams { name: 'unknown' } @@ -89,6 +94,7 @@ export type AllRouteParams = | StorageTrashCanRouteParams | StorageTagsRouteParams | UnknownRouteparams + | TutorialsRouteParams export const useRouteParams = () => { const { pathname } = useRouter() @@ -104,6 +110,13 @@ export const useRouteParams = () => { } } + if (names[0] === 'tutorials') { + return { + name: 'tutorials.show', + path: pathname + } + } + if (names[0] !== 'storages' || names[1] == null) { return { name: 'unknown' @@ -187,3 +200,14 @@ export const usePathnameWithoutNoteId = () => { return '/app' }, [routeParams]) } + +export const currentTutorialPathname = () => { + const routeParams = useRouteParams() + return useMemo(() => { + switch (routeParams.name) { + case 'tutorials.show': + return routeParams.path + } + return '/app' + }, [routeParams]) +} From bf03bb917ab1ecc72310dad437348503b45dd725 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 18:28:18 +0900 Subject: [PATCH 09/13] change name of redirect --- src/components/Tutorials/TutorialsNavigator.tsx | 4 ++-- 1 file changed, 2 insertions(+), 2 deletions(-) diff --git a/src/components/Tutorials/TutorialsNavigator.tsx b/src/components/Tutorials/TutorialsNavigator.tsx index 5c0dfd3b6f..5b90255d0b 100644 --- a/src/components/Tutorials/TutorialsNavigator.tsx +++ b/src/components/Tutorials/TutorialsNavigator.tsx @@ -102,7 +102,7 @@ const TutorialsNavigator = ({ }: TutorialsNavigatorProps) => { .filter(node => node != null) as NavigatorNode[] }, [routeParams, tutorials, toggleSideNavOpenedItem]) - const redirectToTutorial = (node: NavigatorNode) => { + const redirectToTutorialNode = (node: NavigatorNode) => { push(node.href) } @@ -120,7 +120,7 @@ const TutorialsNavigator = ({ }: TutorialsNavigatorProps) => { depth={node.depth} iconPath={node.iconPath} active={node.active} - onClick={() => redirectToTutorial(node)} + onClick={() => redirectToTutorialNode(node)} onFoldButtonClick={() => toggleSideNavOpenedItem(node.id)} folded={ node.children == null || node.children.length === 0 From 47183564d4149a4dba81132b40ed3a6c2cb9b39b Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 18:43:05 +0900 Subject: [PATCH 10/13] remove useless href check --- src/components/Tutorials/TutorialsNavigator.tsx | 6 ++---- 1 file changed, 2 insertions(+), 4 deletions(-) diff --git a/src/components/Tutorials/TutorialsNavigator.tsx b/src/components/Tutorials/TutorialsNavigator.tsx index 5b90255d0b..285df4f5b6 100644 --- a/src/components/Tutorials/TutorialsNavigator.tsx +++ b/src/components/Tutorials/TutorialsNavigator.tsx @@ -48,10 +48,8 @@ const TutorialsNavigator = ({ }: TutorialsNavigatorProps) => { const nodeHref = `${parentNode != null ? parentNode.href : '/app'}/${ tree.slug }` - const folderIsActive = - currentHref == null - ? false - : currentHref.split('/notes/note:')[0] === nodeHref + + const folderIsActive = currentHref.split('/notes/note:')[0] === nodeHref const notesUnderCurrentNode = tree.children.filter( child => child.type === 'note' From 8ff36c574cf6b9cdb8a40c17f9a4b8f5e78c1fdd Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 18:44:15 +0900 Subject: [PATCH 11/13] use node id for map key --- src/components/Tutorials/TutorialsNavigator.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/Tutorials/TutorialsNavigator.tsx b/src/components/Tutorials/TutorialsNavigator.tsx index 285df4f5b6..982d1a065f 100644 --- a/src/components/Tutorials/TutorialsNavigator.tsx +++ b/src/components/Tutorials/TutorialsNavigator.tsx @@ -112,7 +112,7 @@ const TutorialsNavigator = ({ }: TutorialsNavigatorProps) => { return null } return ( - + Date: Mon, 9 Dec 2019 18:48:18 +0900 Subject: [PATCH 12/13] remove null children array on nav node --- src/components/Tutorials/TutorialsNavigator.tsx | 14 +++++--------- 1 file changed, 5 insertions(+), 9 deletions(-) diff --git a/src/components/Tutorials/TutorialsNavigator.tsx b/src/components/Tutorials/TutorialsNavigator.tsx index 982d1a065f..cf7143903b 100644 --- a/src/components/Tutorials/TutorialsNavigator.tsx +++ b/src/components/Tutorials/TutorialsNavigator.tsx @@ -20,7 +20,7 @@ interface NavigatorNode { opened: boolean name: string href: string - children?: NavigatorNode[] + children: NavigatorNode[] active?: boolean } interface TutorialsNavigatorProps {} @@ -72,7 +72,8 @@ const TutorialsNavigator = ({ }: TutorialsNavigatorProps) => { href: nodeHref, active: folderIsActive, depth: currentDepth, - opened: sideNavOpenedItemSet.has(nodeId) + opened: sideNavOpenedItemSet.has(nodeId), + children: [] } const childrenNodes = @@ -120,14 +121,9 @@ const TutorialsNavigator = ({ }: TutorialsNavigatorProps) => { active={node.active} onClick={() => redirectToTutorialNode(node)} onFoldButtonClick={() => toggleSideNavOpenedItem(node.id)} - folded={ - node.children == null || node.children.length === 0 - ? undefined - : !node.opened - } + folded={node.children.length === 0 ? undefined : !node.opened} /> - {node.children != null && - node.children.map(child => renderNode(child, node.opened))} + {node.children.map(child => renderNode(child, node.opened))} ) } From e7ee21c3a1ed77d4c63d1d27fa57f4af924af8c3 Mon Sep 17 00:00:00 2001 From: davy-c Date: Mon, 9 Dec 2019 19:15:33 +0900 Subject: [PATCH 13/13] fix redirect --- src/lib/db/store.ts | 2 +- src/lib/router/redirect.ts | 5 ++++- 2 files changed, 5 insertions(+), 2 deletions(-) diff --git a/src/lib/db/store.ts b/src/lib/db/store.ts index b4736721d5..6b141368dc 100644 --- a/src/lib/db/store.ts +++ b/src/lib/db/store.ts @@ -80,7 +80,6 @@ export function createDbStoreCreator( storageDataList.map(storageData => prepareStorage(storageData, adapter)) ) - setInitialized(true) setStorageMap( storages.reduce( (map, storage) => { @@ -90,6 +89,7 @@ export function createDbStoreCreator( {} as ObjectMap ) ) + setInitialized(true) }, []) const createStorage = useCallback(async (name: string) => { diff --git a/src/lib/router/redirect.ts b/src/lib/router/redirect.ts index 134f0dac0b..51e0828ff9 100644 --- a/src/lib/router/redirect.ts +++ b/src/lib/router/redirect.ts @@ -9,10 +9,13 @@ export default function useRedirectHandler() { const { push, pathname } = useRouter() useEffect(() => { + if (!db.initialized || db.storageMap == null) { + return + } if ( pathname === '/app' && preferences['general.tutorials'] === 'display' && - (db.storageMap == null || Object.keys(db.storageMap).length === 0) + Object.keys(db.storageMap).length === 0 ) { push('/app/tutorials/welcome-pack/guides/notes/note:storage-guide') }