Skip to content

Commit

Permalink
#12 Add measure selection and normalize section in state
Browse files Browse the repository at this point in the history
  • Loading branch information
tscz committed Jan 6, 2020
1 parent 8f7da5d commit d8897d6
Show file tree
Hide file tree
Showing 12 changed files with 202 additions and 78 deletions.
4 changes: 2 additions & 2 deletions src/components/audioManagement/audioManagement.tsx
Expand Up @@ -15,7 +15,7 @@ import {
LoadingStatus,
triggeredPause
} from "../../states/audioSlice";
import { ApplicationState } from "../../states/store";
import { ApplicationState, NormalizedObjects } from "../../states/store";
import { initializedWaveform } from "../../states/waveSlice";
import AudioPlayer from "./audioPlayer";
import PeaksOptions, {
Expand All @@ -26,7 +26,7 @@ import PeaksOptions, {
interface PropsFromState {
audioUrl: string;
status: LoadingStatus;
sections: Section[];
sections: NormalizedObjects<Section>;
measures: Measure[];
zoomLevel: number;
firstMeasureStart: number;
Expand Down
7 changes: 5 additions & 2 deletions src/components/audioManagement/peaksConfig.ts
@@ -1,6 +1,7 @@
import { SegmentAddOptions } from "peaks.js";

import { Section, SectionType } from "../../states/analysisSlice";
import { NormalizedObjects } from "../../states/store";

export const AUDIO_DOM_ELEMENT = "audio_dom_element";
export const ZOOMVIEW_CONTAINER = "zoomview-container";
Expand Down Expand Up @@ -30,9 +31,11 @@ class PeaksOptions {
};
};

static sectionsToSegment = (sections: Section[]) => {
static sectionsToSegment = (sections: NormalizedObjects<Section>) => {
let segments: SegmentAddOptions[] = [];
sections.forEach(section => {
sections.allIds.forEach(id => {
const section = sections.byId[id];

let segment: SegmentAddOptions = {
id:
section.type + "_" + section.firstMeasure + "-" + section.lastMeasure,
Expand Down
23 changes: 23 additions & 0 deletions src/components/measureSelect/measureSelect.stories.tsx
@@ -0,0 +1,23 @@
import React, { useState } from "react";

import MeasureSelect from "./measureSelect";

export default {
title: "Components|MeasureSelect",
component: MeasureSelect
};

export const Basic = () => {
const [measure, setMeasure] = useState(4);

return (
<>
<MeasureSelect
onChange={measure => setMeasure(measure)}
value={measure}
min={0}
max={14}
/>
</>
);
};
10 changes: 10 additions & 0 deletions src/components/measureSelect/measureSelect.test.tsx
@@ -0,0 +1,10 @@
import React from "react";

import TestEnvironment from "../../tests/TestEnvironment";
import MeasureSelect from "./measureSelect";

it("renders without crashing", () => {
TestEnvironment.smokeTest(
<MeasureSelect onChange={() => {}} value={4} min={0} max={14} />
);
});
37 changes: 37 additions & 0 deletions src/components/measureSelect/measureSelect.tsx
@@ -0,0 +1,37 @@
import { MenuItem } from "@material-ui/core";
import Select from "@material-ui/core/Select/Select";
import React, { FunctionComponent } from "react";

const MeasureSelect: FunctionComponent<{
value: number;
min: number;
max: number;
onChange: (value: number) => void;
}> = props => {
const handleChange = (event: React.ChangeEvent<{ value: unknown }>) => {
let newMeasure = event.target.value as number;
props.onChange(newMeasure);
};

return (
<Select
value={props.value}
onChange={handleChange}
style={{
minWidth: "40px"
}}
>
{new Array<number>(props.max - props.min + 1)
.fill(0)
.map((_, idx: number) => {
return (
<MenuItem key={props.min + idx} value={props.min + idx}>
{props.min + idx}
</MenuItem>
);
})}
</Select>
);
};

export default MeasureSelect;
20 changes: 12 additions & 8 deletions src/components/sectionSelect/sectionSelect.stories.tsx
@@ -1,4 +1,4 @@
import React from "react";
import React, { useState } from "react";

import { SectionType } from "../../states/analysisSlice";
import SectionSelect from "./sectionSelect";
Expand All @@ -8,11 +8,15 @@ export default {
component: SectionSelect
};

var currentSelection = SectionType.INTRO;
export const Basic = () => {
const [section, setSection] = useState(SectionType.BRIDGE);

export const Basic = () => (
<SectionSelect
value={currentSelection}
onChange={sectionType => (currentSelection = sectionType)}
></SectionSelect>
);
return (
<>
<SectionSelect
value={section}
onChange={sectionType => setSection(sectionType)}
></SectionSelect>
</>
);
};
11 changes: 11 additions & 0 deletions src/components/sectionSelect/sectionSelect.test.tsx
@@ -0,0 +1,11 @@
import React from "react";

import { SectionType } from "../../states/analysisSlice";
import TestEnvironment from "../../tests/TestEnvironment";
import SectionSelect from "./sectionSelect";

it("renders without crashing", () => {
TestEnvironment.smokeTest(
<SectionSelect onChange={() => {}} value={SectionType.BRIDGE} />
);
});
4 changes: 3 additions & 1 deletion src/components/sectionSelect/sectionSelect.tsx
Expand Up @@ -19,12 +19,14 @@ const SectionSelect: FunctionComponent<{
value={props.value}
onChange={handleChange}
style={{
backgroundColor: PeaksOptions.SECTIONTYPE_TO_COLOR.get(props.value)
backgroundColor: PeaksOptions.SECTIONTYPE_TO_COLOR.get(props.value),
minWidth: "140px"
}}
>
{Object.values(SectionType).map(sectionType => {
return (
<MenuItem
key={sectionType}
value={sectionType}
style={{
backgroundColor: PeaksOptions.SECTIONTYPE_TO_COLOR.get(
Expand Down
21 changes: 14 additions & 7 deletions src/states/analysisSlice.test.ts
Expand Up @@ -14,7 +14,7 @@ import { Page } from "./projectSlice";
import { PersistedState } from "./store";

const initialState: AnalysisState = {
sections: [],
sections: { allIds: [], byId: {} },
bpm: 42,
firstMeasureStart: 3,
secondsPerMeasure: 2.423,
Expand All @@ -25,7 +25,7 @@ const initialState: AnalysisState = {
};

const persistedAnalysisState: AnalysisState = {
sections: [],
sections: { allIds: [], byId: {} },
bpm: 120,
firstMeasureStart: 1,
secondsPerMeasure: 3,
Expand Down Expand Up @@ -58,7 +58,7 @@ it("can reset an analysis from a persisted state", () => {
resettedAnalysis({ state: persistedState })
);
expect(state).toEqual({
sections: [],
sections: { allIds: [], byId: {} },
bpm: 120,
firstMeasureStart: 1,
secondsPerMeasure: 3,
Expand Down Expand Up @@ -128,8 +128,13 @@ it("can add a section", () => {
lastMeasure: 10
};

let id =
section.type + "_" + section.firstMeasure + "_" + section.lastMeasure;

let state: AnalysisState = reducer(undefined, addedSection(section));
expect(state.sections).toContainEqual(section);

expect(state.sections.allIds).toContainEqual(id);
expect(state.sections.byId[id]).toEqual(section);
});

it("can remove a section", () => {
Expand All @@ -141,8 +146,10 @@ it("can remove a section", () => {

let state: AnalysisState = reducer(undefined, addedSection(section));

expect(state.sections.length).toBe(1);
expect(state.sections.allIds.length).toBe(1);
expect(state.sections.byId["BRIDGE_0_10"]).toEqual(section);

state = reducer(state, removedSection("BRIDGE_0-10"));
expect(state.sections.length).toBe(0);
state = reducer(state, removedSection("BRIDGE_0_10"));
expect(state.sections.allIds.length).toBe(0);
expect(state.sections.byId["BRIDGE_0_10"]).toEqual(undefined);
});
48 changes: 22 additions & 26 deletions src/states/analysisSlice.ts
@@ -1,10 +1,10 @@
import { createSlice, PayloadAction } from "@reduxjs/toolkit";
import { PointAddOptions } from "peaks.js";

import { PersistedState } from "./store";
import { NormalizedObjects, PersistedState } from "./store";

export interface AnalysisState {
readonly sections: Section[];
readonly sections: NormalizedObjects<Section>;
readonly audioLength: number;
readonly audioSampleRate: number;
readonly firstMeasureStart: number;
Expand Down Expand Up @@ -43,7 +43,7 @@ export enum SectionType {
}

export const initialAnalysisState: AnalysisState = {
sections: [],
sections: { allIds: [], byId: {} },
bpm: 120,
firstMeasureStart: 0,
secondsPerMeasure: 2,
Expand All @@ -58,37 +58,30 @@ const analysisSlice = createSlice({
initialState: initialAnalysisState,
reducers: {
addedSection(state, action: PayloadAction<Section>) {
state.sections.push(action.payload);
const id = generateSectionId(action.payload);
state.sections.allIds.push(id);
state.sections.byId[id] = action.payload;
},
updatedSection(
state,
action: PayloadAction<{ before: Section; after: Section }>
action: PayloadAction<{ before: string; after: Section }>
) {
state.sections = state.sections.filter(
section =>
section.type +
"_" +
section.firstMeasure +
"-" +
section.lastMeasure !==
action.payload.before.type +
"_" +
action.payload.before.firstMeasure +
"-" +
action.payload.before.lastMeasure
state.sections.allIds = state.sections.allIds.filter(
id => id !== action.payload.before
);
state.sections.push(action.payload.after);

delete state.sections.byId[action.payload.before];

const id = generateSectionId(action.payload.after);
state.sections.allIds.push(id);
state.sections.byId[id] = action.payload.after;
},
removedSection(state, action: PayloadAction<string>) {
state.sections = state.sections.filter(
section =>
section.type +
"_" +
section.firstMeasure +
"-" +
section.lastMeasure !==
action.payload
state.sections.allIds = state.sections.allIds.filter(
id => id !== action.payload
);

delete state.sections.byId[action.payload];
},
resettedAnalysis(state, action: PayloadAction<{ state?: PersistedState }>) {
if (action.payload.state?.analysis) {
Expand Down Expand Up @@ -166,6 +159,9 @@ const analysisSlice = createSlice({
}
});

const generateSectionId = (section: Section) =>
section.type + "_" + section.firstMeasure + "_" + section.lastMeasure;

export const {
addedSection,
removedSection,
Expand Down
6 changes: 6 additions & 0 deletions src/states/store.ts
Expand Up @@ -20,6 +20,12 @@ export interface PersistedState {
analysis: AnalysisState;
project: ProjectState;
}

export interface NormalizedObjects<T> {
byId: { [id: string]: T };
allIds: string[];
}

export const restoredPersistedState = createAction<PersistedState>(
"restoredPersistedState"
);
Expand Down

0 comments on commit d8897d6

Please sign in to comment.