diff --git a/js/components/App.js b/js/components/App.js
index c5e198a9..15195835 100644
--- a/js/components/App.js
+++ b/js/components/App.js
@@ -120,6 +120,7 @@ class App extends React.Component {
analyser={media.getAnalyser()}
width={width}
height={height}
+ playing={this.props.status === "PLAYING"}
/>
)}
@@ -154,6 +155,7 @@ App.propTypes = {
};
const mapStateToProps = state => ({
+ status: state.media.status,
focused: state.windows.focused,
closed: state.display.closed,
genWindowsInfo: state.windows.genWindows
diff --git a/js/components/MilkdropWindow/Background.js b/js/components/MilkdropWindow/Background.js
new file mode 100644
index 00000000..9561d892
--- /dev/null
+++ b/js/components/MilkdropWindow/Background.js
@@ -0,0 +1,26 @@
+import React from "react";
+
+const Background = props => {
+ const { innerRef, ...restProps } = props;
+ return (
+
+ );
+};
+
+export default Background;
diff --git a/js/components/MilkdropWindow/Milkdrop.js b/js/components/MilkdropWindow/Milkdrop.js
new file mode 100644
index 00000000..7abd6e67
--- /dev/null
+++ b/js/components/MilkdropWindow/Milkdrop.js
@@ -0,0 +1,202 @@
+import React from "react";
+import screenfull from "screenfull";
+import PresetOverlay from "./PresetOverlay";
+import Background from "./Background";
+
+const USER_PRESET_TRANSITION_SECONDS = 5.7;
+const PRESET_TRANSITION_SECONDS = 2.7;
+const MILLISECONDS_BETWEEN_PRESET_TRANSITIONS = 15000;
+
+export default class Milkdrop extends React.Component {
+ constructor(props) {
+ super(props);
+ this.state = {
+ isFullscreen: false,
+ presetOverlay: false
+ };
+ this._handleFocusedKeyboardInput = this._handleFocusedKeyboardInput.bind(
+ this
+ );
+ this._handleFullscreenChange = this._handleFullscreenChange.bind(this);
+ }
+
+ async componentDidMount() {
+ this.setState({ currentPreset: this.props.presets.getCurrentIndex() });
+ this.visualizer = this.props.butterchurn.createVisualizer(
+ this.props.analyser.context,
+ this._canvasNode,
+ {
+ width: this.props.width,
+ height: this.props.height,
+ pixelRatio: window.devicePixelRatio || 1
+ }
+ );
+ this._setRendererSize(this.props.width, this.props.height);
+
+ this.visualizer.connectAudio(this.props.analyser);
+
+ // Kick off the animation loop
+ const loop = () => {
+ if (this.props.playing) {
+ this.visualizer.render();
+ }
+ this._animationFrameRequest = window.requestAnimationFrame(loop);
+ };
+ loop();
+
+ this.presetCycle = true;
+ this._unsubscribeFocusedKeyDown = this.props.onFocusedKeyDown(
+ this._handleFocusedKeyboardInput
+ );
+ screenfull.onchange(this._handleFullscreenChange);
+ }
+
+ componentWillUnmount() {
+ this._pauseViz();
+ this._stopCycling();
+ if (this._unsubscribeFocusedKeyDown) {
+ this._unsubscribeFocusedKeyDown();
+ }
+ screenfull.off("change", this._handleFullscreenChange);
+ }
+
+ componentDidUpdate(prevProps) {
+ if (
+ this.props.width !== prevProps.width ||
+ this.props.height !== prevProps.height
+ ) {
+ this._setRendererSize(this.props.width, this.props.height);
+ }
+ }
+
+ _pauseViz() {
+ if (this._animationFrameRequest) {
+ window.cancelAnimationFrame(this._animationFrameRequest);
+ this._animationFrameRequest = null;
+ }
+ }
+
+ _stopCycling() {
+ if (this.cycleInterval) {
+ clearInterval(this.cycleInterval);
+ this.cycleInterval = null;
+ }
+ }
+
+ _restartCycling() {
+ this._stopCycling();
+
+ if (this.presetCycle) {
+ this.cycleInterval = setInterval(() => {
+ this._nextPreset(PRESET_TRANSITION_SECONDS);
+ }, MILLISECONDS_BETWEEN_PRESET_TRANSITIONS);
+ }
+ }
+
+ _setRendererSize(width, height) {
+ this._canvasNode.width = width;
+ this._canvasNode.height = height;
+ this.visualizer.setRendererSize(width, height);
+ }
+
+ _handleFullscreenChange() {
+ if (screenfull.isFullscreen) {
+ this._setRendererSize(window.innerWidth, window.innerHeight);
+ } else {
+ this._setRendererSize(this.props.width, this.props.height);
+ }
+ this.setState({ isFullscreen: screenfull.isFullscreen });
+ }
+
+ _handleRequestFullsceen() {
+ if (screenfull.enabled) {
+ if (!screenfull.isFullscreen) {
+ screenfull.request(this._wrapperNode);
+ } else {
+ screenfull.exit();
+ }
+ }
+ }
+
+ _handleFocusedKeyboardInput(e) {
+ switch (e.keyCode) {
+ case 32: // spacebar
+ this._nextPreset(USER_PRESET_TRANSITION_SECONDS);
+ break;
+ case 8: // backspace
+ this._prevPreset(0);
+ break;
+ case 72: // H
+ this._nextPreset(0);
+ break;
+ case 82: // R
+ this.props.presets.toggleRandomize();
+ break;
+ case 76: // L
+ this.setState({ presetOverlay: !this.state.presetOverlay });
+ e.stopPropagation();
+ break;
+ case 145: // scroll lock
+ case 125: // F14 (scroll lock for OS X)
+ this.presetCycle = !this.presetCycle;
+ this._restartCycling();
+ break;
+ }
+ }
+
+ async _nextPreset(blendTime) {
+ this.selectPreset(await this.props.presets.next(), blendTime);
+ }
+
+ async _prevPreset(blendTime) {
+ this.selectPreset(await this.props.presets.previous(), blendTime);
+ }
+
+ selectPreset(preset, blendTime = 0) {
+ if (preset != null) {
+ this.visualizer.loadPreset(preset, blendTime);
+ this._restartCycling();
+ this.setState({ currentPreset: this.props.presets.getCurrentIndex() });
+ }
+ }
+
+ closePresetOverlay() {
+ this.setState({ presetOverlay: false });
+ }
+
+ render() {
+ const width = this.state.isFullscreen
+ ? window.innerWidth
+ : this.props.width;
+ const height = this.state.isFullscreen
+ ? window.innerHeight
+ : this.props.height;
+ return (
+ (this._wrapperNode = node)}
+ onDoubleClick={() => this._handleRequestFullsceen()}
+ >
+ {this.state.presetOverlay && (
+ this.props.onFocusedKeyDown(listener)}
+ selectPreset={async idx => {
+ this.selectPreset(await this.props.presets.selectIndex(idx), 0);
+ }}
+ closeOverlay={() => this.closePresetOverlay()}
+ />
+ )}
+
+ );
+ }
+}
diff --git a/js/components/MilkdropWindow/PresetOverlay.js b/js/components/MilkdropWindow/PresetOverlay.js
index e1a9aac4..91e61f46 100644
--- a/js/components/MilkdropWindow/PresetOverlay.js
+++ b/js/components/MilkdropWindow/PresetOverlay.js
@@ -8,16 +8,19 @@ class PresetOverlay extends React.Component {
this
);
}
+
componentDidMount() {
this._unsubscribeFocusedKeyDown = this.props.onFocusedKeyDown(
this._handleFocusedKeyboardInput
);
}
+
componentWillUnmount() {
if (this._unsubscribeFocusedKeyDown) {
this._unsubscribeFocusedKeyDown();
}
}
+
_handleFocusedKeyboardInput(e) {
switch (e.keyCode) {
case 38: // up arrow
@@ -43,6 +46,7 @@ class PresetOverlay extends React.Component {
break;
}
}
+
render() {
if (!this.props.presetKeys) {
return (
diff --git a/js/components/MilkdropWindow/Presets.js b/js/components/MilkdropWindow/Presets.js
new file mode 100644
index 00000000..69acc746
--- /dev/null
+++ b/js/components/MilkdropWindow/Presets.js
@@ -0,0 +1,102 @@
+function getRandomIndex(arr) {
+ return Math.floor(Math.random() * arr.length);
+}
+function getRandomValue(arr) {
+ return arr[getRandomIndex(arr)];
+}
+
+function getLast(arr) {
+ return arr[arr.length - 1];
+}
+
+/**
+ * Track a collection of async loaded presets
+ *
+ * Presets can be changed via `next`, `previous` or `selectIndex`. In each case,
+ * a promise is returned. If the promise resolves to `null` it means the
+ * selection was canceled by a subsequent request before it could be fulfilled.
+ * If the request is successful, the promise resolves to the selected preset.
+ *
+ * We assume a model where some portion of the preset are supplied at initialization
+ * and the remainder can be loaded async via the function `getRest`.
+ */
+export default class Presets {
+ constructor({ keys, initialPresets, getRest, randomize = true }) {
+ this._keys = keys; // Alphabetical list of preset names
+ this._presets = initialPresets; // Presets indexed by name
+ this._getRest = getRest; // An async function to get the rest of the presets
+ this._history = []; // Indexes into _keys
+
+ this._randomize = randomize;
+
+ // Initialize with a key that we already have.
+ const avaliableKeys = Object.keys(initialPresets);
+ const currentKey = randomize
+ ? getRandomValue(avaliableKeys)
+ : avaliableKeys[0];
+ this._currentIndex = this._keys.indexOf(currentKey);
+ this._history.push(this._currentIndex);
+ }
+
+ toggleRandomize() {
+ this._randomize = !this._randomize;
+ }
+
+ setRandomize(val) {
+ this._randomize = val;
+ }
+
+ async next() {
+ let idx;
+ if (this._randomize || this._history.length === 0) {
+ idx = getRandomIndex(this._keys);
+ } else {
+ idx = (getLast(this._history) + 1) % this._keys.length;
+ }
+ this._history.push(idx);
+ return this._selectIndex(idx);
+ }
+
+ async previous() {
+ if (this._history.length > 1) {
+ this._history.pop();
+ return this._selectIndex(getLast(this._history));
+ }
+ // We are at the very beginning. There is no "previous" preset.
+ return Promise.resolve();
+ }
+
+ async selectIndex(idx) {
+ // The public version of this method must add to the history
+ this._history.push(idx);
+ return this._selectIndex(idx);
+ }
+
+ async _selectIndex(idx) {
+ const preset = this._presets[this._keys[idx]];
+ if (!preset) {
+ const rest = await this._getRest();
+ this._presets = Object.assign(this._presets, rest);
+ if (getLast(this._history) !== idx) {
+ // This selection must be obsolete. Return null so that
+ // the caller knows this request got canceled.
+ return null;
+ }
+ }
+ this._currentIndex = idx;
+ return this.getCurrent();
+ }
+
+ getKeys() {
+ return this._keys;
+ }
+
+ getCurrentIndex() {
+ return this._currentIndex;
+ }
+
+ getCurrent() {
+ // #matryoshka
+ return this._presets[this._keys[this._currentIndex]];
+ }
+}
diff --git a/js/components/MilkdropWindow/__tests__/Presets.test.js b/js/components/MilkdropWindow/__tests__/Presets.test.js
new file mode 100644
index 00000000..b3080e23
--- /dev/null
+++ b/js/components/MilkdropWindow/__tests__/Presets.test.js
@@ -0,0 +1,173 @@
+import { mockRandom } from "jest-mock-random";
+import Presets from "../Presets";
+
+let presets;
+beforeEach(() => {
+ mockRandom([0.0]);
+ presets = new Presets({
+ keys: ["a", "b"],
+ initialPresets: { a: "Preset A", b: "Preset B" },
+ randomize: true
+ });
+});
+
+describe("initialize", () => {
+ beforeEach(() => {});
+ test("picks a random value", () => {
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+
+ test("picks another random value", () => {
+ mockRandom([0.9]);
+ presets = new Presets({
+ keys: ["a", "b"],
+ initialPresets: { a: "Preset A", b: "Preset B" },
+ randomize: true
+ });
+ expect(presets.getCurrent()).toBe("Preset B");
+ });
+
+ test("picks its random value from the initial presets", () => {
+ presets = new Presets({
+ keys: ["a", "b", "c", "d", "e", "f", "g", "h"],
+ initialPresets: { a: "Preset A" },
+ randomize: true
+ });
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+});
+
+describe("next", () => {
+ test("picks a 'random' preset", async () => {
+ mockRandom([0.9]);
+ presets.next();
+ expect(presets.getCurrent()).toBe("Preset B");
+
+ mockRandom([0.0]);
+ presets.next();
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+
+ test("picks the next key", async () => {
+ presets.setRandomize(false);
+ presets.next();
+ expect(presets.getCurrent()).toBe("Preset B");
+ });
+
+ test("wraps around", async () => {
+ presets.setRandomize(false);
+ presets.next();
+ presets.next();
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+});
+
+describe("previous", () => {
+ test("picks the previous key", async () => {
+ presets.setRandomize(false);
+ presets.next();
+ presets.previous();
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+
+ test("does nothing when you are on the first item", async () => {
+ presets.previous();
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+
+ test("can do sequential previouses", async () => {
+ mockRandom([0.0]);
+ presets = new Presets({
+ keys: ["a", "b", "c", "d"],
+ initialPresets: {
+ a: "Preset A",
+ b: "Preset B",
+ c: "Preset C",
+ d: "Preset D"
+ },
+ randomize: false
+ });
+ presets.next(); // b
+ presets.next(); // c
+ presets.next(); // d
+ presets.previous(); // c
+ presets.previous(); // b
+ presets.previous(); // a
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+
+ test("will successfully resolve an unloaded preset", async () => {
+ mockRandom([0.0]);
+ presets = new Presets({
+ keys: ["a", "b", "c", "d"],
+ initialPresets: {
+ a: "Preset A"
+ },
+ randomize: false,
+ getRest: () =>
+ Promise.resolve({
+ b: "Preset B",
+ c: "Preset C"
+ })
+ });
+ presets.next(); // b
+ presets.next(); // c
+ const final = await presets.previous(); // b
+ expect(final).toBe("Preset B");
+ });
+});
+
+describe("getRest", () => {
+ beforeEach(() => {
+ mockRandom([0.0]);
+ presets = new Presets({
+ keys: ["a", "b"],
+ initialPresets: { a: "Preset A" },
+ getRest: () =>
+ Promise.resolve({
+ b: "Preset B"
+ })
+ });
+ });
+ test("will get the rest of the presets if needed", async () => {
+ mockRandom([0.9]);
+ const resolved = presets.next();
+ expect(presets.getCurrent()).toBe("Preset A");
+ await resolved;
+ expect(presets.getCurrent()).toBe("Preset B");
+ });
+
+ test("next (loading), previous brings us back to where we started", async () => {
+ presets.setRandomize(false);
+ presets.next();
+ expect(presets.getCurrent()).toBe("Preset A");
+ await presets.previous();
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+});
+
+describe("selectIndex", () => {
+ test("adds an entry to the history", async () => {
+ presets.selectIndex(1);
+ presets.previous();
+ expect(presets.getCurrent()).toBe("Preset A");
+ });
+});
+
+describe("getCurrentIndex", () => {
+ test("gets the active index while loading", async () => {
+ presets = new Presets({
+ keys: ["a", "b"],
+ initialPresets: { a: "Preset A" },
+ randomize: false,
+ getRest: () =>
+ Promise.resolve({
+ b: "Preset B"
+ })
+ });
+ const resolved = presets.next();
+ expect(presets.getCurrentIndex()).toBe(0);
+ await resolved;
+ expect(presets.getCurrentIndex()).toBe(1);
+ });
+});
diff --git a/js/components/MilkdropWindow/index.js b/js/components/MilkdropWindow/index.js
index a9ddaf4a..930005e6 100644
--- a/js/components/MilkdropWindow/index.js
+++ b/js/components/MilkdropWindow/index.js
@@ -1,26 +1,47 @@
import React from "react";
-import { connect } from "react-redux";
-import screenfull from "screenfull";
-import PresetOverlay from "./PresetOverlay";
+import Presets from "./Presets";
+import Milkdrop from "./Milkdrop";
+import Background from "./Background";
-const USER_PRESET_TRANSITION_SECONDS = 5.7;
-const PRESET_TRANSITION_SECONDS = 2.7;
-const MILLISECONDS_BETWEEN_PRESET_TRANSITIONS = 15000;
-
-class MilkdropWindow extends React.Component {
- constructor(props) {
- super(props);
- this.state = {
- isFullscreen: false,
- presetOverlay: false,
- currentPreset: -1
- };
- this._handleFocusedKeyboardInput = this._handleFocusedKeyboardInput.bind(
- this
- );
- this._handleFullscreenChange = this._handleFullscreenChange.bind(this);
+// This component is just responsible for loading dependencies.
+// This simplifies the inner component, by allowing
+// it to alwasy assume that it has it's dependencies.
+export default class PresetsLoader extends React.Component {
+ constructor() {
+ super();
+ this.state = { presets: null, butterchurn: null };
}
- componentDidMount() {
+
+ async componentDidMount() {
+ const {
+ butterchurn,
+ presetKeys,
+ minimalPresets
+ } = await loadInitialDependencies();
+
+ this.setState({
+ butterchurn,
+ presets: new Presets({
+ keys: presetKeys,
+ initialPresets: minimalPresets,
+ getRest: loadNonMinimalPresets
+ })
+ });
+ }
+
+ render() {
+ const { butterchurn, presets } = this.state;
+ const loaded = butterchurn != null && presets != null;
+ return loaded ? (
+
+ ) : (
+
+ );
+ }
+}
+
+async function loadInitialDependencies() {
+ return new Promise((resolve, reject) => {
require.ensure(
[
"butterchurn",
@@ -28,265 +49,32 @@ class MilkdropWindow extends React.Component {
"butterchurn-presets/lib/butterchurnPresetPackMeta.min"
],
require => {
- const analyserNode = this.props.analyser;
const butterchurn = require("butterchurn");
const butterchurnMinimalPresets = require("butterchurn-presets/lib/butterchurnPresetsMinimal.min");
const presetPackMeta = require("butterchurn-presets/lib/butterchurnPresetPackMeta.min");
- this.presets = butterchurnMinimalPresets.getPresets();
- this.minmalPresetKeys = Object.keys(this.presets);
- this.presetKeys = presetPackMeta.getMainPresetMeta().presets;
- const presetKey = this.minmalPresetKeys[
- Math.floor(Math.random() * this.minmalPresetKeys.length)
- ];
-
- this.visualizer = butterchurn.createVisualizer(
- analyserNode.context,
- this._canvasNode,
- {
- width: this.props.width,
- height: this.props.height,
- pixelRatio: window.devicePixelRatio || 1
- }
- );
- this._canvasNode.width = this.props.width;
- this._canvasNode.height = this.props.height;
- this.visualizer.connectAudio(analyserNode);
- this.visualizer.loadPreset(this.presets[presetKey], 0);
- // Kick off the animation loop
- const loop = () => {
- if (this.props.status === "PLAYING") {
- this.visualizer.render();
- }
- window.requestAnimationFrame(loop);
- };
- loop();
-
- const presetIdx = this.presetKeys.indexOf(presetKey);
- this.presetHistory = [presetIdx];
- this.presetRandomize = true;
- this.presetCycle = true;
- this._restartCycling();
- this._unsubscribeFocusedKeyDown = this.props.onFocusedKeyDown(
- this._handleFocusedKeyboardInput
- );
- this.setState({ currentPreset: presetIdx });
- },
- e => {
- console.error("Error loading Butterchurn", e);
+ resolve({
+ butterchurn,
+ minimalPresets: butterchurnMinimalPresets.getPresets(),
+ presetKeys: presetPackMeta.getMainPresetMeta().presets
+ });
},
+ reject,
"butterchurn"
);
- }
- componentWillUnmount() {
- this._pauseViz();
- this._stopCycling();
- if (this._unsubscribeFocusedKeyDown) {
- this._unsubscribeFocusedKeyDown();
- }
- screenfull.off("change", this._handleFullscreenChange);
- }
- componentDidUpdate(prevProps) {
- if (
- this.props.width !== prevProps.width ||
- this.props.height !== prevProps.height
- ) {
- this._setRendererSize(this.props.width, this.props.height);
- }
- }
- _pauseViz() {
- if (this.animationFrameRequest) {
- window.cancelAnimationFrame(this.animationFrameRequest);
- this.animationFrameRequest = null;
- }
- }
- _stopCycling() {
- if (this.cycleInterval) {
- clearInterval(this.cycleInterval);
- this.cycleInterval = null;
- }
- }
- _restartCycling() {
- this._stopCycling();
-
- if (this.presetCycle) {
- this.cycleInterval = setInterval(() => {
- this._nextPreset(PRESET_TRANSITION_SECONDS);
- }, MILLISECONDS_BETWEEN_PRESET_TRANSITIONS);
- }
- }
- _setRendererSize(width, height) {
- this._canvasNode.width = width;
- this._canvasNode.height = height;
- // It's possible that the visualizer has not been intialized yet.
- if (this.visualizer != null) {
- this.visualizer.setRendererSize(width, height);
- }
- }
- _handleFullscreenChange() {
- if (screenfull.isFullscreen) {
- this._setRendererSize(window.innerWidth, window.innerHeight);
- } else {
- this._setRendererSize(this.props.width, this.props.height);
- }
- this.setState({ isFullscreen: screenfull.isFullscreen });
- }
- _handleRequestFullsceen() {
- if (screenfull.enabled) {
- if (!screenfull.isFullscreen) {
- screenfull.request(this._wrapperNode);
- } else {
- screenfull.exit();
- }
- }
- }
- _handleFocusedKeyboardInput(e) {
- switch (e.keyCode) {
- case 32: // spacebar
- this._nextPreset(USER_PRESET_TRANSITION_SECONDS);
- break;
- case 8: // backspace
- this._prevPreset(0);
- break;
- case 72: // H
- this._nextPreset(0);
- break;
- case 82: // R
- this.presetRandomize = !this.presetRandomize;
- break;
- case 76: // L
- this.setState({ presetOverlay: !this.state.presetOverlay });
- e.stopPropagation();
- break;
- case 145: // scroll lock
- case 125: // F14 (scroll lock for OS X)
- this.presetCycle = !this.presetCycle;
- this._restartCycling();
- break;
- }
- }
- async _loadMainPresetPack() {
- this.loadingPresets = true;
- return require.ensure(
- ["butterchurn-presets/lib/butterchurnPresetsNonMinimal.min"],
- require => {
- const butterchurnNonMinimalPresets = require("butterchurn-presets/lib/butterchurnPresetsNonMinimal.min");
- Object.assign(this.presets, butterchurnNonMinimalPresets.getPresets());
- this.loadingPresets = false;
- },
- e => {
- console.error("Error loading Butterchurn Presets", e);
- },
- "butterchurn-presets"
- );
- }
- async _nextPreset(blendTime) {
- // The visualizer may not have initialized yet.
- if (this.visualizer != null) {
- let presetIdx;
- if (this.presetRandomize || this.presetHistory.length === 0) {
- presetIdx = Math.floor(this.presetKeys.length * Math.random());
- } else {
- const prevPresetIdx = this.presetHistory[this.presetHistory.length - 1];
- presetIdx = (prevPresetIdx + 1) % this.presetKeys.length;
- }
- this.selectPreset(presetIdx, blendTime);
- }
- }
- _prevPreset(blendTime) {
- if (this.loadingPresets && this.presetQueue.length > 1) {
- this.presetQueue.pop();
- return;
- }
-
- if (this.presetHistory.length > 1 && this.visualizer != null) {
- this.presetHistory.pop();
- const prevPreset = this.presetHistory[this.presetHistory.length - 1];
- this.visualizer.loadPreset(
- this.presets[this.presetKeys[prevPreset]],
- blendTime
- );
- this._restartCycling();
- this.setState({ currentPreset: prevPreset });
- }
- }
- async selectPreset(presetIdx, blendTime) {
- if (this.loadingPresets) {
- this.presetQueue.push(presetIdx);
- return;
- }
-
- let preset = this.presets[this.presetKeys[presetIdx]];
- let selectedIdx;
- if (!preset) {
- this.presetQueue = [presetIdx];
- await this._loadMainPresetPack();
- if (this.presetQueue.length === 0) {
- return;
- }
- selectedIdx = this.presetQueue[this.presetQueue.length - 1];
- preset = this.presets[this.presetKeys[selectedIdx]];
- this.presetHistory = this.presetHistory.concat(this.presetQueue);
- } else {
- selectedIdx = presetIdx;
- this.presetHistory.push(selectedIdx);
- }
- this.visualizer.loadPreset(preset, blendTime);
- this._restartCycling();
- this.setState({ currentPreset: selectedIdx });
- }
- closePresetOverlay() {
- this.setState({ presetOverlay: false });
- }
- render() {
- const width = this.state.isFullscreen
- ? window.innerWidth
- : this.props.width;
- const height = this.state.isFullscreen
- ? window.innerHeight
- : this.props.height;
- return (
- (this._wrapperNode = node)}
- onDoubleClick={() => this._handleRequestFullsceen()}
- >
- {this.state.presetOverlay && (
-
this.props.onFocusedKeyDown(listener)}
- selectPreset={idx => this.selectPreset(idx, 0)}
- closeOverlay={() => this.closePresetOverlay()}
- />
- )}
-
- );
- }
+ });
}
-const mapStateToProps = state => ({
- status: state.media.status
-});
-
-export default connect(mapStateToProps)(MilkdropWindow);
+async function loadNonMinimalPresets() {
+ return new Promise((resolve, reject) => {
+ require.ensure(
+ ["butterchurn-presets/lib/butterchurnPresetsNonMinimal.min"],
+ require => {
+ resolve(
+ require("butterchurn-presets/lib/butterchurnPresetsNonMinimal.min").getPresets()
+ );
+ },
+ reject,
+ "butterchurn-presets"
+ );
+ });
+}
diff --git a/package.json b/package.json
index 533193a1..4413b680 100644
--- a/package.json
+++ b/package.json
@@ -17,7 +17,7 @@
"weight": "npm run build-library > /dev/null && gzip-size built/webamp.bundle.min.js",
"test": "jest --projects config/jest.unit.js config/jest.eslint.js",
"travis-tests": "npm run test && npm run build && npm run build-library",
- "tdd": "jest --watch",
+ "tdd": "jest --projects config/jest.unit.js --watch",
"format": "prettier --write experiments/**/*.js js/**/*.js css/**/*.css !css/**/*.min.css",
"build-skin": "rm skins/base-2.91.wsz && cd skins/base-2.91 && zip -x .* -x 'Skining Updates.txt' -r ../base-2.91.wsz .",
"build-skin-png": "rm skins/base-2.91-png.wsz && cd skins/base-2.91-png && zip -x .* -x 'Skining Updates.txt' -r ../base-2.91-png.wsz .",
@@ -74,6 +74,7 @@
"http-server": "^0.11.1",
"invariant": "^2.2.3",
"jest": "^23.0.0",
+ "jest-mock-random": "^1.0.2",
"jest-runner-eslint": "^0.4.0",
"jsmediatags": "^3.8.1",
"jszip": "^3.1.3",
diff --git a/yarn.lock b/yarn.lock
index bebc0434..c27177be 100644
--- a/yarn.lock
+++ b/yarn.lock
@@ -4341,6 +4341,10 @@ jest-message-util@^23.0.0:
slash "^1.0.0"
stack-utils "^1.0.1"
+jest-mock-random@^1.0.2:
+ version "1.0.2"
+ resolved "https://registry.yarnpkg.com/jest-mock-random/-/jest-mock-random-1.0.2.tgz#81a1aa641fdb3a049bf64e2a7a0411fd8fc3fb20"
+
jest-mock@^23.0.0:
version "23.0.0"
resolved "https://registry.yarnpkg.com/jest-mock/-/jest-mock-23.0.0.tgz#d9d897a1b74dc05c66a737213931496215897dd8"