feat(turbo): add turbo-poll controller

This commit is contained in:
Paul Chavard 2022-05-11 16:29:18 +02:00
parent 144a086dce
commit df914d273f
2 changed files with 104 additions and 8 deletions

View file

@ -1,18 +1,20 @@
import { Application } from '@hotwired/stimulus';
import { ReactController } from './react_controller';
import { TurboEventController } from './turbo_event_controller';
import { GeoAreaController } from './geo_area_controller';
import { TurboInputController } from './turbo_input_controller';
import { AutosaveController } from './autosave_controller';
import { AutosaveStatusController } from './autosave_status_controller';
import { GeoAreaController } from './geo_area_controller';
import { MenuButtonController } from './menu_button_controller';
import { ReactController } from './react_controller';
import { TurboEventController } from './turbo_event_controller';
import { TurboInputController } from './turbo_input_controller';
import { TurboPollController } from './turbo_poll_controller';
const Stimulus = Application.start();
Stimulus.register('autosave-status', AutosaveStatusController);
Stimulus.register('autosave', AutosaveController);
Stimulus.register('geo-area', GeoAreaController);
Stimulus.register('menu-button', MenuButtonController);
Stimulus.register('react', ReactController);
Stimulus.register('turbo-event', TurboEventController);
Stimulus.register('geo-area', GeoAreaController);
Stimulus.register('turbo-input', TurboInputController);
Stimulus.register('autosave', AutosaveController);
Stimulus.register('autosave-status', AutosaveStatusController);
Stimulus.register('menu-button', MenuButtonController);
Stimulus.register('turbo-poll', TurboPollController);

View file

@ -0,0 +1,94 @@
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>();