Switch to an event-based architecture

This commit is contained in:
Surma
2018-12-17 16:40:29 +00:00
parent bfe74b5fb2
commit 672c57b61f
3 changed files with 94 additions and 23 deletions

View File

@@ -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);
},
);
});
}
}

View File

@@ -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

View File

@@ -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) {