67 lines
1.8 KiB
JavaScript
67 lines
1.8 KiB
JavaScript
const DEFAULT_COOLDOWN_SECONDS = 60;
|
|
const sceneExpiresAt = new Map();
|
|
|
|
const requireFunction = (value, name) => {
|
|
if (typeof value !== "function") {
|
|
throw new TypeError(`${name} must be a function`);
|
|
}
|
|
return value;
|
|
};
|
|
|
|
export const createAuthSmsCooldown = ({
|
|
operationCode,
|
|
onChange,
|
|
now = Date.now,
|
|
setIntervalFn = setInterval,
|
|
clearIntervalFn = clearInterval,
|
|
}) => {
|
|
if (typeof operationCode !== "string" || !operationCode.trim()) {
|
|
throw new TypeError("operationCode must be a non-empty string");
|
|
}
|
|
const normalizedOperationCode = operationCode.trim();
|
|
const publish = requireFunction(onChange, "onChange");
|
|
const readNow = requireFunction(now, "now");
|
|
const scheduleInterval = requireFunction(setIntervalFn, "setIntervalFn");
|
|
const cancelInterval = requireFunction(clearIntervalFn, "clearIntervalFn");
|
|
let timer = null;
|
|
|
|
const stopTimer = () => {
|
|
if (timer === null) return;
|
|
cancelInterval(timer);
|
|
timer = null;
|
|
};
|
|
|
|
const sync = () => {
|
|
const expiresAt = sceneExpiresAt.get(normalizedOperationCode) || 0;
|
|
const remaining = Math.max(
|
|
0,
|
|
Math.ceil((expiresAt - Number(readNow())) / 1000),
|
|
);
|
|
if (remaining === 0) {
|
|
sceneExpiresAt.delete(normalizedOperationCode);
|
|
stopTimer();
|
|
} else if (timer === null) {
|
|
timer = scheduleInterval(sync, 1000);
|
|
}
|
|
publish(remaining);
|
|
return remaining;
|
|
};
|
|
|
|
const start = (seconds = DEFAULT_COOLDOWN_SECONDS) => {
|
|
if (!Number.isInteger(seconds) || seconds < 1 || seconds > 300) {
|
|
throw new TypeError("cooldown seconds must be an integer from 1 to 300");
|
|
}
|
|
sceneExpiresAt.set(
|
|
normalizedOperationCode,
|
|
Number(readNow()) + seconds * 1000,
|
|
);
|
|
return sync();
|
|
};
|
|
|
|
return Object.freeze({
|
|
start,
|
|
sync,
|
|
dispose: stopTimer,
|
|
});
|
|
};
|