Skip to content
This repository has been archived by the owner on Feb 29, 2024. It is now read-only.

Add undo and redo #10

Merged
merged 5 commits into from
Jun 3, 2020
Merged
Show file tree
Hide file tree
Changes from all commits
Commits
File filter

Filter by extension

Filter by extension


Conversations
Failed to load comments.
Loading
Jump to
Jump to file
Failed to load files.
Loading
Diff view
Diff view
20 changes: 18 additions & 2 deletions web/generator/generateParser.ts
Original file line number Diff line number Diff line change
@@ -1,6 +1,6 @@
import peg from "pegjs";

export type ParseResult = { functionName: string, parameterList: { name: string, type: string }[] }[];
export type ParseResult = { functionName: string, parameterList: { name: string, type: string }[], categoryList: string[] }[];

const grammar = `
start
Expand All @@ -16,11 +16,13 @@ Functions
}

Function
= functionName:Name _ "(" _ parameterList:ParameterList? _ ")" {
= functionName:Name _ "(" _ parameterList:ParameterList? _ ")" categoryList:CategoryList? {
var parameterList = parameterList || [];
var categoryList = categoryList || [];
return {
functionName,
parameterList,
categoryList,
}
}

Expand All @@ -46,6 +48,20 @@ Parameter
type,
};
}

CategoryList
= _ category:Category _ categories:CategoryList _ {
return [category, ...categories];
}
/ _ category:Category _ {
return [category];
}

Category
= "-" categoryName:[a-zA-Z0-9]+ {
return categoryName.join("");
}

_ "whitespace"
= [ \\t\\n\\r]*
`;
Expand Down
22 changes: 19 additions & 3 deletions web/generator/index.ts
Original file line number Diff line number Diff line change
@@ -1,11 +1,12 @@
import { genDir, baseDir, beginMark, toActionFileName } from "./utils";
import { genDir, baseDir, toUpperSnake } from "./utils";
import * as path from "path";
import * as fs from 'fs-extra';
import { ParseResult, parser } from "./generateParser";
import { writeStateFile } from "./writeStateFile";
import { writeActionsFile } from './writeActionsFile';
import { writeReducerFile } from "./writeReducerFile";
import { writeRootReducerFile } from "./writeRootReducerFile";
import { writeActionTypeCategoriesFile, Categories } from "./writeActionTypeCategoriesFile";

if (!fs.existsSync(genDir)) {
fs.mkdirSync(genDir);
Expand All @@ -15,14 +16,17 @@ fs.readdir(baseDir, async (err, files) => {
if (err) {
throw err;
}

const categories: Categories = new Map<string, Set<string>>();

const reducerNames = files
.filter(file => file.endsWith('.gen'))
.map((file) => {
const reducerName = file.substring(0, file.lastIndexOf('.gen'));
return reducerName;
});

files.map(async file => {
await Promise.all(files.map(async file => {
if (!file.endsWith('.gen')) {
return;
}
Expand All @@ -34,8 +38,20 @@ fs.readdir(baseDir, async (err, files) => {
await writeStateFile(reducerName);
await writeReducerFile(reducerName);

result.forEach(func => {
func.categoryList.forEach(categoryName => {
if (!categories.has(categoryName)) {
categories.set(categoryName, new Set());
}

categories.get(categoryName)?.add(toUpperSnake(func.functionName));
})
})

console.log(`${file} done`);
});
}));

await writeActionTypeCategoriesFile(categories);

await writeRootReducerFile(reducerNames);
});
26 changes: 26 additions & 0 deletions web/generator/writeActionTypeCategoriesFile.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,26 @@
import * as fs from 'fs-extra';
import * as path from 'path';
import { genDir } from "./utils";

export type Categories = Map<string, Set<string>>;

export async function writeActionTypeCategoriesFile(categories: Categories): Promise<void> {
const actionTypeCategoryStrings: string[] = [];

categories.forEach((category, categoryName) => {
const actionTypeStrings: string[] = [];
category.forEach(actionType => actionTypeStrings.push(` '${actionType}',`));
actionTypeCategoryStrings.push(` export const ${categoryName}ActionTypes = [
${actionTypeStrings.join('\n')}
] as const`)
});

const fileContent = `export namespace ActionTypeCategory {
${actionTypeCategoryStrings.join('\n\n')}
}
`;

const destFile = path.join(genDir, 'actionTypeCategory.ts');

await fs.writeFile(destFile, fileContent);
}
5 changes: 5 additions & 0 deletions web/package-lock.json

Some generated files are not rendered by default. Learn more about how customized files appear on GitHub.

1 change: 1 addition & 0 deletions web/package.json
Original file line number Diff line number Diff line change
Expand Up @@ -20,6 +20,7 @@
"react-dom": "^16.13.1",
"react-redux": "^7.2.0",
"redux": "^4.0.5",
"redux-batched-actions": "^0.5.0",
"typescript": "^3.9.2",
"uuid": "^8.0.0"
},
Expand Down
34 changes: 3 additions & 31 deletions web/src/Config/ModeSelectHandler.ts
Original file line number Diff line number Diff line change
@@ -1,12 +1,9 @@
import { store, dispatch } from "~StateStore/store";
import { store } from "~StateStore/store";
import { Mode } from "~NoteView/types";
import { CancellationToken, runAddLongNoteProcess } from "~runAddLongNoteProcess";
import { LongNoteAction } from "~StateStore/_gen/longNote_action.ts";
import { cancleEditingLongNote } from "~NoteView/longNoteEditHandler";

export class ModeSelectHandler {
private prevMode?: Mode;
private static longNoteProcessCancellationToken?: CancellationToken;
private static runAddLongNoteProcessPromise?: ReturnType<typeof runAddLongNoteProcess>;
constructor() {
store.subscribe(() => {
const { mode } = store.getState().modeState;
Expand All @@ -20,33 +17,8 @@ export class ModeSelectHandler {
if (previousMode !== nextMode) {
console.log(`mode changed. ${previousMode}-> ${nextMode}`);
if (previousMode === 'longNoteEdit') {
this.cancelAddLongNoteProcess();
}
if (nextMode === 'longNoteEdit' && !this.runAddLongNoteProcessPromise) {
this.initRunAddLongNoteProcess();
cancleEditingLongNote();
}
}
}

private static initRunAddLongNoteProcess() {
this.longNoteProcessCancellationToken = new CancellationToken();
this.runAddLongNoteProcessPromise = runAddLongNoteProcess(this.longNoteProcessCancellationToken)
.catch((reason) => {
if (reason !== 'token canceled' && reason.message !== 'canceled') {
throw reason;
}
this.cancelAddLongNoteProcess();
})
.finally(() => {
if (store.getState().modeState.mode === 'longNoteEdit') {
this.initRunAddLongNoteProcess();
}
});
}

public static cancelAddLongNoteProcess() {
this.longNoteProcessCancellationToken?.cancel();
this.runAddLongNoteProcessPromise = undefined;
dispatch(LongNoteAction.finishEditingLongNote());
}
}
10 changes: 8 additions & 2 deletions web/src/Inspector/ChangeBarBeatComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -3,9 +3,10 @@ import { Bar } from '~NoteView/types';
import { isNumber } from 'util';
import { dispatch } from '~StateStore/store';
import { BarAction } from '~StateStore/_gen/bar_action.ts';
import { removeOverFlowedNotes } from '~utils/note';
import { Button, InputAdornment, TextField, InputLabel, Select, MenuItem} from '@material-ui/core';
import { convertBarSecondToBeat, convertBarBeatToSecond } from '~utils/bar';
import { batchActions } from 'redux-batched-actions';
import { LongNoteAction } from '~StateStore/_gen/longNote_action.ts';

const durationUnits = ['second', 'beat'] as const;
type DurationUnit = typeof durationUnits[number];
Expand Down Expand Up @@ -84,7 +85,12 @@ export default class ChangeBarBeatComponent extends Component<ChangeBarBeatInput
: inputNumber;

if (bar.beat > beat) {
removeOverFlowedNotes(beat, bar.id, barIndex);
dispatch(batchActions([
LongNoteAction.removeOverflowedLongNotes(bar.id, beat),
BarAction.removeOverflowedNotes(barIndex, beat),
BarAction.changeBarBeat(barIndex, beat),
]));
return;
}

dispatch(BarAction.changeBarBeat(barIndex, beat));
Expand Down
13 changes: 9 additions & 4 deletions web/src/Inspector/CursorLocatedBarInspectorComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -6,6 +6,7 @@ import { LongNoteAction } from '~StateStore/_gen/longNote_action.ts';
import { BarAction } from '~StateStore/_gen/bar_action.ts';
import ChangeBarBeatComponent from './ChangeBarBeatComponent';
import {ButtonGroup, Button, Container, CardContent } from '@material-ui/core';
import { batchActions } from 'redux-batched-actions';

export const CursorLocatedBarInspectorComponent = () => {
const isPlaying = useSelector((state: RootState) => state.playerState.isPlaying);
Expand All @@ -22,12 +23,16 @@ export const CursorLocatedBarInspectorComponent = () => {
}

const removeBar = () => {
dispatch(LongNoteAction.removeLongNotesOnBar(bar.id));
dispatch(BarAction.removeNotesOnBar(barIndex));
dispatch(BarAction.removeBar(barIndex));
dispatch(batchActions([
LongNoteAction.removeLongNotesOnBar(bar.id),
BarAction.removeNotesOnBar(barIndex),
BarAction.removeBar(barIndex),
]));
}

const insertBar = () => dispatch(BarAction.insertNewBar(barIndex, store.getState().configState.defaultBarBeat));
const insertBar = () => {
dispatch(BarAction.insertNewBar(barIndex, store.getState().configState.defaultBarBeat));
}

const Buttons = () => <ButtonGroup
fullWidth
Expand Down
1 change: 0 additions & 1 deletion web/src/Inspector/SelectedNoteInspectorComponent.tsx
Original file line number Diff line number Diff line change
Expand Up @@ -5,7 +5,6 @@ import { noteTypes, NoteType } from '~NoteView/types';
import { BarAction } from '~StateStore/_gen/bar_action.ts';
import { Divider, MenuItem, Select, FormControl, InputLabel, CardContent } from '@material-ui/core';


export const SelectedNoteInspectorComponent = () => {
const selectedNoteId = useSelector((state: RootState) => state.selectNoteState.selectedNoteId);
const bars = store.getState().barState.bars;
Expand Down
14 changes: 9 additions & 5 deletions web/src/NoteView/BarRenderer.ts
Original file line number Diff line number Diff line change
Expand Up @@ -4,8 +4,9 @@ import { dispatch, store } from "~StateStore/store";
import { BarAction } from "~StateStore/_gen/bar_action.ts";
import { SelectNoteAction } from "~StateStore/_gen/selectNote_action.ts";
import uuid from "~utils/uuid";
import { ModeSelectHandler } from "~Config/ModeSelectHandler";
import { removeNote } from "~utils/note";
import { batchActions } from "redux-batched-actions";
import { handleLeftClick, cancleEditingLongNote } from "./longNoteEditHandler";

interface BarRendererProps {
bar: Bar;
Expand Down Expand Up @@ -68,6 +69,7 @@ export class BarRenderer extends Drawable<BarRendererProps> {
})));

if (mode === 'longNoteEdit') {
handleLeftClick(guideDotOnMouse);
break;
}

Expand All @@ -84,8 +86,8 @@ export class BarRenderer extends Drawable<BarRendererProps> {
} break;

case 2: { // right click
if (mode === 'longNoteEdit') {
ModeSelectHandler.cancelAddLongNoteProcess();
if (store.getState().longNoteState.editingLongNote) {
cancleEditingLongNote();
}

const noteOnBeatKey = this.getNoteOnBeatKey(beatKey)
Expand Down Expand Up @@ -126,8 +128,10 @@ export class BarRenderer extends Drawable<BarRendererProps> {
type: store.getState().modeState.noteTypeMode,
});

dispatch(BarAction.addNote(this.props.barIndex, newNote));
dispatch(SelectNoteAction.selectNote(newNote.id));
dispatch(batchActions([
BarAction.addNote(this.props.barIndex, newNote),
SelectNoteAction.selectNote(newNote.id),
]));
}

render(context: CanvasRenderingContext2D): void {
Expand Down
92 changes: 92 additions & 0 deletions web/src/NoteView/longNoteEditHandler.ts
Original file line number Diff line number Diff line change
@@ -0,0 +1,92 @@
import { Position, LongNote, Note } from "./types";
import { store, dispatch } from "~StateStore/store";
import { getNoteOnPosition } from "~utils/note";
import { SelectNoteAction } from "~StateStore/_gen/selectNote_action.ts";
import { LongNoteAction } from "~StateStore/_gen/longNote_action.ts";
import uuid from "~utils/uuid";
import { BarAction } from "~StateStore/_gen/bar_action.ts";
import { getBarIndex } from "~utils/bar";
import { batchActions } from "redux-batched-actions";
import { List } from "immutable";

function setStartNote(position: Position, noteOnPosition?: Note) {
if (noteOnPosition) {
const newLongNote = new LongNote({
id: uuid(),
startNote: noteOnPosition,
})

dispatch(batchActions([
LongNoteAction.updateEditingLongNote(newLongNote),
SelectNoteAction.selectNote(noteOnPosition.id),
]))
return;
}

const newNote = new Note({
position,
id: uuid(),
type: store.getState().modeState.noteTypeMode
});

const newLongNote = new LongNote({
id: uuid(),
startNote: newNote,
})

dispatch(batchActions([
BarAction.addNote(getBarIndex(position.barId), newNote),
LongNoteAction.updateEditingLongNote(newLongNote),
SelectNoteAction.selectNote(newNote.id),
]));
}

export function cancleEditingLongNote() {
dispatch(LongNoteAction.finishEditingLongNote());
}

export function handleLeftClick(position: Position) {
const editingLongNote = store.getState().longNoteState.editingLongNote;
const noteOnPosition = getNoteOnPosition(position);

if (!editingLongNote) {
setStartNote(position, noteOnPosition);
return;
}

if (noteOnPosition) {
dispatch(batchActions([
LongNoteAction.addLongNote((editingLongNote as LongNote).set('endNote', noteOnPosition)),
SelectNoteAction.selectNote(noteOnPosition.id),
LongNoteAction.finishEditingLongNote(),
]))
}

const middlePoints: List<Position> = editingLongNote.middlePoints || List();
const lastMiddlePoint = middlePoints.last(0) || undefined;
console.log(middlePoints.last(0), position);
const isLastMiddlePointClicked = lastMiddlePoint?.barId === position.barId
&& lastMiddlePoint?.beat === position.beat
&& lastMiddlePoint?.key === position.key;

if (isLastMiddlePointClicked) {
middlePoints.pop();
const newNote = new Note({
position,
id: uuid(),
type: store.getState().modeState.noteTypeMode
});

dispatch(batchActions([
BarAction.addNote(getBarIndex(newNote.position.barId), newNote),
LongNoteAction.addLongNote((editingLongNote as LongNote)
.set('middlePoints', middlePoints.pop())
.set('endNote', newNote)),
SelectNoteAction.selectNote(newNote.id),
LongNoteAction.finishEditingLongNote(),
]))
return;
}

dispatch(LongNoteAction.updateEditingLongNote((editingLongNote as LongNote).set('middlePoints', middlePoints.push(position))));
}
Loading