Skip to content

Commit

Permalink
Add scratchpad (#280)
Browse files Browse the repository at this point in the history
* Add Folder.INBOX to types

* Automatically add inbox file for new users (fake api)

* Add inbox to sidebar and display inbox note when inbox is selected

* Change inbox to scratchpad

* Create scratchpad note if it's not there

* Hide note list when viewing scratchpad

* Ensure inbox isn't shown in editor after deleting a note

* Refactor getNewActiveNoteId
  • Loading branch information
Mxchaeltrxn committed Feb 29, 2020
1 parent c2822b2 commit a2db7a4
Show file tree
Hide file tree
Showing 15 changed files with 108 additions and 30 deletions.
17 changes: 12 additions & 5 deletions src/client/api/index.ts
Original file line number Diff line number Diff line change
@@ -1,5 +1,7 @@
import { SyncStatePayload, SettingsState } from '@/types'
import { NoteItem, SyncStatePayload, SettingsState } from '../types'

import { welcomeNote } from '@/api/welcomeNote'
import { scratchpadNote } from '@/api/scratchpadNote'

type PromiseCallback = (value?: any) => void
type GetLocalStorage = (
Expand All @@ -26,11 +28,16 @@ const getUserNotes = () => (resolve: PromiseCallback, reject: PromiseCallback) =

// check if there is any data in localstorage
if (!notes) {
// if there is none (i.e. new user), show the welcomeNote
resolve(welcomeNote)
} else if (JSON.parse(notes)) {
// if there is none (i.e. new user), create the welcomeNote and scratchpadNote
resolve([scratchpadNote, welcomeNote])
} else if (Array.isArray(JSON.parse(notes))) {
// if there is (existing user), show the user's notes
resolve(JSON.parse(notes))
resolve(
// find does not work if the array is empty.
JSON.parse(notes).length === 0 || !JSON.parse(notes).find((note: NoteItem) => note.scratchpad)
? [scratchpadNote, ...JSON.parse(notes)]
: JSON.parse(notes)
)
} else {
reject({
message: 'Something went wrong',
Expand Down
5 changes: 5 additions & 0 deletions src/client/api/scratchpadNote.md
Original file line number Diff line number Diff line change
@@ -0,0 +1,5 @@
# Scratchpad

A place for just random things you may want to jot down such as daily todos.

You cannot create more notes in the scratchpad and you cannot delete the scratchpad note.
13 changes: 13 additions & 0 deletions src/client/api/scratchpadNote.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,13 @@
import uuid from 'uuid/v4'
import moment from 'moment'

import markdown from '@/api/scratchpadNote.md'

export const scratchpadNote = {
id: uuid(),
text: markdown,
category: '',
scratchpad: true,
favorite: false,
created: moment().format(),
}
16 changes: 7 additions & 9 deletions src/client/api/welcomeNote.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,12 +3,10 @@ import moment from 'moment'

import markdown from '@/api/note.md'

export const welcomeNote = [
{
id: uuid(),
text: markdown,
category: '',
favorite: false,
created: moment().format(),
},
]
export const welcomeNote = {
id: uuid(),
text: markdown,
category: '',
favorite: false,
created: moment().format(),
}
1 change: 1 addition & 0 deletions src/client/components/AppSidebar/FolderOption.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -25,6 +25,7 @@ export const FolderOption: React.FC<FolderOptionProps> = ({
const [mainSectionDragState, setMainSectionDragState] = useState({
[Folder.ALL]: false,
[Folder.FAVORITES]: false,
[Folder.SCRATCHPAD]: false,
[Folder.TRASH]: false,
[Folder.CATEGORY]: false,
})
Expand Down
28 changes: 28 additions & 0 deletions src/client/components/AppSidebar/ScratchpadOption.tsx
Original file line number Diff line number Diff line change
@@ -0,0 +1,28 @@
/* eslint-disable import/no-unresolved */
import React from 'react'
import { Edit } from 'react-feather'

import { StringEnum } from '@resources/StringEnum'

import { Folder } from '@/utils/enums'
import { iconColor } from '@/utils/constants'

export interface ScratchpadOptionProps {
active: boolean
swapFolder: (folder: Folder) => {}
}

export const ScratchpadOption: React.FC<ScratchpadOptionProps> = ({ active, swapFolder }) => {
return (
<div
data-testid="scratchpad"
className={`app-sidebar-link ${active ? 'active' : ''}`}
onClick={() => {
swapFolder(Folder.SCRATCHPAD)
}}
>
<Edit size={15} className="app-sidebar-icon" color={iconColor} />
{StringEnum.SCRATCHPAD}
</div>
)
}
2 changes: 2 additions & 0 deletions src/client/containers/AppSidebar.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -10,6 +10,7 @@ import { ActionButton } from '@/components/AppSidebar/ActionButton'
import { LastSyncedNotification } from '@/components/AppSidebar/LastSyncedNotification'
import { AllNotesOption } from '@/components/AppSidebar/AllNotesOption'
import { FolderOption } from '@/components/AppSidebar/FolderOption'
import { ScratchpadOption } from '@/components/AppSidebar/ScratchpadOption'
import { Folder, ContextMenuEnum } from '@/utils/enums'
import { iconColor } from '@/utils/constants'
import { useTempState } from '@/contexts/TempStateContext'
Expand Down Expand Up @@ -204,6 +205,7 @@ export const AppSidebar: React.FC = () => {
/>
</section>
<section className="app-sidebar-main">
<ScratchpadOption active={activeFolder === Folder.SCRATCHPAD} swapFolder={_swapFolder} />
<AllNotesOption active={activeFolder === Folder.ALL} swapFolder={_swapFolder} />
<FolderOption
active={activeFolder === Folder.FAVORITES}
Expand Down
2 changes: 1 addition & 1 deletion src/client/containers/ContextMenuOptions.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -122,7 +122,7 @@ const NotesOptions: React.FC<NotesOptionsProps> = ({ clickedNote }) => {
text={StringEnum.RESTORE_FROM_TRASH}
/>
</>
) : (
) : clickedNote.scratchpad ? null : (
<>
<ContextMenuOption
dataTestID="note-option-favorite"
Expand Down
7 changes: 4 additions & 3 deletions src/client/containers/NoteList.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -38,9 +38,10 @@ export const NoteList: React.FC = () => {

const filter: Record<Folder, (note: NoteItem) => boolean> = {
[Folder.CATEGORY]: note => !note.trash && note.category === activeCategoryId,
[Folder.SCRATCHPAD]: note => !!note.scratchpad,
[Folder.FAVORITES]: note => !note.trash && !!note.favorite,
[Folder.TRASH]: note => !!note.trash,
[Folder.ALL]: note => !note.trash,
[Folder.ALL]: note => !note.trash && !note.scratchpad,
}
const filteredNotes: NoteItem[] = notes
.filter(filter[activeFolder])
Expand Down Expand Up @@ -116,7 +117,7 @@ export const NoteList: React.FC = () => {

const showEmptyTrash = activeFolder === Folder.TRASH && filteredNotes.length > 0

return (
return activeFolder !== Folder.SCRATCHPAD ? (
<aside className="note-sidebar">
<div className="note-sidebar-header">
<SearchBar searchRef={searchRef} searchNotes={_searchNotes} />
Expand Down Expand Up @@ -211,5 +212,5 @@ export const NoteList: React.FC = () => {
})}
</div>
</aside>
)
) : null
}
41 changes: 30 additions & 11 deletions src/client/slices/note.ts
Original file line number Diff line number Diff line change
Expand Up @@ -7,15 +7,18 @@ import { NoteItem, NoteState } from '@/types'
const getNewActiveNoteId = (
notes: NoteItem[],
oldNoteId: string,
activeCategoryId: string
activeCategoryId: string,
activeFolder: Folder
): string => {
const notesNotTrash = notes.filter(note =>
activeCategoryId ? !note.trash && note.category === activeCategoryId : !note.trash
)
const trashedNoteIndex = notesNotTrash.findIndex(note => note.id === oldNoteId)
const newActiveNotes = notes
.filter(note => !note.scratchpad) // filter out all scratchpad notes
.filter(note => (activeFolder !== Folder.TRASH ? !note.trash : note.trash)) // trash or not trash
.filter(note => (activeCategoryId ? note.category === activeCategoryId : true)) // filter category if necessary
const trashedNoteIndex = newActiveNotes.findIndex(note => note.id === oldNoteId)

if (trashedNoteIndex === 0 && newActiveNotes[1]) return newActiveNotes[1].id
if (newActiveNotes[trashedNoteIndex - 1]) return newActiveNotes[trashedNoteIndex - 1].id

if (trashedNoteIndex === 0 && notesNotTrash[1]) return notesNotTrash[1].id
if (notesNotTrash[trashedNoteIndex - 1]) return notesNotTrash[trashedNoteIndex - 1].id
return ''
}

Expand All @@ -25,9 +28,10 @@ export const getFirstNoteId = (folder: Folder, notes: NoteItem[], categoryId?: s
.sort(sortByLastUpdated)
.sort(sortByFavorites)
const firstNote = {
[Folder.ALL]: () => notesNotTrash[0],
[Folder.ALL]: () => notesNotTrash.find(note => !note.scratchpad),
[Folder.CATEGORY]: () => notesNotTrash.find(note => note.category === categoryId),
[Folder.FAVORITES]: () => notesNotTrash.find(note => note.favorite),
[Folder.SCRATCHPAD]: () => notesNotTrash.find(note => note.scratchpad),
[Folder.TRASH]: () => notes.find(note => note.trash),
}[folder]()
return firstNote ? firstNote.id : ''
Expand Down Expand Up @@ -63,7 +67,12 @@ const noteSlice = createSlice({
deleteNote: (state, { payload }: PayloadAction<string>) => ({
...state,
notes: state.notes.filter(note => note.id !== payload),
activeNoteId: getNewActiveNoteId(state.notes, payload, state.activeCategoryId),
activeNoteId: getNewActiveNoteId(
state.notes,
payload,
state.activeCategoryId,
state.activeFolder
),
}),
emptyTrash: state => ({
...state,
Expand Down Expand Up @@ -122,7 +131,12 @@ const noteSlice = createSlice({
notes: state.notes.map(note =>
note.id === payload ? { ...note, trash: !note.trash } : note
),
activeNoteId: getNewActiveNoteId(state.notes, payload, state.activeCategoryId),
activeNoteId: getNewActiveNoteId(
state.notes,
payload,
state.activeCategoryId,
state.activeFolder
),
}),
addFavoriteNote: (state, { payload }: PayloadAction<string>) => ({
...state,
Expand All @@ -131,7 +145,12 @@ const noteSlice = createSlice({
addTrashedNote: (state, { payload }: PayloadAction<string>) => ({
...state,
notes: state.notes.map(note => (note.id === payload ? { ...note, trash: true } : note)),
activeNoteId: getNewActiveNoteId(state.notes, payload, state.activeCategoryId),
activeNoteId: getNewActiveNoteId(
state.notes,
payload,
state.activeCategoryId,
state.activeFolder
),
}),
updateNote: (state, { payload }: PayloadAction<NoteItem>) => ({
...state,
Expand Down
1 change: 1 addition & 0 deletions src/client/types/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -16,6 +16,7 @@ export interface NoteItem {
* Refers to the category UUID and not the actual name.
*/
category?: string
scratchpad?: boolean
trash?: boolean
favorite?: boolean
}
Expand Down
1 change: 1 addition & 0 deletions src/client/utils/constants.ts
Original file line number Diff line number Diff line change
Expand Up @@ -3,6 +3,7 @@ import { Folder } from '@/utils/enums'
export const folderMap: Record<Folder, string> = {
[Folder.ALL]: 'All Notes',
[Folder.FAVORITES]: 'Favorites',
[Folder.SCRATCHPAD]: 'Scratchpad',
[Folder.TRASH]: 'Trash',
[Folder.CATEGORY]: 'Category',
}
Expand Down
1 change: 1 addition & 0 deletions src/client/utils/enums.ts
Original file line number Diff line number Diff line change
Expand Up @@ -2,6 +2,7 @@ export enum Folder {
ALL = 'ALL',
CATEGORY = 'CATEGORY',
FAVORITES = 'FAVORITES',
SCRATCHPAD = 'SCRATCHPAD',
TRASH = 'TRASH',
}

Expand Down
2 changes: 1 addition & 1 deletion src/client/utils/helpers.ts
Original file line number Diff line number Diff line change
Expand Up @@ -66,7 +66,7 @@ export const newNoteHandlerHelper = (
addNote: (note: NoteItem) => WithPayload<NoteItem, Action<string>>,
swapNote: (noteId: string) => WithPayload<string, Action<string>>
) => {
if (activeFolder === Folder.TRASH) {
if ([Folder.TRASH, Folder.SCRATCHPAD].indexOf(activeFolder) !== -1) {
swapFolder(Folder.ALL)
}

Expand Down
1 change: 1 addition & 0 deletions src/resources/StringEnum.ts
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ enum StringEnum {
DELETE_PERMANENTLY = 'Delete permanently',
DOWNLOAD = 'Download',
FAVORITES = 'Favorites',
SCRATCHPAD = 'Scratchpad',
MARK_AS_FAVORITE = 'Mark as favorite',
MOVE_TO_TRASH = 'Move to trash',
NEW_CATEGORY = 'New category...',
Expand Down

0 comments on commit a2db7a4

Please sign in to comment.