Files
squoosh/src/client/initial-app/App/index.tsx
Jake Archibald a3b341f813 New homepage (#861)
* Paste button

* Logo and animated blobs

* Predictable initial blob position

* Initial blob shape

* Update canvas on resize if not updating every frame

* lol

* Get styles from CSS

* Background blobs

* Fade into the center

* Get initial focus

* Pause time while page is hidden

* Footer

* Optimise amount of initial CSS

* More CSS optimisation

* Install button

* Home page with demo loading

* Tweak size

* Replace thumbnails

* Responsive demo section

* Responsive main section

* Remove debug stuff

* Fix prerender SVG size

* Changes from feedback

* Blob nudges (#872)

* more smaller blobs

* less blobs that are practically invisible

* more dynamic speed range and stronger gravity

* Reverting resize observer change

The content rect is different to getBoundingClientRect

Co-authored-by: Adam Argyle <atom@argyleink.com>
2020-12-05 11:21:33 +00:00

134 lines
3.8 KiB
TypeScript

import type { FileDropEvent } from 'file-drop-element';
import type SnackBarElement from 'shared/custom-els/snack-bar';
import type { SnackOptions } from 'shared/custom-els/snack-bar';
import { h, Component } from 'preact';
import { linkRef } from 'shared/prerendered-app/util';
import * as style from './style.css';
import 'add-css:./style.css';
import 'file-drop-element';
import 'shared/custom-els/snack-bar';
import Intro from 'shared/prerendered-app/Intro';
import 'shared/custom-els/loading-spinner';
const ROUTE_EDITOR = '/editor';
const compressPromise = import('client/lazy-app/Compress');
const swBridgePromise = import('client/lazy-app/sw-bridge');
function back() {
window.history.back();
}
interface Props {}
interface State {
awaitingShareTarget: boolean;
file?: File;
isEditorOpen: Boolean;
Compress?: typeof import('client/lazy-app/Compress').default;
}
export default class App extends Component<Props, State> {
state: State = {
awaitingShareTarget: new URL(location.href).searchParams.has(
'share-target',
),
isEditorOpen: false,
file: undefined,
Compress: undefined,
};
snackbar?: SnackBarElement;
constructor() {
super();
compressPromise
.then((module) => {
this.setState({ Compress: module.default });
})
.catch(() => {
this.showSnack('Failed to load app');
});
swBridgePromise.then(async ({ offliner, getSharedImage }) => {
offliner(this.showSnack);
if (!this.state.awaitingShareTarget) return;
const file = await getSharedImage();
// Remove the ?share-target from the URL
history.replaceState('', '', '/');
this.openEditor();
this.setState({ file, awaitingShareTarget: false });
});
// Since iOS 10, Apple tries to prevent disabling pinch-zoom. This is great in theory, but
// really breaks things on Squoosh, as you can easily end up zooming the UI when you mean to
// zoom the image. Once you've done this, it's really difficult to undo. Anyway, this seems to
// prevent it.
document.body.addEventListener('gesturestart', (event: any) => {
event.preventDefault();
});
window.addEventListener('popstate', this.onPopState);
}
private onFileDrop = ({ files }: FileDropEvent) => {
if (!files || files.length === 0) return;
const file = files[0];
this.openEditor();
this.setState({ file });
};
private onIntroPickFile = (file: File) => {
this.openEditor();
this.setState({ file });
};
private showSnack = (
message: string,
options: SnackOptions = {},
): Promise<string> => {
if (!this.snackbar) throw Error('Snackbar missing');
return this.snackbar.showSnackbar(message, options);
};
private onPopState = () => {
this.setState({ isEditorOpen: location.pathname === ROUTE_EDITOR });
};
private openEditor = () => {
if (this.state.isEditorOpen) return;
// Change path, but preserve query string.
const editorURL = new URL(location.href);
editorURL.pathname = ROUTE_EDITOR;
history.pushState(null, '', editorURL.href);
this.setState({ isEditorOpen: true });
};
render(
{}: Props,
{ file, isEditorOpen, Compress, awaitingShareTarget }: State,
) {
const showSpinner = awaitingShareTarget || (isEditorOpen && !Compress);
return (
<div class={style.app}>
<file-drop onfiledrop={this.onFileDrop} class={style.drop}>
{showSpinner ? (
<loading-spinner class={style.appLoader} />
) : isEditorOpen ? (
Compress && (
<Compress file={file!} showSnack={this.showSnack} onBack={back} />
)
) : (
<Intro onFile={this.onIntroPickFile} showSnack={this.showSnack} />
)}
<snack-bar ref={linkRef(this, 'snackbar')} />
</file-drop>
</div>
);
}
}