Add basic history handling (#288) (#309)

* Add basic history handling (#288)

* Move history management to Compress component

* Remove unused pathname property from history

* Rename history listener functions

* Use history.back instead of history.replace

* Support going forward in history. Persist last selected file in runtime

* Add netlify redirects file

* Use 301 status code for redirect

* Cleanup _redirects file

* Use 200 status code for redirects

* Simplify onPopState function

* Always redirect to 301 with url rewrite

* Remove redundant history function

* Remove file check on render. Call openEditor synchronously

* Use pushState only if user is on the initial screen. Mount history listener in constructor

* Simplify openEditor condition

* Update early return condition

* Rolling abstractions back into the main component
This commit is contained in:
Maciej Matuszewski
2018-12-12 13:58:03 +01:00
committed by Jake Archibald
parent 3245987113
commit 129c33fa12
3 changed files with 32 additions and 7 deletions

2
_redirects.ejs Normal file
View File

@@ -0,0 +1,2 @@
/index.html / 301
/* /index.html 301

View File

@@ -9,6 +9,8 @@ import '../../lib/SnackBar';
import Intro from '../intro'; import Intro from '../intro';
import '../custom-els/LoadingSpinner'; import '../custom-els/LoadingSpinner';
const ROUTE_EDITOR = '/editor';
const compressPromise = import( const compressPromise = import(
/* webpackChunkName: "main-app" */ /* webpackChunkName: "main-app" */
'../compress', '../compress',
@@ -18,15 +20,21 @@ const offlinerPromise = import(
'../../lib/offliner', '../../lib/offliner',
); );
function back() {
window.history.back();
}
interface Props {} interface Props {}
interface State { interface State {
file?: File | Fileish; file?: File | Fileish;
isEditorOpen: Boolean;
Compress?: typeof import('../compress').default; Compress?: typeof import('../compress').default;
} }
export default class App extends Component<Props, State> { export default class App extends Component<Props, State> {
state: State = { state: State = {
isEditorOpen: false,
file: undefined, file: undefined,
Compress: undefined, Compress: undefined,
}; };
@@ -53,17 +61,20 @@ export default class App extends Component<Props, State> {
window.STATE = this.state; window.STATE = this.state;
}; };
} }
window.addEventListener('popstate', this.onPopState);
} }
@bind @bind
private onFileDrop(event: FileDropEvent) { private onFileDrop({ file }: FileDropEvent) {
const { file } = event;
if (!file) return; if (!file) return;
this.openEditor();
this.setState({ file }); this.setState({ file });
} }
@bind @bind
private onIntroPickFile(file: File | Fileish) { private onIntroPickFile(file: File | Fileish) {
this.openEditor();
this.setState({ file }); this.setState({ file });
} }
@@ -74,18 +85,25 @@ export default class App extends Component<Props, State> {
} }
@bind @bind
private onBack() { private onPopState() {
this.setState({ file: undefined }); this.setState({ isEditorOpen: location.pathname === ROUTE_EDITOR });
} }
render({}: Props, { file, Compress }: State) { @bind
private openEditor() {
if (this.state.isEditorOpen) return;
history.pushState(null, '', ROUTE_EDITOR);
this.setState({ isEditorOpen: true });
}
render({}: Props, { file, isEditorOpen, Compress }: State) {
return ( return (
<div id="app" class={style.app}> <div id="app" class={style.app}>
<file-drop accept="image/*" onfiledrop={this.onFileDrop} class={style.drop}> <file-drop accept="image/*" onfiledrop={this.onFileDrop} class={style.drop}>
{(!file) {!isEditorOpen
? <Intro onFile={this.onIntroPickFile} showSnack={this.showSnack} /> ? <Intro onFile={this.onIntroPickFile} showSnack={this.showSnack} />
: (Compress) : (Compress)
? <Compress file={file} showSnack={this.showSnack} onBack={this.onBack} /> ? <Compress file={file!} showSnack={this.showSnack} onBack={back} />
: <loading-spinner class={style.appLoader}/> : <loading-spinner class={style.appLoader}/>
} }
<snack-bar ref={linkRef(this, 'snackbar')} /> <snack-bar ref={linkRef(this, 'snackbar')} />

View File

@@ -251,6 +251,11 @@ module.exports = async function (_, env) {
filename: '_headers', filename: '_headers',
}), }),
isProd && new AssetTemplatePlugin({
template: path.join(__dirname, '_redirects.ejs'),
filename: '_redirects',
}),
new ScriptExtHtmlPlugin({ new ScriptExtHtmlPlugin({
inline: ['first'] inline: ['first']
}), }),