Revert "Rm media library (#957)"

This reverts commit d24b86e189.
This commit is contained in:
Jordan Eldredge 2019-12-06 08:09:34 -08:00
parent abc1495d9b
commit b056b252d9
24 changed files with 878 additions and 5 deletions

View file

@ -0,0 +1,17 @@
import * as React from "react";
import LibraryTable from "./LibraryTable";
const AlbumsTable = React.memo(() => {
return (
<LibraryTable
headings={["Album", "Tracks"]}
rows={[
["All (1 album)", "1"],
["Ben Mason", "1"],
]}
widths={[50, 200]}
/>
);
});
export default AlbumsTable;

View file

@ -0,0 +1,19 @@
import * as React from "react";
import LibraryTable from "./LibraryTable";
interface Props {}
export default class ArtistsTable extends React.Component<Props> {
render() {
return (
<LibraryTable
headings={["Album", "Tracks", "Other"]}
rows={[
["All (1 album)", "1", "1"],
["Ben Mason", "1", "1"],
]}
widths={[100, 150, 200]}
/>
);
}
}

View file

@ -0,0 +1,16 @@
import React from "react";
type Props = React.HTMLAttributes<HTMLDivElement>;
// TODO: This should be a `<button>` but I couldn't figure out how to style it with css grid
const LibraryButton = (props: Props) => {
const { children, ...passThroughProps } = props;
return (
<div className="library-button" {...passThroughProps}>
<span className="library-button-left" />
<span className="library-button-center">{children}</span>
<span className="library-button-right" />
</div>
);
};
export default LibraryButton;

View file

@ -0,0 +1,176 @@
import React, { ReactNode } from "react";
import { connect } from "react-redux";
import GenWindow from "../GenWindow";
import { WINDOWS } from "../../constants";
import { AppState, SkinGenExColors } from "../../types";
import * as Selectors from "../../selectors";
interface StateProps {
skinGenExColors: SkinGenExColors;
}
interface OwnProps {
sidebar: ReactNode;
artists: ReactNode;
albums: ReactNode;
tracks: ReactNode;
}
type Props = StateProps & OwnProps;
interface State {
sidebarWidth: number;
topPlaylistSectionHeight: number;
artistsPanelWidth: number;
}
const DIVIDER_WIDTH = 9;
// TODO: Tune these
const SIDEBAR_MIN = 25;
const SIDEBAR_MAX = 200;
class LibraryLayout extends React.Component<Props, State> {
constructor(props: Props) {
super(props);
this.state = {
sidebarWidth: 100, // Pixels
topPlaylistSectionHeight: 0.25, // Percent
artistsPanelWidth: 0.5, // Percent
};
}
_onMouseMove(cb: (e: MouseEvent) => void) {
const handleMouseUp = () => {
document.removeEventListener("mousemove", cb);
document.removeEventListener("mouseup", handleMouseUp);
};
// TODO: Technically there's a leak here since the component could unmount while we are moving
document.addEventListener("mousemove", cb);
document.addEventListener("mouseup", handleMouseUp);
}
_handleSidebarMouseDown = (e: React.MouseEvent<HTMLDivElement>) => {
const { pageX: startX } = e;
const initialWidth = this.state.sidebarWidth;
this._onMouseMove((moveEvent: MouseEvent) => {
let sidebarWidth = initialWidth + moveEvent.pageX - startX;
if (sidebarWidth < SIDEBAR_MIN) {
sidebarWidth = 0;
}
sidebarWidth = Math.min(sidebarWidth, SIDEBAR_MAX);
this.setState({ sidebarWidth });
});
};
_handlePlaylistResizeMouseDown = (
e: React.MouseEvent<HTMLDivElement>,
windowHeight: number
) => {
const { pageY: startY } = e;
const avaliableHeight = windowHeight - DIVIDER_WIDTH;
const initialHeight = avaliableHeight * this.state.topPlaylistSectionHeight;
this._onMouseMove((moveEvent: MouseEvent) => {
const deltaY = moveEvent.pageY - startY;
const topPlaylistSectionPixelHeight = initialHeight + deltaY;
this.setState({
topPlaylistSectionHeight:
topPlaylistSectionPixelHeight / avaliableHeight,
});
});
};
_handleArtistsResizeMouseDown = (
e: React.MouseEvent<HTMLDivElement>,
windowWidth: number
) => {
const { pageX: startX } = e;
const avaliableWidth =
windowWidth - DIVIDER_WIDTH - this.state.sidebarWidth;
const initialWidth = avaliableWidth * this.state.artistsPanelWidth;
this._onMouseMove((moveEvent: MouseEvent) => {
const deltaX = moveEvent.pageX - startX;
const artistsPanelPixelWidth = initialWidth + deltaX;
this.setState({
artistsPanelWidth: artistsPanelPixelWidth / avaliableWidth,
});
});
};
render() {
return (
<GenWindow title={"Winamp Library"} windowId={WINDOWS.MEDIA_LIBRARY}>
{({ width, height }) => (
<div
id="webamp-media-library"
style={{
// TODO: There's probably a better way to fill all avalaible space.
position: "absolute",
top: 0,
bottom: 0,
left: 0,
right: 0,
display: "grid",
gridTemplateColumns: `${this.state.sidebarWidth}px ${DIVIDER_WIDTH}px auto`,
}}
>
{this.state.sidebarWidth === 0 ? <div /> : this.props.sidebar}
<div
className="webamp-media-library-vertical-divider"
onMouseDown={this._handleSidebarMouseDown}
>
<div className="webamp-media-library-vertical-divider-line" />
</div>
<div
style={{
display: "grid",
gridTemplateRows: `${
this.state.topPlaylistSectionHeight
}fr ${DIVIDER_WIDTH}px ${1 -
this.state.topPlaylistSectionHeight}fr`,
}}
>
<div
style={{
display: "grid",
gridTemplateColumns: `${
this.state.artistsPanelWidth
}fr ${DIVIDER_WIDTH}px ${1 - this.state.artistsPanelWidth}fr`,
}}
>
{this.props.artists}
<div
className="webamp-media-library-vertical-divider"
onMouseDown={(e: React.MouseEvent<HTMLDivElement>) =>
this._handleArtistsResizeMouseDown(e, width)
}
>
<div className="webamp-media-library-vertical-divider-line" />
</div>
{this.props.albums}
</div>
<div
className="webamp-media-library-horizontal-divider"
onMouseDown={(e: React.MouseEvent<HTMLDivElement>) =>
this._handlePlaylistResizeMouseDown(e, height)
}
>
<div className="webamp-media-library-horizontal-divider-line" />
</div>
<div style={{ overflow: "hidden" }}>{this.props.tracks}</div>
</div>
</div>
)}
</GenWindow>
);
}
}
const mapStateToProps = (state: AppState): StateProps => {
return {
skinGenExColors: Selectors.getSkinGenExColors(state),
};
};
export default connect(mapStateToProps)(LibraryLayout);

View file

@ -0,0 +1,72 @@
import React, { useState } from "react";
import classnames from "classnames";
interface Props {
headings: Array<string>;
rows: Array<Array<any>>;
widths: Array<number>;
}
export default function LibraryTable(props: Props) {
const [selectedRow, setSelectedRow] = useState<number | null>(null);
const rowStyle = {
display: "grid",
gridTemplateColumns: props.widths.map(width => `${width}px`).join(" "),
};
return (
<div
className="webamp-media-library-item"
style={{ height: "100%", position: "relative" }}
>
<div
className="webamp-media-library-table"
style={{
overflow: "scroll",
height: "100%",
}}
>
<div style={rowStyle} className="library-table-heading">
{props.headings.map((heading, i) => (
<div key={`heading-${i}-${heading}`} style={{ paddingLeft: 5 }}>
{heading}
</div>
))}
</div>
{props.rows.map((row, i) => (
<div
style={{
...rowStyle,
boxSizing: "border-box",
}}
className={classnames("library-table-row", {
selected: i === selectedRow,
})}
onClick={() => setSelectedRow(i)}
key={`row-${i}`}
>
{row.map((text, j) => (
<div
style={{ overflow: "hidden", paddingLeft: 6 }}
key={`cell-${j}`}
>
{text}
</div>
))}
</div>
))}
<div
style={{
zIndex: 99999,
color: "white",
width: 1,
position: "absolute",
left: 50,
top: 0,
height: "100%",
}}
/>
</div>
</div>
);
}

View file

@ -0,0 +1,45 @@
import * as React from "react";
import LibraryButton from "./LibraryButton";
export default class Sidebar extends React.Component {
render() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
height: "100%",
}}
>
<div className="webamp-media-library-item" style={{ flexGrow: 1 }}>
<ul style={{ margin: 3 }}>
<li>
Local Media
<ul>
<li>Audio</li>
<li>Video</li>
</ul>
</li>
<li>Playlist</li>
<li>
Devices
<ul>
<li>CD E:</li>
</ul>
</li>
<li>Internet Radio</li>
<li>Internet TV</li>
</ul>
</div>
<LibraryButton
style={{
width: "100%",
marginTop: 1,
}}
>
Library
</LibraryButton>
</div>
);
}
}

View file

@ -0,0 +1,98 @@
import * as React from "react";
import { connect } from "react-redux";
import * as Selectors from "../../selectors";
import { AppState, PlaylistTrack } from "../../types";
import * as Utils from "../../utils";
import * as FileUtils from "../../fileUtils";
import LibraryButton from "./LibraryButton";
import LibraryTable from "./LibraryTable";
interface StateProps {
tracks: PlaylistTrack[];
filterTracks: (query: string) => PlaylistTrack[];
}
interface State {
filter: string;
}
class TracksTable extends React.Component<StateProps, State> {
constructor(props: StateProps) {
super(props);
this.state = { filter: "" };
}
render() {
return (
<div
style={{
display: "flex",
flexDirection: "column",
height: "100%",
}}
>
<div
style={{
display: "flex",
flexDirection: "row",
height: 23,
}}
>
<span>Search:</span>
<input
style={{ marginLeft: 12, flexGrow: 1 }}
type="text"
className="webamp-media-library-item"
onChange={e => this.setState({ filter: e.target.value })}
/>
</div>
<LibraryTable
headings={[
"Artist",
"Title",
"Album",
"Length",
"Track #",
"Genere",
"Year",
"Filename",
]}
rows={this.props.filterTracks(this.state.filter).map(track => {
return [
track.artist,
track.title,
track.album,
Utils.getTimeStr(track.duration),
1,
"Primus",
2001,
track.url == null
? track.defaultName
: FileUtils.filenameFromUrl(track.url),
];
})}
widths={[100, 100, 100, 100, 100, 100, 100, 100]}
/>
<div style={{ marginTop: 2 }}>
<LibraryButton>Play</LibraryButton>
<LibraryButton>Enqueue</LibraryButton>
<LibraryButton>Play all</LibraryButton>
<LibraryButton>Enqueue all</LibraryButton>
<span id="webamp-media-library-track-summary-duration">
1 item [3:25]
</span>
</div>
</div>
);
}
}
const mapStateToProps = (state: AppState): StateProps => {
return {
tracks: Object.values(Selectors.getTracks(state)),
filterTracks: Selectors.getTracksMatchingFilter(state),
};
};
export default connect(mapStateToProps)(TracksTable);

View file

@ -0,0 +1,20 @@
import * as React from "react";
import "../../../css/media-library-window.css";
import Sidebar from "./Sidebar";
import ArtistsTable from "./ArtistsTable";
import AlbumsTable from "./AlbumsTable";
import TracksTable from "./TracksTable";
import LibraryLayout from "./LibraryLayout";
export default class MediaLibraryWindow extends React.Component<{}> {
render() {
return (
<LibraryLayout
sidebar={<Sidebar />}
artists={<ArtistsTable />}
albums={<AlbumsTable />}
tracks={<TracksTable />}
/>
);
}
}