Skip to content
New issue

Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.

By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.

Already on GitHub? Sign in to your account

move action definitions into separate file #64

Open
aqandrew opened this issue Jul 28, 2023 · 0 comments
Open

move action definitions into separate file #64

aqandrew opened this issue Jul 28, 2023 · 0 comments

Comments

@aqandrew
Copy link
Owner

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

Sign up for free to join this conversation on GitHub. Already have an account? Sign in to comment
Labels
None yet
Projects
None yet
Development

No branches or pull requests

1 participant