mirror of
https://github.com/captbaritone/webamp.git
synced 2026-01-24 02:36:00 +00:00
186 lines
5.5 KiB
TypeScript
186 lines
5.5 KiB
TypeScript
import React from "react";
|
|
import ReactDOM from "react-dom";
|
|
import { connect } from "react-redux";
|
|
import {
|
|
WindowId,
|
|
AppState,
|
|
Dispatch,
|
|
Size,
|
|
MediaStatus,
|
|
FilePicker,
|
|
} from "../types";
|
|
import { WINDOWS } from "../constants";
|
|
import * as Selectors from "../selectors";
|
|
import * as Actions from "../actionCreators";
|
|
import * as Utils from "../utils";
|
|
import MilkdropWindow from "../components/MilkdropWindow";
|
|
import ContextMenuWrapper from "./ContextMenuWrapper";
|
|
import MainContextMenu from "./MainWindow/MainContextMenu";
|
|
import WindowManager from "./WindowManager";
|
|
import MainWindow from "./MainWindow";
|
|
import PlaylistWindow from "./PlaylistWindow";
|
|
import EqualizerWindow from "./EqualizerWindow";
|
|
import MediaLibraryWindow from "./MediaLibraryWindow";
|
|
import Skin from "./Skin";
|
|
|
|
import "../../css/webamp.css";
|
|
import { WebampWindow } from "../reducers/windows";
|
|
import Media from "../media";
|
|
|
|
interface StateProps {
|
|
visualizerStyle: string;
|
|
status: MediaStatus;
|
|
closed: boolean;
|
|
// TODO: Get only the info we really need
|
|
genWindowsInfo: { [windowId: string]: WebampWindow };
|
|
zIndex: number;
|
|
}
|
|
|
|
interface DispatchProps {
|
|
closeWindow(id: WindowId): void;
|
|
browserWindowSizeChanged(size: Size): void;
|
|
clearFocus(): void;
|
|
}
|
|
|
|
interface OwnProps {
|
|
container: HTMLElement;
|
|
filePickers: FilePicker[];
|
|
media: Media;
|
|
}
|
|
|
|
type Props = StateProps & DispatchProps & OwnProps;
|
|
|
|
/**
|
|
* Constructs the windows to render
|
|
*/
|
|
class App extends React.Component<Props> {
|
|
_webampNode: HTMLDivElement | null;
|
|
constructor(props: Props) {
|
|
super(props);
|
|
this._webampNode = null;
|
|
}
|
|
|
|
componentWillMount() {
|
|
this._webampNode = document.createElement("div");
|
|
this._webampNode.id = "webamp";
|
|
// @ts-ignore I think I'm supposed to set this with setAttribute, but I can't confirm.
|
|
this._webampNode.role = "application";
|
|
this._webampNode.style.zIndex = String(this.props.zIndex);
|
|
document.body.appendChild(this._webampNode);
|
|
|
|
this.props.browserWindowSizeChanged(Utils.getWindowSize());
|
|
window.addEventListener("resize", this._handleWindowResize);
|
|
}
|
|
|
|
componentWillUnmount() {
|
|
window.removeEventListener("resize", this._handleWindowResize);
|
|
const webampNode = this._webampNode;
|
|
if (webampNode == null) {
|
|
return;
|
|
}
|
|
const parentNode = webampNode.parentNode;
|
|
if (parentNode != null) {
|
|
parentNode.removeChild(webampNode!);
|
|
}
|
|
}
|
|
|
|
_handleWindowResize = () => {
|
|
if (this._webampNode == null) {
|
|
return;
|
|
}
|
|
// It's a bit tricky to measure the "natural" size of the browser window.
|
|
// Specifically we want to know how large the window would be without our
|
|
// own Webamp windows influencing it. To achieve this, we temporarily make
|
|
// our container `overflow: hidden;`. We then make our container full
|
|
// screen by setting the bottom/right properties to zero. This second part
|
|
// allows our Webamp windows to stay visible during the resize. After we
|
|
// measure, we set the style back so that we don't end up interfering with
|
|
// click events outside of our Webamp windows.
|
|
this._webampNode.style.right = "0";
|
|
this._webampNode.style.bottom = "0";
|
|
this._webampNode.style.overflow = "hidden";
|
|
this.props.browserWindowSizeChanged(Utils.getWindowSize());
|
|
this._webampNode.style.right = null;
|
|
this._webampNode.style.bottom = null;
|
|
this._webampNode.style.overflow = "visible";
|
|
};
|
|
|
|
_renderWindows() {
|
|
const { media, genWindowsInfo, filePickers } = this.props;
|
|
return Utils.objectMap(genWindowsInfo, (w, id) => {
|
|
if (!w.open) {
|
|
return null;
|
|
}
|
|
switch (id) {
|
|
case WINDOWS.MAIN:
|
|
return (
|
|
<MainWindow
|
|
analyser={media.getAnalyser()}
|
|
filePickers={filePickers}
|
|
/>
|
|
);
|
|
case WINDOWS.EQUALIZER:
|
|
return <EqualizerWindow />;
|
|
case WINDOWS.PLAYLIST:
|
|
return <PlaylistWindow analyser={media.getAnalyser()} />;
|
|
case WINDOWS.MEDIA_LIBRARY:
|
|
return <MediaLibraryWindow />;
|
|
case WINDOWS.MILKDROP:
|
|
return <MilkdropWindow analyser={media.getAnalyser()} />;
|
|
default:
|
|
throw new Error(`Tried to render an unknown window: ${id}`);
|
|
}
|
|
});
|
|
}
|
|
|
|
_handleBlur = (e: React.FocusEvent<HTMLElement>) => {
|
|
if (!e.currentTarget.contains(e.relatedTarget as Element)) {
|
|
this.props.clearFocus();
|
|
}
|
|
};
|
|
|
|
render() {
|
|
const { closed, container, filePickers } = this.props;
|
|
if (closed || this._webampNode == null) {
|
|
return null;
|
|
}
|
|
return ReactDOM.createPortal(
|
|
<div onBlur={this._handleBlur}>
|
|
<Skin />
|
|
<ContextMenuWrapper
|
|
renderContents={() => <MainContextMenu filePickers={filePickers} />}
|
|
>
|
|
<WindowManager
|
|
windows={this._renderWindows()}
|
|
container={container}
|
|
/>
|
|
</ContextMenuWrapper>
|
|
</div>,
|
|
this._webampNode
|
|
);
|
|
}
|
|
}
|
|
|
|
const mapStateToProps = (state: AppState): StateProps => {
|
|
return {
|
|
visualizerStyle: Selectors.getVisualizerStyle(state),
|
|
status: state.media.status,
|
|
closed: state.display.closed,
|
|
genWindowsInfo: state.windows.genWindows,
|
|
zIndex: state.display.zIndex,
|
|
};
|
|
};
|
|
|
|
const mapDispatchToProps = (dispatch: Dispatch): DispatchProps => {
|
|
return {
|
|
closeWindow: (id: WindowId) => dispatch(Actions.closeWindow(id)),
|
|
browserWindowSizeChanged: (size: Size) =>
|
|
dispatch(Actions.browserWindowSizeChanged(size)),
|
|
clearFocus: () => dispatch(Actions.setFocusedWindow(null)),
|
|
};
|
|
};
|
|
|
|
export default connect(
|
|
mapStateToProps,
|
|
mapDispatchToProps
|
|
)(App);
|