@applicaster/zapp-react-native-utils 15.0.0-rc.105 → 15.0.0-rc.106

This diff represents the content of publicly available package versions that have been released to one of the supported registries. The information contained in this diff is provided for informational purposes only and reflects changes between package versions as they appear in their respective public registries.
package/README.md CHANGED
@@ -245,12 +245,6 @@ const connectionType = useConnectionInfo(true);
245
245
 
246
246
  `@applicaster/zapp-react-native/reactHooks`
247
247
 
248
- - `useFeedRefresh: ({ reloadData: function, component: { id: boolean | string, rules: {enable_data_refreshing: boolean, refreshing_interval: number} } }) => void` - Hook will call `reloadData` function, in the specified intervals if `enable_data_refreshing` is set to true;
249
-
250
- ```javascript
251
- useFeedRefresh({ reloadData, component });
252
- ```
253
-
254
248
  - `useFeedLoader: ({ feedUrl: string, pipesOptions?: { clearCache?: boolean, loadLocalFavourites?: boolean, silentRefresh?: boolean} }) => ({data: ?ApplicasterFeed, loading: boolean, url: string, error: Error,reloadData: (silentRefresh?: boolean) => void, loadNext: () => void})` - Hook will load data to the redux store and return a feed for the provided DSP URL. If the data for the provided url was already loaded, it will return that value
255
249
 
256
250
  ```javascript
package/package.json CHANGED
@@ -1,6 +1,6 @@
1
1
  {
2
2
  "name": "@applicaster/zapp-react-native-utils",
3
- "version": "15.0.0-rc.105",
3
+ "version": "15.0.0-rc.106",
4
4
  "description": "Applicaster Zapp React Native utilities package",
5
5
  "main": "index.js",
6
6
  "types": "index.d.ts",
@@ -27,7 +27,7 @@
27
27
  },
28
28
  "homepage": "https://github.com/applicaster/quickbrick#readme",
29
29
  "dependencies": {
30
- "@applicaster/applicaster-types": "15.0.0-rc.105",
30
+ "@applicaster/applicaster-types": "15.0.0-rc.106",
31
31
  "buffer": "^5.2.1",
32
32
  "camelize": "^1.0.0",
33
33
  "dayjs": "^1.11.10",
@@ -1,5 +1,3 @@
1
- export { useFeedRefresh, feedRefreshLogger } from "./useFeedRefresh";
2
-
3
1
  export { useFeedLoader } from "./useFeedLoader";
4
2
 
5
3
  export { getSearchContext, getInflatedDataSourceUrl } from "./useInflatedUrl";
@@ -0,0 +1,161 @@
1
+ import { refreshCoordinator } from "..";
2
+ import { getDataRefreshConfig } from "../utils";
3
+ import { Subscription } from "rxjs";
4
+
5
+ jest.mock("../utils", () => ({
6
+ getDataRefreshConfig: jest.fn(),
7
+ }));
8
+
9
+ describe("RefreshCoordinator", () => {
10
+ const component = { id: "c1" } as ZappUIComponent;
11
+ const screenId = "screen-1";
12
+
13
+ beforeEach(() => {
14
+ jest.clearAllMocks();
15
+ refreshCoordinator.clear();
16
+ jest.useFakeTimers();
17
+ });
18
+
19
+ afterEach(() => {
20
+ jest.runOnlyPendingTimers();
21
+ jest.useRealTimers();
22
+ });
23
+
24
+ // ----------------------------------------
25
+ // Singleton
26
+ // ----------------------------------------
27
+ it("returns the same instance via getInstance", () => {
28
+ const instance1 = refreshCoordinator;
29
+ const instance2 = (refreshCoordinator as any).constructor.getInstance();
30
+ expect(instance1).toBe(instance2);
31
+ });
32
+
33
+ // ----------------------------------------
34
+ // register & unregister
35
+ // ----------------------------------------
36
+ it("register starts a timer and returns an unregister function", () => {
37
+ (getDataRefreshConfig as jest.Mock).mockReturnValue({
38
+ refreshIntervalMs: 1000,
39
+ isRefreshingEnabled: true,
40
+ });
41
+
42
+ const unregister = refreshCoordinator.register(component, screenId);
43
+
44
+ expect(typeof unregister).toBe("function");
45
+ expect((refreshCoordinator as any).timers.size).toBe(1);
46
+
47
+ // Calling unregister should remove timer
48
+ unregister();
49
+ expect((refreshCoordinator as any).timers.size).toBe(0);
50
+ });
51
+
52
+ it("does not register if refreshing is disabled", () => {
53
+ (getDataRefreshConfig as jest.Mock).mockReturnValue({
54
+ refreshIntervalMs: 1000,
55
+ isRefreshingEnabled: false,
56
+ });
57
+
58
+ const unregister = refreshCoordinator.register(component, screenId);
59
+
60
+ expect(unregister).toBeInstanceOf(Function);
61
+ expect((refreshCoordinator as any).timers.size).toBe(0);
62
+ });
63
+
64
+ it("replaces existing timer if component is re-registered", () => {
65
+ (getDataRefreshConfig as jest.Mock).mockReturnValue({
66
+ refreshIntervalMs: 1000,
67
+ isRefreshingEnabled: true,
68
+ });
69
+
70
+ const unregister1 = refreshCoordinator.register(component, screenId);
71
+ const unregister2 = refreshCoordinator.register(component, screenId);
72
+
73
+ expect((refreshCoordinator as any).timers.size).toBe(1);
74
+ expect(unregister1).toBeInstanceOf(Function);
75
+ expect(unregister2).toBeInstanceOf(Function);
76
+ });
77
+
78
+ // ----------------------------------------
79
+ // subscribeTo
80
+ // ----------------------------------------
81
+ it("subscribeTo only triggers listener for matching component & screen", () => {
82
+ (getDataRefreshConfig as jest.Mock).mockReturnValue({
83
+ refreshIntervalMs: 1000,
84
+ isRefreshingEnabled: true,
85
+ });
86
+
87
+ const listener = jest.fn();
88
+ refreshCoordinator.register(component, screenId);
89
+ const sub = refreshCoordinator.subscribeTo(component, screenId, listener);
90
+
91
+ // Emit a refresh manually via private Subject
92
+ (refreshCoordinator as any).refresh$.next({ component, screenId });
93
+ expect(listener).toHaveBeenCalledTimes(1);
94
+
95
+ // Non-matching component should not call listener
96
+ (refreshCoordinator as any).refresh$.next({
97
+ component: { id: "other" } as ZappUIComponent,
98
+ screenId,
99
+ });
100
+
101
+ expect(listener).toHaveBeenCalledTimes(1);
102
+
103
+ sub.unsubscribe();
104
+ });
105
+
106
+ // ----------------------------------------
107
+ // subscribeToAny
108
+ // ----------------------------------------
109
+ it("subscribeToAny triggers listener with throttling and replaces previous listener", () => {
110
+ const listener1 = jest.fn();
111
+ const listener2 = jest.fn();
112
+
113
+ // First subscription
114
+ const sub1: Subscription = refreshCoordinator.subscribeToAny(listener1);
115
+ expect(sub1).toBeInstanceOf(Subscription);
116
+
117
+ // Second subscription replaces first
118
+ const sub2: Subscription = refreshCoordinator.subscribeToAny(listener2);
119
+ expect(sub2).toBeInstanceOf(Subscription);
120
+
121
+ // Emit multiple events quickly
122
+ (refreshCoordinator as any).refresh$.next({ component, screenId });
123
+ (refreshCoordinator as any).refresh$.next({ component, screenId });
124
+
125
+ // Because of throttleTime(500ms), fast events are ignored
126
+ jest.advanceTimersByTime(500);
127
+ expect(listener1).not.toHaveBeenCalled();
128
+ expect(listener2).toHaveBeenCalledTimes(1);
129
+
130
+ sub2.unsubscribe();
131
+ });
132
+
133
+ // ----------------------------------------
134
+ // clear
135
+ // ----------------------------------------
136
+ it("clear stops all timers, anyListener, and recreates subject", () => {
137
+ (getDataRefreshConfig as jest.Mock).mockReturnValue({
138
+ refreshIntervalMs: 1000,
139
+ isRefreshingEnabled: true,
140
+ });
141
+
142
+ const listener = jest.fn();
143
+ refreshCoordinator.register(component, screenId);
144
+ refreshCoordinator.subscribeToAny(listener);
145
+
146
+ expect((refreshCoordinator as any).timers.size).toBe(1);
147
+ expect((refreshCoordinator as any).anyListenerSubscription).toBeDefined();
148
+
149
+ refreshCoordinator.clear();
150
+
151
+ expect((refreshCoordinator as any).timers.size).toBe(0);
152
+ expect((refreshCoordinator as any).anyListenerSubscription).toBeUndefined();
153
+
154
+ // New events on new Subject still work
155
+ const newListener = jest.fn();
156
+ refreshCoordinator.subscribeToAny(newListener);
157
+ (refreshCoordinator as any).refresh$.next({ component, screenId });
158
+ jest.advanceTimersByTime(500);
159
+ expect(newListener).toHaveBeenCalled();
160
+ });
161
+ });
@@ -0,0 +1,216 @@
1
+ import { noop } from "@applicaster/zapp-react-native-utils/functionUtils";
2
+ import { interval, Subject, Subscription } from "rxjs";
3
+ import { map, filter, throttleTime } from "rxjs/operators";
4
+
5
+ import { getDataRefreshConfig } from "./utils";
6
+
7
+ export { getDataRefreshConfig, type RefreshConfig } from "./utils";
8
+
9
+ const THROTTLE_TIMEOUT = 500; // ms
10
+
11
+ type ComponentToRefresh = {
12
+ component: ZappUIComponent;
13
+ screenId: string;
14
+ };
15
+
16
+ /**
17
+ * Public contract for refresh coordination.
18
+ *
19
+ * register() → starts refresh timer for a component with screen context
20
+ * subscribeTo() → listen to refresh events for a specific component and screen
21
+ * subscribeToAny() → listen to all refresh events with throttling
22
+ */
23
+ interface IRefreshCoordinator {
24
+ register(component: ZappUIComponent, screenId: string): () => void;
25
+ subscribeTo(
26
+ component: ZappUIComponent,
27
+ screenId: string,
28
+ listener: () => void
29
+ ): Subscription;
30
+ subscribeToAny(
31
+ listener: (componentToRefresh: ComponentToRefresh) => void
32
+ ): Subscription;
33
+ }
34
+
35
+ /**
36
+ * Singleton implementation of RefreshCoordinator.
37
+ *
38
+ * Design goals:
39
+ * - One shared coordinator across the app
40
+ * - Multiple components can register refresh timers with screen context
41
+ * - Multiple listeners can subscribe to refresh events
42
+ * - Safe cleanup of timers and subscriptions
43
+ */
44
+ class RefreshCoordinator implements IRefreshCoordinator {
45
+ private static instance: RefreshCoordinator;
46
+
47
+ /**
48
+ * Active timers per component/screen pair ((componentId, screenId) → RxJS Subscription).
49
+ * Each entry is keyed by a composite of componentId and screenId (via getKey),
50
+ * and gets its own interval stream for periodic refresh.
51
+ */
52
+ private timers: Map<string, Subscription> = new Map();
53
+
54
+ /**
55
+ * Central event stream emitting components that need refresh.
56
+ * All component interval streams push into this Subject.
57
+ * Multiple listeners can subscribe to receive refresh events for specific components.
58
+ * This is the core stream that coordinates all refresh operations.
59
+ */
60
+ private refresh$ = new Subject<ComponentToRefresh>();
61
+
62
+ // Store the current single subscriber for subscribeToAny
63
+ private anyListenerSubscription?: Subscription;
64
+
65
+ private constructor() {}
66
+
67
+ public static getInstance(): RefreshCoordinator {
68
+ if (!RefreshCoordinator.instance) {
69
+ RefreshCoordinator.instance = new RefreshCoordinator();
70
+ }
71
+
72
+ return RefreshCoordinator.instance;
73
+ }
74
+
75
+ /**
76
+ * Registers a component for periodic refresh.
77
+ *
78
+ * Behavior:
79
+ * - If the component was previously registered, its timer is replaced.
80
+ * - If refreshing is disabled, nothing is scheduled.
81
+ * - Returns an unregister function for manual cleanup.
82
+ * - Each component gets its own interval stream for periodic refresh.
83
+ */
84
+ public register(component: ZappUIComponent, screenId: string): () => void {
85
+ const componentId: ZappUIComponent["id"] = component.id;
86
+
87
+ // Ensure no duplicate timer exists for this component
88
+ this.unregister(componentId, screenId);
89
+
90
+ const refreshConfig = getDataRefreshConfig(component);
91
+
92
+ // If refreshing disabled → do nothing
93
+ if (!refreshConfig.isRefreshingEnabled) {
94
+ return noop;
95
+ }
96
+
97
+ /**
98
+ * Create interval stream:
99
+ * - Emits every refreshIntervalMs
100
+ * - Maps emission to the ComponentToRefresh object
101
+ * - Pushes component into central refresh$ stream
102
+ */
103
+ const subscription: Subscription = interval(refreshConfig.refreshIntervalMs)
104
+ .pipe(
105
+ map(() => ({
106
+ component,
107
+ screenId,
108
+ }))
109
+ )
110
+ .subscribe(this.refresh$);
111
+
112
+ this.timers.set(this.getKey(componentId, screenId), subscription);
113
+
114
+ return (): void => this.unregister(componentId, screenId);
115
+ }
116
+
117
+ /**
118
+ * Stops and removes timer for a specific component.
119
+ * Cleans up the RxJS subscription for that component's interval stream.
120
+ */
121
+ private unregister(
122
+ componentId: ZappUIComponent["id"],
123
+ screenId: string
124
+ ): void {
125
+ const key = this.getKey(componentId, screenId);
126
+ const subscription: Subscription | undefined = this.timers.get(key);
127
+
128
+ if (subscription) {
129
+ subscription.unsubscribe();
130
+ this.timers.delete(key);
131
+ }
132
+ }
133
+
134
+ private getKey(componentId: ZappUIComponent["id"], screenId: string): string {
135
+ return `componentId:${componentId}___screenId:${screenId}`;
136
+ }
137
+
138
+ /**
139
+ * Subscribes to refresh events for a specific component.
140
+ *
141
+ * Each subscription is independent - multiple listeners can subscribe
142
+ * to the same component's refresh events.
143
+ */
144
+ public subscribeTo(
145
+ component: ZappUIComponent,
146
+ screenId: string,
147
+ listener: () => void
148
+ ): Subscription {
149
+ return this.refresh$
150
+ .pipe(
151
+ filter(
152
+ (componentToRefresh: ComponentToRefresh) =>
153
+ componentToRefresh.component.id === component.id &&
154
+ componentToRefresh.screenId === screenId
155
+ )
156
+ )
157
+ .subscribe(listener);
158
+ }
159
+
160
+ /**
161
+ * Subscribes a single global listener to all refresh events.
162
+ *
163
+ * Behavior:
164
+ * - Only one active listener exists at a time.
165
+ * - If a previous listener was already subscribed, it is automatically unsubscribed.
166
+ * - Throttles refresh events to emit at most once per THROTTLE_TIMEOUT interval.
167
+ * - Useful for scenarios where you want a single handler to react
168
+ * to any component refresh without having multiple active callbacks.
169
+ *
170
+ * @param listener - Callback to invoke whenever any component triggers a refresh
171
+ * @returns The RxJS Subscription object for this listener
172
+ */
173
+ public subscribeToAny(
174
+ listener: (componentToRefresh: ComponentToRefresh) => void
175
+ ): Subscription {
176
+ // Unsubscribe previous listener if present to enforce "single active listener" policy
177
+ if (this.anyListenerSubscription) {
178
+ this.anyListenerSubscription.unsubscribe();
179
+ }
180
+
181
+ // Create a new throttled subscription
182
+ this.anyListenerSubscription = this.refresh$
183
+ .pipe(throttleTime(THROTTLE_TIMEOUT))
184
+ .subscribe(listener);
185
+
186
+ return this.anyListenerSubscription;
187
+ }
188
+
189
+ /**
190
+ * Fully resets coordinator state.
191
+ *
192
+ * Clears:
193
+ * - All component timers
194
+ * - Any active "subscribeToAny" listener
195
+ * - Recreates internal Subject to allow fresh subscriptions
196
+ */
197
+ public clear(): void {
198
+ // Stop and remove all active timers
199
+ this.timers.forEach((sub: Subscription) => sub.unsubscribe());
200
+ this.timers.clear();
201
+
202
+ // Stop any active "subscribeToAny" subscription
203
+ if (this.anyListenerSubscription) {
204
+ this.anyListenerSubscription.unsubscribe();
205
+ this.anyListenerSubscription = undefined;
206
+ }
207
+
208
+ // Complete old Subject to release subscribers
209
+ this.refresh$.complete();
210
+
211
+ // Recreate Subject so coordinator can be reused
212
+ this.refresh$ = new Subject<ComponentToRefresh>();
213
+ }
214
+ }
215
+
216
+ export const refreshCoordinator = RefreshCoordinator.getInstance();
@@ -0,0 +1,104 @@
1
+ import {
2
+ getDataRefreshConfig,
3
+ MINIMUM_REFRESHING_INTERVAL_IN_SECONDS,
4
+ DEFAULT_REFRESHING_INTERVAL_IN_SECONDS,
5
+ } from "..";
6
+
7
+ describe("getDataRefreshConfig", () => {
8
+ it("returns correct values when rules are defined", () => {
9
+ const component = {
10
+ rules: {
11
+ enable_data_refreshing: true,
12
+ refreshing_interval: 30,
13
+ },
14
+ } as any;
15
+
16
+ const result = getDataRefreshConfig(component);
17
+
18
+ expect(result).toEqual({
19
+ isRefreshingEnabled: true,
20
+ refreshIntervalMs: 30 * 1000,
21
+ });
22
+ });
23
+
24
+ it("returns false for isRefreshingEnabled if rules flag is false", () => {
25
+ const component = {
26
+ rules: {
27
+ enable_data_refreshing: false,
28
+ refreshing_interval: 45,
29
+ },
30
+ } as any;
31
+
32
+ const result = getDataRefreshConfig(component);
33
+
34
+ expect(result).toEqual({
35
+ isRefreshingEnabled: false,
36
+ refreshIntervalMs: 45 * 1000,
37
+ });
38
+ });
39
+
40
+ it("applies minimum interval if given interval is too low", () => {
41
+ const component = {
42
+ rules: {
43
+ enable_data_refreshing: true,
44
+ refreshing_interval: 2,
45
+ },
46
+ } as any;
47
+
48
+ const result = getDataRefreshConfig(component);
49
+
50
+ expect(result.refreshIntervalMs).toBe(
51
+ MINIMUM_REFRESHING_INTERVAL_IN_SECONDS * 1000
52
+ );
53
+ });
54
+
55
+ it("uses default interval when rules.refreshing_interval is undefined", () => {
56
+ const component = {
57
+ rules: {
58
+ enable_data_refreshing: true,
59
+ },
60
+ } as any;
61
+
62
+ const result = getDataRefreshConfig(component);
63
+
64
+ expect(result.refreshIntervalMs).toBe(
65
+ DEFAULT_REFRESHING_INTERVAL_IN_SECONDS * 1000
66
+ );
67
+ });
68
+
69
+ it("handles missing rules gracefully", () => {
70
+ const component = {} as any;
71
+
72
+ const result = getDataRefreshConfig(component);
73
+
74
+ expect(result).toEqual({
75
+ isRefreshingEnabled: false,
76
+ refreshIntervalMs: DEFAULT_REFRESHING_INTERVAL_IN_SECONDS * 1000,
77
+ });
78
+ });
79
+
80
+ it("handles null component gracefully", () => {
81
+ const result = getDataRefreshConfig(null as any);
82
+
83
+ expect(result).toEqual({
84
+ isRefreshingEnabled: false,
85
+ refreshIntervalMs: DEFAULT_REFRESHING_INTERVAL_IN_SECONDS * 1000,
86
+ });
87
+ });
88
+
89
+ it("handles string values for rules correctly", () => {
90
+ const component = {
91
+ rules: {
92
+ enable_data_refreshing: "true",
93
+ refreshing_interval: "25",
94
+ },
95
+ } as any;
96
+
97
+ const result = getDataRefreshConfig(component);
98
+
99
+ expect(result).toEqual({
100
+ isRefreshingEnabled: true,
101
+ refreshIntervalMs: 25 * 1000,
102
+ });
103
+ });
104
+ });
@@ -0,0 +1,29 @@
1
+ import { isTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
2
+ import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
3
+
4
+ export const MINIMUM_REFRESHING_INTERVAL_IN_SECONDS = 5; // 5 seconds
5
+
6
+ export const DEFAULT_REFRESHING_INTERVAL_IN_SECONDS = 60; // 1 minute
7
+
8
+ export type RefreshConfig = {
9
+ isRefreshingEnabled: boolean;
10
+ refreshIntervalMs: number;
11
+ };
12
+
13
+ export const getDataRefreshConfig = (
14
+ component: ZappUIComponent
15
+ ): RefreshConfig => {
16
+ const isRefreshingEnabled = isTrue(component?.rules?.enable_data_refreshing);
17
+
18
+ const refreshing_interval = toNumberWithDefault(
19
+ DEFAULT_REFRESHING_INTERVAL_IN_SECONDS,
20
+ component?.rules?.refreshing_interval
21
+ );
22
+
23
+ return {
24
+ isRefreshingEnabled,
25
+ refreshIntervalMs:
26
+ Math.max(refreshing_interval, MINIMUM_REFRESHING_INTERVAL_IN_SECONDS) *
27
+ 1000,
28
+ };
29
+ };
@@ -1,75 +0,0 @@
1
- import { renderHook } from "@testing-library/react-native";
2
-
3
- jest.useFakeTimers();
4
-
5
- jest.mock("@applicaster/zapp-react-native-utils/reactHooks/navigation", () => ({
6
- useIsScreenActive: () => true,
7
- }));
8
-
9
- const { useFeedRefresh, feedRefreshLogger } = require("..");
10
-
11
- describe("useFeedRefresh", () => {
12
- const reloadData = jest.fn();
13
-
14
- const component = {
15
- id: "foo",
16
- rules: { enable_data_refreshing: true, refreshing_interval: 61 },
17
- };
18
-
19
- it("Calls reloadData after passed time lapses", () => {
20
- renderHook(() => useFeedRefresh({ reloadData, component }));
21
-
22
- expect(reloadData).not.toBeCalled();
23
- jest.runOnlyPendingTimers();
24
- expect(reloadData).toBeCalled();
25
- });
26
-
27
- it("Logs warning message if refresh time set below minimum value", () => {
28
- const loggerSpy = jest.spyOn(feedRefreshLogger, "warning");
29
-
30
- renderHook(() =>
31
- useFeedRefresh({
32
- reloadData,
33
- component: {
34
- ...component,
35
- rules: { enable_data_refreshing: true, refreshing_interval: 2 },
36
- },
37
- })
38
- );
39
-
40
- expect(loggerSpy).toBeCalled();
41
- });
42
-
43
- it("Calls reloadData on a Component that had a timer setup and was re-mounted", () => {
44
- reloadData.mockReset();
45
-
46
- // it is not called before rendering hook"
47
- expect(reloadData).not.toBeCalled();
48
-
49
- // it is not called when rendering new component (Not previously rendered)
50
- renderHook(() =>
51
- useFeedRefresh({ reloadData, component: { ...component, id: "bar" } })
52
- );
53
-
54
- expect(reloadData).not.toBeCalled();
55
-
56
- // it is called when rendering previously rendered component",
57
- renderHook(() => useFeedRefresh({ reloadData, component }));
58
- expect(reloadData).toBeCalled();
59
- });
60
-
61
- it("Doesn't refresh data when enable_data_refreshing if false", () => {
62
- reloadData.mockReset();
63
-
64
- renderHook(() =>
65
- useFeedRefresh({
66
- reloadData,
67
- component: { ...component, rules: { enable_data_refreshing: false } },
68
- })
69
- );
70
-
71
- jest.runOnlyPendingTimers();
72
-
73
- expect(reloadData).not.toBeCalled();
74
- });
75
- });
@@ -1,65 +0,0 @@
1
- import * as React from "react";
2
- import { path, max } from "ramda";
3
- import { isTrue } from "@applicaster/zapp-react-native-utils/booleanUtils";
4
- import { toNumberWithDefault } from "@applicaster/zapp-react-native-utils/numberUtils";
5
- import { useIsScreenActive } from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
6
-
7
- import { reactHooksLogger } from "../logger";
8
- import { ReloadDataFunction } from "./useFeedLoader";
9
-
10
- export const feedRefreshLogger = reactHooksLogger.addSubsystem("feed-refresh");
11
-
12
- type Props = {
13
- reloadData?: ReloadDataFunction;
14
- component: ZappUIComponent;
15
- };
16
-
17
- const previousTimersMap = {};
18
- const minimumRefreshingInterval = 5;
19
- const defaultRefreshInterval = 60;
20
-
21
- export const useFeedRefresh = ({ reloadData, component }: Props): void => {
22
- const isDataRefreshingEnabled = isTrue(
23
- path(["rules", "enable_data_refreshing"], component)
24
- );
25
-
26
- const refreshing_interval = toNumberWithDefault(
27
- defaultRefreshInterval,
28
- component?.rules?.refreshing_interval
29
- );
30
-
31
- const refreshingIntervalInMilliseconds =
32
- max(refreshing_interval, minimumRefreshingInterval) * 1000;
33
-
34
- const isScreenActive = useIsScreenActive();
35
-
36
- React.useEffect(() => {
37
- if (isDataRefreshingEnabled && refreshing_interval && isScreenActive) {
38
- if (refreshing_interval < minimumRefreshingInterval) {
39
- feedRefreshLogger.warning({
40
- message: `You set your feed refresh interval to ${refreshing_interval} when minimum value is ${minimumRefreshingInterval}seconds.Your feed will refresh after ${minimumRefreshingInterval}seconds.`,
41
- });
42
- }
43
-
44
- if (!reloadData || typeof reloadData !== "function") {
45
- feedRefreshLogger.warning({
46
- message:
47
- "reloadData function is undefined, feed refresh feature won't work properly",
48
- });
49
- }
50
-
51
- if (previousTimersMap[component?.id]) {
52
- reloadData?.();
53
- }
54
-
55
- const refreshInterval = setInterval(() => {
56
- reloadData?.();
57
- }, refreshingIntervalInMilliseconds);
58
-
59
- return () => {
60
- previousTimersMap[component?.id] = true;
61
- clearInterval(refreshInterval);
62
- };
63
- }
64
- }, [reloadData, isScreenActive]);
65
- };