Skip to content

Commit

Permalink
Support @@trigger protocol in operator interval (#289)
Browse files Browse the repository at this point in the history
* Add implementation and test

* Add docs

* Fix overloads error

* Fix invalid type-tests (after adding overloads)
  • Loading branch information
igorkamyshev authored Jul 22, 2023
1 parent fcdc737 commit eae305b
Show file tree
Hide file tree
Showing 4 changed files with 109 additions and 23 deletions.
66 changes: 48 additions & 18 deletions src/interval/index.ts
Original file line number Diff line number Diff line change
Expand Up @@ -9,6 +9,20 @@ import {
is,
} from 'effector';

export function interval<S extends unknown, F extends unknown>(config: {
timeout: number | Store<number>;
start: Event<S>;
stop?: Event<F>;
leading?: boolean;
trailing?: boolean;
}): { tick: Event<void>; isRunning: Store<boolean> };

export function interval(config: {
timeout: number | Store<number>;
leading?: boolean;
trailing?: boolean;
}): TriggerProtocol;

export function interval<S extends unknown, F extends unknown>({
timeout,
start,
Expand All @@ -17,11 +31,14 @@ export function interval<S extends unknown, F extends unknown>({
trailing = false,
}: {
timeout: number | Store<number>;
start: Event<S>;
start?: Event<S>;
stop?: Event<F>;
leading?: boolean;
trailing?: boolean;
}): { tick: Event<void>; isRunning: Store<boolean> } {
}): { tick: Event<void>; isRunning: Store<boolean> } & TriggerProtocol {
const setup = (start ?? createEvent()) as Event<void>;
const teardown = (stop ?? createEvent()) as Event<void>;

const tick = createEvent();
const $isRunning = createStore(false);
const $timeout = toStoreNumber(timeout);
Expand Down Expand Up @@ -65,19 +82,19 @@ export function interval<S extends unknown, F extends unknown>({
});

guard({
clock: start,
clock: setup,
source: $timeout,
filter: $notRunning,
target: timeoutFx,
});

if (leading) {
const onReady = guard({ clock: start, filter: $notRunning }) as Event<S>;
const onReady = guard({ clock: setup, filter: $notRunning });
sample({ clock: onReady, target: tick });
}

sample({
clock: start,
clock: setup,
fn: () => true,
target: $isRunning,
});
Expand All @@ -97,23 +114,29 @@ export function interval<S extends unknown, F extends unknown>({
}),
});

if (stop) {
if (trailing) {
sample({
clock: stop,
target: tick,
});
}

$isRunning.on(stop, () => false);

if (trailing) {
sample({
clock: stop,
target: cleanupFx,
clock: teardown,
target: tick,
});
}

return { tick, isRunning: $isRunning };
$isRunning.on(teardown, () => false);

sample({
clock: teardown,
target: cleanupFx,
});

return {
tick,
isRunning: $isRunning,
'@@trigger': {
setup,
teardown,
fired: tick,
},
};
}

function toStoreNumber(value: number | Store<number> | unknown): Store<number> {
Expand All @@ -126,3 +149,10 @@ function toStoreNumber(value: number | Store<number> | unknown): Store<number> {
`timeout parameter in interval method should be number or Store. "${typeof value}" was passed`,
);
}

/**
* @see {@link https://withease.pages.dev/protocols/trigger.html}
*/
type TriggerProtocol = {
'@@trigger': { fired: Event<void>; setup: Event<void>; teardown: Event<void> };
};
36 changes: 35 additions & 1 deletion src/interval/interval.fork.test.ts
Original file line number Diff line number Diff line change
@@ -1,4 +1,11 @@
import { allSettled, createEvent, fork, createStore, guard } from 'effector';
import {
allSettled,
createEvent,
fork,
createStore,
guard,
createWatch,
} from 'effector';
import { argumentHistory, wait, watch } from '../../test-library';
import { interval } from '.';

Expand Down Expand Up @@ -96,3 +103,30 @@ test('does not leaves unresolved timeout effect, if stopped', async () => {

expect(scope.getState($count)).toEqual(6);
});

describe('@@trigger', () => {
test('fire tick on start and stop after teardown', async () => {
const listener = jest.fn();
const intervalTrigger = interval({ timeout: 1 });

const scope = fork();

const unwatch = createWatch({
unit: intervalTrigger['@@trigger'].fired,
fn: listener,
scope,
});

allSettled(intervalTrigger['@@trigger'].setup, { scope });

await wait(1);
expect(listener).toBeCalledTimes(1);

await allSettled(intervalTrigger['@@trigger'].teardown, { scope });

await wait(10);
expect(listener).toBeCalledTimes(1);

unwatch();
});
});
24 changes: 24 additions & 0 deletions src/interval/readme.md
Original file line number Diff line number Diff line change
Expand Up @@ -78,3 +78,27 @@ setTimeout(() => stopCounter(), 5000);
```

[Try it](https://share.effector.dev/EOVzc3df)

### `@@trigger` protocol

`interval` supports [`@@trigger` protocol](https://withease.pages.dev/protocols/trigger.html). It means that you can use `interval` whatever you can use `trigger` with, just do not pass `start` and `stop` options.

```ts
import { interval } from 'patronum';

somethingThatRequiresTrigger({
trigger: interval({ timeout: 1000 }),
});
```

For example, you can use `interval` to refresh data in the Query from the library [Farfetched](https://farfetched.pages.dev/tutorial/trigger_api.html#external-triggers) using `@@trigger` protocol.

```ts
import { keepFresh } from '@farfetched/core';
import { interval } from 'patronum';

keepFresh(someQuery, {
// 👇 Query will be refreshed each 1000 ms
triggers: [interval({ timeout: 1000 })],
});
```
6 changes: 2 additions & 4 deletions test-typings/interval.ts
Original file line number Diff line number Diff line change
Expand Up @@ -38,23 +38,21 @@ import { interval } from '../src/interval';
}),
);

// @ts-expect-error
interval({
timeout: 100,
start: createEvent<void>(),
stop: createEvent<void>(),
// @ts-expect-error
leading: 1,
// @ts-expect-error
trailing: '',
});

// @ts-expect-error
interval({
timeout: 100,
start: createEvent<void>(),
stop: createEvent<void>(),
// @ts-expect-error
leading: [],
// @ts-expect-error
trailing: null,
});
}
Expand Down

1 comment on commit eae305b

@github-actions
Copy link

Choose a reason for hiding this comment

The reason will be displayed to describe this comment to others. Learn more.

🚛 size-compare report

Comparing fcdc7376...eae305b1

File +/- Base Current +/- gzip Base gzip Current gzip
dist/and/index.cjs = 397 B 397 B = 251 B 251 B
dist/and/index.js = 413 B 413 B = 245 B 245 B
dist/babel-preset.cjs = 434 B 434 B = 249 B 249 B
dist/combine-events/index.cjs = 2.46 kB 2.46 kB = 841 B 841 B
dist/combine-events/index.js = 3.95 kB 3.95 kB = 1.35 kB 1.35 kB
dist/condition/index.cjs = 1.37 kB 1.37 kB = 505 B 505 B
dist/condition/index.js = 1.29 kB 1.29 kB = 461 B 461 B
dist/debounce/index.cjs = 3.99 kB 3.99 kB = 1.31 kB 1.31 kB
dist/debounce/index.js = 3.84 kB 3.84 kB = 1.29 kB 1.29 kB
dist/debug/index.cjs = 11.3 kB 11.3 kB = 3.16 kB 3.16 kB
dist/debug/index.js = 13.3 kB 13.3 kB = 3.79 kB 3.79 kB
dist/delay/index.cjs = 1.79 kB 1.79 kB = 727 B 727 B
dist/delay/index.js = 1.84 kB 1.84 kB = 719 B 719 B
dist/either/index.cjs = 600 B 600 B = 334 B 334 B
dist/either/index.js = 477 B 477 B = 270 B 270 B
dist/empty/index.cjs = 175 B 175 B = 151 B 151 B
dist/empty/index.js = 79 B 79 B = 89 B 89 B
dist/equals/index.cjs = 336 B 336 B = 249 B 249 B
dist/equals/index.js = 221 B 221 B = 179 B 179 B
dist/every/index.cjs = 1.22 kB 1.22 kB = 513 B 513 B
dist/every/index.js = 1.08 kB 1.08 kB = 442 B 442 B
dist/format/index.cjs = 642 B 642 B = 366 B 366 B
dist/format/index.js = 687 B 687 B = 368 B 368 B
dist/in-flight/index.cjs = 641 B 641 B = 357 B 357 B
dist/in-flight/index.js = 546 B 546 B = 305 B 305 B
dist/index.cjs = 1.62 kB 1.62 kB = 369 B 369 B
dist/index.js = 1.13 kB 1.13 kB = 285 B 285 B
dist/interval/index.cjs +9.56% 3.88 kB 4.25 kB +11.28% 1.14 kB 1.26 kB
dist/interval/index.js +9.00% 3.74 kB 4.08 kB +11.12% 1.12 kB 1.25 kB
dist/macro.cjs = 1.91 kB 1.91 kB = 808 B 808 B
dist/not/index.cjs = 161 B 161 B = 148 B 148 B
dist/not/index.js = 69 B 69 B = 81 B 81 B
dist/once/index.cjs = 597 B 597 B = 308 B 308 B
dist/once/index.js = 465 B 465 B = 242 B 242 B
dist/or/index.cjs = 393 B 393 B = 249 B 249 B
dist/or/index.js = 411 B 411 B = 245 B 245 B
dist/patronum.cjs +0.83% 19.2 kB 19.3 kB +1.16% 6.05 kB 6.12 kB
dist/patronum.js +0.70% 18.1 kB 18.2 kB +1.18% 6.1 kB 6.17 kB
dist/patronum.umd.js +0.87% 20.2 kB 20.4 kB +1.09% 6.16 kB 6.22 kB
dist/pending/index.cjs = 909 B 909 B = 495 B 495 B
dist/pending/index.js = 828 B 828 B = 444 B 444 B
dist/reset/index.cjs = 526 B 526 B = 312 B 312 B
dist/reset/index.js = 439 B 439 B = 256 B 256 B
dist/reshape/index.cjs = 419 B 419 B = 242 B 242 B
dist/reshape/index.js = 379 B 379 B = 201 B 201 B
dist/snapshot/index.cjs = 763 B 763 B = 350 B 350 B
dist/snapshot/index.js = 648 B 648 B = 292 B 292 B
dist/some/index.cjs = 1.16 kB 1.16 kB = 474 B 474 B
dist/some/index.js = 1.02 kB 1.02 kB = 407 B 407 B
dist/split-map/index.cjs = 628 B 628 B = 359 B 359 B
dist/split-map/index.js = 575 B 575 B = 318 B 318 B
dist/spread/index.cjs = 1.26 kB 1.26 kB = 534 B 534 B
dist/spread/index.js = 1.28 kB 1.28 kB = 516 B 516 B
dist/status/index.cjs = 426 B 426 B = 265 B 265 B
dist/status/index.js = 339 B 339 B = 208 B 208 B
dist/throttle/index.cjs = 2.1 kB 2.1 kB = 814 B 814 B
dist/throttle/index.js = 1.99 kB 1.99 kB = 775 B 775 B
dist/time/index.cjs = 719 B 719 B = 376 B 376 B
dist/time/index.js = 621 B 621 B = 323 B 323 B

Please sign in to comment.