import React, { useEffect, useCallback } from "react"; import Fullscreen from "../Fullscreen"; import { connect } from "react-redux"; import { useWindowSize, useScreenSize } from "../../hooks"; import GenWindow from "../GenWindow"; import { WINDOWS } from "../../constants"; import * as Selectors from "../../selectors"; import * as Actions from "../../actionCreators"; import { AppState, Dispatch, TransitionType } from "../../types"; import Visualizer from "./Visualizer"; import "../../../css/milkdrop-window.css"; import Background from "./Background"; import PresetOverlay from "./PresetOverlay"; import DropTarget from "../DropTarget"; import MilkdropContextMenu from "./MilkdropContextMenu"; import Desktop from "./Desktop"; const MILLISECONDS_BETWEEN_PRESET_TRANSITIONS = 15000; interface StateProps { desktop: boolean; fullscreen: boolean; overlay: boolean; presetsAreCycling: boolean; currentPresetIndex: number | null; // Index trackTitle: string | null; mediaIsPlaying: boolean; } interface DispatchProps { closeWindow(): void; toggleDesktop(): void; toggleFullscreen(): void; setFullscreen(fullscreen: boolean): void; togglePresetOverlay(): void; selectRandomPreset(): void; toggleRandomize(): void; toggleCycling(): void; handlePresetDrop(e: React.DragEvent): void; selectNextPreset(transitionType?: TransitionType): void; selectPreviousPreset(transitionType?: TransitionType): void; scheduleMilkdropMessage(message: string): void; } interface OwnProps { analyser: AnalyserNode; } type Props = StateProps & DispatchProps & OwnProps; function useKeyHandler(props: Props) { const { selectNextPreset, selectPreviousPreset, toggleRandomize, togglePresetOverlay, scheduleMilkdropMessage, trackTitle, toggleCycling, } = props; // Handle keyboard events return useCallback( (e: KeyboardEvent) => { switch (e.keyCode) { case 32: // spacebar selectNextPreset(); break; case 8: // backspace selectPreviousPreset(TransitionType.IMMEDIATE); break; case 72: // H selectNextPreset(TransitionType.IMMEDIATE); break; case 82: // R toggleRandomize(); break; case 76: // L togglePresetOverlay(); e.stopPropagation(); break; case 84: // T if (trackTitle != null) { scheduleMilkdropMessage(trackTitle); } e.stopPropagation(); break; case 145: // scroll lock case 125: // F14 (scroll lock for OS X) toggleCycling(); break; } }, [ selectNextPreset, selectPreviousPreset, toggleRandomize, togglePresetOverlay, scheduleMilkdropMessage, trackTitle, toggleCycling, ] ); } function Milkdrop(props: Props) { const handleKeyDown = useKeyHandler(props); // Cycle presets useEffect(() => { if (!props.presetsAreCycling || !props.mediaIsPlaying) { return; } const intervalId = setInterval( props.selectNextPreset, MILLISECONDS_BETWEEN_PRESET_TRANSITIONS ); return () => clearInterval(intervalId); }, [ props.selectNextPreset, props.presetsAreCycling, props.currentPresetIndex, props.mediaIsPlaying, ]); const screenSize = useScreenSize(); const windowSize = useWindowSize(); if (props.desktop) { return ( ); } return ( {(genWindowSize: { width: number; height: number }) => { const size = props.fullscreen ? screenSize : genWindowSize; return ( {props.overlay && }
); }}
); } const mapStateToProps = (state: AppState): StateProps => ({ desktop: Selectors.getMilkdropDesktopEnabled(state), fullscreen: Selectors.getMilkdropFullscreenEnabled(state), overlay: Selectors.getPresetOverlayOpen(state), presetsAreCycling: Selectors.getPresetsAreCycling(state), currentPresetIndex: Selectors.getCurrentPresetIndex(state), trackTitle: Selectors.getCurrentTrackDisplayName(state), mediaIsPlaying: Selectors.getMediaIsPlaying(state), }); const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => ({ closeWindow: () => dispatch(Actions.closeWindow(WINDOWS.MILKDROP)), toggleDesktop: () => dispatch(Actions.toggleMilkdropDesktop()), toggleFullscreen: () => dispatch(Actions.toggleMilkdropFullscreen()), setFullscreen: (fullscreen: boolean) => dispatch(Actions.setMilkdropFullscreen(fullscreen)), togglePresetOverlay: () => dispatch(Actions.togglePresetOverlay()), selectRandomPreset: () => dispatch(Actions.selectRandomPreset()), toggleRandomize: () => dispatch(Actions.toggleRandomizePresets()), toggleCycling: () => dispatch(Actions.togglePresetCycling()), handlePresetDrop: e => dispatch(Actions.handlePresetDrop(e)), selectNextPreset: (transitionType?: TransitionType) => dispatch(Actions.selectNextPreset(transitionType)), selectPreviousPreset: (transitionType?: TransitionType) => dispatch(Actions.selectPreviousPreset(transitionType)), scheduleMilkdropMessage: (message: string) => dispatch(Actions.scheduleMilkdropMessage(message)), }); export default connect(mapStateToProps, mapDispatchToProps)(Milkdrop);