@applicaster/quick-brick-core 16.0.0-rc.124 → 16.0.0-rc.125
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/App/DeepLinking/URLSchemeHandler/SchemeHandlerHooks/usePresentSchemeHandler.ts +3 -0
- package/App/index.tsx +2 -0
- package/App/layoutPreview/LayoutPreviewReminder.tsx +136 -0
- package/App/layoutPreview/__tests__/persistence.test.ts +63 -0
- package/App/layoutPreview/persistence.ts +228 -0
- package/App/remoteContextReloader/getRemoteContextData/getNativeRemoteContextData.ts +64 -19
- package/package.json +8 -8
|
@@ -28,6 +28,7 @@ import {
|
|
|
28
28
|
|
|
29
29
|
import { log_error, log_info } from "../../logger";
|
|
30
30
|
import { useRivers } from "@applicaster/zapp-react-native-utils/reactHooks/state";
|
|
31
|
+
import { rememberLayoutPreview } from "../../../layoutPreview/persistence";
|
|
31
32
|
|
|
32
33
|
const getFeed = (query) => ({
|
|
33
34
|
content: { src: query.data_source },
|
|
@@ -155,6 +156,8 @@ export function usePresentSchemeHandler({
|
|
|
155
156
|
rivers_configuration_id
|
|
156
157
|
);
|
|
157
158
|
|
|
159
|
+
await rememberLayoutPreview(rivers_configuration_id);
|
|
160
|
+
|
|
158
161
|
onFinish((done) => {
|
|
159
162
|
actions?.setAppReady();
|
|
160
163
|
navigator.replace(homeRiver);
|
package/App/index.tsx
CHANGED
|
@@ -32,6 +32,7 @@ import { withScreenDataContextProvider } from "@applicaster/zapp-react-native-ui
|
|
|
32
32
|
import { LogicAggregatorContainer } from "./LogicAggregatorContainer";
|
|
33
33
|
import { LoadingOverlay } from "./LoadingOverlay";
|
|
34
34
|
import { NotificationToastRenderer } from "./NotificationToastRenderer";
|
|
35
|
+
import { LayoutPreviewReminder } from "./layoutPreview/LayoutPreviewReminder";
|
|
35
36
|
import { ZappAppWrapper } from "./components/ZappAppWrapper";
|
|
36
37
|
import { registerCoreUIActions } from "./uiActions";
|
|
37
38
|
|
|
@@ -82,6 +83,7 @@ const App = ({
|
|
|
82
83
|
<ModalProvider />
|
|
83
84
|
</URLSchemeListener>
|
|
84
85
|
<LoadingOverlay />
|
|
86
|
+
<LayoutPreviewReminder />
|
|
85
87
|
</ErrorBoundary>
|
|
86
88
|
<LogicAggregatorContainer
|
|
87
89
|
components={React.useMemo(
|
|
@@ -0,0 +1,136 @@
|
|
|
1
|
+
import * as React from "react";
|
|
2
|
+
import { AppState, Platform } from "react-native";
|
|
3
|
+
|
|
4
|
+
import { useAppSelector } from "@applicaster/zapp-react-native-redux/hooks";
|
|
5
|
+
import { selectAppReady } from "@applicaster/zapp-react-native-redux";
|
|
6
|
+
import {
|
|
7
|
+
showAlertDialog,
|
|
8
|
+
showConfirmationDialog,
|
|
9
|
+
} from "@applicaster/zapp-react-native-utils/alertUtils";
|
|
10
|
+
import { confirmationDialogStore } from "@applicaster/zapp-react-native-ui-components/Contexts/ConfirmDialogState";
|
|
11
|
+
import { isWeb } from "@applicaster/zapp-react-native-utils/reactUtils";
|
|
12
|
+
import * as QuickBrickManager from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
13
|
+
import { QUICK_BRICK_EVENTS } from "@applicaster/zapp-react-native-bridge/QuickBrick";
|
|
14
|
+
import { notificationToastManager } from "../NotificationToastRenderer/NotificationToastManager";
|
|
15
|
+
import {
|
|
16
|
+
PREVIEW_BACK,
|
|
17
|
+
PREVIEW_CONTINUE,
|
|
18
|
+
PREVIEW_MESSAGE,
|
|
19
|
+
PREVIEW_TITLE,
|
|
20
|
+
UNAVAILABLE_MESSAGE,
|
|
21
|
+
UNAVAILABLE_TITLE,
|
|
22
|
+
acknowledgeLayoutPreview,
|
|
23
|
+
consumePreviewUnavailable,
|
|
24
|
+
leavePreviewForDefault,
|
|
25
|
+
readReminderState,
|
|
26
|
+
shouldShowReminder,
|
|
27
|
+
} from "./persistence";
|
|
28
|
+
|
|
29
|
+
let promptOpen = false;
|
|
30
|
+
let checkingPrompt = false;
|
|
31
|
+
|
|
32
|
+
function showUnavailableNotice(): void {
|
|
33
|
+
if (Platform.isTV && !isWeb()) {
|
|
34
|
+
showAlertDialog({
|
|
35
|
+
title: UNAVAILABLE_TITLE,
|
|
36
|
+
message: UNAVAILABLE_MESSAGE,
|
|
37
|
+
okButtonText: "OK",
|
|
38
|
+
});
|
|
39
|
+
|
|
40
|
+
return;
|
|
41
|
+
}
|
|
42
|
+
|
|
43
|
+
notificationToastManager.showToast({
|
|
44
|
+
message: UNAVAILABLE_TITLE,
|
|
45
|
+
extraMessage: UNAVAILABLE_MESSAGE,
|
|
46
|
+
});
|
|
47
|
+
}
|
|
48
|
+
|
|
49
|
+
function showPreviewDialog(): void {
|
|
50
|
+
const onContinue = () => {
|
|
51
|
+
promptOpen = false;
|
|
52
|
+
void acknowledgeLayoutPreview();
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
const onBack = () => {
|
|
56
|
+
promptOpen = false;
|
|
57
|
+
|
|
58
|
+
void leavePreviewForDefault().finally(() => {
|
|
59
|
+
QuickBrickManager.sendQuickBrickEvent(
|
|
60
|
+
QUICK_BRICK_EVENTS.FORCE_APP_RELOAD
|
|
61
|
+
);
|
|
62
|
+
});
|
|
63
|
+
};
|
|
64
|
+
|
|
65
|
+
showConfirmationDialog({
|
|
66
|
+
title: PREVIEW_TITLE,
|
|
67
|
+
message: PREVIEW_MESSAGE,
|
|
68
|
+
cancelButtonText: PREVIEW_CONTINUE,
|
|
69
|
+
okButtonText: PREVIEW_BACK,
|
|
70
|
+
cancelCompletion: onContinue,
|
|
71
|
+
confirmCompletion: onBack,
|
|
72
|
+
});
|
|
73
|
+
|
|
74
|
+
if (!isWeb()) return;
|
|
75
|
+
|
|
76
|
+
// Remote Back hides the web dialog without calling either completion.
|
|
77
|
+
const unsubscribe = confirmationDialogStore.subscribe((state) => {
|
|
78
|
+
if (state.isDialogVisible) return;
|
|
79
|
+
|
|
80
|
+
promptOpen = false;
|
|
81
|
+
unsubscribe();
|
|
82
|
+
});
|
|
83
|
+
}
|
|
84
|
+
|
|
85
|
+
async function presentLayoutPreviewPrompt(): Promise<void> {
|
|
86
|
+
if (promptOpen || checkingPrompt) return;
|
|
87
|
+
|
|
88
|
+
checkingPrompt = true;
|
|
89
|
+
|
|
90
|
+
try {
|
|
91
|
+
if (consumePreviewUnavailable()) {
|
|
92
|
+
showUnavailableNotice();
|
|
93
|
+
|
|
94
|
+
return;
|
|
95
|
+
}
|
|
96
|
+
|
|
97
|
+
const { hasOverride, ackAt } = await readReminderState();
|
|
98
|
+
|
|
99
|
+
if (!shouldShowReminder(hasOverride, ackAt, Date.now())) return;
|
|
100
|
+
|
|
101
|
+
promptOpen = true;
|
|
102
|
+
showPreviewDialog();
|
|
103
|
+
} finally {
|
|
104
|
+
checkingPrompt = false;
|
|
105
|
+
}
|
|
106
|
+
}
|
|
107
|
+
|
|
108
|
+
export function LayoutPreviewReminder(): null {
|
|
109
|
+
const appReady = useAppSelector(selectAppReady);
|
|
110
|
+
const checkedForeground = React.useRef(false);
|
|
111
|
+
|
|
112
|
+
React.useEffect(() => {
|
|
113
|
+
if (!appReady) return;
|
|
114
|
+
|
|
115
|
+
const showIfNeeded = () => {
|
|
116
|
+
if (checkedForeground.current) return;
|
|
117
|
+
|
|
118
|
+
checkedForeground.current = true;
|
|
119
|
+
void presentLayoutPreviewPrompt();
|
|
120
|
+
};
|
|
121
|
+
|
|
122
|
+
showIfNeeded();
|
|
123
|
+
|
|
124
|
+
const subscription = AppState.addEventListener("change", (state) => {
|
|
125
|
+
if (state === "background") {
|
|
126
|
+
checkedForeground.current = false;
|
|
127
|
+
}
|
|
128
|
+
|
|
129
|
+
if (state === "active") showIfNeeded();
|
|
130
|
+
});
|
|
131
|
+
|
|
132
|
+
return () => subscription.remove();
|
|
133
|
+
}, [appReady]);
|
|
134
|
+
|
|
135
|
+
return null;
|
|
136
|
+
}
|
|
@@ -0,0 +1,63 @@
|
|
|
1
|
+
import { NativeModules } from "react-native";
|
|
2
|
+
|
|
3
|
+
import {
|
|
4
|
+
SUPPRESSION_MS,
|
|
5
|
+
applyPersistedLayoutPreview,
|
|
6
|
+
leavePreviewForDefault,
|
|
7
|
+
shouldKeepOverride,
|
|
8
|
+
shouldShowReminder,
|
|
9
|
+
} from "../persistence";
|
|
10
|
+
|
|
11
|
+
const mockStore: Record<string, string> = {};
|
|
12
|
+
|
|
13
|
+
jest.mock(
|
|
14
|
+
"@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage",
|
|
15
|
+
() => ({
|
|
16
|
+
localStorage: {
|
|
17
|
+
getItem: async (key: string) => mockStore[key] ?? null,
|
|
18
|
+
setItem: async (key: string, value: string) => {
|
|
19
|
+
mockStore[key] = value;
|
|
20
|
+
},
|
|
21
|
+
removeItem: async (key: string) => {
|
|
22
|
+
delete mockStore[key];
|
|
23
|
+
},
|
|
24
|
+
},
|
|
25
|
+
})
|
|
26
|
+
);
|
|
27
|
+
|
|
28
|
+
describe("layout preview persistence", () => {
|
|
29
|
+
it("keeps a non-default layout id and drops the version default", () => {
|
|
30
|
+
expect(shouldKeepOverride("preview", "default")).toBe(true);
|
|
31
|
+
expect(shouldKeepOverride("default", "default")).toBe(false);
|
|
32
|
+
expect(shouldKeepOverride(null, "default")).toBe(false);
|
|
33
|
+
expect(shouldKeepOverride("preview", null)).toBe(true);
|
|
34
|
+
});
|
|
35
|
+
|
|
36
|
+
it("suppresses the reminder for two hours after acknowledgment", () => {
|
|
37
|
+
const now = 1_000_000;
|
|
38
|
+
|
|
39
|
+
expect(shouldShowReminder(true, null, now)).toBe(true);
|
|
40
|
+
expect(shouldShowReminder(false, null, now)).toBe(false);
|
|
41
|
+
|
|
42
|
+
expect(shouldShowReminder(true, now - SUPPRESSION_MS + 1, now)).toBe(false);
|
|
43
|
+
|
|
44
|
+
expect(shouldShowReminder(true, now - SUPPRESSION_MS, now)).toBe(true);
|
|
45
|
+
});
|
|
46
|
+
|
|
47
|
+
it("keeps the version default for exactly one load after Back to Default", async () => {
|
|
48
|
+
const switchLayout = jest.fn(async () => true);
|
|
49
|
+
|
|
50
|
+
NativeModules.AppLoaderBridge = {
|
|
51
|
+
getDefaultLayoutId: async () => "default",
|
|
52
|
+
switchLayout,
|
|
53
|
+
};
|
|
54
|
+
|
|
55
|
+
mockStore.layout_preview_id = "preview";
|
|
56
|
+
|
|
57
|
+
await leavePreviewForDefault();
|
|
58
|
+
|
|
59
|
+
expect(await applyPersistedLayoutPreview()).toBe("default");
|
|
60
|
+
expect(switchLayout).toHaveBeenLastCalledWith("default", null);
|
|
61
|
+
expect(await applyPersistedLayoutPreview()).toBe("absent");
|
|
62
|
+
});
|
|
63
|
+
});
|
|
@@ -0,0 +1,228 @@
|
|
|
1
|
+
import { NativeModules } from "react-native";
|
|
2
|
+
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
3
|
+
|
|
4
|
+
// Separate from active_layout_id, which the switchLayout action owns.
|
|
5
|
+
export const LAYOUT_PREVIEW_ID_KEY = "layout_preview_id";
|
|
6
|
+
|
|
7
|
+
export const LAYOUT_PREVIEW_ACK_KEY = "layout_preview_ack_at";
|
|
8
|
+
|
|
9
|
+
// Set by Back to Default; makes the reload that follows skip layout selectors once.
|
|
10
|
+
export const LAYOUT_PREVIEW_RESTORE_DEFAULT_KEY =
|
|
11
|
+
"layout_preview_restore_default";
|
|
12
|
+
|
|
13
|
+
export const SUPPRESSION_MS = 2 * 60 * 60 * 1000;
|
|
14
|
+
|
|
15
|
+
export const PREVIEW_TITLE = "You're Viewing a Preview Layout";
|
|
16
|
+
|
|
17
|
+
export const PREVIEW_MESSAGE =
|
|
18
|
+
"This device is currently displaying a preview layout instead of the version default layout.";
|
|
19
|
+
|
|
20
|
+
export const PREVIEW_CONTINUE = "Continue Preview";
|
|
21
|
+
|
|
22
|
+
export const PREVIEW_BACK = "Back to Default Layout";
|
|
23
|
+
|
|
24
|
+
export const UNAVAILABLE_TITLE = "Preview layout unavailable";
|
|
25
|
+
|
|
26
|
+
export const UNAVAILABLE_MESSAGE =
|
|
27
|
+
"This device was returned to the version default layout.";
|
|
28
|
+
|
|
29
|
+
export type PreviewApplyResult = "applied" | "absent" | "invalid" | "default";
|
|
30
|
+
|
|
31
|
+
let unavailableNotice = false;
|
|
32
|
+
|
|
33
|
+
export function shouldKeepOverride(
|
|
34
|
+
layoutId: string | null | undefined,
|
|
35
|
+
defaultId: string | null | undefined
|
|
36
|
+
): boolean {
|
|
37
|
+
return Boolean(layoutId) && layoutId !== defaultId;
|
|
38
|
+
}
|
|
39
|
+
|
|
40
|
+
export function shouldShowReminder(
|
|
41
|
+
hasOverride: boolean,
|
|
42
|
+
ackAt: number | null,
|
|
43
|
+
now: number
|
|
44
|
+
): boolean {
|
|
45
|
+
if (!hasOverride) return false;
|
|
46
|
+
if (ackAt == null || Number.isNaN(ackAt)) return true;
|
|
47
|
+
|
|
48
|
+
return now - ackAt >= SUPPRESSION_MS;
|
|
49
|
+
}
|
|
50
|
+
|
|
51
|
+
export function markPreviewUnavailable(): void {
|
|
52
|
+
unavailableNotice = true;
|
|
53
|
+
}
|
|
54
|
+
|
|
55
|
+
export function consumePreviewUnavailable(): boolean {
|
|
56
|
+
const pending = unavailableNotice;
|
|
57
|
+
unavailableNotice = false;
|
|
58
|
+
|
|
59
|
+
return pending;
|
|
60
|
+
}
|
|
61
|
+
|
|
62
|
+
async function removeKey(key: string): Promise<void> {
|
|
63
|
+
try {
|
|
64
|
+
await localStorage.removeItem(key);
|
|
65
|
+
} catch {
|
|
66
|
+
// key was already absent
|
|
67
|
+
}
|
|
68
|
+
}
|
|
69
|
+
|
|
70
|
+
async function readOverrideId(): Promise<string | null> {
|
|
71
|
+
try {
|
|
72
|
+
const id = await localStorage.getItem(LAYOUT_PREVIEW_ID_KEY);
|
|
73
|
+
|
|
74
|
+
return id || null;
|
|
75
|
+
} catch {
|
|
76
|
+
return null;
|
|
77
|
+
}
|
|
78
|
+
}
|
|
79
|
+
|
|
80
|
+
export async function getDefaultLayoutId(): Promise<string | null> {
|
|
81
|
+
try {
|
|
82
|
+
const id = await NativeModules.AppLoaderBridge?.getDefaultLayoutId?.();
|
|
83
|
+
|
|
84
|
+
return id || null;
|
|
85
|
+
} catch {
|
|
86
|
+
return null;
|
|
87
|
+
}
|
|
88
|
+
}
|
|
89
|
+
|
|
90
|
+
export async function clearLayoutPreview(): Promise<void> {
|
|
91
|
+
await removeKey(LAYOUT_PREVIEW_ID_KEY);
|
|
92
|
+
await removeKey(LAYOUT_PREVIEW_ACK_KEY);
|
|
93
|
+
}
|
|
94
|
+
|
|
95
|
+
/**
|
|
96
|
+
* Drops the pin and points the loader at the version default.
|
|
97
|
+
* Used by Back to Default and by a preview link that targets the default layout.
|
|
98
|
+
*/
|
|
99
|
+
export async function returnToDefaultLayout(): Promise<void> {
|
|
100
|
+
const defaultId = await getDefaultLayoutId();
|
|
101
|
+
|
|
102
|
+
await clearLayoutPreview();
|
|
103
|
+
|
|
104
|
+
if (!defaultId || !NativeModules.AppLoaderBridge?.switchLayout) return;
|
|
105
|
+
|
|
106
|
+
try {
|
|
107
|
+
await NativeModules.AppLoaderBridge.switchLayout(defaultId, null);
|
|
108
|
+
} catch {
|
|
109
|
+
// the pin is already gone; the next load uses the version default
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
113
|
+
/** Back to Default from the reminder. The app reloads next, and that load must stay on the version default. */
|
|
114
|
+
export async function leavePreviewForDefault(): Promise<void> {
|
|
115
|
+
await localStorage.setItem(LAYOUT_PREVIEW_RESTORE_DEFAULT_KEY, "true");
|
|
116
|
+
await returnToDefaultLayout();
|
|
117
|
+
}
|
|
118
|
+
|
|
119
|
+
/** Same as returnToDefaultLayout, plus a one-shot unavailable notice. */
|
|
120
|
+
export async function restoreInvalidPreview(): Promise<void> {
|
|
121
|
+
markPreviewUnavailable();
|
|
122
|
+
await returnToDefaultLayout();
|
|
123
|
+
}
|
|
124
|
+
|
|
125
|
+
/**
|
|
126
|
+
* Pins a layout id, or clears the pin when the id is the version default.
|
|
127
|
+
* A storage failure still leaves the in-session preview in place.
|
|
128
|
+
*/
|
|
129
|
+
export async function rememberLayoutPreview(layoutId: string): Promise<void> {
|
|
130
|
+
if (!layoutId) return;
|
|
131
|
+
|
|
132
|
+
try {
|
|
133
|
+
const defaultId = await getDefaultLayoutId();
|
|
134
|
+
|
|
135
|
+
if (!shouldKeepOverride(layoutId, defaultId)) {
|
|
136
|
+
await returnToDefaultLayout();
|
|
137
|
+
|
|
138
|
+
return;
|
|
139
|
+
}
|
|
140
|
+
|
|
141
|
+
const current = await readOverrideId();
|
|
142
|
+
|
|
143
|
+
if (current !== layoutId) {
|
|
144
|
+
await removeKey(LAYOUT_PREVIEW_ACK_KEY);
|
|
145
|
+
}
|
|
146
|
+
|
|
147
|
+
await localStorage.setItem(LAYOUT_PREVIEW_ID_KEY, layoutId);
|
|
148
|
+
} catch {
|
|
149
|
+
// this session already loaded the layout; the pin is best-effort
|
|
150
|
+
}
|
|
151
|
+
}
|
|
152
|
+
|
|
153
|
+
export async function acknowledgeLayoutPreview(): Promise<void> {
|
|
154
|
+
await localStorage.setItem(LAYOUT_PREVIEW_ACK_KEY, String(Date.now()));
|
|
155
|
+
}
|
|
156
|
+
|
|
157
|
+
export async function readReminderState(): Promise<{
|
|
158
|
+
hasOverride: boolean;
|
|
159
|
+
ackAt: number | null;
|
|
160
|
+
}> {
|
|
161
|
+
const layoutId = await readOverrideId();
|
|
162
|
+
const defaultId = await getDefaultLayoutId();
|
|
163
|
+
let ackRaw: string | null = null;
|
|
164
|
+
|
|
165
|
+
try {
|
|
166
|
+
ackRaw = await localStorage.getItem(LAYOUT_PREVIEW_ACK_KEY);
|
|
167
|
+
} catch {
|
|
168
|
+
ackRaw = null;
|
|
169
|
+
}
|
|
170
|
+
|
|
171
|
+
const ackAt = ackRaw == null || ackRaw === "" ? null : Number(ackRaw);
|
|
172
|
+
|
|
173
|
+
return {
|
|
174
|
+
hasOverride: shouldKeepOverride(layoutId, defaultId),
|
|
175
|
+
ackAt: ackAt != null && !Number.isNaN(ackAt) ? ackAt : null,
|
|
176
|
+
};
|
|
177
|
+
}
|
|
178
|
+
|
|
179
|
+
/**
|
|
180
|
+
* Re-applies a stored preview before layout-manager selection.
|
|
181
|
+
* "invalid" means the pin was cleared and the version default was restored,
|
|
182
|
+
* so the caller must not run selectLayout over the top of that fallback.
|
|
183
|
+
*/
|
|
184
|
+
export async function applyPersistedLayoutPreview(): Promise<PreviewApplyResult> {
|
|
185
|
+
let restoreDefault = null;
|
|
186
|
+
|
|
187
|
+
try {
|
|
188
|
+
restoreDefault = await localStorage.getItem(
|
|
189
|
+
LAYOUT_PREVIEW_RESTORE_DEFAULT_KEY
|
|
190
|
+
);
|
|
191
|
+
} catch {
|
|
192
|
+
restoreDefault = null;
|
|
193
|
+
}
|
|
194
|
+
|
|
195
|
+
if (restoreDefault) {
|
|
196
|
+
await removeKey(LAYOUT_PREVIEW_RESTORE_DEFAULT_KEY);
|
|
197
|
+
|
|
198
|
+
const defaultId = await getDefaultLayoutId();
|
|
199
|
+
|
|
200
|
+
if (defaultId) {
|
|
201
|
+
await NativeModules.AppLoaderBridge.switchLayout(defaultId, null);
|
|
202
|
+
}
|
|
203
|
+
|
|
204
|
+
return "default";
|
|
205
|
+
}
|
|
206
|
+
|
|
207
|
+
const layoutId = await readOverrideId();
|
|
208
|
+
|
|
209
|
+
if (!layoutId) return "absent";
|
|
210
|
+
|
|
211
|
+
const defaultId = await getDefaultLayoutId();
|
|
212
|
+
|
|
213
|
+
if (!shouldKeepOverride(layoutId, defaultId)) {
|
|
214
|
+
await clearLayoutPreview();
|
|
215
|
+
|
|
216
|
+
return "absent";
|
|
217
|
+
}
|
|
218
|
+
|
|
219
|
+
try {
|
|
220
|
+
await NativeModules.AppLoaderBridge.switchLayout(layoutId, null);
|
|
221
|
+
|
|
222
|
+
return "applied";
|
|
223
|
+
} catch {
|
|
224
|
+
await restoreInvalidPreview();
|
|
225
|
+
|
|
226
|
+
return "invalid";
|
|
227
|
+
}
|
|
228
|
+
}
|
|
@@ -14,6 +14,56 @@ import { ACTIVE_LAYOUT_ID_STORAGE_KEY } from "../consts";
|
|
|
14
14
|
const { AppLoaderBridge } = NativeModules;
|
|
15
15
|
import { localStorage } from "@applicaster/zapp-react-native-bridge/ZappStorage/LocalStorage";
|
|
16
16
|
import { useErrorStore } from "../../ErrorBoundary/store";
|
|
17
|
+
import {
|
|
18
|
+
PreviewApplyResult,
|
|
19
|
+
applyPersistedLayoutPreview,
|
|
20
|
+
restoreInvalidPreview,
|
|
21
|
+
} from "../../layoutPreview/persistence";
|
|
22
|
+
|
|
23
|
+
/**
|
|
24
|
+
* Points the loader at the layout to fetch. A persisted device preview wins
|
|
25
|
+
* over the layout-manager plugin (experiment, geo, language) and over active_layout_id.
|
|
26
|
+
*/
|
|
27
|
+
async function selectActiveLayout(
|
|
28
|
+
runtimeConfigurationUrls,
|
|
29
|
+
plugins,
|
|
30
|
+
isTabletDevice: boolean
|
|
31
|
+
): Promise<PreviewApplyResult> {
|
|
32
|
+
let preview: PreviewApplyResult = "absent";
|
|
33
|
+
|
|
34
|
+
try {
|
|
35
|
+
preview = await applyPersistedLayoutPreview();
|
|
36
|
+
|
|
37
|
+
if (preview !== "absent") return preview;
|
|
38
|
+
|
|
39
|
+
const layoutManagerPlugin = R.find(
|
|
40
|
+
R.pathSatisfies(R.is(Function), ["module", "selectLayout"])
|
|
41
|
+
)(plugins);
|
|
42
|
+
|
|
43
|
+
if (layoutManagerPlugin) {
|
|
44
|
+
await layoutManagerPlugin.module?.selectLayout({
|
|
45
|
+
runtimeConfigurationUrls,
|
|
46
|
+
plugin: layoutManagerPlugin,
|
|
47
|
+
isTablet: isTabletDevice,
|
|
48
|
+
});
|
|
49
|
+
|
|
50
|
+
return preview;
|
|
51
|
+
}
|
|
52
|
+
|
|
53
|
+
const layoutId = await localStorage.getItem(ACTIVE_LAYOUT_ID_STORAGE_KEY);
|
|
54
|
+
|
|
55
|
+
if (layoutId) {
|
|
56
|
+
await AppLoaderBridge?.switchLayout(layoutId, null);
|
|
57
|
+
}
|
|
58
|
+
} catch (error) {
|
|
59
|
+
logger.error({
|
|
60
|
+
message: "Error selecting layout using layout manager plugin",
|
|
61
|
+
data: error,
|
|
62
|
+
});
|
|
63
|
+
}
|
|
64
|
+
|
|
65
|
+
return preview;
|
|
66
|
+
}
|
|
17
67
|
|
|
18
68
|
export async function getNativeRemoteContextData(
|
|
19
69
|
dispatch,
|
|
@@ -35,32 +85,27 @@ export async function getNativeRemoteContextData(
|
|
|
35
85
|
|
|
36
86
|
loadAppContextData(dispatch, R.mergeAll([...generalConfiguration]));
|
|
37
87
|
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
88
|
+
const preview = await selectActiveLayout(
|
|
89
|
+
runtimeConfigurationUrls,
|
|
90
|
+
plugins,
|
|
91
|
+
isTabletDevice
|
|
92
|
+
);
|
|
42
93
|
|
|
43
|
-
|
|
44
|
-
await layoutManagerPlugin.module?.selectLayout({
|
|
45
|
-
runtimeConfigurationUrls,
|
|
46
|
-
plugin: layoutManagerPlugin,
|
|
47
|
-
isTablet: isTabletDevice,
|
|
48
|
-
});
|
|
49
|
-
} else {
|
|
50
|
-
const layoutId = await localStorage.getItem(ACTIVE_LAYOUT_ID_STORAGE_KEY);
|
|
94
|
+
let layout;
|
|
51
95
|
|
|
52
|
-
|
|
53
|
-
|
|
54
|
-
}
|
|
55
|
-
}
|
|
96
|
+
try {
|
|
97
|
+
layout = await getPromiseForType("layout");
|
|
56
98
|
} catch (error) {
|
|
99
|
+
if (preview !== "applied") throw error;
|
|
100
|
+
|
|
57
101
|
logger.error({
|
|
58
|
-
message: "
|
|
102
|
+
message: "Persisted preview layout failed to load",
|
|
59
103
|
data: error,
|
|
60
104
|
});
|
|
61
|
-
}
|
|
62
105
|
|
|
63
|
-
|
|
106
|
+
await restoreInvalidPreview();
|
|
107
|
+
layout = await getPromiseForType("layout");
|
|
108
|
+
}
|
|
64
109
|
|
|
65
110
|
const layoutLoadingErrorMessage = "Failed to load layout data";
|
|
66
111
|
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/quick-brick-core",
|
|
3
|
-
"version": "16.0.0-rc.
|
|
3
|
+
"version": "16.0.0-rc.125",
|
|
4
4
|
"description": "Core package for Applicaster's Quick Brick App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"types": "index.d.ts",
|
|
@@ -28,13 +28,13 @@
|
|
|
28
28
|
},
|
|
29
29
|
"homepage": "https://github.com/applicaster/quickbrick#readme",
|
|
30
30
|
"dependencies": {
|
|
31
|
-
"@applicaster/applicaster-types": "16.0.0-rc.
|
|
32
|
-
"@applicaster/quick-brick-core-plugins": "16.0.0-rc.
|
|
33
|
-
"@applicaster/zapp-pipes-v2-client": "16.0.0-rc.
|
|
34
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.
|
|
35
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-rc.
|
|
36
|
-
"@applicaster/zapp-react-native-ui-components": "16.0.0-rc.
|
|
37
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-rc.
|
|
31
|
+
"@applicaster/applicaster-types": "16.0.0-rc.125",
|
|
32
|
+
"@applicaster/quick-brick-core-plugins": "16.0.0-rc.125",
|
|
33
|
+
"@applicaster/zapp-pipes-v2-client": "16.0.0-rc.125",
|
|
34
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.125",
|
|
35
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.125",
|
|
36
|
+
"@applicaster/zapp-react-native-ui-components": "16.0.0-rc.125",
|
|
37
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.125",
|
|
38
38
|
"atob": "^2.1.2",
|
|
39
39
|
"axios": "^0.28.0",
|
|
40
40
|
"btoa": "^1.2.1",
|