diff --git a/.github/PULL_REQUEST_TEMPLATE.md b/.github/PULL_REQUEST_TEMPLATE.md index 2934627a1..bf89545e4 100644 --- a/.github/PULL_REQUEST_TEMPLATE.md +++ b/.github/PULL_REQUEST_TEMPLATE.md @@ -1,12 +1,16 @@ ### Fix -***(Required)*** Add a concise description of what you fixed. If this is related to an issue, add a link to it. If applicable, add screenshots, animations, or videos to help illustrate the fix. + +**_(Required)_** Add a concise description of what you fixed. If this is related to an issue, add a link to it. If applicable, add screenshots, animations, or videos to help illustrate the fix. ### Test -***(Required)*** List the steps to test the behavior. For example: + +**_(Required)_** List the steps to test the behavior. For example: + > 1. Go to... > 2. Tap on... > 3. See error... ### Release -***(Required)*** Add a concise statement to `RELEASE-NOTES.txt` if the changes should be included in release notes. Include details about updating the notes in this section. For example: -`RELEASE-NOTES.txt` was updated with: + +**_(Required)_** Add a concise statement to `RELEASE-NOTES.md` if the changes should be included in release notes. Include details about updating the notes in this section. For example: +`RELEASE-NOTES.md` was updated with: diff --git a/RELEASE-NOTES.txt b/RELEASE-NOTES.md similarity index 89% rename from RELEASE-NOTES.txt rename to RELEASE-NOTES.md index adfecb172..2c0d41b29 100644 --- a/RELEASE-NOTES.txt +++ b/RELEASE-NOTES.md @@ -1,5 +1,34 @@ # Changelog +## [v1.16.0] + +### Enhancements + +- Added keyboard shortcuts help dialog [#1983](https://github.com/Automattic/simplenote-electron/pull/1983) +- Improve search performance [#1941](https://github.com/Automattic/simplenote-electron/pull/1941), [#1966](https://github.com/Automattic/simplenote-electron/pull/1966), [#1979](https://github.com/Automattic/simplenote-electron/pull/1979), [#1982](https://github.com/Automattic/simplenote-electron/pull/1982) + +### Fixes + +- Highlight all search matches in Markdown previews and in note list [#1987](https://github.com/Automattic/simplenote-electron/pull/1987) +- Fix for blurry fonts on LCD screens [#2003](https://github.com/Automattic/simplenote-electron/pull/2003) + +### Other Changes + +- Renamed RELEASE-NOTES to fix integrations broken by #1576 [#2018](https://github.com/Automattic/simplenote-electron/pull/2018) +- Extensive refactoring to move more code into Redux state: + [#1920](https://github.com/Automattic/simplenote-electron/pull/1920) (tag selection), + [#1928](https://github.com/Automattic/simplenote-electron/pull/1928) (system tag), + [#1971](https://github.com/Automattic/simplenote-electron/pull/1971) (select trash and show all notes), + [#1981](https://github.com/Automattic/simplenote-electron/pull/1981) (dialog renderer), + [#1989](https://github.com/Automattic/simplenote-electron/pull/1989) (previous index), + [#1991](https://github.com/Automattic/simplenote-electron/pull/1991) (tagsLoaded), + [#1995](https://github.com/Automattic/simplenote-electron/pull/1995) (compositeNoteList), + [#1996](https://github.com/Automattic/simplenote-electron/pull/1996) (tag suggestions), + [#1999](https://github.com/Automattic/simplenote-electron/pull/1999) (load tags), + [#2004](https://github.com/Automattic/simplenote-electron/pull/2004) (isElectron and isMac utils), +- Replace custom height cache with one provided by react-virtualized [#1829](https://github.com/Automattic/simplenote-electron/pull/1829) +- Dependencies updated [#1951](https://github.com/Automattic/simplenote-electron/pull/1951), [#1993](https://github.com/Automattic/simplenote-electron/pull/1993) + ## [v1.15.1] ### Other Changes @@ -9,6 +38,7 @@ ## [v1.15.0] ### Enhancements + - Stop erasing the copy buffer if copying empty editor selections [#1847](https://github.com/Automattic/simplenote-electron/pull/1847) - Allow for un-selecting a tag by tab or right arrow [#1853](https://github.com/Automattic/simplenote-electron/pull/1853) @qualitymanifest @@ -50,11 +80,13 @@ ## [v1.14.0] ### Enhancements + - Keep note open when transitioning to small screen in focus mode [#1763](https://github.com/Automattic/simplenote-electron/pull/1763) - Added GenericName (description) field for app on Linux [#1761](https://github.com/Automattic/simplenote-electron/pull/1761) - Allow width attribute on img tags [#1833](https://github.com/Automattic/simplenote-electron/pull/1833) ### Fixes + - Makes settings scrollable on shorter smaller view ports [#1767](https://github.com/Automattic/simplenote-electron/pull/1767) - After selecting a revision to restore ensure that choice is sync'd [#1774](https://github.com/Automattic/simplenote-electron/pull/1774) - Added indication that publish url has been copied [#1743](https://github.com/Automattic/simplenote-electron/pull/1743) @@ -104,9 +136,9 @@ ### Enhancements - - Updated menu icon to use the new icon set [#1694](https://github.com/Automattic/simplenote-electron/pull/1694) - - Added script to deploy web app [#1723](https://github.com/Automattic/simplenote-electron/pull/1723) - - Prioritize search results where title matches query [#1705](https://github.com/Automattic/simplenote-electron/pull/1705) +- Updated menu icon to use the new icon set [#1694](https://github.com/Automattic/simplenote-electron/pull/1694) +- Added script to deploy web app [#1723](https://github.com/Automattic/simplenote-electron/pull/1723) +- Prioritize search results where title matches query [#1705](https://github.com/Automattic/simplenote-electron/pull/1705) ### Fixes @@ -126,7 +158,7 @@ ### Fixes - - Fixed bug that only shows the first line of text in note list preview [#1647](https://github.com/Automattic/simplenote-electron/pull/1647) +- Fixed bug that only shows the first line of text in note list preview [#1647](https://github.com/Automattic/simplenote-electron/pull/1647) ### Other Changes @@ -134,26 +166,26 @@ ### Enhancements - - Add ability to select system as a theme option and make it the default - - Added support for the unicode bullet • in list items - - Display a notice that notes are loading when notes are loading - - In dev mode open Chrome Dev Tools in a separate window +- Add ability to select system as a theme option and make it the default +- Added support for the unicode bullet • in list items +- Display a notice that notes are loading when notes are loading +- In dev mode open Chrome Dev Tools in a separate window ### Fixes - - Rework WordPress.com signin to prevent infinite looping and login failures [#1627](https://github.com/Automattic/simplenote-electron/pull/1627) - - Update link to release-notes in updater config: CHANGELOG -> RELEASE_NOTES - - Stop showing that there are no notes when initially loading notes from the server. [#1680](https://github.com/Automattic/simplenote-electron/pull/1680) +- Rework WordPress.com signin to prevent infinite looping and login failures [#1627](https://github.com/Automattic/simplenote-electron/pull/1627) +- Update link to release-notes in updater config: CHANGELOG -> RELEASE_NOTES +- Stop showing that there are no notes when initially loading notes from the server. [#1680](https://github.com/Automattic/simplenote-electron/pull/1680) - ### Other changes +### Other changes - - Updated dependencies +- Updated dependencies ## [v1.9.1] ### Fixes - - Prevent ulimited duplication of changes after signing out and signing in [#1664](https://github.com/Automattic/simplenote-electron/pull/1664) +- Prevent ulimited duplication of changes after signing out and signing in [#1664](https://github.com/Automattic/simplenote-electron/pull/1664) ## [v1.9.0] @@ -161,8 +193,8 @@ - Open new note automatically upon creation [1582](https://github.com/Automattic/simplenote-electron/pull/1582) - Updated colors to use Color Studio, the color palette for Automattic products - - [#1565](https://github.com/Automattic/simplenote-electron/pull/1565) - - [#1612](https://github.com/Automattic/simplenote-electron/pull/1612) + - [#1565](https://github.com/Automattic/simplenote-electron/pull/1565) + - [#1612](https://github.com/Automattic/simplenote-electron/pull/1612) ### Fixes @@ -170,8 +202,8 @@ - Fixes vertical spacing with nested markdown lists - Fixes sort order on revision slider when the timestamps don't match the change sequence [#1605](https://github.com/Automattic/simplenote-electron/pull/1605) - Prevents note corruption when receiving remote updates when local updates are pending - - [#1598](https://github.com/Automattic/simplenote-electron/pull/1598) - - [#1599](https://github.com/Automattic/simplenote-electron/pull/1599) + - [#1598](https://github.com/Automattic/simplenote-electron/pull/1598) + - [#1599](https://github.com/Automattic/simplenote-electron/pull/1599) ### Other changes diff --git a/desktop/config-updater.json b/desktop/config-updater.json index 1e8b2420d..238ddb4bd 100644 --- a/desktop/config-updater.json +++ b/desktop/config-updater.json @@ -1,7 +1,7 @@ { "updater": { "downloadUrl": "https://github.com/Automattic/simplenote-electron/releases/latest", - "changelogUrl": "https://github.com/Automattic/simplenote-electron/blob/master/RELEASE-NOTES.txt", + "changelogUrl": "https://github.com/Automattic/simplenote-electron/blob/master/RELEASE-NOTES.md", "apiUrl": "https://api.github.com/repos/automattic/simplenote-electron/releases/latest", "delay": 2000, "interval": 600000 diff --git a/desktop/menus/view-menu.js b/desktop/menus/view-menu.js index 420f48956..674f5ba8d 100644 --- a/desktop/menus/view-menu.js +++ b/desktop/menus/view-menu.js @@ -137,7 +137,6 @@ const buildViewMenu = (settings) => { }, { label: 'Focus Mode', - accelerator: 'CommandOrControl+Shift+F', type: 'checkbox', checked: settings.focusModeEnabled, click: appCommandSender({ action: 'toggleFocusMode' }), diff --git a/lib/app-layout/index.tsx b/lib/app-layout/index.tsx index 289e2c951..0150a57a9 100644 --- a/lib/app-layout/index.tsx +++ b/lib/app-layout/index.tsx @@ -35,6 +35,7 @@ type OwnProps = { type StateProps = { isNoteOpen: boolean; keyboardShortcutsAreOpen: boolean; + showNoteList: boolean; }; type DispatchProps = { @@ -67,11 +68,15 @@ export class AppLayout extends Component { keyboardShortcutsAreOpen ? hideKeyboardShortcuts() : showKeyboardShortcuts(); + + event.stopPropagation(); + event.preventDefault(); } }; render = () => { const { + showNoteList, isFocusMode = false, isNavigationOpen, isNoteInfoOpen, @@ -89,6 +94,8 @@ export class AppLayout extends Component { 'is-showing-note-info': isNoteInfoOpen, }); + const editorVisible = !(showNoteList && isSmallScreen); + const placeholder = (
@@ -104,19 +111,21 @@ export class AppLayout extends Component {
-
- - } - /> - -
+ {editorVisible && ( +
+ + } + /> + +
+ )} ); @@ -128,6 +137,7 @@ const mapStateToProps: S.MapState = ({ }) => ({ keyboardShortcutsAreOpen: dialogs.includes('KEYBINDINGS'), isNoteOpen: !showNoteList, + showNoteList, }); const mapDispatchToProps: S.MapDispatch = { diff --git a/lib/app.tsx b/lib/app.tsx index 29dba6ed6..31b0ca5c6 100644 --- a/lib/app.tsx +++ b/lib/app.tsx @@ -238,7 +238,7 @@ export const App = connect( return false; } - if (cmdOrCtrl && shiftKey && 'KeyF' === code) { + if (cmdOrCtrl && !shiftKey && 'KeyF' === code) { this.props.focusSearchField(); event.stopPropagation(); @@ -246,6 +246,14 @@ export const App = connect( return false; } + if (cmdOrCtrl && shiftKey && 'KeyF' === code) { + this.props.toggleFocusMode(); + + event.stopPropagation(); + event.preventDefault(); + return false; + } + if (cmdOrCtrl && shiftKey && 'KeyN' === code) { this.props.actions.newNote({ noteBucket: this.props.noteBucket, @@ -257,11 +265,7 @@ export const App = connect( return false; } - if ( - this.props.ui.note && - cmdOrCtrl && - ('Delete' === code || 'Backspace' === code) - ) { + if (this.props.ui.note && cmdOrCtrl && 'Delete' === code) { this.props.actions.trashNote({ noteBucket: this.props.noteBucket, note: this.props.ui.note, @@ -275,6 +279,13 @@ export const App = connect( return false; } + // prevent default browser behavior for search + // will bubble up from note-detail + if (cmdOrCtrl && 'KeyG' === code) { + event.stopPropagation(); + event.preventDefault(); + } + return true; }; diff --git a/lib/dialogs/keybindings/index.tsx b/lib/dialogs/keybindings/index.tsx index 87db17ec9..7b6a447b0 100644 --- a/lib/dialogs/keybindings/index.tsx +++ b/lib/dialogs/keybindings/index.tsx @@ -40,7 +40,6 @@ const Keys = ({ export class AboutDialog extends Component { render() { const { closeDialog } = this.props; - const CmdOrCtrl = isMac ? 'Cmd' : 'Ctrl'; return ( @@ -55,11 +54,21 @@ export class AboutDialog extends Component {
  • - Focus search field + Toggle focus mode
  • - Search within note + Focus search field +
  • +
  • + + Jump to next match in note + +
  • +
  • + + Jump to previous match in note +
  • {isElectron && (
  • @@ -99,11 +108,13 @@ export class AboutDialog extends Component {
  • - Select previous note + Open note above current one
  • - Select next note + + Open note below current one +
  • @@ -112,9 +123,7 @@ export class AboutDialog extends Component {
  • - Show note list -
    - (on narrow screens) + Toggle note list (on narrow screens)
  • @@ -127,7 +136,15 @@ export class AboutDialog extends Component { Create new note
  • - Trash note + + Trash note +
  • {isElectron && (
  • diff --git a/lib/dialogs/keybindings/style.scss b/lib/dialogs/keybindings/style.scss index ab74e1531..90fd6da32 100644 --- a/lib/dialogs/keybindings/style.scss +++ b/lib/dialogs/keybindings/style.scss @@ -1,15 +1,7 @@ .keybindings { .dialog { - max-width: 1120px; + max-width: 500px; margin: auto; - - @media only screen and (max-width: 1150px) { - max-width: 760px; - } - - @media only screen and (max-width: 780px) { - max-width: 480px; - } } .dialog-content { @@ -51,8 +43,9 @@ } .keybindings__key-list { - min-width: 80px; + min-width: 180px; display: inline-block; + text-align: right; } .keybindings__key-description { @@ -65,11 +58,6 @@ max-height: 480px; overflow-y: scroll; - width: 100%; - display: flex; - flex-direction: row; - flex-wrap: wrap; - section { margin-right: 2em; diff --git a/lib/note-content-editor.tsx b/lib/note-content-editor.tsx index c599d5a41..4aef4902d 100644 --- a/lib/note-content-editor.tsx +++ b/lib/note-content-editor.tsx @@ -39,6 +39,7 @@ const isElectron = (() => { })(); type StateProps = { + editingEnabled: boolean; searchQuery: string; }; @@ -373,7 +374,9 @@ class NoteContentEditor extends Component { } } -const mapStateToProps: S.MapState = ({ ui: { searchQuery } }) => ({ +const mapStateToProps: S.MapState = ({ + ui: { searchQuery, showNoteList }, +}) => ({ searchQuery, }); diff --git a/lib/note-detail/index.tsx b/lib/note-detail/index.tsx index 29c989c81..833231da4 100644 --- a/lib/note-detail/index.tsx +++ b/lib/note-detail/index.tsx @@ -63,7 +63,7 @@ export class NoteDetail extends Component { // Ensures note gets saved if user abruptly quits the app window.addEventListener('beforeunload', this.queueNoteSync.flush); - window.addEventListener('keydown', this.handlePreviewKeydown, false); + window.addEventListener('keydown', this.handlePreviewKeydown, true); if (previewingMarkdown) { this.updateMarkdown(); @@ -104,7 +104,7 @@ export class NoteDetail extends Component { componentWillUnmount() { window.removeEventListener('beforeunload', this.queueNoteSync.flush); document.removeEventListener('copy', this.copyRenderedNote, false); - window.removeEventListener('keydown', this.handlePreviewKeydown, false); + window.removeEventListener('keydown', this.handlePreviewKeydown, true); } copyRenderedNote = (event) => { @@ -209,6 +209,9 @@ export class NoteDetail extends Component { cmdOrCtrl && code === 'KeyG' ) { + event.stopPropagation(); + event.preventDefault(); + const matches = this.noteDetail.current.querySelectorAll( 'span.search-match' ); diff --git a/lib/note-editor/index.tsx b/lib/note-editor/index.tsx index 061b55221..38f312d27 100644 --- a/lib/note-editor/index.tsx +++ b/lib/note-editor/index.tsx @@ -6,21 +6,26 @@ import { property } from 'lodash'; import NoteDetail from '../note-detail'; import { toggleEditMode } from '../state/ui/actions'; -import { closeNote, markdownNote } from '../state/ui/actions'; +import { markdownNote, toggleNoteList } from '../state/ui/actions'; import * as S from '../state'; import * as T from '../types'; +type OwnProps = { + isSmallScreen: boolean; +}; + type StateProps = { note: T.NoteEntity | null; }; type DispatchProps = { toggleMarkdown: (note: T.NoteEntity, enableMarkdown: boolean) => any; + toggleNoteList: () => any; toggleEditMode: () => any; }; -type Props = DispatchProps & StateProps; +type Props = OwnProps & DispatchProps & StateProps; export class NoteEditor extends Component { static displayName = 'NoteEditor'; @@ -28,7 +33,6 @@ export class NoteEditor extends Component { static propTypes = { allTags: PropTypes.array.isRequired, isEditorActive: PropTypes.bool.isRequired, - isSmallScreen: PropTypes.bool.isRequired, noteBucket: PropTypes.object.isRequired, fontSize: PropTypes.number, onUpdateContent: PropTypes.func.isRequired, @@ -84,14 +88,6 @@ export class NoteEditor extends Component { return false; } - // open note list - if (this.props.isSmallScreen && cmdOrCtrl && shiftKey && 'KeyL' === code) { - this.props.closeNote(); - event.stopPropagation(); - event.preventDefault(); - return false; - } - // toggle between tag editor and note editor if ( !shiftKey && @@ -101,13 +97,13 @@ export class NoteEditor extends Component { ) { // prefer focusing the edit field first if (!this.editFieldHasFocus()) { - this.focusNoteEditor && this.focusNoteEditor(); + this.focusNoteEditor?.(); event.stopPropagation(); event.preventDefault(); return false; - } else if (!this.tagFieldHasFocus()) { - this.focusTagField && this.focusTagField(); + } else { + this.focusTagField?.(); event.stopPropagation(); event.preventDefault(); @@ -184,7 +180,7 @@ const mapStateToProps: S.MapState = ({ }); const mapDispatchToProps: S.MapDispatch = (dispatch) => ({ - closeNote: () => dispatch(closeNote()), + toggleNoteList: () => dispatch(toggleNoteList()), toggleMarkdown: (note, enableMarkdown) => dispatch(markdownNote(note, enableMarkdown)), toggleEditMode: () => dispatch(toggleEditMode()), diff --git a/lib/note-list/index.tsx b/lib/note-list/index.tsx index 6db46c16d..335394354 100644 --- a/lib/note-list/index.tsx +++ b/lib/note-list/index.tsx @@ -40,6 +40,7 @@ type StateProps = { selectedNote: T.NoteEntity | null; selectedNoteContent: string; selectedNotePreview: { title: string; preview: string }; + showNoteList: boolean; showTrash: boolean; tagResultsFound: number; }; @@ -49,6 +50,8 @@ type DispatchProps = { onEmptyTrash: () => any; onSelectNote: (note: T.NoteEntity | null) => any; onPinNote: (note: T.NoteEntity, shouldPin: boolean) => any; + openNote: (note: T.NoteEntity) => any; + toggleNoteList: () => any; }; type Props = Readonly; @@ -74,10 +77,9 @@ const heightCache = new CellMeasurerCache({ * @param notes list of filtered notes * @param searchQuery search searchQuery * @param noteDisplay list view style: comfy, condensed, expanded + * @param openNote used to select a note and open it in the editor * @param selectedNoteId id of currently selected note - * @param onSelectNote used to change the current note selection * @param onPinNote used to pin a note to the top of the list - * @param isSmallScreen whether we're in a narrow view * @returns does the actual rendering for the List */ const renderNote = ( @@ -86,16 +88,14 @@ const renderNote = ( searchQuery, noteDisplay, highlightedIndex, - onSelectNote, onPinNote, - isSmallScreen, + openNote, }: { searchQuery: string; noteDisplay: T.ListDisplayMode; highlightedIndex: number; - onSelectNote: DispatchProps['onSelectNote']; onPinNote: DispatchProps['onPinNote']; - isSmallScreen: boolean; + openNote: DispatchProps['openNote']; } ): ListRowRenderer => ({ index, key, parent, style }) => { const note = notes[index]; @@ -130,7 +130,7 @@ const renderNote = ( const isPinned = note.data.systemTags.includes('pinned'); const isPublished = !!note.data.publishURL; const classes = classNames('note-list-item', { - 'note-list-item-selected': !isSmallScreen && highlightedIndex === index, + 'note-list-item-selected': highlightedIndex === index, 'note-list-item-pinned': isPinned, 'published-note': isPublished, }); @@ -138,8 +138,6 @@ const renderNote = ( const terms = getTerms(searchQuery).map(makeFilterDecorator); const decorators = [checkboxDecorator, ...terms]; - const selectNote = () => onSelectNote(note); - return ( openNote(note)} >
    {decorateWith(decorators, title)} @@ -223,6 +221,7 @@ export class NoteList extends Component { openedTag, selectedNote, selectedNoteContent, + showNoteList, showTrash, tagResultsFound, } = nextProps; @@ -232,7 +231,8 @@ export class NoteList extends Component { noteDisplay !== this.props.noteDisplay || notes !== this.props.notes || tagResultsFound !== this.props.tagResultsFound || - selectedNoteContent !== this.props.selectedNoteContent + selectedNoteContent !== this.props.selectedNoteContent || + showNoteList !== this.props.showNoteList ) { heightCache.clearAll(); } @@ -249,6 +249,7 @@ export class NoteList extends Component { } if (notes.length === 0 && selectedNote) { + // unselect active note if it doesn't match search this.props.closeNote(); this.setState({ selectedIndex: null }); } @@ -288,7 +289,8 @@ export class NoteList extends Component { prevProps.noteDisplay !== this.props.noteDisplay || prevProps.notes !== this.props.notes || prevProps.tagResultsFound !== this.props.tagResultsFound || - prevProps.selectedNoteContent !== this.props.selectedNoteContent + prevProps.selectedNoteContent !== this.props.selectedNoteContent || + prevProps.showNoteList !== this.props.showNoteList ) { heightCache.clearAll(); } @@ -300,7 +302,7 @@ export class NoteList extends Component { handleShortcut = (event: KeyboardEvent) => { const { ctrlKey, code, metaKey, shiftKey } = event; - const { notes } = this.props; + const { isSmallScreen, notes, showNoteList } = this.props; const { selectedIndex: index } = this.state; const highlightedIndex = this.getHighlightedIndex(this.props); @@ -308,7 +310,7 @@ export class NoteList extends Component { const cmdOrCtrl = ctrlKey || metaKey; if (cmdOrCtrl && shiftKey && code === 'KeyK') { if (-1 === highlightedIndex || index < 0 || !notes[index - 1]?.id) { - return true; + return false; } this.props.onSelectNote(notes[index - 1]); @@ -323,7 +325,7 @@ export class NoteList extends Component { index >= notes.length || !notes[index + 1]?.id ) { - return true; + return false; } this.props.onSelectNote(notes[index + 1]); @@ -332,6 +334,27 @@ export class NoteList extends Component { return false; } + if (isSmallScreen && cmdOrCtrl && shiftKey && code === 'KeyL') { + this.props.toggleNoteList(); + + event.stopPropagation(); + event.preventDefault(); + return false; + } + + if ( + isSmallScreen && + showNoteList && + code === 'Enter' && + highlightedIndex !== null + ) { + this.props.openNote(notes[highlightedIndex]); + + event.stopPropagation(); + event.preventDefault(); + return false; + } + return true; }; @@ -385,10 +408,9 @@ export class NoteList extends Component { render() { const { hasLoaded, - isSmallScreen, noteDisplay, notes, - onSelectNote, + openNote, onEmptyTrash, onPinNote, searchQuery, @@ -411,9 +433,8 @@ export class NoteList extends Component { searchQuery, highlightedIndex, noteDisplay, - onSelectNote, onPinNote, - isSmallScreen, + openNote, }); const isEmptyList = compositeNoteList.length === 0; @@ -450,12 +471,13 @@ export class NoteList extends Component { rowCount={compositeNoteList.length} rowHeight={heightCache.rowHeight} rowRenderer={renderNoteRow} + scrollToIndex={highlightedIndex} width={width} /> )}
    - {!!showTrash && emptyTrashButton} + {showTrash && emptyTrashButton} )} @@ -463,7 +485,7 @@ export class NoteList extends Component { } } -const { emptyTrash, loadAndSelectNote } = appState.actionCreators; +const { emptyTrash } = appState.actionCreators; const mapStateToProps: S.MapState = ({ appState: state, @@ -472,6 +494,7 @@ const mapStateToProps: S.MapState = ({ note, openedTag, searchQuery, + showNoteList, showTrash, tagSuggestions, }, @@ -508,6 +531,7 @@ const mapStateToProps: S.MapState = ({ selectedNote: note, selectedNotePreview, selectedNoteContent: get(note, 'data.content'), + showNoteList, showTrash, tagResultsFound: tagSuggestions.length, }; @@ -524,6 +548,8 @@ const mapDispatchToProps: S.MapDispatch = ( analytics.tracks.recordEvent('list_note_opened'); }, onPinNote: (note, shouldPin) => dispatch(actions.ui.pinNote(note, shouldPin)), + openNote: (note: T.NoteEntity) => dispatch(actions.ui.openNote(note)), + toggleNoteList: () => dispatch(actions.ui.toggleNoteList()), }); export default connect(mapStateToProps, mapDispatchToProps)(NoteList); diff --git a/lib/note-toolbar/index.tsx b/lib/note-toolbar/index.tsx index e06b1cada..9f392749a 100644 --- a/lib/note-toolbar/index.tsx +++ b/lib/note-toolbar/index.tsx @@ -14,9 +14,9 @@ import ShareIcon from '../icons/share'; import SidebarIcon from '../icons/sidebar'; import { - closeNote, toggleEditMode, toggleNoteInfo, + toggleNoteList, toggleRevisions, } from '../state/ui/actions'; @@ -24,9 +24,9 @@ import * as S from '../state'; import * as T from '../types'; type DispatchProps = { - closeNote: () => any; toggleEditMode: () => any; toggleNoteInfo: () => any; + toggleNoteList: () => any; toggleRevisions: () => any; }; @@ -87,8 +87,8 @@ export class NoteToolbar extends Component {
    } - onClick={this.props.closeNote} - title="Back" + onClick={this.props.toggleNoteList} + title="Back • Ctrl+Shift+L" />
    {markdownEnabled && ( @@ -141,8 +141,8 @@ export class NoteToolbar extends Component {
    } - onClick={this.props.closeNote} - title="Back" + onClick={this.props.toggleNoteList} + title="Back • Ctrl+Shift+L" />
    @@ -185,9 +185,9 @@ const mapStateToProps: S.MapState = ({ }; const mapDispatchToProps: S.MapDispatch = { - closeNote, toggleEditMode, toggleNoteInfo, + toggleNoteList, toggleRevisions, }; diff --git a/lib/search/worker.ts b/lib/search/worker.ts index c443cfb2f..d821a084c 100644 --- a/lib/search/worker.ts +++ b/lib/search/worker.ts @@ -103,6 +103,7 @@ export const updateNote = (noteId: T.EntityId, data) => { { ...data, content: data.content.toLocaleLowerCase(), + deleted: !!data.deleted, tags: noteTags, }, ]); diff --git a/lib/state/action-types.ts b/lib/state/action-types.ts index c2a3254cb..99cb7d936 100644 --- a/lib/state/action-types.ts +++ b/lib/state/action-types.ts @@ -56,6 +56,7 @@ export type FilterNotes = Action< { notes: T.NoteEntity[]; tags: T.TagEntity[] } >; export type FocusSearchField = Action<'FOCUS_SEARCH_FIELD'>; +export type OpenNote = Action<'OPEN_NOTE', { note: T.NoteEntity }>; export type OpenTag = Action<'OPEN_TAG', { tag: T.TagEntity }>; export type RemoteNoteUpdate = Action< 'REMOTE_NOTE_UPDATE', @@ -93,6 +94,7 @@ export type TagsLoaded = Action< >; export type ToggleEditMode = Action<'TOGGLE_EDIT_MODE'>; export type ToggleNavigation = Action<'NAVIGATION_TOGGLE'>; +export type ToggleNoteList = Action<'NOTE_LIST_TOGGLE'>; export type ToggleNoteInfo = Action<'NOTE_INFO_TOGGLE'>; export type ToggleRevisions = Action<'REVISIONS_TOGGLE'>; export type ToggleSimperiumConnectionStatus = Action< @@ -112,6 +114,7 @@ export type ActionType = | FilterNotes | FocusSearchField | RemoteNoteUpdate + | OpenNote | OpenTag | RestoreNote | Search @@ -139,6 +142,7 @@ export type ActionType = | TagsLoaded | ToggleEditMode | ToggleNavigation + | ToggleNoteList | ToggleNoteInfo | ToggleRevisions | ToggleSimperiumConnectionStatus diff --git a/lib/state/ui/actions.ts b/lib/state/ui/actions.ts index fe71d0f19..7aea6328d 100644 --- a/lib/state/ui/actions.ts +++ b/lib/state/ui/actions.ts @@ -60,6 +60,11 @@ export const publishNote: A.ActionCreator = ( shouldHaveTag: shoudlPublish, }); +export const openNote: A.ActionCreator = (note: T.NoteEntity) => ({ + type: 'OPEN_NOTE', + note, +}); + export const openTag: A.ActionCreator = (tag: T.TagEntity) => ({ type: 'OPEN_TAG', tag, @@ -135,6 +140,10 @@ export const toggleNavigation: A.ActionCreator = () => ({ type: 'NAVIGATION_TOGGLE', }); +export const toggleNoteList: A.ActionCreator = () => ({ + type: 'NOTE_LIST_TOGGLE', +}); + export const toggleNoteInfo: A.ActionCreator = () => ({ type: 'NOTE_INFO_TOGGLE', }); diff --git a/lib/state/ui/reducer.ts b/lib/state/ui/reducer.ts index 3e4aa2866..dedea7f64 100644 --- a/lib/state/ui/reducer.ts +++ b/lib/state/ui/reducer.ts @@ -39,12 +39,12 @@ const editingTags: A.Reducer = (state = false, action) => { switch (action.type) { case 'TAG_EDITING_TOGGLE': return !state; + case 'OPEN_NOTE': case 'SELECT_NOTE': case 'OPEN_TAG': case 'SELECT_TRASH': case 'SHOW_ALL_NOTES': case 'NAVIGATION_TOGGLE': - case 'App.toggleNoteInfo': return false; default: return state; @@ -80,6 +80,7 @@ const noteRevisions: A.Reducer = ( case 'STORE_REVISIONS': return action.revisions; case 'CREATE_NOTE': + case 'OPEN_NOTE': case 'SELECT_NOTE': return emptyList as T.NoteEntity[]; default: @@ -107,6 +108,7 @@ const selectedRevision: A.Reducer = ( case 'SELECT_REVISION': return action.revision; case 'CREATE_NOTE': + case 'OPEN_NOTE': case 'REVISIONS_TOGGLE': case 'SELECT_NOTE': return null; @@ -115,12 +117,12 @@ const selectedRevision: A.Reducer = ( } }; -const showNoteList: A.Reducer = (state = false, action) => { +const showNoteList: A.Reducer = (state = true, action) => { switch (action.type) { - case 'CLOSE_NOTE': - return true; + case 'NOTE_LIST_TOGGLE': + return !state; - case 'SELECT_NOTE': + case 'OPEN_NOTE': return false; default: @@ -185,6 +187,7 @@ const showRevisions: A.Reducer = (state = false, action) => { switch (action.type) { case 'REVISIONS_TOGGLE': return !state; + case 'OPEN_NOTE': case 'SELECT_NOTE': case 'CREATE_NOTE': return false; @@ -218,6 +221,7 @@ const note: A.Reducer = (state = null, action) => { case 'TRASH_NOTE': case 'OPEN_TAG': return null; + case 'OPEN_NOTE': case 'SELECT_NOTE': return action.options ? { diff --git a/lib/tag-field/index.tsx b/lib/tag-field/index.tsx index 4125a3832..57937b6d7 100644 --- a/lib/tag-field/index.tsx +++ b/lib/tag-field/index.tsx @@ -1,6 +1,5 @@ import React, { Component, - KeyboardEvent, KeyboardEventHandler, MouseEvent, RefObject, @@ -76,10 +75,12 @@ export class TagField extends Component { this.props.storeHasFocus(this.hasFocus); document.addEventListener('click', this.unselect, true); + window.addEventListener('keydown', this.preventStealingFocus, true); } componentWillUnmount() { document.removeEventListener('click', this.unselect, true); + window.removeEventListener('keydown', this.preventStealingFocus, true); } componentDidUpdate() { @@ -120,9 +121,7 @@ export class TagField extends Component { ); if (selectedTag === tagName) { - this.setState({ selectedTag: '' }, () => { - invoke(this, 'tagInput.focus'); - }); + this.setState({ selectedTag: '' }, () => this.tagInput?.focus()); } analytics.tracks.recordEvent('editor_tag_removed'); @@ -165,6 +164,16 @@ export class TagField extends Component { } }; + preventStealingFocus = ({ ctrlKey, metaKey, code }: KeyboardEvent) => { + const cmdOrCtrl = ctrlKey || metaKey; + + if (cmdOrCtrl && 'KeyT' === code) { + this.setState({ selectedTag: '' }); + } + + return true; + }; + updateTags = (tags) => this.props.updateNoteTags({ note: this.props.note, tags }); @@ -190,7 +199,7 @@ export class TagField extends Component { setTimeout(() => this.setState({ showEmailTooltip: false }), 5000); }; - onKeyDown = (e: KeyboardEvent) => { + onKeyDown = (e: React.KeyboardEvent) => { if (this.state.showEmailTooltip) { this.hideEmailTooltip(); } @@ -209,7 +218,7 @@ export class TagField extends Component { storeTagInput = (value: string, callback?: (...args: any) => any) => this.setState({ tagInput: value }, callback); - unselect = (event: KeyboardEvent) => { + unselect = (event: React.KeyboardEvent) => { if (!this.state.selectedTag) { return; } diff --git a/package-lock.json b/package-lock.json index f7418bceb..bafbbf94c 100644 --- a/package-lock.json +++ b/package-lock.json @@ -1,6 +1,6 @@ { "name": "simplenote", - "version": "1.15.1", + "version": "1.16.0", "lockfileVersion": 1, "requires": true, "dependencies": { diff --git a/package.json b/package.json index e63a15f42..1ece33f87 100644 --- a/package.json +++ b/package.json @@ -6,7 +6,7 @@ "email": "support@simplenote.com" }, "productName": "Simplenote", - "version": "1.15.1", + "version": "1.16.0", "main": "desktop/index.js", "license": "GPL-2.0", "homepage": "https://simplenote.com",