-
Notifications
You must be signed in to change notification settings - Fork 6
Open
Description
Action definitions account for almost half of App.jsx's length:
Lines 70 to 511 in 4612f11
| const dispatchDeleteTrack = () => { | |
| // If a track that's not last is being deleted, | |
| if (selectedTrackNumber < tracks.length - 1) { | |
| const nextTracksFirstDurationAtSelectedMeasureNumber = durations.find( | |
| (duration) => | |
| duration.id === | |
| measures.find( | |
| (measure) => | |
| measure.id === | |
| tracks[selectedTrackNumber + 1].measures[selectedMeasureNumber] | |
| ).durations[0] | |
| ); | |
| // Select first duration of next track's measure at selectedMeasureNumber | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: nextTracksFirstDurationAtSelectedMeasureNumber.id, | |
| }); | |
| } | |
| // Otherwise, select first duration of previous track's measure at selectedMeasureNumber | |
| else if (selectedTrackNumber !== 0) { | |
| const previousTracksFirstDurationAtSelectedMeasureNumber = durations.find( | |
| (duration) => | |
| duration.id === | |
| measures.find( | |
| (measure) => | |
| measure.id === | |
| tracks[selectedTrackNumber - 1].measures[selectedMeasureNumber] | |
| ).durations[0] | |
| ); | |
| dispatch({ | |
| type: actionTypes.SELECT_TRACK, | |
| trackNumber: selectedTrackNumber - 1, | |
| }); | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: previousTracksFirstDurationAtSelectedMeasureNumber.id, | |
| }); | |
| } | |
| dispatch({ | |
| type: actionTypes.DELETE_TRACK, | |
| trackId: tracks[selectedTrackNumber].id, | |
| }); | |
| }; | |
| const dispatchAddTrack = useCallback( | |
| (trackToAdd) => { | |
| let newTrackId = nanoid(); | |
| // TODO Turn ID array generation into a function | |
| let measureIds = | |
| tracks.length === 0 | |
| ? [nanoid()] | |
| : tracks[0].measures.map((measure) => nanoid()); | |
| let durationIds = | |
| tracks.length === 0 | |
| ? [nanoid()] | |
| : tracks[0].measures.map((measure) => nanoid()); | |
| dispatch({ | |
| type: actionTypes.ADD_TRACK, | |
| id: newTrackId, | |
| measures: measureIds, | |
| durationIds: durationIds, | |
| durationLength: selectedDuration?.length, | |
| ...trackToAdd, | |
| }); | |
| return { | |
| newTrackId: newTrackId, | |
| durationIdToSelect: durationIds[selectedMeasureNumber], | |
| }; | |
| }, | |
| [selectedMeasureNumber, selectedDuration, tracks] | |
| ); | |
| const dispatchShortenDuration = useCallback( | |
| (durationId) => { | |
| dispatch({ | |
| type: actionTypes.SET_DURATION_LENGTH, | |
| durationId: durationId, | |
| newLength: selectedDuration?.length / 2, | |
| }); | |
| }, | |
| [selectedDuration] | |
| ); | |
| const dispatchLengthenDuration = useCallback( | |
| (durationId) => { | |
| dispatch({ | |
| type: actionTypes.SET_DURATION_LENGTH, | |
| durationId: durationId, | |
| newLength: selectedDuration?.length * 2, | |
| }); | |
| }, | |
| [selectedDuration] | |
| ); | |
| const dispatchSelectPreviousString = useCallback(() => { | |
| dispatch({ | |
| type: actionTypes.SELECT_STRING, | |
| stringNumber: | |
| selectedStringNumber === 0 | |
| ? selectedTrack?.tuning.length - 1 | |
| : selectedStringNumber - 1, | |
| }); | |
| }, [selectedTrack, selectedStringNumber]); | |
| const dispatchSelectNextString = useCallback(() => { | |
| dispatch({ | |
| type: actionTypes.SELECT_STRING, | |
| stringNumber: (selectedStringNumber + 1) % selectedTrack?.tuning.length, | |
| }); | |
| }, [selectedTrack, selectedStringNumber]); | |
| const dispatchSelectPreviousDuration = useCallback(() => { | |
| // If currently selected duration is NOT first in the measure, | |
| if (selectedDurationId !== selectedMeasure?.durations[0]) { | |
| // Select the previous duration | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: | |
| selectedMeasure?.durations[ | |
| selectedMeasure?.durations.findIndex( | |
| (durationId) => durationId === selectedDurationId | |
| ) - 1 | |
| ], | |
| }); | |
| } else if (selectedMeasureNumber > 0) { | |
| const previousMeasure = measures.find( | |
| (measure) => | |
| measure.id === selectedTrack?.measures[selectedMeasureNumber - 1] | |
| ); | |
| const durationIdToSelect = previousMeasure.durations.slice(-1)[0]; | |
| // Select the last duration of the previous measure | |
| dispatch({ | |
| type: actionTypes.SELECT_MEASURE, | |
| measureNumber: selectedMeasureNumber - 1, | |
| }); | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: durationIdToSelect, | |
| }); | |
| } | |
| }, [ | |
| measures, | |
| selectedTrack, | |
| selectedMeasureNumber, | |
| selectedMeasure, | |
| selectedDurationId, | |
| ]); | |
| const dispatchSelectNextDuration = useCallback(() => { | |
| let shouldCheckIfMeasureIsLast = false; | |
| // If there's a note at this duration, | |
| // Or if this duration is a rest, | |
| if (selectedDuration?.notes.length || selectedDuration?.isRest) { | |
| // If this is the last duration, | |
| if (selectedDurationId === selectedMeasure?.durations.slice(-1)[0]) { | |
| // If the measure's total length === maximum, | |
| if (currentBarDuration === currentBarMaximumDuration) { | |
| shouldCheckIfMeasureIsLast = true; | |
| } | |
| // Add a new duration to this measure | |
| else { | |
| let newDurationId = nanoid(); | |
| dispatch({ | |
| type: actionTypes.ADD_DURATION, | |
| measureId: selectedMeasure?.id, | |
| newDurationId: newDurationId, | |
| length: selectedDuration?.length, | |
| isDotted: selectedDuration?.isDotted, | |
| }); | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: newDurationId, | |
| }); | |
| } | |
| } | |
| // Select the next duration in this measure | |
| else { | |
| const nextDuration = durations.find( | |
| (duration) => | |
| duration.id === | |
| selectedMeasure?.durations[ | |
| selectedMeasure?.durations.findIndex( | |
| (durationId) => durationId === selectedDurationId | |
| ) + 1 | |
| ] | |
| ); | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: nextDuration.id, | |
| }); | |
| } | |
| } else { | |
| shouldCheckIfMeasureIsLast = true; | |
| } | |
| if (shouldCheckIfMeasureIsLast) { | |
| // If selectedMeasure is last, | |
| // Add a new measure | |
| if (selectedMeasureNumber === selectedTrack?.measures.length - 1) { | |
| // TODO Use parallel arrays like in dispatchAddTrack instead | |
| // Create a mapping from track IDs to new measure IDs | |
| let trackMeasureIds = tracks.reduce((map, track) => { | |
| map[track.id] = { | |
| measureId: nanoid(), | |
| durationId: nanoid(), | |
| }; | |
| return map; | |
| }, {}); | |
| // TODO Pass in current measure's time signature | |
| dispatch({ | |
| type: actionTypes.ADD_MEASURE, | |
| trackMeasureIds: trackMeasureIds, | |
| durationLength: selectedDuration?.length, | |
| }); | |
| dispatch({ | |
| type: actionTypes.SELECT_MEASURE, | |
| measureNumber: selectedMeasureNumber + 1, | |
| }); | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: trackMeasureIds[selectedTrack?.id].durationId, | |
| }); | |
| } | |
| // Select the next measure | |
| else { | |
| const nextMeasure = measures.find( | |
| (measure) => | |
| measure.id === selectedTrack?.measures[selectedMeasureNumber + 1] | |
| ); | |
| const durationIdToSelect = nextMeasure.durations[0]; | |
| dispatch({ | |
| type: actionTypes.SELECT_MEASURE, | |
| measureNumber: selectedMeasureNumber + 1, | |
| }); | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: durationIdToSelect, | |
| }); | |
| } | |
| } | |
| }, [ | |
| tracks, | |
| measures, | |
| durations, | |
| selectedTrack, | |
| selectedMeasureNumber, | |
| selectedMeasure, | |
| selectedDurationId, | |
| selectedDuration, | |
| currentBarDuration, | |
| currentBarMaximumDuration, | |
| ]); | |
| const dispatchDeleteMeasure = useCallback(() => { | |
| if (selectedTrack?.measures.length > 1) { | |
| let newSelectedMeasureNumber; | |
| if (selectedMeasureNumber > 0) { | |
| newSelectedMeasureNumber = selectedMeasureNumber - 1; | |
| dispatch({ | |
| type: actionTypes.SELECT_MEASURE, | |
| measureNumber: newSelectedMeasureNumber, | |
| }); | |
| } else { | |
| newSelectedMeasureNumber = selectedMeasureNumber + 1; | |
| } | |
| const durationToSelect = durations.find( | |
| (duration) => | |
| duration.id === | |
| measures.find( | |
| (measure) => | |
| measure.id === selectedTrack?.measures[newSelectedMeasureNumber] | |
| ).durations[0] | |
| ); | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: durationToSelect.id, | |
| }); | |
| // Even though dispatch runs synchronously, selectedMeasureNumber does not change within this closure, | |
| // so this still deletes the correct measure after SELECT_MEASURE has executed | |
| dispatch({ | |
| type: actionTypes.DELETE_MEASURE, | |
| measureNumber: selectedMeasureNumber, | |
| }); | |
| } | |
| }, [measures, durations, selectedTrack, selectedMeasureNumber]); | |
| const dispatchDeleteNote = useCallback(() => { | |
| let needToSelectNewDuration = false; | |
| // If there is a note at this selected duration/string, | |
| if (selectedPositionHasNote) { | |
| // Delete that note | |
| // TODO These lines are horribly inefficient | |
| const selectedNoteId = selectedDuration?.notes.find( | |
| (noteId) => | |
| notes.find((note) => note.id === noteId).string === | |
| selectedStringNumber | |
| ); | |
| dispatch({ type: actionTypes.DELETE_NOTE, noteId: selectedNoteId }); | |
| // If the deleted note was the last one in the selected duration, | |
| if (selectedDuration?.notes.length === 1) { | |
| // Turn the duration into a rest | |
| dispatch({ | |
| type: actionTypes.ADD_REST, | |
| durationId: selectedDurationId, | |
| }); | |
| } | |
| // Don't select a new duration if we're in the first duration of the document | |
| else if ( | |
| !( | |
| selectedMeasureNumber === 0 && | |
| selectedDurationId === selectedMeasure?.durations[0] | |
| ) | |
| ) { | |
| needToSelectNewDuration = true; | |
| } | |
| } else { | |
| // If the selected duration is a rest, | |
| if (selectedDuration?.isRest) { | |
| // If this is the only duration in the measure, | |
| if (selectedMeasure?.durations.length === 1) { | |
| // Change the duration to NOT a rest | |
| dispatch({ | |
| type: actionTypes.MARK_DURATION_AS_NOT_REST, | |
| durationId: selectedDurationId, | |
| }); | |
| } else { | |
| // Delete that duration | |
| dispatch({ | |
| type: actionTypes.DELETE_DURATION, | |
| durationId: selectedDurationId, | |
| }); | |
| needToSelectNewDuration = true; | |
| } | |
| } | |
| } | |
| if (needToSelectNewDuration) { | |
| let durationIdToSelect; | |
| // If the selected duration is first in the measure, | |
| if (selectedDurationId === selectedMeasure?.durations[0]) { | |
| // If the first measure of the document is selected, | |
| if (selectedMeasureNumber === 0) { | |
| // Select the next duration of this measure | |
| durationIdToSelect = | |
| selectedMeasure?.durations[ | |
| selectedMeasure?.durations.findIndex( | |
| (durationId) => durationId === selectedDurationId | |
| ) + 1 | |
| ]; | |
| } else { | |
| // Select the previous measure's last duration | |
| durationIdToSelect = measures[ | |
| selectedMeasureNumber - 1 | |
| ].durations.slice(-1)[0]; | |
| dispatch({ | |
| type: actionTypes.SELECT_MEASURE, | |
| measureNumber: selectedMeasureNumber - 1, | |
| }); | |
| } | |
| } else { | |
| // Select this measure's previous duration | |
| durationIdToSelect = | |
| selectedMeasure?.durations[ | |
| selectedMeasure?.durations.findIndex( | |
| (durationId) => durationId === selectedDurationId | |
| ) - 1 | |
| ]; | |
| } | |
| dispatch({ | |
| type: actionTypes.SELECT_DURATION, | |
| durationId: durationIdToSelect, | |
| }); | |
| } | |
| }, [ | |
| measures, | |
| notes, | |
| selectedMeasureNumber, | |
| selectedMeasure, | |
| selectedDurationId, | |
| selectedDuration, | |
| selectedStringNumber, | |
| selectedPositionHasNote, | |
| ]); | |
| const dispatchAddNote = useCallback( | |
| (fretNumber) => { | |
| const fretInputTime = Date.now(); | |
| setLastFretInputTime(fretInputTime); | |
| // TODO See other comment about "horribly inefficient" | |
| // TODO selectedNote is probably a good variable to have | |
| const currentFretNumber = | |
| notes.find( | |
| (note) => | |
| note.string === selectedStringNumber && | |
| selectedDuration?.notes.includes(note.id) | |
| )?.fret || 0; | |
| const enteredFretNumber = parseInt(fretNumber); | |
| const newFretNumber = currentFretNumber * 10 + enteredFretNumber; | |
| dispatch({ | |
| type: actionTypes.ADD_NOTE, | |
| durationId: selectedDurationId, | |
| id: nanoid(), | |
| string: selectedStringNumber, | |
| fret: | |
| fretInputTime - lastFretInputTime < SAME_FRET_NUMBER_CUTOFF_TIME && | |
| newFretNumber <= MAXIMUM_FRET_NUMBER | |
| ? newFretNumber | |
| : enteredFretNumber, | |
| }); | |
| }, | |
| [ | |
| notes, | |
| selectedDurationId, | |
| selectedDuration, | |
| selectedStringNumber, | |
| lastFretInputTime, | |
| ] | |
| ); |
These can live in a custom hook instead: https://stackoverflow.com/questions/59556939/reactjs-create-a-separate-file-for-functions-that-dispatch-actions
Metadata
Metadata
Assignees
Labels
No labels