@applicaster/zapp-react-native-utils 13.0.0-alpha.1383266100 → 13.0.0-alpha.1400045041

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.
Files changed (41) hide show
  1. package/README.md +0 -10
  2. package/analyticsUtils/currentScreenProperities.ts +6 -4
  3. package/appUtils/contextKeysManager/index.ts +2 -2
  4. package/appUtils/contextKeysManager/utils/index.ts +2 -1
  5. package/appUtils/focusManager/__tests__/__snapshots__/focusManager.test.js.snap +2 -0
  6. package/appUtils/focusManager/__tests__/focusManager.test.js +82 -0
  7. package/appUtils/focusManager/__tests__/index.test.ts +1 -0
  8. package/appUtils/focusManager/index.ts +25 -3
  9. package/appUtils/focusManager/utils/index.ts +14 -0
  10. package/appUtils/focusManagerAux/index.ts +2 -2
  11. package/appUtils/platform/platformUtils.ts +6 -0
  12. package/equalUtils/__tests__/isEqual/combination.test.ts +263 -0
  13. package/equalUtils/index.ts +49 -30
  14. package/focusManager/FocusManager.ts +11 -1
  15. package/focusManager/index.ts +41 -4
  16. package/manifestUtils/sharedConfiguration/screenPicker/__tests__/utils.test.js +304 -0
  17. package/manifestUtils/sharedConfiguration/screenPicker/stylesFields.js +875 -0
  18. package/manifestUtils/sharedConfiguration/screenPicker/utils.js +130 -0
  19. package/package.json +4 -6
  20. package/reactHooks/navigation/__mocks__/useNavigation.ts +3 -0
  21. package/reactHooks/navigation/index.ts +7 -19
  22. package/reactHooks/navigation/useGetTabBarHeight.ts +1 -3
  23. package/reactHooks/navigation/useProfilerLogging.ts +15 -0
  24. package/reactUtils/__tests__/__snapshots__/reactUtilsHelpers.test.js.snap +16 -4
  25. package/reactUtils/__tests__/reactUtils.test.js +1 -19
  26. package/reactUtils/__tests__/reactUtilsHelpers.test.js +72 -71
  27. package/reactUtils/createContext/__tests__/__snapshots__/helpers.test.js.snap +6 -72
  28. package/reactUtils/createContext/__tests__/__snapshots__/provider.test.js.snap +3 -28
  29. package/reactUtils/createContext/__tests__/__snapshots__/withConsumer.test.js.snap +3 -4
  30. package/reactUtils/createContext/__tests__/__snapshots__/withProvider.test.js.snap +7 -3
  31. package/reactUtils/createContext/__tests__/helpers.test.js +107 -121
  32. package/reactUtils/createContext/__tests__/{index.test.js → index.test.ts} +2 -2
  33. package/reactUtils/createContext/__tests__/provider.test.js +4 -6
  34. package/reactUtils/createContext/__tests__/withConsumer.test.js +12 -12
  35. package/reactUtils/createContext/__tests__/withProvider.test.js +4 -4
  36. package/reactUtils/createContext/helpers.ts +6 -1
  37. package/reactUtils/createContext/index.ts +11 -25
  38. package/reactUtils/index.ts +0 -11
  39. package/testUtils/index.tsx +0 -10
  40. package/reactUtils/createContext/__tests__/index-no-react.test.js +0 -27
  41. /package/reactUtils/createContext/__tests__/__snapshots__/{index.test.js.snap → index.test.ts.snap} +0 -0
package/README.md CHANGED
@@ -376,16 +376,6 @@ useEffect(() => {
376
376
  }, []);
377
377
  ```
378
378
 
379
- ### testUtils
380
-
381
- `@applicaster/zapp-react-native-utils/testUtils`
382
-
383
- - `testIDEquals: (testId: any) => boolean | { width: number, height: number }` - Function to be used in tandem with enzyme findWhere
384
-
385
- ```javascript
386
- wrapper.findWhere(testIDEquals("text"));
387
- ```
388
-
389
379
  ### appUtils - contextKeysManager
390
380
 
391
381
  `@applicaster/zapp-react-native-utils/appUtils/contextKeyManager`
@@ -72,12 +72,14 @@ const mappedScreenPropertiesScreen = (
72
72
  screenData
73
73
  ): AnalyticsScreenProperties => {
74
74
  return {
75
- screen_name: screenData?.targetScreen?.name,
75
+ screen_name: screenData?.targetScreen?.name || screenData?.name,
76
76
  screen_entry_title: screenData?.title,
77
77
  screen_entry_id: R.isNil(screenData?.type) ? null : screenData?.id,
78
- screen_layout_id: screenData?.targetScreen?.id,
79
- screen_type: screenData?.targetScreen?.type,
80
- home_screen: screenData?.targetScreen?.home,
78
+ screen_layout_id: screenData?.targetScreen?.id || screenData?.id,
79
+ screen_type: screenData?.targetScreen?.type || screenData?.type,
80
+ home_screen: !R.isNil(screenData?.targetScreen?.home)
81
+ ? screenData?.targetScreen?.home
82
+ : screenData?.home,
81
83
  };
82
84
  };
83
85
 
@@ -16,12 +16,12 @@ import {
16
16
 
17
17
  export { StorageLevel };
18
18
 
19
- type KeyNameObj = {
19
+ export type KeyNameObj = {
20
20
  namespace: string;
21
21
  key: string;
22
22
  };
23
23
 
24
- type KeyName = string | KeyNameObj;
24
+ export type KeyName = string | KeyNameObj;
25
25
 
26
26
  type ParsedKeyAndNamespace = {
27
27
  namespace?: string;
@@ -1,10 +1,11 @@
1
1
  import * as R from "ramda";
2
2
  import { DEFAULT_NAMESPACE } from "../consts";
3
+ import { KeyName, KeyNameObj } from "../index";
3
4
 
4
5
  const lastDotRegex = /\.([^.]+)$/;
5
6
  const splitByLastDot = R.compose(R.init, R.split(lastDotRegex));
6
7
 
7
- export function getNamespaceAndKey(namespacedKey) {
8
+ export function getNamespaceAndKey(namespacedKey: KeyName): KeyNameObj {
8
9
  if (typeof namespacedKey !== "string") return namespacedKey;
9
10
 
10
11
  const namespacedKeyProps = namespacedKey.includes(".")
@@ -28,10 +28,12 @@ exports[`focusManager should be defined 1`] = `
28
28
  "moveFocus": [Function],
29
29
  "on": [Function],
30
30
  "press": [Function],
31
+ "pressIn": [Function],
31
32
  "pressOut": [Function],
32
33
  "recoverFocus": [Function],
33
34
  "register": [Function],
34
35
  "removeHandler": [Function],
36
+ "resetFocus": [Function],
35
37
  "setFocus": [Function],
36
38
  "setInitialFocus": [Function],
37
39
  "unregister": [Function],
@@ -1,5 +1,6 @@
1
1
  import { focusManager } from "../index.ts";
2
2
  import { focusManager as focusManagerIOS } from "../index.ios.ts";
3
+ import { findFirstFocusableItem } from "../utils";
3
4
 
4
5
  describe("focusManagerIOS", () => {
5
6
  it("should be defined", () => {
@@ -8,6 +9,67 @@ describe("focusManagerIOS", () => {
8
9
  });
9
10
  });
10
11
 
12
+ const focusableTree = {
13
+ root: {
14
+ children: [
15
+ {
16
+ children: [
17
+ {
18
+ children: [
19
+ {
20
+ children: [],
21
+ component: {
22
+ isGroup: true,
23
+ },
24
+ id: "route/1/1",
25
+ },
26
+ {
27
+ children: [],
28
+ component: {
29
+ isGroup: true,
30
+ },
31
+ id: "route/1/2",
32
+ },
33
+ ],
34
+ component: {
35
+ isGroup: true,
36
+ },
37
+ id: "route/1",
38
+ },
39
+ {
40
+ children: [
41
+ {
42
+ children: [],
43
+ component: {
44
+ isGroup: true,
45
+ },
46
+ id: "route/2/1",
47
+ },
48
+ {
49
+ children: [],
50
+ component: {
51
+ isGroup: false,
52
+ },
53
+ id: "route/2/2",
54
+ },
55
+ ],
56
+ component: {
57
+ isGroup: true,
58
+ },
59
+ id: "route/2",
60
+ },
61
+ ],
62
+ component: {
63
+ isGroup: true,
64
+ },
65
+ id: "/river/1234",
66
+ },
67
+ ],
68
+ component: null,
69
+ id: "root",
70
+ },
71
+ };
72
+
11
73
  describe("focusManager", () => {
12
74
  it("should be defined", () => {
13
75
  expect(focusManager).toBeDefined();
@@ -45,4 +107,24 @@ describe("focusManager", () => {
45
107
  it("placeholder test", () => {
46
108
  expect(true).toBe(true);
47
109
  });
110
+
111
+ it("findFirstFocusableItem returns focusableItem", () => {
112
+ const expectedItemId =
113
+ focusableTree.root.children[0].children[1].children[1].id;
114
+
115
+ const focusableItem = findFirstFocusableItem(focusableTree.root.children);
116
+
117
+ expect(
118
+ focusableItem !== undefined && expectedItemId === focusableItem.id
119
+ ).toBe(true);
120
+ });
121
+
122
+ it("findFirstFocusableItem does not return focusableItem", () => {
123
+ focusableTree.root.children[0].children[1].children[1].component.isGroup =
124
+ true;
125
+
126
+ const focusableItem = findFirstFocusableItem(focusableTree.root.children);
127
+
128
+ expect(focusableItem === undefined).toBe(true);
129
+ });
48
130
  });
@@ -30,6 +30,7 @@ describe("focusManager", () => {
30
30
  "isGroupItemFocused",
31
31
  "recoverFocus",
32
32
  "isCurrentFocusOnTheTopScreen",
33
+ "resetFocus",
33
34
  ];
34
35
 
35
36
  expectedMethods.forEach((method) => {
@@ -13,6 +13,7 @@ import {
13
13
  import { subscriber } from "../../functionUtils";
14
14
  import { coreLogger } from "../../logger";
15
15
  import { ACTION } from "./utils/enums";
16
+ import { findFirstFocusableItem } from "./utils";
16
17
 
17
18
  const logger = coreLogger.addSubsystem("focusManager");
18
19
 
@@ -345,6 +346,14 @@ export const focusManager = (function () {
345
346
  });
346
347
  }
347
348
 
349
+ function pressIn() {
350
+ const currentFocusable = getCurrentFocus();
351
+
352
+ if (currentFocusable) {
353
+ currentFocusable.pressIn(null);
354
+ }
355
+ }
356
+
348
357
  /**
349
358
  * calls the `press` method on the current focus component
350
359
  * @public
@@ -484,20 +493,29 @@ export const focusManager = (function () {
484
493
  return haveSameParentBeforeRoot(currentFocusNode, R.last(routes));
485
494
  }
486
495
 
487
- function recoverFocus() {
496
+ function resetFocus() {
488
497
  if (!isCurrentFocusOnTheTopScreen()) {
489
- // We've failed to set focused node on the new screen => run focus recovery
498
+ // We've failed to set focused node on the new screen => run focus reset
490
499
  setInitialFocus();
491
500
  }
492
501
  }
493
502
 
503
+ /**
504
+ * sets the focus on first focusable item
505
+ * @public
506
+ */
507
+ function recoverFocus() {
508
+ const focusableItem = findFirstFocusableItem(focusableTree.root.children);
509
+ focusableItem && setFocus(focusableItem.id);
510
+ }
511
+
494
512
  /**
495
513
  * moves the focus in a provided direction
496
514
  * @public
497
515
  * @param {Object} direction of the scroll
498
516
  */
499
517
  function moveFocus(direction) {
500
- if (!isCurrentFocusOnTheTopScreen()) {
518
+ if (!isCurrentFocusOnTheTopScreen() && currentFocusNode) {
501
519
  // We are opening new screen. We need to ignore movement of focus.
502
520
  return false;
503
521
  }
@@ -520,6 +538,8 @@ export const focusManager = (function () {
520
538
  } else {
521
539
  currentFocusNode.component.failedLostFocus(currentFocusNode, direction);
522
540
  }
541
+ } else {
542
+ recoverFocus();
523
543
  }
524
544
  }
525
545
 
@@ -552,6 +572,7 @@ export const focusManager = (function () {
552
572
  invokeHandler,
553
573
  removeHandler,
554
574
  longPress,
575
+ pressIn,
555
576
  pressOut,
556
577
  enableFocus,
557
578
  setInitialFocus,
@@ -563,5 +584,6 @@ export const focusManager = (function () {
563
584
  isGroupItemFocused,
564
585
  recoverFocus,
565
586
  isCurrentFocusOnTheTopScreen,
587
+ resetFocus,
566
588
  };
567
589
  })();
@@ -98,3 +98,17 @@ export const waitForContent = (focusableTree) => {
98
98
  conditionFn: contentHasAnyChildren,
99
99
  });
100
100
  };
101
+
102
+ export const findFirstFocusableItem = (items) => {
103
+ for (const item of items) {
104
+ if (!item.component.isGroup) {
105
+ return item;
106
+ } else {
107
+ if (item.children) {
108
+ const focusableItem = findFirstFocusableItem(item.children);
109
+
110
+ if (focusableItem) return focusableItem;
111
+ }
112
+ }
113
+ }
114
+ };
@@ -54,7 +54,7 @@ export async function setFocusOnContent(currentRoute: string) {
54
54
  // For doing it we have to wait registration of first focusable node into focusableTree
55
55
  // we wait until content node starts having children
56
56
 
57
- const { setInitialFocus, focusableTree } = focusManager;
57
+ const { setInitialFocus, focusableTree, getCurrentFocus } = focusManager;
58
58
 
59
59
  try {
60
60
  await waitForActiveScreen(currentRoute, focusableTree);
@@ -71,6 +71,6 @@ export async function setFocusOnContent(currentRoute: string) {
71
71
  );
72
72
  }
73
73
  } catch (error) {
74
- setFocusOnContent(currentRoute);
74
+ !getCurrentFocus() && setFocusOnContent(currentRoute);
75
75
  }
76
76
  }
@@ -77,6 +77,12 @@ export class ClosedCaptioningManager {
77
77
 
78
78
  private constructor() {
79
79
  this.initialize();
80
+
81
+ window["vizioDebug"] = {
82
+ setCCEnabled: (isCCEnabled: boolean) => {
83
+ this.ccState$.next(isCCEnabled);
84
+ },
85
+ };
80
86
  }
81
87
 
82
88
  static getInstance(): ClosedCaptioningManager {
@@ -0,0 +1,263 @@
1
+ import { isEqual } from "../..";
2
+
3
+ const array = [1, 2, 3];
4
+ const sameProp = "sameProp";
5
+
6
+ describe("isEqual - comprehensive tests", () => {
7
+ describe("all conditions together", () => {
8
+ it("all conditions checked and not equal", () => {
9
+ const prev = {
10
+ byValueProp: { a: 1, b: 2 },
11
+ byRefProp: array,
12
+ byComparatorProp: "test",
13
+ extraProp1: 123,
14
+ extraProp2: sameProp,
15
+ };
16
+
17
+ const next = {
18
+ byValueProp: { a: 1, b: 2 },
19
+ byRefProp: array,
20
+ byComparatorProp: "different",
21
+ extraProp1: 456,
22
+ extraProp2: sameProp,
23
+ };
24
+
25
+ const customComparator = jest.fn().mockReturnValue(true);
26
+
27
+ const result = isEqual({
28
+ byValue: ["byValueProp"],
29
+ byReference: ["byRefProp"],
30
+ byComparator: {
31
+ comparator: customComparator,
32
+ keys: ["byComparatorProp"],
33
+ },
34
+ compareRemainingKeysBySameValue: true,
35
+ })(prev, next);
36
+
37
+ expect(result).toBe(false);
38
+
39
+ expect(customComparator).toHaveBeenCalledWith(
40
+ prev.byComparatorProp,
41
+ next.byComparatorProp
42
+ );
43
+ });
44
+
45
+ it("all conditions pass with exact remaining keys check", () => {
46
+ const prev = {
47
+ byValueProp: { a: 1, b: 2 },
48
+ byRefProp: array,
49
+ byComparatorProp: "test",
50
+ extraProp: sameProp,
51
+ };
52
+
53
+ const next = {
54
+ byValueProp: { a: 1, b: 2 },
55
+ byRefProp: array,
56
+ byComparatorProp: "test",
57
+ extraProp: sameProp,
58
+ };
59
+
60
+ const customComparator = jest.fn().mockReturnValue(true);
61
+
62
+ const result = isEqual({
63
+ byValue: ["byValueProp"],
64
+ byReference: ["byRefProp"],
65
+ byComparator: {
66
+ comparator: customComparator,
67
+ keys: ["byComparatorProp"],
68
+ },
69
+ compareRemainingKeysBySameValue: true,
70
+ })(prev, next);
71
+
72
+ expect(result).toBe(true);
73
+
74
+ expect(customComparator).toHaveBeenCalledWith(
75
+ prev.byComparatorProp,
76
+ next.byComparatorProp
77
+ );
78
+ });
79
+
80
+ it("byReference check fails first (early return)", () => {
81
+ const prev = {
82
+ byValueProp: { a: 1, b: 2 },
83
+ byRefProp: array,
84
+ byComparatorProp: sameProp,
85
+ };
86
+
87
+ const next = {
88
+ byValueProp: { a: 1, b: 2 },
89
+ byRefProp: [1, 2, 3],
90
+ byComparatorProp: sameProp,
91
+ };
92
+
93
+ const customComparator = jest.fn().mockReturnValue(true);
94
+
95
+ const result = isEqual({
96
+ byValue: ["byValueProp"],
97
+ byReference: ["byRefProp"],
98
+ byComparator: {
99
+ comparator: customComparator,
100
+ keys: ["byComparatorProp"],
101
+ },
102
+ })(prev, next);
103
+
104
+ expect(result).toBe(false);
105
+ expect(customComparator).not.toHaveBeenCalled();
106
+ });
107
+
108
+ it("byValue check fails after byReference passes", () => {
109
+ const prev = {
110
+ byValueProp: { a: 1, b: 2 },
111
+ byRefProp: array,
112
+ byComparatorProp: sameProp,
113
+ };
114
+
115
+ const next = {
116
+ byValueProp: { a: 1, b: 3 },
117
+ byRefProp: array,
118
+ byComparatorProp: sameProp,
119
+ };
120
+
121
+ const customComparator = jest.fn().mockReturnValue(true);
122
+
123
+ const result = isEqual({
124
+ byValue: ["byValueProp"],
125
+ byReference: ["byRefProp"],
126
+ byComparator: {
127
+ comparator: customComparator,
128
+ keys: ["byComparatorProp"],
129
+ },
130
+ })(prev, next);
131
+
132
+ expect(result).toBe(false);
133
+ expect(customComparator).not.toHaveBeenCalled();
134
+ });
135
+
136
+ it("byComparator check fails after other checks pass", () => {
137
+ const prev = {
138
+ byValueProp: { a: 1, b: 2 },
139
+ byRefProp: array,
140
+ byComparatorProp: "test",
141
+ };
142
+
143
+ const next = {
144
+ byValueProp: { a: 1, b: 2 },
145
+ byRefProp: array,
146
+ byComparatorProp: "different",
147
+ };
148
+
149
+ const customComparator = jest.fn().mockReturnValue(false);
150
+
151
+ const result = isEqual({
152
+ byValue: ["byValueProp"],
153
+ byReference: ["byRefProp"],
154
+ byComparator: {
155
+ comparator: customComparator,
156
+ keys: ["byComparatorProp"],
157
+ },
158
+ })(prev, next);
159
+
160
+ expect(result).toBe(false);
161
+
162
+ expect(customComparator).toHaveBeenCalledWith(
163
+ prev.byComparatorProp,
164
+ next.byComparatorProp
165
+ );
166
+ });
167
+
168
+ it("remaining keys comparison fails", () => {
169
+ const sharedArray = [1, 2, 3];
170
+
171
+ const prev = {
172
+ byValueProp: { a: 1, b: 2 },
173
+ byRefProp: sharedArray,
174
+ byComparatorProp: "test",
175
+ extraProp: "original",
176
+ };
177
+
178
+ const next = {
179
+ byValueProp: { a: 1, b: 2 },
180
+ byRefProp: sharedArray,
181
+ byComparatorProp: "different",
182
+ extraProp: "changed",
183
+ };
184
+
185
+ const customComparator = jest.fn().mockReturnValue(true);
186
+
187
+ const result = isEqual({
188
+ byValue: ["byValueProp"],
189
+ byReference: ["byRefProp"],
190
+ byComparator: {
191
+ comparator: customComparator,
192
+ keys: ["byComparatorProp"],
193
+ },
194
+ compareRemainingKeysBySameValue: true,
195
+ })(prev, next);
196
+
197
+ expect(result).toBe(false);
198
+ expect(customComparator).toHaveBeenCalled();
199
+ });
200
+
201
+ it("with debug flag", () => {
202
+ const consoleSpy = jest.spyOn(console, "log").mockImplementation();
203
+
204
+ const prev = {
205
+ byValueProp: { a: 1, b: 2 },
206
+ byRefProp: array,
207
+ byComparatorProp: "test",
208
+ extraProp: sameProp,
209
+ };
210
+
211
+ const next = {
212
+ byValueProp: { a: 1, b: 2 },
213
+ byRefProp: array,
214
+ byComparatorProp: "different",
215
+ extraProp: sameProp,
216
+ };
217
+
218
+ const customComparator = jest.fn().mockReturnValue(true);
219
+
220
+ const result = isEqual({
221
+ byValue: ["byValueProp"],
222
+ byReference: ["byRefProp"],
223
+ byComparator: {
224
+ comparator: customComparator,
225
+ keys: ["byComparatorProp"],
226
+ },
227
+ compareRemainingKeysBySameValue: true,
228
+ debug: true,
229
+ })(prev, next);
230
+
231
+ expect(result).toBe(true);
232
+ expect(consoleSpy).toHaveBeenCalled();
233
+
234
+ consoleSpy.mockRestore();
235
+ });
236
+
237
+ it("handles missing properties", () => {
238
+ const prev = {
239
+ byValueProp: { a: 1, b: 2 },
240
+ byRefProp: [1, 2, 3],
241
+ };
242
+
243
+ const next = {
244
+ byValueProp: { a: 1, b: 2 },
245
+ byComparatorProp: "test",
246
+ };
247
+
248
+ const customComparator = jest.fn().mockReturnValue(true);
249
+
250
+ const result = isEqual({
251
+ byValue: ["byValueProp"],
252
+ byReference: ["byRefProp"],
253
+ byComparator: {
254
+ comparator: customComparator,
255
+ keys: ["byComparatorProp"],
256
+ },
257
+ })(prev, next);
258
+
259
+ expect(result).toBe(false);
260
+ expect(customComparator).not.toHaveBeenCalled();
261
+ });
262
+ });
263
+ });
@@ -1,6 +1,8 @@
1
1
  /* eslint no-console: off */
2
2
 
3
- import * as R from "ramda";
3
+ import { equals } from "ramda";
4
+
5
+ const alwaysTrue = () => true;
4
6
 
5
7
  export const isParentFocusEqual = (
6
8
  prevParentFocus: ParentFocus,
@@ -18,7 +20,7 @@ export const isEqual =
18
20
  ({
19
21
  byValue = [],
20
22
  byReference = [],
21
- byComparator = { comparator: R.always(true), keys: [] },
23
+ byComparator = { comparator: alwaysTrue, keys: [] },
22
24
  debug = false,
23
25
  compareRemainingKeysBySameValue = false,
24
26
  }: {
@@ -29,42 +31,59 @@ export const isEqual =
29
31
  compareRemainingKeysBySameValue?: boolean;
30
32
  }) =>
31
33
  (prev, next) => {
32
- const isEqualByValue = R.isEmpty(byValue)
33
- ? true
34
- : R.all((key) => R.equals(prev[key], next[key]), byValue);
34
+ for (const key of byReference) {
35
+ if (prev[key] !== next[key]) {
36
+ if (debug) console.log({ isEqualByReference: false });
35
37
 
36
- const isEqualByReference = R.isEmpty(byReference)
37
- ? true
38
- : R.all((key) => prev[key] === next[key], byReference);
38
+ return false;
39
+ }
40
+ }
39
41
 
40
- const isEqualByComparator = R.isEmpty(byComparator.keys)
41
- ? true
42
- : R.all(
43
- (key) => byComparator.comparator(prev[key], next[key]),
44
- byComparator.keys
45
- );
42
+ for (const key of byValue) {
43
+ if (!equals(prev[key], next[key])) {
44
+ if (debug) console.log({ isEqualByValue: false });
46
45
 
47
- if (debug) {
48
- console.log({ isEqualByValue, isEqualByReference, isEqualByComparator });
46
+ return false;
47
+ }
48
+ }
49
+
50
+ for (const key of byComparator.keys) {
51
+ if (!byComparator.comparator(prev[key], next[key])) {
52
+ if (debug) console.log({ isEqualByComparator: false });
53
+
54
+ return false;
55
+ }
49
56
  }
50
57
 
51
58
  if (compareRemainingKeysBySameValue) {
52
- const undeclaredKeys = R.difference(
53
- [...byValue, ...byReference, byComparator.keys],
54
- Object.keys(prev)
55
- );
59
+ const allKeys = new Set([...Object.keys(prev), ...Object.keys(next)]);
60
+
61
+ const declaredKeys = new Set([
62
+ ...byValue,
63
+ ...byReference,
64
+ ...byComparator.keys,
65
+ ]);
56
66
 
57
- const isRestKeysEqualAllBySameValue = undeclaredKeys.every((key) =>
58
- Object.is(prev[key], next[key])
59
- );
67
+ for (const key of Array.from(allKeys)) {
68
+ if (!declaredKeys.has(key) && !Object.is(prev[key], next[key])) {
69
+ if (debug) console.log({ isRestKeysEqualAllBySameValue: false });
60
70
 
61
- return (
62
- isEqualByValue &&
63
- isEqualByReference &&
64
- isEqualByComparator &&
65
- isRestKeysEqualAllBySameValue
66
- );
71
+ return false;
72
+ }
73
+ }
74
+ }
75
+
76
+ // if we made it here, everything matched
77
+ if (debug) {
78
+ console.log({
79
+ isEqualByValue: true,
80
+ isEqualByReference: true,
81
+ isEqualByComparator: true,
82
+ isRestKeysEqualAllBySameValue: compareRemainingKeysBySameValue
83
+ ? true
84
+ : undefined,
85
+ });
67
86
  }
68
87
 
69
- return isEqualByValue && isEqualByReference && isEqualByComparator;
88
+ return true;
70
89
  };