@applicaster/zapp-react-native-utils 11.0.0-rc.99 → 12.0.0-rc.1
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/actionsExecutor/ActionExecutor.ts +117 -0
- package/actionsExecutor/ActionExecutorContext.tsx +90 -132
- package/appUtils/HooksManager/index.js +4 -1
- package/appUtils/focusManager/index.ts +0 -2
- package/appUtils/playerManager/OverlayObserver/utils.ts +22 -9
- package/appUtils/playerManager/player.ts +23 -1
- package/cloudEventsUtils/index.ts +56 -0
- package/navigationUtils/__tests__/__snapshots__/navigationUtils.test.js.snap +1 -1
- package/navigationUtils/__tests__/navigationUtils.test.js +4 -2
- package/navigationUtils/index.ts +3 -1
- package/package.json +2 -2
- package/reactHooks/feed/__tests__/useFeedLoader.test.tsx +2 -1
- package/reactHooks/feed/useBatchLoading.ts +1 -1
- package/reactHooks/layout/index.ts +7 -3
- package/reactHooks/navigation/__mocks__/index.ts +1 -0
- package/reactHooks/navigation/index.ts +33 -62
- package/reactHooks/navigation/useNavigation.ts +21 -0
- package/reactHooks/screen/index.ts +6 -1
- package/reactHooks/screen/useCurrentScreenData.ts +8 -2
- package/reactHooks/screen/useScreenContext.ts +40 -2
- package/reactUtils/helpers.tsx +2 -0
- package/testUtils/index.tsx +87 -0
- package/theme/index.ts +2 -1
- package/testUtils/index.ts +0 -34
|
@@ -0,0 +1,117 @@
|
|
|
1
|
+
import { createLogger } from "../logger";
|
|
2
|
+
|
|
3
|
+
export enum ActionResult {
|
|
4
|
+
Success = "success",
|
|
5
|
+
Error = "error",
|
|
6
|
+
Cancel = "cancel",
|
|
7
|
+
}
|
|
8
|
+
|
|
9
|
+
const { log_error, log_debug, log_info } = createLogger({
|
|
10
|
+
subsystem: "ActionExecutor",
|
|
11
|
+
category: "General",
|
|
12
|
+
});
|
|
13
|
+
|
|
14
|
+
class ActionExecutor {
|
|
15
|
+
private registeredActions: Record<string, any> = {};
|
|
16
|
+
// TODO: Remove context
|
|
17
|
+
|
|
18
|
+
handleAction = async (
|
|
19
|
+
action: ActionType,
|
|
20
|
+
entry: ZappEntry = null
|
|
21
|
+
): Promise<ActionResult> => {
|
|
22
|
+
const handler = this.registeredActions[action.type];
|
|
23
|
+
|
|
24
|
+
if (!handler) {
|
|
25
|
+
log_error(
|
|
26
|
+
`handleAction: no handler found for action type: ${action.type}`,
|
|
27
|
+
{ action, entry }
|
|
28
|
+
);
|
|
29
|
+
|
|
30
|
+
return;
|
|
31
|
+
}
|
|
32
|
+
|
|
33
|
+
log_debug(
|
|
34
|
+
`handleAction: ${action.type}${
|
|
35
|
+
entry ? `, entry id ${entry.id}, title: ${entry.title}` : ""
|
|
36
|
+
}`,
|
|
37
|
+
{ action, entry }
|
|
38
|
+
);
|
|
39
|
+
|
|
40
|
+
try {
|
|
41
|
+
return handler(action, entry);
|
|
42
|
+
} catch (error) {
|
|
43
|
+
log_error(
|
|
44
|
+
`handleAction: error handling action type: ${action.type}, message: ${error.message}`,
|
|
45
|
+
{ action, entry, error }
|
|
46
|
+
);
|
|
47
|
+
|
|
48
|
+
throw error;
|
|
49
|
+
}
|
|
50
|
+
};
|
|
51
|
+
|
|
52
|
+
handleActions = async (
|
|
53
|
+
actionTypes: ActionType[],
|
|
54
|
+
entry: ZappEntry = null
|
|
55
|
+
): Promise<ActionResult> => {
|
|
56
|
+
for (const action of actionTypes) {
|
|
57
|
+
// TODO: Handle action result error
|
|
58
|
+
const result = await this.handleAction(action, entry);
|
|
59
|
+
|
|
60
|
+
if (result === ActionResult.Cancel) {
|
|
61
|
+
log_info(`handleActions: action: ${action.type} canceled`);
|
|
62
|
+
|
|
63
|
+
return result;
|
|
64
|
+
}
|
|
65
|
+
}
|
|
66
|
+
|
|
67
|
+
return ActionResult.Success;
|
|
68
|
+
};
|
|
69
|
+
|
|
70
|
+
handleEntryActions = async (entry: ZappEntry): Promise<ActionResult> => {
|
|
71
|
+
const tapActions = entry.extensions?.tap_actions?.actions;
|
|
72
|
+
|
|
73
|
+
if (!tapActions) {
|
|
74
|
+
return;
|
|
75
|
+
}
|
|
76
|
+
|
|
77
|
+
if (!Array.isArray(tapActions)) {
|
|
78
|
+
log_error(
|
|
79
|
+
`handleEntryActions: tapActions is not an array for id: ${entry.id} title: ${entry.title}`,
|
|
80
|
+
{ entry }
|
|
81
|
+
);
|
|
82
|
+
|
|
83
|
+
return;
|
|
84
|
+
}
|
|
85
|
+
|
|
86
|
+
if (tapActions.length === 0) {
|
|
87
|
+
return;
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
await this.handleActions(tapActions, entry);
|
|
91
|
+
};
|
|
92
|
+
|
|
93
|
+
unregisterAction = (actionType: string) => {
|
|
94
|
+
delete this.registeredActions[actionType];
|
|
95
|
+
};
|
|
96
|
+
|
|
97
|
+
registerAction = (
|
|
98
|
+
type: string,
|
|
99
|
+
handler: (action: ActionType, entry?: ZappEntry) => Promise<ActionResult>
|
|
100
|
+
) => {
|
|
101
|
+
if (this.registeredActions[type]) {
|
|
102
|
+
log_error(`registerAction: action type: ${type} already registered`);
|
|
103
|
+
|
|
104
|
+
return () => {};
|
|
105
|
+
}
|
|
106
|
+
|
|
107
|
+
this.registeredActions[type] = handler;
|
|
108
|
+
log_debug(`registerAction: action type: ${type} registered`);
|
|
109
|
+
|
|
110
|
+
return () => {
|
|
111
|
+
this.unregisterAction(type);
|
|
112
|
+
log_debug(`registerAction: action type: ${type} un-registered`);
|
|
113
|
+
};
|
|
114
|
+
};
|
|
115
|
+
}
|
|
116
|
+
|
|
117
|
+
export const actionExecutor = new ActionExecutor();
|
|
@@ -1,23 +1,25 @@
|
|
|
1
1
|
/// <reference types="@applicaster/applicaster-types" />
|
|
2
2
|
|
|
3
3
|
import React, { createContext, useMemo } from "react";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
actionExecutor as _actionExecutor,
|
|
6
|
+
ActionResult,
|
|
7
|
+
} from "./ActionExecutor";
|
|
5
8
|
import { batchSaveToLocalStorage } from "../zappFrameworkUtils/localStorageHelper";
|
|
6
|
-
import * as QuickBrickManager from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
7
9
|
import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
10
|
+
import * as QuickBrickManager from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
8
11
|
import { showConfirmationDialog } from "../alertUtils";
|
|
12
|
+
import { createCloudEvent, sendCloudEvent } from "../cloudEventsUtils";
|
|
13
|
+
import { createLogger } from "../logger";
|
|
14
|
+
import {
|
|
15
|
+
StorageMultiSelectProvider
|
|
16
|
+
} from "@applicaster/zapp-react-native-bridge/ZappStorage/StorageMultiSelectProvider";
|
|
9
17
|
|
|
10
18
|
const { log_error, log_debug, log_info } = createLogger({
|
|
11
19
|
subsystem: "ActionExecutorContext",
|
|
12
20
|
category: "General",
|
|
13
21
|
});
|
|
14
22
|
|
|
15
|
-
export enum ActionResult {
|
|
16
|
-
Success = "success",
|
|
17
|
-
Error = "error",
|
|
18
|
-
Cancel = "cancel",
|
|
19
|
-
}
|
|
20
|
-
|
|
21
23
|
type ActionExecutorContextType = {
|
|
22
24
|
registerAction: (
|
|
23
25
|
type: string,
|
|
@@ -35,161 +37,114 @@ type Props = {
|
|
|
35
37
|
children: React.ReactNode;
|
|
36
38
|
};
|
|
37
39
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
// TODO: Remove context
|
|
44
|
-
|
|
45
|
-
constructor() {
|
|
46
|
-
this.prepareDefaultActions();
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
prepareDefaultActions = () => {
|
|
50
|
-
this.registerAction("localStorageSet", async (action) => {
|
|
51
|
-
const namespaces = action.options.content;
|
|
52
|
-
await batchSaveToLocalStorage(namespaces);
|
|
53
|
-
// TODO: Add support for ownershipKey and ownershipNamespace
|
|
40
|
+
const prepareDefaultActions = (actionExecutor) => {
|
|
41
|
+
actionExecutor.registerAction("localStorageSet", async (action) => {
|
|
42
|
+
const namespaces = action.options.content;
|
|
43
|
+
await batchSaveToLocalStorage(namespaces);
|
|
44
|
+
// TODO: Add support for ownershipKey and ownershipNamespace
|
|
54
45
|
|
|
55
|
-
|
|
56
|
-
|
|
46
|
+
return ActionResult.Success;
|
|
47
|
+
});
|
|
57
48
|
|
|
58
|
-
|
|
59
|
-
|
|
49
|
+
actionExecutor.registerAction("appRestart", async () => {
|
|
50
|
+
log_info(`handleAction: ${QUICK_BRICK_EVENTS.FORCE_APP_RELOAD} event`);
|
|
60
51
|
|
|
61
|
-
|
|
62
|
-
QUICK_BRICK_EVENTS.FORCE_APP_RELOAD
|
|
63
|
-
);
|
|
52
|
+
QuickBrickManager.sendQuickBrickEvent(QUICK_BRICK_EVENTS.FORCE_APP_RELOAD);
|
|
64
53
|
|
|
65
|
-
|
|
66
|
-
|
|
54
|
+
return ActionResult.Success;
|
|
55
|
+
});
|
|
67
56
|
|
|
68
|
-
|
|
69
|
-
|
|
57
|
+
actionExecutor.registerAction("confirmDialog", async (action) => {
|
|
58
|
+
log_info("handleAction: confirmDialog event");
|
|
70
59
|
|
|
71
|
-
|
|
72
|
-
|
|
73
|
-
|
|
74
|
-
|
|
75
|
-
|
|
76
|
-
});
|
|
60
|
+
const confirmationPromise = new Promise<ActionResult>((resolve) => {
|
|
61
|
+
showConfirmationDialog({
|
|
62
|
+
...action.options,
|
|
63
|
+
confirmCompletion: () => resolve(ActionResult.Success),
|
|
64
|
+
cancelCompletion: () => resolve(ActionResult.Cancel),
|
|
77
65
|
});
|
|
66
|
+
});
|
|
78
67
|
|
|
79
|
-
|
|
80
|
-
|
|
81
|
-
};
|
|
82
|
-
|
|
83
|
-
handleAction = async (
|
|
84
|
-
action: ActionType,
|
|
85
|
-
entry: ZappEntry = null
|
|
86
|
-
): Promise<ActionResult> => {
|
|
87
|
-
const handler = this.registeredActions[action.type];
|
|
88
|
-
|
|
89
|
-
if (!handler) {
|
|
90
|
-
log_error(
|
|
91
|
-
`handleAction: no handler found for action type: ${action.type}`,
|
|
92
|
-
{ action, entry }
|
|
93
|
-
);
|
|
94
|
-
|
|
95
|
-
return;
|
|
96
|
-
}
|
|
97
|
-
|
|
98
|
-
log_debug(
|
|
99
|
-
`handleAction: ${action.type}${
|
|
100
|
-
entry ? `, entry id ${entry.id}, title: ${entry.title}` : ""
|
|
101
|
-
}`,
|
|
102
|
-
{ action, entry }
|
|
103
|
-
);
|
|
68
|
+
return confirmationPromise;
|
|
69
|
+
});
|
|
104
70
|
|
|
71
|
+
actionExecutor.registerAction("sendCloudEvent", async (action, entry) => {
|
|
105
72
|
try {
|
|
106
|
-
|
|
107
|
-
} catch (error) {
|
|
108
|
-
log_error(
|
|
109
|
-
`handleAction: error handling action type: ${action.type}, message: ${error.message}`,
|
|
110
|
-
{ action, entry, error }
|
|
111
|
-
);
|
|
112
|
-
|
|
113
|
-
throw error;
|
|
114
|
-
}
|
|
115
|
-
};
|
|
73
|
+
const options = action.options;
|
|
116
74
|
|
|
117
|
-
|
|
118
|
-
|
|
119
|
-
|
|
120
|
-
|
|
121
|
-
|
|
122
|
-
// TODO: Handle action result error
|
|
123
|
-
const result = await this.handleAction(action, entry);
|
|
75
|
+
const cloudEvent = await createCloudEvent({
|
|
76
|
+
type: options.type || "com.applicaster.selector.action.v1",
|
|
77
|
+
data: options.data || entry,
|
|
78
|
+
subject: options.subject || entry?.id,
|
|
79
|
+
});
|
|
124
80
|
|
|
125
|
-
|
|
126
|
-
log_info(`handleActions: action: ${action.type} canceled`);
|
|
81
|
+
log_info("handleAction: sendCloudEvent event", { cloudEvent });
|
|
127
82
|
|
|
128
|
-
|
|
129
|
-
}
|
|
130
|
-
}
|
|
131
|
-
|
|
132
|
-
return ActionResult.Success;
|
|
133
|
-
};
|
|
83
|
+
const { error, code } = await sendCloudEvent(cloudEvent, options.url);
|
|
134
84
|
|
|
135
|
-
|
|
136
|
-
|
|
85
|
+
if (error) {
|
|
86
|
+
log_error("sendCloudEvent: error sending cloud event", { error });
|
|
137
87
|
|
|
138
|
-
|
|
139
|
-
|
|
140
|
-
}
|
|
88
|
+
return ActionResult.Error;
|
|
89
|
+
}
|
|
141
90
|
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
`handleEntryActions: tapActions is not an array for id: ${entry.id} title: ${entry.title}`,
|
|
145
|
-
{ entry }
|
|
146
|
-
);
|
|
91
|
+
if (code && code >= 200 && code < 300 && !error) {
|
|
92
|
+
log_info("sendCloudEvent: cloud event sent successfully");
|
|
147
93
|
|
|
148
|
-
|
|
94
|
+
return ActionResult.Success;
|
|
95
|
+
}
|
|
96
|
+
} catch (error) {
|
|
97
|
+
log_error("sendCloudEvent: error sending cloud event", {
|
|
98
|
+
action,
|
|
99
|
+
error,
|
|
100
|
+
});
|
|
149
101
|
}
|
|
150
102
|
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
}
|
|
103
|
+
return ActionResult.Error;
|
|
104
|
+
});
|
|
154
105
|
|
|
155
|
-
|
|
156
|
-
|
|
106
|
+
actionExecutor.registerAction(
|
|
107
|
+
"localStorageToggleFlag",
|
|
108
|
+
async (action: ActionType, entry?: ZappEntry): Promise<ActionResult> => {
|
|
109
|
+
const keyNamespace = action.options?.key;
|
|
110
|
+
const tag = entry?.extensions?.tag;
|
|
157
111
|
|
|
158
|
-
|
|
159
|
-
|
|
160
|
-
};
|
|
112
|
+
if (keyNamespace && tag) {
|
|
113
|
+
const multiSelectProvider = StorageMultiSelectProvider.getProvider(keyNamespace);
|
|
161
114
|
|
|
162
|
-
|
|
163
|
-
|
|
164
|
-
|
|
165
|
-
) => {
|
|
166
|
-
if (this.registeredActions[type]) {
|
|
167
|
-
log_error(`registerAction: action type: ${type} already registered`);
|
|
115
|
+
const selectedItems = await multiSelectProvider.getSelectedAsync();
|
|
116
|
+
const isTagInSelectedItems = selectedItems.includes(tag);
|
|
117
|
+
log_info(`handleAction: localStorageToggleFlag event will ${isTagInSelectedItems ? "remove" : "add"} tag: ${tag} for keyNamespace: ${keyNamespace}, current selectedItems: ${selectedItems}`);
|
|
168
118
|
|
|
169
|
-
|
|
170
|
-
|
|
119
|
+
if (selectedItems.includes(tag)) {
|
|
120
|
+
await multiSelectProvider.removeItem(tag);
|
|
121
|
+
} else {
|
|
122
|
+
await multiSelectProvider.addItem(tag);
|
|
123
|
+
}
|
|
124
|
+
} else {
|
|
125
|
+
log_error("handleAction: localStorageToggleFlag event missing keyNamespace or tag", { keyNamespace, tag });
|
|
171
126
|
|
|
172
|
-
|
|
173
|
-
|
|
127
|
+
return ActionResult.Error;
|
|
128
|
+
}
|
|
174
129
|
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
};
|
|
179
|
-
};
|
|
180
|
-
}
|
|
130
|
+
return ActionResult.Success;
|
|
131
|
+
});
|
|
132
|
+
};
|
|
181
133
|
|
|
182
|
-
export const
|
|
134
|
+
export const ActionExecutorContext =
|
|
135
|
+
createContext<ActionExecutorContextType>(null);
|
|
183
136
|
|
|
184
137
|
export function withActionExecutor(Component) {
|
|
138
|
+
prepareDefaultActions(_actionExecutor);
|
|
139
|
+
|
|
185
140
|
return function ActionExecutorComponent(props: Props) {
|
|
186
141
|
const handlers = useMemo(() => {
|
|
187
142
|
return {
|
|
188
|
-
unregisterAction:
|
|
189
|
-
registerAction:
|
|
190
|
-
handleAction:
|
|
191
|
-
handleActions:
|
|
192
|
-
handleEntryActions:
|
|
143
|
+
unregisterAction: _actionExecutor.unregisterAction,
|
|
144
|
+
registerAction: _actionExecutor.registerAction,
|
|
145
|
+
handleAction: _actionExecutor.handleAction,
|
|
146
|
+
handleActions: _actionExecutor.handleActions,
|
|
147
|
+
handleEntryActions: _actionExecutor.handleEntryActions,
|
|
193
148
|
};
|
|
194
149
|
}, []);
|
|
195
150
|
|
|
@@ -200,3 +155,6 @@ export function withActionExecutor(Component) {
|
|
|
200
155
|
);
|
|
201
156
|
};
|
|
202
157
|
}
|
|
158
|
+
|
|
159
|
+
// TODO: Please use direct import from the ActionExecutor.ts
|
|
160
|
+
export const actionExecutor = _actionExecutor;
|
|
@@ -1,7 +1,10 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
2
|
|
|
3
3
|
import { subscriber } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
4
|
-
import {
|
|
4
|
+
import {
|
|
5
|
+
QUICK_BRICK_EVENTS,
|
|
6
|
+
sendQuickBrickEvent,
|
|
7
|
+
} from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
5
8
|
|
|
6
9
|
import { Hook } from "./Hook";
|
|
7
10
|
import { HOOKS_EVENTS, HOOKS_TYPE } from "./constants";
|
|
@@ -269,8 +269,6 @@ export const focusManager = (function () {
|
|
|
269
269
|
logger.log({ message: `focusAutoEnabled ${focusAutoEnabled}` });
|
|
270
270
|
|
|
271
271
|
if (focusAutoEnabled) {
|
|
272
|
-
logger.log({ message: `setInitialFocus ${setInitialFocus}` });
|
|
273
|
-
|
|
274
272
|
setInitialFocus(initialFocusOnContent ? node : undefined);
|
|
275
273
|
}
|
|
276
274
|
}
|
|
@@ -4,27 +4,40 @@ import {
|
|
|
4
4
|
RequestBuilder,
|
|
5
5
|
} from "@applicaster/zapp-pipes-v2-client";
|
|
6
6
|
import { log_error, log_info } from "./OverlaysObserver";
|
|
7
|
-
import * as R from "ramda
|
|
7
|
+
import * as R from "ramda";
|
|
8
8
|
import { toNumber, toNumberWithDefault } from "../../../numberUtils";
|
|
9
9
|
|
|
10
|
-
export const parseTimeToSeconds = (timeStr) => {
|
|
10
|
+
export const parseTimeToSeconds = (timeStr: string): number => {
|
|
11
|
+
const validationError = new Error("Invalid time format");
|
|
12
|
+
|
|
13
|
+
if (!timeStr) {
|
|
14
|
+
throw validationError;
|
|
15
|
+
}
|
|
16
|
+
|
|
17
|
+
const toTime = (value: number, max: number = 59): number => {
|
|
18
|
+
// Validate the time value
|
|
19
|
+
if (Number.isNaN(value) || value < 0 || value > max) {
|
|
20
|
+
throw validationError;
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
return value;
|
|
24
|
+
};
|
|
25
|
+
|
|
11
26
|
// Split the time string by colon
|
|
12
27
|
const parts = timeStr.split(":").map((part) => parseInt(part, 10));
|
|
13
28
|
|
|
14
|
-
let seconds = 0;
|
|
15
|
-
|
|
16
29
|
// Calculate the seconds based on the number of parts
|
|
17
30
|
if (parts.length === 2) {
|
|
18
31
|
// Format is MM:SS
|
|
19
|
-
|
|
32
|
+
return toTime(parts[0]) * 60 + toTime(parts[1]);
|
|
20
33
|
} else if (parts.length === 3) {
|
|
21
34
|
// Format is HH:MM:SS
|
|
22
|
-
|
|
35
|
+
return (
|
|
36
|
+
toTime(parts[0], 23) * 3600 + toTime(parts[1]) * 60 + toTime(parts[2])
|
|
37
|
+
);
|
|
23
38
|
} else {
|
|
24
|
-
throw
|
|
39
|
+
throw validationError;
|
|
25
40
|
}
|
|
26
|
-
|
|
27
|
-
return seconds;
|
|
28
41
|
};
|
|
29
42
|
|
|
30
43
|
export const retrieveFeedUrl = (entry: ZappEntry) =>
|
|
@@ -1,4 +1,5 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
+
import { BehaviorSubject } from "rxjs";
|
|
2
3
|
import { isEmptyOrNil } from "@applicaster/zapp-react-native-utils/cellUtils";
|
|
3
4
|
import { createLogger, utilsLogger } from "../../logger";
|
|
4
5
|
import { getBoolFromConfigValue } from "../../configurationUtils";
|
|
@@ -35,18 +36,28 @@ export class Player {
|
|
|
35
36
|
readonly playerState: QuickBrickPlayer.PlayerState;
|
|
36
37
|
|
|
37
38
|
public playerPluginId: string;
|
|
38
|
-
|
|
39
|
+
protected _entry: ZappEntry | null;
|
|
39
40
|
|
|
40
41
|
protected config?: Record<string, any>;
|
|
41
42
|
|
|
42
43
|
protected startPosition?: number | null;
|
|
43
44
|
readonly playerRole: PlayerRole = PlayerRole.Unspecified;
|
|
44
45
|
private chaptersObserver?: OverlaysObserver | null;
|
|
46
|
+
private entryObservable?: BehaviorSubject<ZappEntry> | null;
|
|
45
47
|
|
|
46
48
|
protected listeners: {
|
|
47
49
|
[key: string]: QuickBrickPlayer.SharedPlayerCallBacks;
|
|
48
50
|
} = {};
|
|
49
51
|
|
|
52
|
+
get entry(): ZappEntry | null {
|
|
53
|
+
return this._entry;
|
|
54
|
+
}
|
|
55
|
+
|
|
56
|
+
set entry(newEntry: ZappEntry | null) {
|
|
57
|
+
this._entry = newEntry;
|
|
58
|
+
this.entryObservable?.next(newEntry);
|
|
59
|
+
}
|
|
60
|
+
|
|
50
61
|
constructor(props) {
|
|
51
62
|
this.playerState = {
|
|
52
63
|
// We want isLive null by default to be able not to show controls if we do not know type
|
|
@@ -80,6 +91,7 @@ export class Player {
|
|
|
80
91
|
this.onVideoProgress = this.onVideoProgress.bind(this);
|
|
81
92
|
this.onVideoLoad = this.onVideoLoad.bind(this);
|
|
82
93
|
this.isFullScreenSupported = this.isFullScreenSupported.bind(this);
|
|
94
|
+
this.entryObservable = null;
|
|
83
95
|
}
|
|
84
96
|
|
|
85
97
|
getOverlayObservable = () => {
|
|
@@ -90,6 +102,14 @@ export class Player {
|
|
|
90
102
|
return this.chaptersObserver;
|
|
91
103
|
};
|
|
92
104
|
|
|
105
|
+
getEntryObservable = (): BehaviorSubject<ZappEntry | null> => {
|
|
106
|
+
if (!this.entryObservable) {
|
|
107
|
+
this.entryObservable = new BehaviorSubject(this.entry);
|
|
108
|
+
}
|
|
109
|
+
|
|
110
|
+
return this.entryObservable;
|
|
111
|
+
};
|
|
112
|
+
|
|
93
113
|
/**
|
|
94
114
|
* ---------------------------------------- PLAYER SETTERS GETTERS ----------------------------------------
|
|
95
115
|
* These methods let you get information about the content / ad playing
|
|
@@ -469,6 +489,8 @@ export class Player {
|
|
|
469
489
|
this.invokeListenersUpdate({
|
|
470
490
|
callbackName: SharedPlayerCallBacksKeys.OnPlayerClose,
|
|
471
491
|
});
|
|
492
|
+
|
|
493
|
+
this.entryObservable?.complete();
|
|
472
494
|
};
|
|
473
495
|
|
|
474
496
|
onAdBegin = (event) => {
|
|
@@ -0,0 +1,56 @@
|
|
|
1
|
+
import { v4 as uuid } from "uuid";
|
|
2
|
+
import { RequestBuilder } from "@applicaster/zapp-pipes-v2-client";
|
|
3
|
+
import { sessionStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/SessionStorage";
|
|
4
|
+
import { getAppUrlScheme } from "@applicaster/zapp-react-native-utils/appDataUtils";
|
|
5
|
+
|
|
6
|
+
let source: string | null = null;
|
|
7
|
+
|
|
8
|
+
const addSource = async () => {
|
|
9
|
+
if (source) {
|
|
10
|
+
return source;
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
const appUrlScheme = getAppUrlScheme();
|
|
14
|
+
const bundleId = await sessionStorage.getItem("bundleIdentifier");
|
|
15
|
+
const appVersion = await sessionStorage.getItem("version_name");
|
|
16
|
+
|
|
17
|
+
if (!appUrlScheme || !bundleId || !appVersion) {
|
|
18
|
+
throw Error(
|
|
19
|
+
`Error: Can not create client data addSource: Check appUrlScheme, bundleId, appversion. Item(s) not exist.
|
|
20
|
+
{appUrlScheme: ${appUrlScheme}, bundleId: ${bundleId}, appVersion: ${appVersion}}`
|
|
21
|
+
);
|
|
22
|
+
}
|
|
23
|
+
|
|
24
|
+
source = `${appUrlScheme}://${bundleId}/versions/${appVersion}`;
|
|
25
|
+
|
|
26
|
+
return source;
|
|
27
|
+
};
|
|
28
|
+
|
|
29
|
+
export const createCloudEvent = async ({ type, subject, data }) => {
|
|
30
|
+
return {
|
|
31
|
+
specversion: "1.0",
|
|
32
|
+
type,
|
|
33
|
+
datacontenttype: "application/json",
|
|
34
|
+
subject,
|
|
35
|
+
source: await addSource(),
|
|
36
|
+
time: new Date().toISOString(),
|
|
37
|
+
data,
|
|
38
|
+
id: uuid(),
|
|
39
|
+
};
|
|
40
|
+
};
|
|
41
|
+
|
|
42
|
+
export const sendCloudEvent = async (cloudEvent: any, url: string) => {
|
|
43
|
+
if (!url) {
|
|
44
|
+
throw new Error("Feed locator is missing");
|
|
45
|
+
}
|
|
46
|
+
|
|
47
|
+
const requestBuilder = new RequestBuilder();
|
|
48
|
+
|
|
49
|
+
const response = await requestBuilder
|
|
50
|
+
.setUrl(url)
|
|
51
|
+
.setMethod("POST")
|
|
52
|
+
.addBodyParams(cloudEvent)
|
|
53
|
+
.call<ZappFeed>();
|
|
54
|
+
|
|
55
|
+
return response;
|
|
56
|
+
};
|
|
@@ -1,5 +1,5 @@
|
|
|
1
1
|
import * as R from "ramda";
|
|
2
|
-
import * as navigationUtils from "
|
|
2
|
+
import * as navigationUtils from "..";
|
|
3
3
|
import {
|
|
4
4
|
river,
|
|
5
5
|
riverPlugin,
|
|
@@ -148,7 +148,9 @@ describe("getNavigationProps", () => {
|
|
|
148
148
|
activeRiver: R.merge(baseScreen, { navigations }),
|
|
149
149
|
};
|
|
150
150
|
|
|
151
|
-
expect(
|
|
151
|
+
expect(
|
|
152
|
+
getNavigationProps({ navigator, title: "testTitle", category })
|
|
153
|
+
).toMatchSnapshot();
|
|
152
154
|
});
|
|
153
155
|
});
|
|
154
156
|
|
package/navigationUtils/index.ts
CHANGED
|
@@ -95,10 +95,12 @@ export function resolveNavigationPlugin({
|
|
|
95
95
|
* @returns {Object} props
|
|
96
96
|
*/
|
|
97
97
|
export function getNavigationProps({
|
|
98
|
-
navigator: { activeRiver
|
|
98
|
+
navigator: { activeRiver },
|
|
99
|
+
title = "",
|
|
99
100
|
category,
|
|
100
101
|
}: {
|
|
101
102
|
navigator: QuickBrickAppNavigator;
|
|
103
|
+
title: string;
|
|
102
104
|
category: string;
|
|
103
105
|
}): Nullable<
|
|
104
106
|
Partial<ZappNavigation> & { selected: string; title: string; home: boolean }
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-native-utils",
|
|
3
|
-
"version": "
|
|
3
|
+
"version": "12.0.0-rc.1",
|
|
4
4
|
"description": "Applicaster Zapp React Native utilities package",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,7 +28,7 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "
|
|
31
|
+
"@applicaster/applicaster-types": "12.0.0-rc.1",
|
|
32
32
|
"buffer": "^5.2.1",
|
|
33
33
|
"camelize": "^1.0.0",
|
|
34
34
|
"create-react-context": "^0.2.3",
|
|
@@ -7,6 +7,7 @@ import * as React from "react";
|
|
|
7
7
|
import configureStore from "redux-mock-store";
|
|
8
8
|
import thunk from "redux-thunk";
|
|
9
9
|
import * as navigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation";
|
|
10
|
+
import * as useNavigationHooks from "@applicaster/zapp-react-native-utils/reactHooks/navigation/useNavigation";
|
|
10
11
|
|
|
11
12
|
const mockStore = configureStore([thunk]);
|
|
12
13
|
|
|
@@ -35,7 +36,7 @@ jest.mock("../../logger", () => ({
|
|
|
35
36
|
}));
|
|
36
37
|
|
|
37
38
|
jest
|
|
38
|
-
.spyOn(
|
|
39
|
+
.spyOn(useNavigationHooks, "useNavigation")
|
|
39
40
|
.mockImplementation(() => navigator);
|
|
40
41
|
|
|
41
42
|
jest.spyOn(navigationHooks, "useRoute").mockImplementation(() => routeData);
|
|
@@ -9,7 +9,7 @@ import {
|
|
|
9
9
|
getInflatedDataSourceUrl,
|
|
10
10
|
getSearchContext,
|
|
11
11
|
} from "@applicaster/zapp-react-native-utils/reactHooks";
|
|
12
|
-
import { useScreenContext } from "
|
|
12
|
+
import { useScreenContext } from "../screen/useScreenContext";
|
|
13
13
|
|
|
14
14
|
type Options = {
|
|
15
15
|
initialBatchSize?: number;
|
|
@@ -2,7 +2,7 @@ import * as R from "ramda";
|
|
|
2
2
|
import { StatusBar, Platform } from "react-native";
|
|
3
3
|
|
|
4
4
|
import { platformSelect, isTV } from "../../reactUtils";
|
|
5
|
-
import {
|
|
5
|
+
import { useIsNavBarVisible } from "../navigation";
|
|
6
6
|
import { useSafeAreaInsets } from "react-native-safe-area-context";
|
|
7
7
|
import { isTablet } from "./isTablet";
|
|
8
8
|
|
|
@@ -48,9 +48,13 @@ export function useStatusBarHeight() {
|
|
|
48
48
|
});
|
|
49
49
|
}
|
|
50
50
|
|
|
51
|
+
const getNavbarHeight = () => {
|
|
52
|
+
return isTV() ? 60 : platformSelect({ ios: 44, android: 56 });
|
|
53
|
+
};
|
|
54
|
+
|
|
51
55
|
export function useCurrentNavbarHeight() {
|
|
52
|
-
const
|
|
53
|
-
const navbarHeight =
|
|
56
|
+
const isNavBarVisible = useIsNavBarVisible();
|
|
57
|
+
const navbarHeight = getNavbarHeight();
|
|
54
58
|
|
|
55
59
|
return isNavBarVisible ? navbarHeight : 0;
|
|
56
60
|
}
|
|
@@ -9,46 +9,30 @@ import {
|
|
|
9
9
|
useContentTypes,
|
|
10
10
|
usePickFromState,
|
|
11
11
|
} from "@applicaster/zapp-react-native-redux/hooks";
|
|
12
|
-
import { NavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NavigationContext";
|
|
13
12
|
import { HooksManager } from "@applicaster/zapp-react-native-utils/appUtils/HooksManager";
|
|
14
13
|
|
|
15
14
|
import { legacyScreenData } from "@applicaster/quick-brick-core/App/NavigationProvider/utils";
|
|
16
15
|
import { LONG_KEY_PRESS_TIMEOUT } from "@applicaster/quick-brick-core/const";
|
|
17
16
|
import { useZappHooksModalState } from "@applicaster/zapp-react-dom-ui-components/Components/ZappHooksModal/ZappHooksModal";
|
|
18
|
-
import { isNavBarVisible as shouldShowNavBar } from "@applicaster/zapp-react-native-ui-components/Components/Screen/navigationHandler";
|
|
19
17
|
import { ZappHookModalContext } from "@applicaster/zapp-react-native-ui-components/Contexts";
|
|
20
18
|
import { PathnameContext } from "@applicaster/zapp-react-native-ui-components/Contexts/PathnameContext";
|
|
21
19
|
import { HookModalContextT } from "@applicaster/zapp-react-native-ui-components/Contexts/ZappHookModalContext";
|
|
22
|
-
import {
|
|
23
|
-
import { useCurrentScreenData } from "@applicaster/zapp-react-native-utils/reactHooks/screen";
|
|
20
|
+
import { getNavigationProps } from "@applicaster/zapp-react-native-utils/navigationUtils";
|
|
24
21
|
import { HOOKS_EVENTS } from "../../appUtils/HooksManager/constants";
|
|
25
22
|
import { getRiverFromRoute, getTargetRoute } from "../../navigationUtils";
|
|
26
23
|
import { platformSelect } from "../../reactUtils";
|
|
27
24
|
import { useConnectionInfo } from "../connection";
|
|
28
25
|
import { getTargetScreenData } from "../screen/useTargetScreenData";
|
|
26
|
+
import { useNavbarState } from "@applicaster/zapp-react-native-utils/reactHooks/screen/useScreenContext";
|
|
29
27
|
|
|
30
|
-
|
|
28
|
+
import { useNavigation } from "./useNavigation";
|
|
29
|
+
|
|
30
|
+
export { useNavigation } from "./useNavigation";
|
|
31
31
|
|
|
32
32
|
const { ProfilerBridge } = NativeModules;
|
|
33
33
|
|
|
34
34
|
export const usePathname = () => React.useContext(PathnameContext);
|
|
35
35
|
|
|
36
|
-
/**
|
|
37
|
-
* Return NavigationContext
|
|
38
|
-
*/
|
|
39
|
-
export const useNavigation = (
|
|
40
|
-
memoized: boolean = false
|
|
41
|
-
): QuickBrickAppNavigator => {
|
|
42
|
-
const navigator = useContext(NavigationContext);
|
|
43
|
-
const memoizedNavigator = useMemo(() => navigator, []);
|
|
44
|
-
|
|
45
|
-
if (memoized) {
|
|
46
|
-
return memoizedNavigator;
|
|
47
|
-
}
|
|
48
|
-
|
|
49
|
-
return navigator;
|
|
50
|
-
};
|
|
51
|
-
|
|
52
36
|
export const useBackHandler = (cb: () => boolean) => {
|
|
53
37
|
React.useEffect(() => {
|
|
54
38
|
BackHandler.addEventListener("hardwareBackPress", cb);
|
|
@@ -59,25 +43,6 @@ export const useBackHandler = (cb: () => boolean) => {
|
|
|
59
43
|
}, [cb]);
|
|
60
44
|
};
|
|
61
45
|
|
|
62
|
-
export const useNavBarStateForScreen = (pathname: string) => {
|
|
63
|
-
const navigator = useNavigation();
|
|
64
|
-
|
|
65
|
-
const [isNavBarVisible, setIsNavBarVisible] = React.useState(
|
|
66
|
-
navigator.isNavBarVisible
|
|
67
|
-
);
|
|
68
|
-
|
|
69
|
-
React.useEffect(() => {
|
|
70
|
-
if (
|
|
71
|
-
navigator.currentRoute === pathname &&
|
|
72
|
-
isNavBarVisible !== navigator.isNavBarVisible
|
|
73
|
-
) {
|
|
74
|
-
setIsNavBarVisible(navigator.isNavBarVisible);
|
|
75
|
-
}
|
|
76
|
-
}, [pathname, navigator.isNavBarVisible]);
|
|
77
|
-
|
|
78
|
-
return { isNavBarVisible, canGoBack: navigator.canGoBack };
|
|
79
|
-
};
|
|
80
|
-
|
|
81
46
|
type Callbacks = Partial<{
|
|
82
47
|
handleHookPresent: ({
|
|
83
48
|
route,
|
|
@@ -204,12 +169,13 @@ export enum MenuTypes {
|
|
|
204
169
|
}
|
|
205
170
|
|
|
206
171
|
export const useNavigationPluginData = (): ZappNavigation | undefined => {
|
|
207
|
-
const {
|
|
172
|
+
const {
|
|
173
|
+
screenData: useRouteScreenData,
|
|
174
|
+
}: { screenData: QuickBrickNavigationData | null } = useRoute();
|
|
208
175
|
|
|
209
|
-
const navigations =
|
|
210
|
-
|
|
211
|
-
|
|
212
|
-
: (screenData as ZappRiver)?.navigations ?? [];
|
|
176
|
+
const navigations = useRouteScreenData?.targetScreen
|
|
177
|
+
? (useRouteScreenData.targetScreen as ZappRiver).navigations
|
|
178
|
+
: (useRouteScreenData as ZappRiver).navigations;
|
|
213
179
|
|
|
214
180
|
const navigationMenu = navigations?.find((nav) => nav.category === "menu");
|
|
215
181
|
|
|
@@ -261,23 +227,15 @@ export const useRunIfLongPress = (
|
|
|
261
227
|
return { onPress, onPressOut };
|
|
262
228
|
};
|
|
263
229
|
|
|
230
|
+
/**
|
|
231
|
+
* @returns boolean - navbar visiblity status
|
|
232
|
+
*/
|
|
264
233
|
export const useIsNavBarVisible = (): boolean => {
|
|
265
|
-
const
|
|
266
|
-
const screenData = useCurrentScreenData();
|
|
267
|
-
|
|
268
|
-
const navBarVisible = React.useMemo(() => {
|
|
269
|
-
return shouldShowNavBar(
|
|
270
|
-
currentRoute,
|
|
271
|
-
screenData,
|
|
272
|
-
isNavBarVisible && !routeIsPlayerScreen(currentRoute),
|
|
273
|
-
canGoBack()
|
|
274
|
-
);
|
|
275
|
-
}, [isNavBarVisible, currentRoute, screenData?.id]);
|
|
234
|
+
const visible = useNavbarState().visible;
|
|
276
235
|
|
|
277
|
-
return
|
|
236
|
+
return useMemo(() => visible, [visible]);
|
|
278
237
|
};
|
|
279
238
|
|
|
280
|
-
// starts with modal/
|
|
281
239
|
const isModalPathname = (pathname: string) => /^modal\//.test(pathname);
|
|
282
240
|
|
|
283
241
|
const isVideoModalPathname = (pathname: string) =>
|
|
@@ -320,8 +278,11 @@ export const useRoute = () => {
|
|
|
320
278
|
|
|
321
279
|
const currentRouteScreenData = legacyScreenData(
|
|
322
280
|
{
|
|
323
|
-
screen:
|
|
324
|
-
|
|
281
|
+
screen:
|
|
282
|
+
currentStack?.state?.screen ??
|
|
283
|
+
navigator.data?.screen ??
|
|
284
|
+
({} as ZappRiver),
|
|
285
|
+
entry: currentStack?.state?.entry ?? ({} as ZappEntry),
|
|
325
286
|
},
|
|
326
287
|
|
|
327
288
|
plugins
|
|
@@ -346,6 +307,14 @@ export const useRoute = () => {
|
|
|
346
307
|
|
|
347
308
|
export const useGetBottomTabBarHeight = (): number => {
|
|
348
309
|
const insets = useSafeAreaInsets();
|
|
310
|
+
const navigator = useNavigation();
|
|
311
|
+
|
|
312
|
+
const navProps = getNavigationProps({
|
|
313
|
+
navigator,
|
|
314
|
+
title: "",
|
|
315
|
+
category: "menu",
|
|
316
|
+
});
|
|
317
|
+
|
|
349
318
|
const navType = useNavigationType();
|
|
350
319
|
const isBottomBarNavigation = navType === MenuTypes.bottomTabBar;
|
|
351
320
|
|
|
@@ -354,8 +323,10 @@ export const useGetBottomTabBarHeight = (): number => {
|
|
|
354
323
|
|
|
355
324
|
if (!isBottomBarNavigation) return 0;
|
|
356
325
|
|
|
357
|
-
|
|
326
|
+
const tabBarHeight = platformSelect({
|
|
358
327
|
ios: TAB_BAR_HEIGHT_IOS + insets.bottom,
|
|
359
328
|
android: TAB_BAR_HEIGHT_ANDROID,
|
|
360
|
-
});
|
|
329
|
+
}) as number;
|
|
330
|
+
|
|
331
|
+
return tabBarHeight + Number(navProps?.styles?.top_border_width ?? 0);
|
|
361
332
|
};
|
|
@@ -0,0 +1,21 @@
|
|
|
1
|
+
/* eslint max-len: off */
|
|
2
|
+
|
|
3
|
+
import { useMemo, useContext } from "react";
|
|
4
|
+
|
|
5
|
+
import { NavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NavigationContext";
|
|
6
|
+
|
|
7
|
+
/**
|
|
8
|
+
* Return NavigationContext
|
|
9
|
+
*/
|
|
10
|
+
export const useNavigation = (
|
|
11
|
+
memoized: boolean = false
|
|
12
|
+
): QuickBrickAppNavigator => {
|
|
13
|
+
const navigator = useContext(NavigationContext);
|
|
14
|
+
const memoizedNavigator = useMemo(() => navigator, []);
|
|
15
|
+
|
|
16
|
+
if (memoized) {
|
|
17
|
+
return memoizedNavigator;
|
|
18
|
+
}
|
|
19
|
+
|
|
20
|
+
return navigator;
|
|
21
|
+
};
|
|
@@ -2,4 +2,9 @@ export { useCurrentScreenData } from "./useCurrentScreenData";
|
|
|
2
2
|
|
|
3
3
|
export { useTargetScreenData } from "./useTargetScreenData";
|
|
4
4
|
|
|
5
|
-
export {
|
|
5
|
+
export {
|
|
6
|
+
useScreenContext,
|
|
7
|
+
useLegacyScreenData,
|
|
8
|
+
useNavbarState,
|
|
9
|
+
useSetNavbarState,
|
|
10
|
+
} from "./useScreenContext";
|
|
@@ -1,10 +1,16 @@
|
|
|
1
|
-
import {
|
|
1
|
+
import { useRivers } from "../state";
|
|
2
2
|
import { useScreenContext } from "./useScreenContext";
|
|
3
3
|
|
|
4
|
+
/**
|
|
5
|
+
* used to get the screen configuration from the layout.json
|
|
6
|
+
* when screenIdOverride passed. It will return the screen configuration from the useScreenContext() by default
|
|
7
|
+
* @param screenIdOverride - screen id to get the screen configuration
|
|
8
|
+
* @returns ZappRiver - screen configuration
|
|
9
|
+
* */
|
|
4
10
|
export const useCurrentScreenData = (
|
|
5
11
|
screenIdOverride: string | null = null
|
|
6
12
|
): ZappRiver => {
|
|
7
|
-
const
|
|
13
|
+
const rivers = useRivers();
|
|
8
14
|
const screenData = useScreenContext();
|
|
9
15
|
|
|
10
16
|
if (screenIdOverride) {
|
|
@@ -1,8 +1,46 @@
|
|
|
1
|
-
import { useMemo } from "react";
|
|
1
|
+
import { useContext, useMemo } from "react";
|
|
2
2
|
|
|
3
3
|
import { useModalNavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ModalNavigationContext";
|
|
4
4
|
import { useNestedNavigationContext } from "@applicaster/zapp-react-native-ui-components/Contexts/NestedNavigationContext";
|
|
5
|
-
import { useNavigation } from "
|
|
5
|
+
import { useNavigation } from "../navigation";
|
|
6
|
+
|
|
7
|
+
import { ScreenContext } from "@applicaster/zapp-react-native-ui-components/Contexts/ScreenContext";
|
|
8
|
+
|
|
9
|
+
function useScreenContextV2() {
|
|
10
|
+
return useContext(ScreenContext);
|
|
11
|
+
}
|
|
12
|
+
|
|
13
|
+
/**
|
|
14
|
+
* Equivalent to existing useNavigation().screenData
|
|
15
|
+
* @returns legacyFormatScreenData - screen configuration
|
|
16
|
+
*/
|
|
17
|
+
export function useLegacyScreenData() {
|
|
18
|
+
const { legacyFormatScreenData } = useScreenContextV2();
|
|
19
|
+
|
|
20
|
+
return useMemo(() => legacyFormatScreenData, [legacyFormatScreenData]);
|
|
21
|
+
}
|
|
22
|
+
|
|
23
|
+
export function useNavbarState() {
|
|
24
|
+
const {
|
|
25
|
+
navBar: { title, summary, visible },
|
|
26
|
+
} = useScreenContextV2();
|
|
27
|
+
|
|
28
|
+
return useMemo(
|
|
29
|
+
() => ({ title, summary, visible }),
|
|
30
|
+
[title, summary, visible]
|
|
31
|
+
);
|
|
32
|
+
}
|
|
33
|
+
|
|
34
|
+
export function useSetNavbarState() {
|
|
35
|
+
const {
|
|
36
|
+
navBar: { setSummary, setTitle, setVisible },
|
|
37
|
+
} = useScreenContextV2();
|
|
38
|
+
|
|
39
|
+
return useMemo(
|
|
40
|
+
() => ({ setSummary, setTitle, setVisible }),
|
|
41
|
+
[setSummary, setTitle, setVisible]
|
|
42
|
+
);
|
|
43
|
+
}
|
|
6
44
|
|
|
7
45
|
export function useScreenContext(): NavigationScreenData {
|
|
8
46
|
const navigator = useNavigation();
|
package/reactUtils/helpers.tsx
CHANGED
|
@@ -9,6 +9,8 @@ export const isNilOrEmpty = R.either(R.isNil, R.isEmpty);
|
|
|
9
9
|
|
|
10
10
|
export const isNotEmpty = R.complement(R.isEmpty);
|
|
11
11
|
|
|
12
|
+
export const withoutNil = R.reject(R.isNil);
|
|
13
|
+
|
|
12
14
|
/**
|
|
13
15
|
* tells wether a React Component is a class component or not
|
|
14
16
|
* @param {ReactComponent} Component to test
|
|
@@ -0,0 +1,87 @@
|
|
|
1
|
+
import * as R from "ramda";
|
|
2
|
+
|
|
3
|
+
import React from "react";
|
|
4
|
+
import { View } from "react-native";
|
|
5
|
+
|
|
6
|
+
import { Provider } from "react-redux";
|
|
7
|
+
import thunk from "redux-thunk";
|
|
8
|
+
import configureStore from "redux-mock-store";
|
|
9
|
+
|
|
10
|
+
import { render } from "@testing-library/react-native";
|
|
11
|
+
|
|
12
|
+
/**
|
|
13
|
+
* Function to be used in tandem with enzyme findWhere(node => node.prop('testID') === 'yourID')
|
|
14
|
+
* Example:
|
|
15
|
+
* wrapper.findWhere(testIDEquals('yourID'))
|
|
16
|
+
* @param {*} testId
|
|
17
|
+
* @return boolean
|
|
18
|
+
*/
|
|
19
|
+
export const testIDEquals = (testId: any): boolean =>
|
|
20
|
+
R.compose(R.equals(testId), R.invoker(1, "prop")("testID"));
|
|
21
|
+
|
|
22
|
+
export const getByTestId = (wrapper, testId) => {
|
|
23
|
+
try {
|
|
24
|
+
return wrapper?.queryAllByTestId(testId)?.[0] ?? undefined;
|
|
25
|
+
} catch (e) {
|
|
26
|
+
// eslint-disable-next-line no-console
|
|
27
|
+
console.log("getByTestId E:", e);
|
|
28
|
+
|
|
29
|
+
return undefined;
|
|
30
|
+
}
|
|
31
|
+
};
|
|
32
|
+
|
|
33
|
+
/** Helper function for react-test-renderer, accepts type and renderer
|
|
34
|
+
* * @param {*} type React Component type instance or name of component
|
|
35
|
+
* * @param {*} renderer renderer instance
|
|
36
|
+
*/
|
|
37
|
+
export const findByType = (type, renderer) => {
|
|
38
|
+
try {
|
|
39
|
+
renderer.root ? renderer.root.findByType(type) : renderer.findByType(type);
|
|
40
|
+
} catch {
|
|
41
|
+
return undefined;
|
|
42
|
+
}
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export function createComponentMock(mockName, hasChildren = false) {
|
|
46
|
+
return hasChildren
|
|
47
|
+
? jest.fn(({ children }) => (
|
|
48
|
+
<View comp={mockName} testID={mockName}>
|
|
49
|
+
{typeof children === "function" ? children() : children}
|
|
50
|
+
</View>
|
|
51
|
+
))
|
|
52
|
+
: jest.fn(() => <View comp={mockName} testID={mockName} />);
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
const initialState = {
|
|
56
|
+
styles: {},
|
|
57
|
+
remoteConfigurations: {
|
|
58
|
+
styles: {},
|
|
59
|
+
},
|
|
60
|
+
components: {
|
|
61
|
+
River: createComponentMock("river"),
|
|
62
|
+
AppContainer: createComponentMock("appContainer", true),
|
|
63
|
+
DisplayError: createComponentMock("displayError"),
|
|
64
|
+
},
|
|
65
|
+
rivers: [{ id: "A1234", home: true }],
|
|
66
|
+
appSettings: {},
|
|
67
|
+
plugins: [],
|
|
68
|
+
appState: { appReady: true },
|
|
69
|
+
};
|
|
70
|
+
|
|
71
|
+
export const WrappedWithProviders = ({ children, store = {} }: any) => {
|
|
72
|
+
const _store = configureStore([thunk])({ ...initialState, store });
|
|
73
|
+
|
|
74
|
+
return <Provider store={_store}>{children}</Provider>;
|
|
75
|
+
};
|
|
76
|
+
|
|
77
|
+
/**
|
|
78
|
+
* Helper test function - replacement of render() - with providers
|
|
79
|
+
* @param component component to render in test
|
|
80
|
+
* @param store optional store to pass to the provider
|
|
81
|
+
* @returns
|
|
82
|
+
*/
|
|
83
|
+
export const renderWithProviders = (component, store?: unknown) => {
|
|
84
|
+
return render(
|
|
85
|
+
<WrappedWithProviders store={store}>{component}</WrappedWithProviders>
|
|
86
|
+
);
|
|
87
|
+
};
|
package/theme/index.ts
CHANGED
|
@@ -48,11 +48,12 @@ export function useThemeSelector(): [string, (string) => void] {
|
|
|
48
48
|
* @return {Theme}
|
|
49
49
|
*/
|
|
50
50
|
export function createTheme(options) {
|
|
51
|
-
const { id, props } = options;
|
|
51
|
+
const { id, props, decorateFeed } = options;
|
|
52
52
|
|
|
53
53
|
return {
|
|
54
54
|
isTheme: true,
|
|
55
55
|
id,
|
|
56
56
|
getProps: populateConfigurationValues(props),
|
|
57
|
+
decorateFeed,
|
|
57
58
|
};
|
|
58
59
|
}
|
package/testUtils/index.ts
DELETED
|
@@ -1,34 +0,0 @@
|
|
|
1
|
-
import * as R from "ramda";
|
|
2
|
-
|
|
3
|
-
/**
|
|
4
|
-
* Function to be used in tandem with enzyme findWhere(node => node.prop('testID') === 'yourID')
|
|
5
|
-
* Example:
|
|
6
|
-
* wrapper.findWhere(testIDEquals('yourID'))
|
|
7
|
-
* @param {*} testId
|
|
8
|
-
* @return boolean
|
|
9
|
-
*/
|
|
10
|
-
export const testIDEquals = (testId: any): boolean =>
|
|
11
|
-
R.compose(R.equals(testId), R.invoker(1, "prop")("testID"));
|
|
12
|
-
|
|
13
|
-
export const getByTestId = (wrapper, testId) => {
|
|
14
|
-
try {
|
|
15
|
-
return wrapper?.queryAllByTestId(testId)?.[0] ?? undefined;
|
|
16
|
-
} catch (e) {
|
|
17
|
-
// eslint-disable-next-line no-console
|
|
18
|
-
console.log("getByTestId E:", e);
|
|
19
|
-
|
|
20
|
-
return undefined;
|
|
21
|
-
}
|
|
22
|
-
};
|
|
23
|
-
|
|
24
|
-
/** Helper function for react-test-renderer, accepts type and renderer
|
|
25
|
-
* * @param {*} type React Component type instance or name of component
|
|
26
|
-
* * @param {*} renderer renderer instance
|
|
27
|
-
*/
|
|
28
|
-
export const findByType = (type, renderer) => {
|
|
29
|
-
try {
|
|
30
|
-
renderer.root ? renderer.root.findByType(type) : renderer.findByType(type);
|
|
31
|
-
} catch {
|
|
32
|
-
return undefined;
|
|
33
|
-
}
|
|
34
|
-
};
|