mirror of
https://github.com/GoogleChromeLabs/squoosh.git
synced 2025-11-11 16:26:20 +00:00
Switch to an event-based architecture
This commit is contained in:
@@ -2,14 +2,16 @@ import App from './index';
|
||||
|
||||
import { expose } from 'comlink';
|
||||
|
||||
const API_VERSION = 1;
|
||||
|
||||
export function exposeAPI(app: App) {
|
||||
self.parent.postMessage('READY', '*');
|
||||
self.parent.postMessage({ type: 'READY', version: API_VERSION }, '*');
|
||||
self.addEventListener('message', (ev: MessageEvent) => {
|
||||
if (ev.data !== 'READY?') {
|
||||
return;
|
||||
}
|
||||
ev.stopPropagation();
|
||||
self.parent.postMessage('READY', '*');
|
||||
self.parent.postMessage({ type: 'READY', version: API_VERSION }, '*');
|
||||
});
|
||||
expose(new API(app), self.parent);
|
||||
}
|
||||
@@ -18,11 +20,13 @@ class API {
|
||||
constructor(private app: App) { }
|
||||
|
||||
async setFile(blob: Blob, name: string) {
|
||||
await new Promise((resolve) => {
|
||||
this.app.setState({ file: new File([blob], name) }, resolve);
|
||||
});
|
||||
await new Promise((resolve) => {
|
||||
document.addEventListener('squooshingdone', resolve, { once: true });
|
||||
return new Promise(async (resolve) => {
|
||||
document.addEventListener(
|
||||
'squoosh:processingstart',
|
||||
() => resolve(),
|
||||
{ once: true },
|
||||
);
|
||||
this.app.openFile(new File([blob], name));
|
||||
});
|
||||
}
|
||||
|
||||
@@ -30,8 +34,41 @@ class API {
|
||||
if (!this.app.state.file || !this.app.compressInstance) {
|
||||
throw new Error('No file has been loaded');
|
||||
}
|
||||
if (
|
||||
!this.app.compressInstance!.state.loading &&
|
||||
!this.app.compressInstance!.state.sides[side].loading
|
||||
) {
|
||||
return this.app.compressInstance!.state.sides[side].file;
|
||||
}
|
||||
|
||||
await this.app.compressInstance.compressionJobs[side];
|
||||
return this.app.compressInstance.state.sides[side].file;
|
||||
return new Promise((resolve, reject) => {
|
||||
document.addEventListener(
|
||||
'squoosh:processingdone',
|
||||
(ev) => {
|
||||
if ((ev as CustomEvent).detail.side !== side) {
|
||||
return;
|
||||
}
|
||||
resolve(this.app.compressInstance!.state.sides[side].file);
|
||||
},
|
||||
);
|
||||
document.addEventListener(
|
||||
'squoosh:processingabort',
|
||||
(ev) => {
|
||||
if ((ev as CustomEvent).detail.side !== side) {
|
||||
return;
|
||||
}
|
||||
reject('aborted');
|
||||
},
|
||||
);
|
||||
document.addEventListener(
|
||||
'squoosh:processingerroor',
|
||||
(ev) => {
|
||||
if ((ev as CustomEvent).detail.side !== side) {
|
||||
return;
|
||||
}
|
||||
reject((ev as CustomEvent).detail.msg);
|
||||
},
|
||||
);
|
||||
});
|
||||
}
|
||||
}
|
||||
|
||||
@@ -73,6 +73,11 @@ export default class App extends Component<Props, State> {
|
||||
import('./client-api').then(m => m.exposeAPI(this));
|
||||
}
|
||||
|
||||
@bind openFile(file: File | Fileish) {
|
||||
this.openEditor();
|
||||
this.setState({ file });
|
||||
}
|
||||
|
||||
@bind
|
||||
private onFileDrop({ files }: FileDropEvent) {
|
||||
if (!files || files.length === 0) return;
|
||||
@@ -83,8 +88,7 @@ export default class App extends Component<Props, State> {
|
||||
|
||||
@bind
|
||||
private onIntroPickFile(file: File | Fileish) {
|
||||
this.openEditor();
|
||||
this.setState({ file });
|
||||
return this.openFile(file);
|
||||
}
|
||||
|
||||
@bind
|
||||
|
||||
@@ -205,7 +205,6 @@ const originalDocumentTitle = document.title;
|
||||
|
||||
export default class Compress extends Component<Props, State> {
|
||||
widthQuery = window.matchMedia('(max-width: 599px)');
|
||||
compressionJobs: [Promise<void>, Promise<void>] = [Promise.resolve(), Promise.resolve()];
|
||||
|
||||
state: State = {
|
||||
source: undefined,
|
||||
@@ -306,7 +305,7 @@ export default class Compress extends Component<Props, State> {
|
||||
// The image only needs updated if the encoder/preprocessor settings have changed, or the
|
||||
// source has changed.
|
||||
if (sourceDataChanged || encoderChanged || preprocessorChanged) {
|
||||
this.compressionJobs[i] = this.updateImage(i, {
|
||||
this.queueUpdateImage(i, {
|
||||
skipPreprocessing: !sourceDataChanged && !preprocessorChanged,
|
||||
});
|
||||
}
|
||||
@@ -396,8 +395,8 @@ export default class Compress extends Component<Props, State> {
|
||||
// Either processor is good enough here.
|
||||
const processor = this.leftProcessor;
|
||||
|
||||
this.setState({ loadingCounter, loading: true });
|
||||
|
||||
this.setState({ loadingCounter, loading: true }, this.signalProcessingStart);
|
||||
// this.signalProcessingStart();
|
||||
// Abort any current encode jobs, as they're redundant now.
|
||||
this.leftProcessor.abortCurrent();
|
||||
this.rightProcessor.abortCurrent();
|
||||
@@ -445,7 +444,9 @@ export default class Compress extends Component<Props, State> {
|
||||
this.updateDocumentTitle(file.name);
|
||||
this.setState(newState);
|
||||
} catch (err) {
|
||||
if (err.name === 'AbortError') return;
|
||||
if (err.name === 'AbortError') {
|
||||
return;
|
||||
}
|
||||
console.error(err);
|
||||
// Another file has been opened/processed before this one processed.
|
||||
if (this.state.loadingCounter !== loadingCounter) return;
|
||||
@@ -467,7 +468,8 @@ export default class Compress extends Component<Props, State> {
|
||||
|
||||
this.updateImageTimeoutIds[index] = self.setTimeout(
|
||||
() => {
|
||||
this.updateImage(index, options).catch((err) => {
|
||||
this.updateImage(index, options)
|
||||
.catch((err) => {
|
||||
console.error(err);
|
||||
});
|
||||
},
|
||||
@@ -475,6 +477,31 @@ export default class Compress extends Component<Props, State> {
|
||||
);
|
||||
}
|
||||
|
||||
@bind
|
||||
private dispatchCustomEvent(name: string, detail?: {}) {
|
||||
(this.base || document).dispatchEvent(new CustomEvent(name, { detail, bubbles: true }));
|
||||
}
|
||||
|
||||
@bind
|
||||
private signalProcessingStart() {
|
||||
this.dispatchCustomEvent('squoosh:processingstart');
|
||||
}
|
||||
|
||||
@bind
|
||||
private signalProcessingDone(side: 0|1) {
|
||||
this.dispatchCustomEvent('squoosh:processingdone', { side });
|
||||
}
|
||||
|
||||
@bind
|
||||
private signalProcessingAbort(side: 0|1) {
|
||||
this.dispatchCustomEvent('squoosh:processingabort', { side });
|
||||
}
|
||||
|
||||
@bind
|
||||
private signalProcessingError(side: 0|1, msg: string) {
|
||||
this.dispatchCustomEvent('squoosh:processingerror', { side, msg });
|
||||
}
|
||||
|
||||
private async updateImage(index: number, options: UpdateImageOptions = {}): Promise<void> {
|
||||
const {
|
||||
skipPreprocessing = false,
|
||||
@@ -490,9 +517,7 @@ export default class Compress extends Component<Props, State> {
|
||||
loading: true,
|
||||
});
|
||||
|
||||
this.setState({ sides }, () => this.base!.dispatchEvent(
|
||||
new CustomEvent('squooshingdone', { bubbles: true }),
|
||||
));
|
||||
this.setState({ sides });
|
||||
|
||||
const side = sides[index];
|
||||
const settings = side.latestSettings;
|
||||
@@ -538,8 +563,13 @@ export default class Compress extends Component<Props, State> {
|
||||
});
|
||||
}
|
||||
} catch (err) {
|
||||
if (err.name === 'AbortError') return;
|
||||
this.props.showSnack(`Processing error (type=${settings.encoderState.type}): ${err}`);
|
||||
if (err.name === 'AbortError') {
|
||||
this.signalProcessingAbort(index as 0 | 1);
|
||||
return;
|
||||
}
|
||||
const errorMsg = `Processing error (type=${settings.encoderState.type}): ${err}`;
|
||||
this.signalProcessingError(index as 0|1, errorMsg);
|
||||
this.props.showSnack(errorMsg);
|
||||
throw err;
|
||||
}
|
||||
}
|
||||
@@ -562,7 +592,7 @@ export default class Compress extends Component<Props, State> {
|
||||
encodedSettings: settings,
|
||||
});
|
||||
|
||||
this.setState({ sides });
|
||||
this.setState({ sides }, () => this.signalProcessingDone(index as 0|1));
|
||||
}
|
||||
|
||||
render({ onBack }: Props, { loading, sides, source, mobileView }: State) {
|
||||
|
||||
Reference in New Issue
Block a user