From cfa3475c422d4668e5ea96812cfdf2378204420f Mon Sep 17 00:00:00 2001 From: Simon Leigh Date: Mon, 23 Dec 2019 16:14:21 +0900 Subject: [PATCH 1/9] note create, note delete, toggle view, focus search keybinds --- src/components/NotePage/NoteList/NoteList.tsx | 17 +++++++++ src/components/NotePage/NotePage.tsx | 36 ++++++++++++++++++- 2 files changed, 52 insertions(+), 1 deletion(-) diff --git a/src/components/NotePage/NoteList/NoteList.tsx b/src/components/NotePage/NoteList/NoteList.tsx index 4f9fafcbf1..570fca97f8 100644 --- a/src/components/NotePage/NoteList/NoteList.tsx +++ b/src/components/NotePage/NoteList/NoteList.tsx @@ -17,6 +17,10 @@ import { } from '../../../lib/styled/styleFunctions' import { IconEdit, IconLoupe, IconArrowSingleDown } from '../../icons' import { useTranslation } from 'react-i18next' +import { + useGlobalKeyDownHandler, + isWithGeneralCtrlKey +} from '../../../lib/keyboard' export const StyledNoteListContainer = styled.div` display: flex; @@ -157,6 +161,18 @@ const NoteList = ({ ) const listRef = useRef(null) + const searchRef = useRef(null) + + useGlobalKeyDownHandler(e => { + switch (e.key) { + case 'p': + if (isWithGeneralCtrlKey(e)) { + e.preventDefault() + e.stopPropagation() + searchRef.current!.focus() + } + } + }) const focusList = useCallback(() => { listRef.current!.focus() @@ -166,6 +182,7 @@ const NoteList = ({
{ } }, [filteredNotes, currentNoteIndex, router, currentPathnameWithoutNoteId]) - return ( + useGlobalKeyDownHandler(e => { + switch (e.key) { + case 'Backspace': + if (storageId != null && e.shiftKey && isWithGeneralCtrlKey(e)) { + db.trashNote(storageId, currentNote._id) + } + break + case 'Enter': + if (isWithGeneralCtrlKey(e)) { + createNote() + } + break + case 's': + if (isWithGeneralCtrlKey(e) && e.altKey) { + toggleViewMode('split') + } + break + case 'e': + if (isWithGeneralCtrlKey(e) && e.altKey) { + toggleViewMode('edit') + } + break + case 'p': + if (isWithGeneralCtrlKey(e) && e.altKey) { + toggleViewMode('preview') + } + break + } + }) + + return storageId != null ? ( Date: Thu, 26 Dec 2019 14:39:05 +0900 Subject: [PATCH 2/9] after merge --- src/components/NotePage/NotePage.tsx | 2 +- src/components/PreferencesModal/PreferencesModal.tsx | 1 + 2 files changed, 2 insertions(+), 1 deletion(-) diff --git a/src/components/NotePage/NotePage.tsx b/src/components/NotePage/NotePage.tsx index fed90a2147..6c9d6e44f2 100644 --- a/src/components/NotePage/NotePage.tsx +++ b/src/components/NotePage/NotePage.tsx @@ -279,7 +279,7 @@ export default () => { } }) - return storageId != null ? ( + return ( { + const { t } = useTranslation() const { closed, toggleClosed } = usePreferences() const [tab, setTab] = useState('general') const { t } = useTranslation() From 780bd64f01e88e0888916200150824dc128d41d4 Mon Sep 17 00:00:00 2001 From: davy-c Date: Thu, 26 Dec 2019 14:39:22 +0900 Subject: [PATCH 3/9] keybindings only for desktop app --- src/lib/keyboard.ts | 2 ++ 1 file changed, 2 insertions(+) diff --git a/src/lib/keyboard.ts b/src/lib/keyboard.ts index 66d14ab2e0..dab0c3c51c 100644 --- a/src/lib/keyboard.ts +++ b/src/lib/keyboard.ts @@ -1,10 +1,12 @@ import { useEffect } from 'react' import { osName } from './utils' +import isElectron from 'is-electron' export const useGlobalKeyDownHandler = ( handler: (event: KeyboardEvent) => void ) => { return useEffect(() => { + if (!isElectron()) return window.addEventListener('keydown', handler) return () => { window.removeEventListener('keydown', handler) From b42d6aa7f699f4a68cf5538ba375891ea070db24 Mon Sep 17 00:00:00 2001 From: davy-c Date: Thu, 26 Dec 2019 17:05:33 +0900 Subject: [PATCH 4/9] fixed app bindings --- package.json | 1 + src/components/NotePage/NoteList/NoteList.tsx | 64 ++++++++----------- src/components/NotePage/NotePage.tsx | 52 +++++++++------ .../PreferencesModal/PreferencesModal.tsx | 2 +- .../Tutorials/TutorialsNoteList.tsx | 38 +++++++---- src/lib/i18n/enUS.ts | 4 +- src/lib/i18n/ja.ts | 16 +++-- 7 files changed, 97 insertions(+), 80 deletions(-) diff --git a/package.json b/package.json index 732783c0ec..59523f85b9 100644 --- a/package.json +++ b/package.json @@ -10,6 +10,7 @@ }, "scripts": { "dev": "env-cmd cross-env NODE_ENV=development TS_NODE_PROJECT=\"tsconfig-webpack.json\" webpack-dev-server --mode development --open-page \"app\"", + "dev:app": "npm run build:electron && npm run start", "start": "electron app/index.js", "lint": "eslint src/* --ext .ts,.tsx", "format": "prettier --write \"src/**/*\"", diff --git a/src/components/NotePage/NoteList/NoteList.tsx b/src/components/NotePage/NoteList/NoteList.tsx index 570fca97f8..450459d8d7 100644 --- a/src/components/NotePage/NoteList/NoteList.tsx +++ b/src/components/NotePage/NoteList/NoteList.tsx @@ -1,11 +1,4 @@ -import React, { - useCallback, - KeyboardEvent, - useRef, - ChangeEventHandler, - useMemo, - useState -} from 'react' +import React, { useCallback, useRef, ChangeEventHandler } from 'react' import NoteItem from './NoteItem' import { PopulatedNoteDoc } from '../../../lib/db/types' import styled from '../../../lib/styled' @@ -21,6 +14,7 @@ import { useGlobalKeyDownHandler, isWithGeneralCtrlKey } from '../../../lib/keyboard' +import { NoteListSortOptions } from '../NotePage' export const StyledNoteListContainer = styled.div` display: flex; @@ -102,8 +96,6 @@ export const StyledNoteListContainer = styled.div` } ` -type SortProps = 'createdAt' | 'title' | 'updatedAt' - type NoteListProps = { currentStorageId?: string currentNoteId?: string @@ -115,6 +107,7 @@ type NoteListProps = { navigateUp: () => void basePathname: string lastCreatedNoteId: string + setSort: (option: NoteListSortOptions) => void } const NoteList = ({ @@ -127,7 +120,8 @@ const NoteList = ({ setSearchInput, navigateDown, navigateUp, - lastCreatedNoteId + lastCreatedNoteId, + setSort }: NoteListProps) => { const { t } = useTranslation() const updateSearchInput: ChangeEventHandler = useCallback( @@ -136,41 +130,33 @@ const NoteList = ({ }, [setSearchInput] ) - const [sort, setSort] = useState('updatedAt') - - const sortedNotes = useMemo(() => { - return notes.sort((first, second) => { - return sort === 'title' - ? first[sort].localeCompare(second[sort]) - : second[sort].localeCompare(first[sort]) - }) - }, [notes, sort]) - - const handleListKeyDown = useCallback( - (event: KeyboardEvent) => { - switch (event.key) { - case 'ArrowDown': - navigateDown() - break - case 'ArrowUp': - navigateUp() - break - } - }, - [navigateUp, navigateDown] - ) const listRef = useRef(null) const searchRef = useRef(null) useGlobalKeyDownHandler(e => { switch (e.key) { - case 'p': + case 's': + if (isWithGeneralCtrlKey(e) && !e.shiftKey) { + searchRef.current!.focus() + } + break + case 'j': if (isWithGeneralCtrlKey(e)) { e.preventDefault() e.stopPropagation() - searchRef.current!.focus() + navigateDown() + } + break + case 'k': + if (isWithGeneralCtrlKey(e)) { + e.preventDefault() + e.stopPropagation() + navigateUp() } + break + default: + break } }) @@ -200,15 +186,15 @@ const NoteList = ({
-
    - {sortedNotes.map(note => { +
      + {notes.map(note => { const noteIsCurrentNote = note._id === currentNoteId return (
    • diff --git a/src/components/NotePage/NotePage.tsx b/src/components/NotePage/NotePage.tsx index 6c9d6e44f2..1fb6e280ee 100644 --- a/src/components/NotePage/NotePage.tsx +++ b/src/components/NotePage/NotePage.tsx @@ -35,6 +35,8 @@ export type BreadCrumbs = { folderIsActive: boolean }[] +export type NoteListSortOptions = 'createdAt' | 'title' | 'updatedAt' + export default () => { const db = useDb() @@ -50,12 +52,12 @@ export default () => { return db.storageMap[storageId] }, [db.storageMap, storageId]) const router = useRouter() + const { t } = useTranslation() const [search, setSearchInput] = useState('') const currentPathnameWithoutNoteId = usePathnameWithoutNoteId() const { push } = useRouter() const [lastCreatedNoteId, setLastCreatedNoteId] = useState('') - - const { t } = useTranslation() + const [sort, setSort] = useState('updatedAt') useEffect(() => { setLastCreatedNoteId('') @@ -123,15 +125,22 @@ export default () => { }, [db.storageMap, currentStorage, routeParams]) const filteredNotes = useMemo(() => { - if (search.trim() === '') return notes - const regex = new RegExp(escapeRegExp(search), 'i') - return notes.filter( - note => - note.tags.join().match(regex) || - note.title.match(regex) || - note.content.match(regex) - ) - }, [search, notes]) + let filteredNotes = notes + if (search.trim() != '') { + const regex = new RegExp(escapeRegExp(search), 'i') + filteredNotes = notes.filter( + note => + note.tags.join().match(regex) || + note.title.match(regex) || + note.content.match(regex) + ) + } + return filteredNotes.sort((first, second) => { + return sort === 'title' + ? first[sort].localeCompare(second[sort]) + : second[sort].localeCompare(first[sort]) + }) + }, [search, notes, sort]) const currentNoteIndex = useMemo(() => { for (let i = 0; i < filteredNotes.length; i++) { @@ -143,7 +152,9 @@ export default () => { }, [filteredNotes, noteId]) const currentNote: PopulatedNoteDoc | undefined = useMemo(() => { - return filteredNotes[currentNoteIndex] + return filteredNotes[currentNoteIndex] != null + ? filteredNotes[currentNoteIndex] + : undefined }, [filteredNotes, currentNoteIndex]) const createNote = useCallback(async () => { @@ -252,27 +263,31 @@ export default () => { useGlobalKeyDownHandler(e => { switch (e.key) { case 'Backspace': - if (storageId != null && e.shiftKey && isWithGeneralCtrlKey(e)) { - db.trashNote(storageId, currentNote._id) + if (currentNote != null && isWithGeneralCtrlKey(e)) { + if (!currentNote.trashed) { + db.trashNote(currentNote.storageId, currentNote._id) + } else { + purgeNote(currentNote.storageId, currentNote._id) + } } break - case 'Enter': + case 'n': if (isWithGeneralCtrlKey(e)) { createNote() } break case 's': - if (isWithGeneralCtrlKey(e) && e.altKey) { + if (isWithGeneralCtrlKey(e) && e.shiftKey) { toggleViewMode('split') } break case 'e': - if (isWithGeneralCtrlKey(e) && e.altKey) { + if (isWithGeneralCtrlKey(e) && e.shiftKey) { toggleViewMode('edit') } break case 'p': - if (isWithGeneralCtrlKey(e) && e.altKey) { + if (isWithGeneralCtrlKey(e) && e.shiftKey) { toggleViewMode('preview') } break @@ -295,6 +310,7 @@ export default () => { navigateUp={navigateUp} currentNoteId={currentNote ? currentNote._id : undefined} lastCreatedNoteId={lastCreatedNoteId} + setSort={setSort} /> } right={ diff --git a/src/components/PreferencesModal/PreferencesModal.tsx b/src/components/PreferencesModal/PreferencesModal.tsx index 9b785e44ed..cd2ddb48ac 100644 --- a/src/components/PreferencesModal/PreferencesModal.tsx +++ b/src/components/PreferencesModal/PreferencesModal.tsx @@ -59,7 +59,7 @@ const PreferencesModal = () => { const { t } = useTranslation() const { closed, toggleClosed } = usePreferences() const [tab, setTab] = useState('general') - const { t } = useTranslation() + const keydownHandler = useMemo(() => { return (event: KeyboardEvent) => { if (!closed && event.key === 'Escape') { diff --git a/src/components/Tutorials/TutorialsNoteList.tsx b/src/components/Tutorials/TutorialsNoteList.tsx index b552f0a632..e11e49dcbc 100644 --- a/src/components/Tutorials/TutorialsNoteList.tsx +++ b/src/components/Tutorials/TutorialsNoteList.tsx @@ -1,8 +1,12 @@ -import React, { useCallback, KeyboardEvent, useRef } from 'react' +import React, { useCallback, useRef } from 'react' import { TutorialsNavigatorTreeItem } from '../../lib/tutorials' import TutorialsNoteItem from './TutorialsNoteItem' import { StyledNoteListContainer } from '../NotePage/NoteList/NoteList' import { useTranslation } from 'react-i18next' +import { + isWithGeneralCtrlKey, + useGlobalKeyDownHandler +} from '../../lib/keyboard' type TutorialsNoteListProps = { currentTree: TutorialsNavigatorTreeItem @@ -21,19 +25,27 @@ const TutorialsNoteList = ({ basePathname, selectedNote }: TutorialsNoteListProps) => { - const handleListKeyDown = useCallback( - (event: KeyboardEvent) => { - switch (event.key) { - case 'ArrowDown': + useGlobalKeyDownHandler(e => { + console.log(e.key) + switch (e.key) { + case 'j': + if (isWithGeneralCtrlKey(e)) { + e.preventDefault() + e.stopPropagation() navigateDown() - break - case 'ArrowUp': + } + break + case 'k': + if (isWithGeneralCtrlKey(e)) { + e.preventDefault() + e.stopPropagation() navigateUp() - break - } - }, - [navigateUp, navigateDown] - ) + } + break + default: + break + } + }) const listRef = useRef(null) @@ -52,7 +64,7 @@ const TutorialsNoteList = ({ return ( -
        +
          {notes.map(note => { const noteIsCurrentNote = selectedNote != null && diff --git a/src/lib/i18n/enUS.ts b/src/lib/i18n/enUS.ts index c53e200bff..7bb673b458 100644 --- a/src/lib/i18n/enUS.ts +++ b/src/lib/i18n/enUS.ts @@ -58,8 +58,8 @@ export default { 'note.nothingMessage': 'No notes could be found.', 'note.noTitle': 'No title', 'note.date': 'ago', - 'note.createKeyMac': 'Mac: Command(⌘) + Enter', - 'note.createKeyWinLin': 'Windows/Linux: Ctrl + Enter', + 'note.createKeyMac': 'Mac: Command(⌘) + n', + 'note.createKeyWinLin': 'Windows/Linux: Ctrl + n', 'note.createkeymessage1': 'to create a new note', 'note.createkeymessage2': 'Select a storage', 'note.createkeymessage3': 'to create a new note', diff --git a/src/lib/i18n/ja.ts b/src/lib/i18n/ja.ts index 381b7e05b7..70b8ae500a 100644 --- a/src/lib/i18n/ja.ts +++ b/src/lib/i18n/ja.ts @@ -58,8 +58,8 @@ export default { 'note.nothingMessage': 'ノートが見つかりません', 'note.noTitle': '無題', 'note.date': '前', - 'note.createKeyMac': 'Mac: Command(⌘) + Enter', - 'note.createKeyWinLin': 'Windows/Linux: Ctrl + Enter', + 'note.createKeyMac': 'Mac: Command(⌘) + n', + 'note.createKeyWinLin': 'Windows/Linux: Ctrl + n', 'note.createkeymessage1': 'ノート作成', 'note.createkeymessage2': 'ストレージを選択しましょう', 'note.createkeymessage3': 'ノート作成', @@ -70,8 +70,7 @@ export default { //About 'about.about': '概要', - 'about.boostnoteDescription': - 'オープンソースの開発者のためのノートアプリ', + 'about.boostnoteDescription': 'オープンソースの開発者のためのノートアプリ', 'about.website': 'ウェブサイト', 'about.boostWiki': 'チームのためのBoost Note', 'about.platform': 'クロスプラットフォーム', @@ -86,7 +85,8 @@ export default { //Billing 'billing.billing': '課金', - 'billing.message': 'プランをアップグレードするためにはサインインしてください', + 'billing.message': + 'プランをアップグレードするためにはサインインしてください', 'billing.basic': '無料', 'billing.current': '今のプラン', 'billing.premium': 'プレミアム', @@ -125,7 +125,8 @@ export default { 'Boost Noteは、改善のためにエンドポイント数等のみデータを取得しています。あなたのノートの中身を見たり、データを取得することは絶対にありません。Boost Noteはオープンソースであるため、どのように動いているかGitHubから確認することができます。', 'preferences.analyticsDescription2': 'データ取得をオンにするかオフにするか、選ぶことができます。', - 'preferences.analyticsLabel': 'Boost Noteの改善のために、データ取得をオンにする', + 'preferences.analyticsLabel': + 'Boost Noteの改善のために、データ取得をオンにする', 'preferences.displayTutorialsLabel': 'チュートリアルを表示', // Preferences EditorTab @@ -148,7 +149,8 @@ export default { // Preferences ImportTab 'preferences.import': 'データ移行', 'preferences.description': '旧Boost Noteアプリからデータを移行する', - 'preferences.importFlow1': '1. あなたのPCで、Boost Noteのファイルが入っているフォルダを選択します', + 'preferences.importFlow1': + '1. あなたのPCで、Boost Noteのファイルが入っているフォルダを選択します', 'preferences.importFlow2': '2. .csonファイルを選択し、下のフォームにドラッグ&ドロップしてください', 'preferences.importFlow3': From 8b129acab70f3a07bc4f89f370ddcc1d96e3d5c3 Mon Sep 17 00:00:00 2001 From: davy-c Date: Thu, 26 Dec 2019 17:31:29 +0900 Subject: [PATCH 5/9] remove key log --- src/components/Tutorials/TutorialsNoteList.tsx | 1 - 1 file changed, 1 deletion(-) diff --git a/src/components/Tutorials/TutorialsNoteList.tsx b/src/components/Tutorials/TutorialsNoteList.tsx index e11e49dcbc..a3c1ef653c 100644 --- a/src/components/Tutorials/TutorialsNoteList.tsx +++ b/src/components/Tutorials/TutorialsNoteList.tsx @@ -26,7 +26,6 @@ const TutorialsNoteList = ({ selectedNote }: TutorialsNoteListProps) => { useGlobalKeyDownHandler(e => { - console.log(e.key) switch (e.key) { case 'j': if (isWithGeneralCtrlKey(e)) { From a65517f1aeb7f3c2c47f693cb0a4eb30cf52d4f6 Mon Sep 17 00:00:00 2001 From: davy-c Date: Thu, 26 Dec 2019 18:10:44 +0900 Subject: [PATCH 6/9] toggle view to ctrl T --- src/components/NotePage/NotePage.tsx | 24 ++++++++++++------------ 1 file changed, 12 insertions(+), 12 deletions(-) diff --git a/src/components/NotePage/NotePage.tsx b/src/components/NotePage/NotePage.tsx index 1fb6e280ee..fb4321eacf 100644 --- a/src/components/NotePage/NotePage.tsx +++ b/src/components/NotePage/NotePage.tsx @@ -276,19 +276,19 @@ export default () => { createNote() } break - case 's': + case 't': if (isWithGeneralCtrlKey(e) && e.shiftKey) { - toggleViewMode('split') - } - break - case 'e': - if (isWithGeneralCtrlKey(e) && e.shiftKey) { - toggleViewMode('edit') - } - break - case 'p': - if (isWithGeneralCtrlKey(e) && e.shiftKey) { - toggleViewMode('preview') + switch (generalStatus['noteViewMode']) { + case 'edit': + toggleViewMode('split') + break + case 'split': + toggleViewMode('preview') + break + default: + toggleViewMode('edit') + break + } } break } From 56ec9526a8f75f1ffa7a779e22f552b3b2686e51 Mon Sep 17 00:00:00 2001 From: Simon Leigh Date: Thu, 26 Dec 2019 18:38:44 +0900 Subject: [PATCH 7/9] fixed t -> T for use with shift with e.key --- src/components/NotePage/NotePage.tsx | 2 +- 1 file changed, 1 insertion(+), 1 deletion(-) diff --git a/src/components/NotePage/NotePage.tsx b/src/components/NotePage/NotePage.tsx index fb4321eacf..36cde874d3 100644 --- a/src/components/NotePage/NotePage.tsx +++ b/src/components/NotePage/NotePage.tsx @@ -276,7 +276,7 @@ export default () => { createNote() } break - case 't': + case 'T': if (isWithGeneralCtrlKey(e) && e.shiftKey) { switch (generalStatus['noteViewMode']) { case 'edit': From 72c072a5501ba0719a3260eb62111d86224b0f16 Mon Sep 17 00:00:00 2001 From: davy-c Date: Thu, 26 Dec 2019 18:50:34 +0900 Subject: [PATCH 8/9] mac fix --- src/components/NotePage/NotePage.tsx | 1 + 1 file changed, 1 insertion(+) diff --git a/src/components/NotePage/NotePage.tsx b/src/components/NotePage/NotePage.tsx index 36cde874d3..5abfdaf07f 100644 --- a/src/components/NotePage/NotePage.tsx +++ b/src/components/NotePage/NotePage.tsx @@ -277,6 +277,7 @@ export default () => { } break case 'T': + case 't': if (isWithGeneralCtrlKey(e) && e.shiftKey) { switch (generalStatus['noteViewMode']) { case 'edit': From 7155eadea9899beca377a3aa78e129f07a3cd5c5 Mon Sep 17 00:00:00 2001 From: davy-c Date: Fri, 27 Dec 2019 09:07:54 +0900 Subject: [PATCH 9/9] fix return pretty --- src/lib/keyboard.ts | 5 ++++- 1 file changed, 4 insertions(+), 1 deletion(-) diff --git a/src/lib/keyboard.ts b/src/lib/keyboard.ts index dab0c3c51c..343f40c7dc 100644 --- a/src/lib/keyboard.ts +++ b/src/lib/keyboard.ts @@ -6,7 +6,10 @@ export const useGlobalKeyDownHandler = ( handler: (event: KeyboardEvent) => void ) => { return useEffect(() => { - if (!isElectron()) return + if (!isElectron()) { + return + } + window.addEventListener('keydown', handler) return () => { window.removeEventListener('keydown', handler)