Skip to content

Instantly share code, notes, and snippets.

@popuguytheparrot
Created July 15, 2021 21:14
Show Gist options
  • Save popuguytheparrot/18720ef5526518857b840537472d5c13 to your computer and use it in GitHub Desktop.
Save popuguytheparrot/18720ef5526518857b840537472d5c13 to your computer and use it in GitHub Desktop.
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