Skip to content

move action definitions into separate file #64

@aqandrew

Description

@aqandrew

Action definitions account for almost half of App.jsx's length:

fret-zone/src/App.jsx

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

No one assigned

    Labels

    No labels
    No labels

    Projects

    No projects

    Milestone

    No milestone

    Relationships

    None yet

    Development

    No branches or pull requests

    Issue actions