Improve architecture of Milkdrop code

This commit is contained in:
Jordan Eldredge 2019-03-07 21:36:40 -08:00
parent 325a1dc536
commit 37b3340cd2
6 changed files with 130 additions and 161 deletions

View file

@ -13,7 +13,6 @@ import zaxon from "../../skins/ZaxonRemake1-0.wsz";
import green from "../../skins/Green-Dimension-V2.wsz";
import base from "../../skins/base-2.91-png.wsz";
import internetArchive from "../../skins/Internet-Archive.wsz";
import partialButterchurnOptions from "../../js/components/MilkdropWindow/options";
import { WINDOWS } from "../../js/constants";
import * as Selectors from "../../js/selectors";
@ -147,6 +146,42 @@ Raven.context(async () => {
let __butterchurnOptions = null;
let __initialWindowLayout = null;
if (isButterchurnSupported()) {
const partialButterchurnOptions = {
importButterchurn: () => {
return import(/* webpackChunkName: "butterchurn-initial-dependencies" */
// @ts-ignore
"butterchurn");
},
getPresets: async () => {
if ("URLSearchParams" in window) {
const params = new URLSearchParams(location.search);
const butterchurnPresetUrlParam = params.get("butterchurnPresetUrl");
const milkdropPresetUrl = params.get("milkdropPresetUrl");
const initialPresets = [];
if (butterchurnPresetUrlParam) {
initialPresets.push({
// TODO: Get name
name: "foo",
butterchurnPresetUrl: butterchurnPresetUrlParam
});
} else if (milkdropPresetUrl) {
throw new Error("We still need to implement this");
}
const resp = await fetch(
"https://unpkg.com/butterchurn-presets-weekly@0.0.2/weeks/week1/presets.json"
);
// TODO: Fallback to some other presets?
const namesToPresetUrls = await resp.json();
const presets = Object.entries(namesToPresetUrls).map(
([name, butterchurnPresetUrl]) => {
return { name, butterchurnPresetUrl };
}
);
return [...initialPresets, ...presets];
}
return [];
}
};
const startWithMilkdropHidden =
library ||
document.body.clientWidth < MIN_MILKDROP_WIDTH ||

View file

@ -10,44 +10,54 @@ import {
Dispatchable,
TransitionType,
Preset,
LazyButterchurnPresetJson
ButterchurnOptions,
StatePreset
} from "../types";
import * as FileUtils from "../fileUtils";
export function initializePresets(presetOptions: any): Dispatchable {
return async dispatch => {
const { loadInitialDependencies, loadNonMinimalPresets } = presetOptions;
const {
butterchurn,
presetKeys,
minimalPresets
} = await loadInitialDependencies();
dispatch({ type: GOT_BUTTERCHURN, butterchurn });
const presets: Preset[] = presetKeys.map((key: string) => {
if (minimalPresets[key] != null) {
return {
type: "BUTTERCHURN_JSON",
name: key,
definition: minimalPresets[key]
};
function normalizePresetTypes(preset: Preset): StatePreset {
const { name } = preset;
if (preset.butterchurnPresetObject != null) {
return {
type: "RESOLVED",
name,
preset: preset.butterchurnPresetObject
};
} else if (preset.getButterchrunPresetObject) {
return {
type: "UNRESOLVED",
name,
getPreset: preset.getButterchrunPresetObject
};
} else if (preset.butterchurnPresetUrl != null) {
return {
type: "UNRESOLVED",
name,
getPreset: async () => {
const resp = await fetch(preset.butterchurnPresetUrl);
return resp.json();
}
return {
type: "LAZY_BUTTERCHURN_JSON",
name: key,
getDefinition: async () => {
// TODO: Avoid a race where we try to resolve this promise more than once in parallel.
const nonMinimalPresets = await loadNonMinimalPresets();
return nonMinimalPresets[key];
}
};
};
}
throw new Error("Invalid preset object");
}
export function initializePresets(
presetOptions: ButterchurnOptions
): Dispatchable {
return async dispatch => {
const { getPresets, importButterchurn } = presetOptions;
importButterchurn().then(butterchurn => {
dispatch({ type: GOT_BUTTERCHURN, butterchurn: butterchurn.default });
});
dispatch(loadPresets(presets));
const presets = await getPresets();
const normalizePresets = presets.map(normalizePresetTypes);
dispatch(loadPresets(normalizePresets));
};
}
export function loadPresets(presets: Preset[]): Dispatchable {
export function loadPresets(presets: StatePreset[]): Dispatchable {
return (dispatch, getState) => {
const presetLength = getState().milkdrop.presets.length;
dispatch({ type: GOT_BUTTERCHURN_PRESETS, presets });
@ -57,12 +67,15 @@ export function loadPresets(presets: Preset[]): Dispatchable {
export function appendPresetFileList(fileList: FileList): Dispatchable {
return async dispatch => {
const presets: Preset[] = Array.from(fileList).map(file => {
const JSON_EXT = ".json";
const MILK_EXT = ".milk";
const filename = file.name.toLowerCase();
if (filename.endsWith(MILK_EXT)) {
// Not sure why we need this type definition.
const presets: StatePreset[] = Array.from(fileList).map(
(file): StatePreset => {
const JSON_EXT = ".json";
const MILK_EXT = ".milk";
const filename = file.name.toLowerCase();
if (filename.endsWith(MILK_EXT)) {
throw new Error(".milk preset support not yet implemented");
// Not sure why we need this type definition.
/*
const lazy: LazyButterchurnPresetJson = {
type: "LAZY_BUTTERCHURN_JSON",
name: file.name.slice(0, file.name.length - MILK_EXT.length),
@ -71,24 +84,25 @@ export function appendPresetFileList(fileList: FileList): Dispatchable {
return {};
}
};
throw new Error(".milk preset support not yet implemented");
return lazy;
} else if (filename.endsWith(JSON_EXT)) {
// Not sure why we need this type definition.
const lazy: LazyButterchurnPresetJson = {
type: "LAZY_BUTTERCHURN_JSON",
name: file.name.slice(0, file.name.length - JSON_EXT.length),
getDefinition: async () => {
const str = await FileUtils.genStringFromFileReference(file);
// TODO: How should we handle the case where json parsing fails?
return JSON.parse(str);
}
};
return lazy;
} else {
throw new Error("Invalid type");
*/
} else if (filename.endsWith(JSON_EXT)) {
// Not sure why we need this type definition.
const lazy: StatePreset = {
type: "UNRESOLVED",
name: file.name.slice(0, file.name.length - JSON_EXT.length),
getPreset: async () => {
const str = await FileUtils.genStringFromFileReference(file);
// TODO: How should we handle the case where json parsing fails?
return JSON.parse(str);
}
};
return lazy;
} else {
throw new Error("Invalid type");
}
}
});
);
dispatch(loadPresets(presets));
// TODO: Select the first of these presets
};
@ -128,11 +142,11 @@ export function requestPresetAtIndex(
return;
}
switch (preset.type) {
case "BUTTERCHURN_JSON":
case "RESOLVED":
dispatch({ type: SELECT_PRESET_AT_INDEX, index, transitionType });
return;
case "LAZY_BUTTERCHURN_JSON":
const json = await preset.getDefinition();
case "UNRESOLVED":
const json = await preset.getPreset();
// What if the index has changed?
// Perhaps we could hold a reference to the preset at the index before
// we await and confirm that it hasn't changed after the await?

View file

@ -1,42 +1,4 @@
import {
ButterchurnOptions,
InitialButterchurnDependencies
} from "../../types";
async function loadInitialDependencies(): Promise<
InitialButterchurnDependencies
> {
const [butterchurn, butterchurnMinimalPresets, presetPackMeta] =
// prettier-ignore
await Promise.all([
import(
/* webpackChunkName: "butterchurn-initial-dependencies" */
// @ts-ignore
"butterchurn"
),
import(
/* webpackChunkName: "butterchurn-initial-dependencies" */
// @ts-ignore
"butterchurn-presets/lib/butterchurnPresetsMinimal.min"
),
import(
/* webpackChunkName: "butterchurn-initial-dependencies" */
// @ts-ignore
"butterchurn-presets/lib/butterchurnPresetPackMeta.min"
)
]);
return {
butterchurn: butterchurn.default,
minimalPresets: butterchurnMinimalPresets.default.getPresets(),
presetKeys: presetPackMeta.default.getMainPresetMeta().presets
};
}
async function loadNonMinimalPresets() {
return (await import(/* webpackChunkName: "butterchurn-non-minimal-presets" */
// @ts-ignore
"butterchurn-presets/lib/butterchurnPresetsNonMinimal.min")).default.getPresets();
}
import { ButterchurnOptions } from "../../types";
async function loadConvertPreset() {
const { convertPreset } =
@ -48,19 +10,3 @@ async function loadConvertPreset() {
);
return convertPreset;
}
const options: ButterchurnOptions = {
loadConvertPreset,
loadInitialDependencies,
loadNonMinimalPresets,
presetConverterEndpoint:
"https://p2tpeb5v8b.execute-api.us-east-2.amazonaws.com/default/milkdropShaderConverter"
};
if ("URLSearchParams" in window) {
const params = new URLSearchParams(location.search);
options.initialButterchurnPresetUrl = params.get("butterchurnPresetUrl");
options.initialMilkdropPresetUrl = params.get("milkdropPresetUrl");
}
export default options;

View file

@ -1,4 +1,4 @@
import { Action, PresetId, Preset } from "../types";
import { Action, PresetId, StatePreset } from "../types";
import {
SET_MILKDROP_DESKTOP,
GOT_BUTTERCHURN_PRESETS,
@ -14,7 +14,7 @@ export interface MilkdropState {
desktop: boolean;
overlay: boolean;
presetOrder: PresetId[];
presets: Preset[];
presets: StatePreset[];
currentPresetIndex: number | null;
butterchurn: any;
transitionType: TransitionType;
@ -42,24 +42,16 @@ export const milkdrop = (
case GOT_BUTTERCHURN_PRESETS:
return {
...state,
presets: state.presets.concat(
action.presets.map(preset => {
switch (preset.type) {
case "BUTTERCHURN_JSON":
case "LAZY_BUTTERCHURN_JSON":
return preset;
}
})
)
presets: state.presets.concat(action.presets)
};
case RESOLVE_PRESET_AT_INDEX:
const preset = state.presets[action.index];
return {
...state,
presets: Utils.replaceAtIndex(state.presets, action.index, {
type: "RESOLVED",
name: preset.name,
type: "BUTTERCHURN_JSON",
definition: action.json
preset: action.json
})
};
case SELECT_PRESET_AT_INDEX:

View file

@ -635,10 +635,10 @@ export function getCurrentPreset(state: AppState): any | null {
return null;
}
const preset = state.milkdrop.presets[index];
if (preset == null || preset.type !== "BUTTERCHURN_JSON") {
if (preset == null || preset.type === "UNRESOLVED") {
return null;
}
return preset.definition;
return preset.preset;
}
export function getPresetNames(state: AppState): string[] {

View file

@ -115,57 +115,39 @@ type SkinData = {
skinGenExColors: SkinGenExColors | null;
};
export interface InitialButterchurnDependencies {
// TODO: Type these
butterchurn: any;
minimalPresets: any;
presetKeys: any;
}
export interface ButterchurnOptions {
loadNonMinimalPresets(): Promise<any>;
loadInitialDependencies(): Promise<InitialButterchurnDependencies>;
loadConvertPreset(): Promise<any>;
presetConverterEndpoint: string;
initialMilkdropPresetUrl?: string | null;
initialButterchurnPresetUrl?: string | null;
}
// This is what we actually pass to butterchurn
type ButterchurnPresetJson = {
type: "BUTTERCHURN_JSON";
name: string;
definition: Object;
};
export type LazyButterchurnPresetJson = {
type: "LAZY_BUTTERCHURN_JSON";
name: string;
getDefinition: () => Promise<Object>;
butterchurnPresetObject: Object;
};
// A URL that points to a Butterchurn preset
interface ButterchurnPresetUrl {
type: "BUTTERCHURN_URL";
url: string;
name: string;
butterchurnPresetUrl: string;
}
// A URL that points to a .milk preset
interface MilkdropPresetUrl {
type: "MILKDROP_URL";
url: string;
}
export type LazyButterchurnPresetJson = {
name: string;
getButterchrunPresetObject: () => Promise<Object>;
};
export type PresetDefinition =
export type Preset =
| ButterchurnPresetJson
| LazyButterchurnPresetJson
| ButterchurnPresetUrl
| MilkdropPresetUrl;
| LazyButterchurnPresetJson;
export type Preset = ButterchurnPresetJson | LazyButterchurnPresetJson;
export type StatePreset =
| { type: "RESOLVED"; name: string; preset: Object }
| { type: "UNRESOLVED"; name: string; getPreset: () => Promise<Object> };
export type PresetId = string;
export interface ButterchurnOptions {
getPresets(): Promise<Preset[]>;
importButterchurn(): Promise<any>;
}
export interface EqfPreset {
name: string;
hz60: number;
@ -483,7 +465,7 @@ export type Action =
}
| {
type: "GOT_BUTTERCHURN_PRESETS";
presets: Preset[];
presets: StatePreset[];
}
| {
type: "GOT_BUTTERCHURN";