Created
July 15, 2021 21:14
-
-
Save popuguytheparrot/18720ef5526518857b840537472d5c13 to your computer and use it in GitHub Desktop.
This file contains bidirectional Unicode text that may be interpreted or compiled differently than what appears below. To review, open the file in an editor that reveals hidden Unicode characters.
Learn more about bidirectional Unicode characters
import { useCallback, useContext, useEffect, useRef, useState } from 'react'; | |
import { UserContext } from 'src/contexts/User'; | |
import { v4 } from '@lukeed/uuid'; | |
import { NoteEntityType, NoteRes } from 'src/typings/notes'; | |
import { requestAddNote, requestSaveNote } from 'src/apiServices/notes'; | |
import { EntityState, useEntityState } from 'src/hooks/useEntityState'; | |
import debounce from 'lodash.debounce'; | |
interface EditField { | |
field: keyof NoteRes; | |
value: string; | |
} | |
export function useNotes( | |
request: ({ entityType, entityId }: { entityType: NoteEntityType; entityId: string }) => Promise<Array<NoteRes>>, | |
customerId: string, | |
entityType: NoteEntityType | |
) { | |
const { | |
user: { login }, | |
} = useContext(UserContext); | |
const [notesList, changeNotes] = useState<NoteRes[]>([]); | |
const [currentNote, changeNote] = useState<NoteRes>({ ...initNote }); | |
const pristineNote = useRef<NoteRes>({ ...initNote }); | |
const [entityState, changeState] = useEntityState(); | |
const [editing, toggleEditing] = useState(false); | |
const createNote = useCallback<() => NoteRes>(() => { | |
const newNote: NoteRes = { ...initNote, createdBy: login, id: v4(), createdDate: new Date().toUTCString() }; | |
return newNote; | |
}, [login]); | |
const validateOnSave = useCallback(() => { | |
if (!currentNote.note || !currentNote.title) { | |
throw new Error(); | |
} | |
}, [currentNote.note, currentNote.title]); | |
const saveNote = useCallback( | |
(id: string) => { | |
try { | |
validateOnSave(); | |
pristineNote.current = { ...currentNote }; | |
changeState(EntityState.loading); | |
requestSaveNote({ note: currentNote, entityType, entityId: customerId }).then(() => { | |
changeNotes((prev) => prev.map((note) => (note.id === id ? currentNote : note))); | |
changeState(EntityState.finished); | |
}); | |
} catch (err) { | |
changeState(EntityState.error); | |
} | |
}, | |
[changeState, currentNote, customerId, entityType, validateOnSave] | |
); | |
const selectNote = useCallback( | |
(id: string) => { | |
if (id === undefined || id === currentNote.id) { | |
return; | |
} | |
const note = notesList.find((note) => note.id === id)!; | |
pristineNote.current = { ...note }; | |
changeNote(note); | |
}, | |
[currentNote.id, notesList] | |
); | |
const addNote = useCallback(() => { | |
const newNote = createNote(); | |
requestAddNote({ note: newNote, entityType, entityId: customerId }).then((id) => { | |
const noteWithId = { ...newNote, id }; | |
pristineNote.current = { ...noteWithId }; | |
changeNotes((notes) => [noteWithId, ...notes]); | |
changeNote(noteWithId); | |
}); | |
}, [createNote, customerId, entityType]); | |
const trimNote = useCallback(({ field, value }: EditField) => { | |
return debounce(() => { | |
changeNote((prev) => ({ ...prev, [field]: value.trim() })); | |
}, 500); | |
}, []); | |
const editNote = useCallback( | |
({ field, value }: EditField) => { | |
changeState(EntityState.idle); | |
toggleEditing(true); | |
const editNote = notesList.find((note) => note.id === currentNote.id)!; | |
editNote[field] = value; | |
changeNote(editNote); | |
changeNotes((prev) => prev.map((note) => (note.id === editNote.id ? editNote : note))); | |
}, | |
[changeState, currentNote.id, notesList] | |
); | |
const cancelChange = useCallback((callback: (text: string) => void) => { | |
const prevNote = { ...pristineNote.current }; | |
changeNote(prevNote); | |
changeNotes((prev) => prev.map((note) => (note.id === prevNote.id ? prevNote : note))); | |
callback(prevNote.note); | |
}, []); | |
const onFocus = useCallback(() => { | |
toggleEditing(true); | |
}, []); | |
const onBlur = useCallback(() => { | |
toggleEditing(false); | |
}, []); | |
useEffect(() => { | |
async function fetch() { | |
const notes = await request({ entityType, entityId: customerId }); | |
if (notes.length === 0) { | |
return null; | |
} | |
const [firstNote] = notes; | |
changeNotes((prevNotes) => [...notes, ...prevNotes]); | |
changeNote(firstNote); | |
} | |
changeState(EntityState.loading); | |
fetch() | |
.then((res) => { | |
if (res === null) { | |
addNote(); | |
} | |
}) | |
.finally(() => { | |
changeState(EntityState.finished); | |
}); | |
}, [addNote, changeState, customerId, entityType, request]); | |
return { | |
notes: notesList, | |
note: currentNote, | |
loading: entityState === EntityState.loading, | |
error: entityState === EntityState.error, | |
saveNote, | |
addNote, | |
selectNote, | |
editNote: ({ field, value }: EditField) => { | |
editNote({ field, value }); | |
trimNote({ field, value }); | |
}, | |
cancelChange, | |
onFocus, | |
onBlur, | |
editing, | |
}; | |
} | |
const initNote = { | |
createdBy: '', | |
createdDate: '', | |
entityId: '', | |
entityType: '', | |
lastModifiedDate: '', | |
note: '', | |
id: '', | |
title: '', | |
}; |
Sign up for free
to join this conversation on GitHub.
Already have an account?
Sign in to comment