mirror of
https://github.com/captbaritone/webamp.git
synced 2026-07-22 17:47:16 +00:00
Progress
This commit is contained in:
parent
98ca95749c
commit
18b23d6a17
7 changed files with 100 additions and 64 deletions
|
|
@ -99,7 +99,9 @@ export {
|
|||
requestPresetAtIndex,
|
||||
selectRandomPreset,
|
||||
selectNextPreset,
|
||||
togglePresetOverlay
|
||||
togglePresetOverlay,
|
||||
appendPresetFileList,
|
||||
handlePresetDrop
|
||||
} from "./milkdrop";
|
||||
|
||||
import * as Selectors from "../selectors";
|
||||
|
|
|
|||
|
|
@ -6,7 +6,13 @@ import {
|
|||
TOGGLE_PRESET_OVERLAY
|
||||
} from "../actionTypes";
|
||||
import * as Selectors from "../selectors";
|
||||
import { Dispatchable, TransitionType } from "../types";
|
||||
import {
|
||||
Dispatchable,
|
||||
TransitionType,
|
||||
Preset,
|
||||
LazyButterchurnPresetJson
|
||||
} from "../types";
|
||||
import * as FileUtils from "../fileUtils";
|
||||
|
||||
export function initializePresets(presetOptions: any): Dispatchable {
|
||||
return async dispatch => {
|
||||
|
|
@ -18,7 +24,7 @@ export function initializePresets(presetOptions: any): Dispatchable {
|
|||
} = await loadInitialDependencies();
|
||||
dispatch({ type: GOT_BUTTERCHURN, butterchurn });
|
||||
|
||||
const presets = presetKeys.map((key: string) => {
|
||||
const presets: Preset[] = presetKeys.map((key: string) => {
|
||||
if (minimalPresets[key] != null) {
|
||||
return {
|
||||
type: "BUTTERCHURN_JSON",
|
||||
|
|
@ -37,14 +43,54 @@ export function initializePresets(presetOptions: any): Dispatchable {
|
|||
};
|
||||
});
|
||||
|
||||
dispatch({ type: GOT_BUTTERCHURN_PRESETS, presets });
|
||||
dispatch(requestPresetAtIndex(0, TransitionType.IMMEDIATE));
|
||||
dispatch(loadPresets(presets));
|
||||
};
|
||||
}
|
||||
|
||||
export function appendPresetFileList(presets: FileList[]): Dispatchable {
|
||||
return async dispatch => {
|
||||
export function loadPresets(presets: Preset[]): Dispatchable {
|
||||
return (dispatch, getState) => {
|
||||
const presetLength = getState().milkdrop.presets.length;
|
||||
dispatch({ type: GOT_BUTTERCHURN_PRESETS, presets });
|
||||
dispatch(requestPresetAtIndex(presetLength, TransitionType.IMMEDIATE));
|
||||
};
|
||||
}
|
||||
|
||||
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 lazy: LazyButterchurnPresetJson = {
|
||||
type: "LAZY_BUTTERCHURN_JSON",
|
||||
name: file.name.slice(0, file.name.length - MILK_EXT.length),
|
||||
getDefinition: async () => {
|
||||
// TODO: Post this blob to the url end point and get the json back
|
||||
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");
|
||||
}
|
||||
});
|
||||
dispatch(loadPresets(presets));
|
||||
// TODO: Select the first of these presets
|
||||
};
|
||||
}
|
||||
|
||||
|
|
@ -88,6 +134,8 @@ export function requestPresetAtIndex(
|
|||
case "LAZY_BUTTERCHURN_JSON":
|
||||
const json = await preset.getDefinition();
|
||||
// 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?
|
||||
dispatch({ type: RESOLVE_PRESET_AT_INDEX, index, json });
|
||||
dispatch({ type: SELECT_PRESET_AT_INDEX, index, transitionType });
|
||||
return;
|
||||
|
|
@ -95,45 +143,9 @@ export function requestPresetAtIndex(
|
|||
};
|
||||
}
|
||||
|
||||
function _presetNameFromURL(url: string): string {
|
||||
try {
|
||||
const urlParts = url.split("/");
|
||||
const lastPart = urlParts[urlParts.length - 1];
|
||||
const presetName = lastPart.substring(0, lastPart.length - 5); // remove .milk or .json
|
||||
return decodeURIComponent(presetName);
|
||||
} catch (e) {
|
||||
// if something goes wrong parsing url, just use url as the preset name
|
||||
console.error(e);
|
||||
return url;
|
||||
}
|
||||
}
|
||||
|
||||
async function _fetchPreset(
|
||||
presetUrl: string,
|
||||
{ isButterchurn }: { isButterchurn: boolean }
|
||||
) {
|
||||
const response = await fetch(presetUrl);
|
||||
if (!response.ok) {
|
||||
console.error(response.statusText);
|
||||
alert(`Unable to load MilkDrop preset from ${presetUrl}`);
|
||||
return null;
|
||||
}
|
||||
const presetName = _presetNameFromURL(presetUrl);
|
||||
|
||||
let preset = null;
|
||||
if (isButterchurn) {
|
||||
try {
|
||||
preset = await response.json();
|
||||
} catch (e) {
|
||||
console.error(e);
|
||||
alert(`Failed to parse MilkDrop preset from ${presetUrl}`);
|
||||
return null;
|
||||
}
|
||||
} else {
|
||||
preset = { file: await response.blob() };
|
||||
}
|
||||
|
||||
return { [presetName]: preset };
|
||||
export function handlePresetDrop(e: React.DragEvent): Dispatchable {
|
||||
// TODO: Ensure we actually select the new preset.
|
||||
return appendPresetFileList(e.dataTransfer.files);
|
||||
}
|
||||
|
||||
export function togglePresetOverlay(): Dispatchable {
|
||||
|
|
|
|||
|
|
@ -46,6 +46,7 @@ interface StateProps {
|
|||
interface DispatchProps {
|
||||
requestPresetAtIndex(i: number): void;
|
||||
togglePresetOverlay(): void;
|
||||
appendPresetFileList(fileList: FileList): void;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
|
|
@ -168,7 +169,7 @@ class PresetOverlay extends React.Component<Props, State> {
|
|||
}
|
||||
// TODO: Technically there is a race condition here, since the component
|
||||
// could get unmounted before the promise resolves.
|
||||
this.props.loadPresets(fileReferences);
|
||||
this.props.appendPresetFileList(fileReferences);
|
||||
}
|
||||
|
||||
render() {
|
||||
|
|
@ -226,7 +227,9 @@ function mapDispatchToProps(dispatch: Dispatch): DispatchProps {
|
|||
requestPresetAtIndex: (i: number) => {
|
||||
dispatch(Actions.requestPresetAtIndex(i, TransitionType.DEFAULT));
|
||||
},
|
||||
togglePresetOverlay: () => dispatch(Actions.togglePresetOverlay())
|
||||
togglePresetOverlay: () => dispatch(Actions.togglePresetOverlay()),
|
||||
appendPresetFileList: (fileList: FileList) =>
|
||||
dispatch(Actions.appendPresetFileList(fileList))
|
||||
};
|
||||
}
|
||||
|
||||
|
|
|
|||
|
|
@ -1,20 +1,17 @@
|
|||
// @ts-ignore #hook-types
|
||||
import React, { useEffect, useState, useRef } from "react";
|
||||
import { connect } from "react-redux";
|
||||
import screenfull from "screenfull";
|
||||
import ContextMenuWrapper from "../ContextMenuWrapper";
|
||||
import GenWindow from "../GenWindow";
|
||||
import { WINDOWS, VISUALIZERS } from "../../constants";
|
||||
import { WINDOWS } from "../../constants";
|
||||
import * as Selectors from "../../selectors";
|
||||
import * as Actions from "../../actionCreators";
|
||||
import MilkdropContextMenu from "./MilkdropContextMenu";
|
||||
import Desktop from "./Desktop";
|
||||
import { AppState, Dispatch } from "../../types";
|
||||
import Visualizer from "./Visualizer";
|
||||
|
||||
import "../../../css/milkdrop-window.css";
|
||||
import Background from "./Background";
|
||||
import PresetOverlay from "./PresetOverlay";
|
||||
import DropTarget from "../DropTarget";
|
||||
|
||||
const MILLISECONDS_BETWEEN_PRESET_TRANSITIONS = 15000;
|
||||
|
||||
|
|
@ -28,11 +25,14 @@ interface DispatchProps {
|
|||
toggleDesktop(): void;
|
||||
togglePresetOverlay(): void;
|
||||
selectRandomPreset(): void;
|
||||
handlePresetDrop(e: React.DragEvent): void;
|
||||
}
|
||||
|
||||
interface OwnProps {
|
||||
analyser: AnalyserNode;
|
||||
onFocusedKeyDown(cb: (e: KeyboardEvent) => void): () => void;
|
||||
onFocusedKeyDown(
|
||||
cb: (e: React.KeyboardEvent<HTMLDivElement>) => void
|
||||
): () => void;
|
||||
}
|
||||
|
||||
type Props = StateProps & DispatchProps & OwnProps;
|
||||
|
|
@ -79,18 +79,25 @@ function Milkdrop(props: Props) {
|
|||
);
|
||||
return () => clearImmediate(intervalId);
|
||||
}, [props.selectRandomPreset]);
|
||||
|
||||
return (
|
||||
<GenWindow title={"Milkdrop"} windowId={WINDOWS.MILKDROP}>
|
||||
{({ height, width }: { width: number; height: number }) => (
|
||||
<Background>
|
||||
{props.overlay && (
|
||||
<PresetOverlay
|
||||
<DropTarget handleDrop={props.handlePresetDrop}>
|
||||
{props.overlay && (
|
||||
<PresetOverlay
|
||||
width={width}
|
||||
height={height}
|
||||
onFocusedKeyDown={props.onFocusedKeyDown}
|
||||
/>
|
||||
)}
|
||||
<Visualizer
|
||||
width={width}
|
||||
height={height}
|
||||
onFocusedKeyDown={props.onFocusedKeyDown}
|
||||
analyser={props.analyser}
|
||||
/>
|
||||
)}
|
||||
<Visualizer width={width} height={height} analyser={props.analyser} />
|
||||
</DropTarget>
|
||||
</Background>
|
||||
)}
|
||||
</GenWindow>
|
||||
|
|
@ -106,7 +113,8 @@ const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({
|
|||
closeWindow: () => dispatch(Actions.closeWindow(WINDOWS.MILKDROP)),
|
||||
toggleDesktop: () => dispatch(Actions.toggleMilkdropDesktop()),
|
||||
togglePresetOverlay: () => dispatch(Actions.togglePresetOverlay()),
|
||||
selectRandomPreset: () => dispatch(Actions.selectRandomPreset())
|
||||
selectRandomPreset: () => dispatch(Actions.selectRandomPreset()),
|
||||
handlePresetDrop: e => dispatch(Actions.handlePresetDrop(e))
|
||||
});
|
||||
|
||||
export default connect(
|
||||
|
|
|
|||
|
|
@ -66,6 +66,19 @@ export async function genArrayBufferFromFileReference(
|
|||
});
|
||||
}
|
||||
|
||||
export async function genStringFromFileReference(
|
||||
fileReference: File
|
||||
): Promise<string> {
|
||||
return new Promise((resolve, reject) => {
|
||||
const reader = new FileReader();
|
||||
reader.onload = () => {
|
||||
resolve(reader.result as string);
|
||||
};
|
||||
reader.onerror = reject;
|
||||
reader.readAsText(fileReference);
|
||||
});
|
||||
}
|
||||
|
||||
interface PromptForFileReferenceOptions {
|
||||
accept?: string | null;
|
||||
directory?: boolean;
|
||||
|
|
|
|||
|
|
@ -48,8 +48,6 @@ export const milkdrop = (
|
|||
case "BUTTERCHURN_JSON":
|
||||
case "LAZY_BUTTERCHURN_JSON":
|
||||
return preset;
|
||||
default:
|
||||
throw new Error(`Invalid preset type: ${preset.type}`);
|
||||
}
|
||||
})
|
||||
)
|
||||
|
|
|
|||
|
|
@ -138,7 +138,7 @@ type ButterchurnPresetJson = {
|
|||
definition: Object;
|
||||
};
|
||||
|
||||
type LazyButterchurnPresetJson = {
|
||||
export type LazyButterchurnPresetJson = {
|
||||
type: "LAZY_BUTTERCHURN_JSON";
|
||||
name: string;
|
||||
getDefinition: () => Promise<Object>;
|
||||
|
|
@ -483,7 +483,7 @@ export type Action =
|
|||
}
|
||||
| {
|
||||
type: "GOT_BUTTERCHURN_PRESETS";
|
||||
presets: PresetDefinition[];
|
||||
presets: Preset[];
|
||||
}
|
||||
| {
|
||||
type: "GOT_BUTTERCHURN";
|
||||
|
|
|
|||
Loading…
Add table
Add a link
Reference in a new issue