mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-14 01:37:26 +00:00
* 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>
134 lines
3.8 KiB
TypeScript
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>
|
|
);
|
|
}
|
|
}
|