demarches-normaliennes/app/javascript/controllers/turbo_poll_controller.ts
2022-05-17 16:08:47 +02:00

94 lines
2.2 KiB
TypeScript

import { httpRequest } from '@utils';
import { ApplicationController } from './application_controller';
const DEFAULT_POLL_INTERVAL = 3000;
const DEFAULT_MAX_CHECKS = 5;
// Periodically check the state of a URL.
//
// Each time the given URL is requested, a turbo-stream is rendered, causing the state to be refreshed.
//
// This is used mainly to refresh attachments during the anti-virus check,
// but also to refresh the state of a pending spreadsheet export.
export class TurboPollController extends ApplicationController {
static values = {
url: String,
maxChecks: { type: Number, default: DEFAULT_MAX_CHECKS },
interval: { type: Number, default: DEFAULT_POLL_INTERVAL }
};
declare readonly urlValue: string;
declare readonly intervalValue: number;
declare readonly maxChecksValue: number;
#timer?: number;
#abortController?: AbortController;
connect(): void {
const state = this.nextState();
if (state) {
this.schedule(state);
}
}
disconnect(): void {
this.cancel();
}
refresh() {
this.cancel();
this.#abortController = new AbortController();
httpRequest(this.urlValue, { signal: this.#abortController.signal })
.turbo()
.catch(() => null);
}
private schedule(state: PollState): void {
this.cancel();
this.#timer = setTimeout(() => {
this.refresh();
}, state.interval);
}
private cancel(): void {
clearTimeout(this.#timer);
this.#abortController?.abort();
this.#abortController = window.AbortController
? new AbortController()
: undefined;
}
private nextState(): PollState | false {
const state = pollers.get(this.urlValue);
if (!state) {
return this.resetState();
}
state.interval *= 1.5;
state.checks += 1;
if (state.checks <= this.maxChecksValue) {
return state;
} else {
this.resetState();
return false;
}
}
private resetState(): PollState {
const state = {
interval: this.intervalValue,
checks: 0
};
pollers.set(this.urlValue, state);
return state;
}
}
type PollState = {
interval: number;
checks: number;
};
// We keep a global state of the pollers. It will be reset on every page change.
const pollers = new Map<string, PollState>();