diff --git a/src/DownloadLink.js b/src/DownloadLink.js new file mode 100644 index 00000000..aa06695c --- /dev/null +++ b/src/DownloadLink.js @@ -0,0 +1,55 @@ +import React from "react"; +import { Subject } from "rxjs"; +import { switchMap, distinctUntilChanged, map } from "rxjs/operators"; +import Disposable from "./Disposable"; + +// The `download` attribute on `` tags is not respected on cross origin +// assets. However, it does work for Object URLs. So, we download the skin as +// soon as we show the link and swap out the href with the Object URL as soon as +// it's loaded. The skin should already be cahced, so it should not actually +// result in an extra network request. +// +// There may be a breif time where the download link will use the hash name instead +// of the real name, but it's probably too short to actually ever be hit by a real user. +export default class DownloadLink extends React.Component { + constructor(props) { + super(props); + this.state = { href: null }; + this._hrefs = new Subject(); + this._disposables = new Disposable(); + } + + componentDidMount() { + this._disposables.add( + this._hrefs + .pipe( + distinctUntilChanged(), + switchMap(href => fetch(href)), + switchMap(response => response.blob()), + map(blob => URL.createObjectURL(blob)) + ) + .subscribe(url => { + this.setState({ href: url }); + }) + ); + this._hrefs.next(this.props.href); + } + + componentDidUpdate(_prevProps, prevState) { + if (prevState.href !== this.state.href) { + URL.revokeObjectURL(prevState.href); + } + this._hrefs.next(this.props.href); + } + + componentWillUnmount() { + if (this.state.href) { + URL.revokeObjectURL(this.state.href); + } + this._disposables.dispose(); + } + + render() { + return ; + } +} diff --git a/src/FocusedSkin.js b/src/FocusedSkin.js index a263828b..6e289671 100644 --- a/src/FocusedSkin.js +++ b/src/FocusedSkin.js @@ -2,6 +2,7 @@ import React from "react"; import { connect } from "react-redux"; import WebampComponent from "./WebampComponent"; import Readme from "./Readme"; +import DownloadLink from "./DownloadLink"; import * as Utils from "./utils"; import * as Selectors from "./redux/selectors"; import { SCREENSHOT_HEIGHT, SCREENSHOT_WIDTH } from "./constants"; @@ -156,7 +157,12 @@ class FocusedSkin extends React.Component {