const { merge, timer, Subject, combineLatest } = require("rxjs");
const {
tap,
mapTo,
skip,
filter,
startWith,
takeUntil,
switchMap,
} = require("rxjs/operators");
const DELAY = 500;
const MIN_DURATION = 1200;
const taskStartSubject = new Subject();
const taskStart = taskStartSubject.asObservable();
const taskEndSubject = new Subject();
const taskEnd = taskEndSubject.asObservable();
const busyDelayTimer = timer(DELAY);
const busyMinDurationTimer = timer(MIN_DURATION + DELAY);
const busyDelayTimerStart = taskStart.pipe(switchMap(() => busyDelayTimer));
const busyDelayTimerEnd = busyDelayTimerStart.pipe(takeUntil(taskEnd));
const emitOnTaskEnd = taskEnd.pipe(mapTo(1));
const emitOnDelayTimerEnd = busyDelayTimerEnd.pipe(mapTo(-1));
const emitOnMinDurationEnd = busyMinDurationTimer.pipe(mapTo(-1));
////////////// start/////////////////
const raceBetweenTaskAndDelay = combineLatest([
emitOnTaskEnd.pipe(startWith(null)),
emitOnDelayTimerEnd.pipe(startWith(null)),
]).pipe(skip(1));
const taskEndBeforeDelay = raceBetweenTaskAndDelay.pipe(
filter(([taskEndFirst, timerEndFirst]) => {
return taskEndFirst === 1 && timerEndFirst === null;
})
);
const shouldNotShowSpinner = taskEndBeforeDelay.pipe(mapTo(false));
const taskEndAfterTimeout = raceBetweenTaskAndDelay.pipe(
filter(([taskEndFirst, timerEndFirst]) => {
return taskEndFirst === null && timerEndFirst === -1;
})
);
const shouldShowSpinner = taskEndAfterTimeout.pipe(mapTo(true));
const showSpinner = shouldShowSpinner.pipe(
tap(() => {
console.timeLog("spinner");
console.log("show");
})
);
/////////////// end ///////////////
const raceBetweenTaskAndMinDuration = combineLatest([
emitOnTaskEnd.pipe(startWith(null)),
emitOnMinDurationEnd.pipe(startWith(null)),
]).pipe(skip(1));
const hideSpinnerUntilMinDurationEnd = raceBetweenTaskAndMinDuration.pipe(
filter(([taskEndFirst, timerEndFirst]) => {
return taskEndFirst === 1 && timerEndFirst === null;
})
);
const hideSpinnerAfterTimerAndTaskEnd = raceBetweenTaskAndMinDuration.pipe(
filter(([taskEndFirst, timerEndFirst]) => {
return taskEndFirst === 1 && timerEndFirst === -1;
})
);
const hideSpinner = merge(
// case 1: should not show spinner at all
shouldNotShowSpinner,
// case 2: task end, but wait until min duration timer ends
combineLatest([hideSpinnerUntilMinDurationEnd, emitOnMinDurationEnd]),
// case 3: task takes a long time, wait unitl its end
hideSpinnerAfterTimerAndTaskEnd
).pipe(
tap(() => {
console.timeLog("spinner");
console.log("hide");
})
);
const Spinner = showSpinner.pipe(takeUntil(hideSpinner));
// test
Spinner.subscribe();
console.log("task start");
console.time("spinner");
taskStartSubject.next();
// Case 1: Should not show spinner
setTimeout(() => {
taskEndSubject.next();
}, 50);
// Case 2: Should show spinner when busyMinDurationMs end
/*
setTimeout(() => {
taskEndSubject.next();
}, 600);
*/
// Case 3: Should show spinner until task ends
/*
setTimeout(() => {
taskEndSubject.next();
}, 2000);
*/