import React, { useEffect } from "react"; import WebampComponent from "../WebampComponent"; import * as Utils from "../utils"; import { SCREENSHOT_HEIGHT, SCREENSHOT_WIDTH } from "../constants"; import { delay, switchMap } from "rxjs/operators"; import { Subject, combineLatest, timer, fromEvent, from } from "rxjs"; import Disposable from "../Disposable"; import Metadata from "./Metadata"; import SkinReadme from "../SkinReadme"; function useSkinData({ hash, skinData, setSkinData }) { useEffect(() => { if ( skinData != null && skinData.color != null && skinData.fileName != null ) { return; } // TODO: Move this to Epic const subscription = from(fetch(`https://api.webamp.org/skins/${hash}`)) .pipe(switchMap((response) => response.json())) .subscribe((body) => { setSkinData(hash, { md5: hash, fileName: body.canonicalFilename, color: body.averageColor, nsfw: body.nsfw, }); }); return () => { subscription.unsubscribe(); }; }, [hash, setSkinData, skinData]); } class BaseFocusedSkin extends React.Component { constructor(props) { super(props); this._disposable = new Disposable(); // TODO: Handle the case were we come from a permalink if (this.props.initialPosition == null) { this.state = Object.assign( { previewLoaded: false, loaded: false, transitionComplete: true, }, this._getCenteredState() ); } else { this.state = { loaded: false, centered: false, transitionComplete: false, top: this.props.initialPosition.top, left: this.props.initialPosition.left, width: this.props.initialWidth, height: this.props.initialHeight, }; } this._webampLoadedEvents = new Subject(); this._transitionBeginEvents = new Subject(); const transitionComplete = this._transitionBeginEvents.pipe(delay(500)); // Emit after both Webamp has loaded, and the transition is complete const startWebampFadein = combineLatest( this._webampLoadedEvents, transitionComplete ); // This value matches the opacity transition timing for `#webamp` in CSS const webampFadeinComplete = startWebampFadein.pipe(delay(400)); this._disposable.add( startWebampFadein.subscribe(() => { document.body.classList.add("webamp-loaded"); }), webampFadeinComplete.subscribe(() => { this.setState({ loaded: true }); }), () => { document.body.classList.remove("webamp-loaded"); } ); } componentDidMount() { this._disposable.add( fromEvent(window.document, "keydown").subscribe((e) => { if (e.key === "ArrowRight") { this.props.selectRelativeSkin(1); } else if (e.key === "ArrowLeft") { this.props.selectRelativeSkin(-1); } }) ); if (!this.state.centered) { this._disposable.add( timer(0).subscribe(() => { // TODO: Observe DOM and recenter this.setState(this._getCenteredState()); this._transitionBeginEvents.next(null); }) ); } else { this._transitionBeginEvents.next(null); } } componentDidUpdate(prevProps) { if (prevProps.skinUrl !== this.props.skinUrl) { document.body.classList.remove("webamp-loaded"); } } componentWillUnmount() { this._disposable.dispose(); } handleWebampLoaded = () => { this._webampLoadedEvents.next(null); }; _getCenteredState() { // TODO: Observe DOM and recenter const { windowWidth, windowHeight } = Utils.getWindowSize(); return { centered: true, top: (windowHeight - SCREENSHOT_HEIGHT) / 2, left: (windowWidth - SCREENSHOT_WIDTH) / 2, height: SCREENSHOT_HEIGHT, width: SCREENSHOT_WIDTH, }; } render() { const { loaded } = this.state; const transform = `translateX(${Math.round( this.state.left )}px) translateY(${Math.round(this.state.top)}px)`; return ( {this.state.centered && ( <>
{this.props.fileExplorerOpen && } )}
{loaded || ( this.setState({ previewLoaded: true })} src={Utils.screenshotUrlFromHash(this.props.hash)} alt={this.props.skinData && this.props.skinData.fileName} /> )}
); } } export default (props) => { const { hash, skinData, gotSkinData } = props; useSkinData({ hash, skinData, setSkinData: gotSkinData }); return ; };