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