@applicaster/zapp-react-dom-app 16.0.0-rc.127 → 16.0.0-rc.128
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.
|
@@ -0,0 +1,221 @@
|
|
|
1
|
+
import axios from "axios";
|
|
2
|
+
|
|
3
|
+
jest.mock("axios", () => ({ get: jest.fn() }));
|
|
4
|
+
|
|
5
|
+
const mockAppData = {
|
|
6
|
+
accountsAccountId: "account",
|
|
7
|
+
bundleIdentifier: "com.app",
|
|
8
|
+
store: "samsung",
|
|
9
|
+
versionName: "1.0",
|
|
10
|
+
appFamilyId: "family",
|
|
11
|
+
// appData.json is camelized
|
|
12
|
+
riversConfigurationId: "default-layout",
|
|
13
|
+
};
|
|
14
|
+
|
|
15
|
+
jest.mock("@applicaster/zapp-react-native-redux/AppStore", () => ({
|
|
16
|
+
appStore: { get: () => mockAppData },
|
|
17
|
+
}));
|
|
18
|
+
|
|
19
|
+
const get = axios.get as jest.Mock;
|
|
20
|
+
|
|
21
|
+
const appUrl =
|
|
22
|
+
"https://assets-secure.applicaster.com/zapp/accounts/account/apps/com.app/samsung_app_store/1.0";
|
|
23
|
+
|
|
24
|
+
const familyUrl =
|
|
25
|
+
"https://assets-secure.applicaster.com/zapp/accounts/account/app_families/family";
|
|
26
|
+
|
|
27
|
+
const storage = new Map<string, string>();
|
|
28
|
+
|
|
29
|
+
(global as any).window = {
|
|
30
|
+
localStorage: {
|
|
31
|
+
getItem: (key) => (storage.has(key) ? storage.get(key) : null),
|
|
32
|
+
setItem: (key, value) => storage.set(key, String(value)),
|
|
33
|
+
},
|
|
34
|
+
};
|
|
35
|
+
|
|
36
|
+
const cacheKey = (key: string) => `loader_cache_::_${key}`;
|
|
37
|
+
|
|
38
|
+
const ok = (data: unknown, etag?: string) =>
|
|
39
|
+
Promise.resolve({
|
|
40
|
+
status: 200,
|
|
41
|
+
data: JSON.stringify(data),
|
|
42
|
+
headers: { etag },
|
|
43
|
+
});
|
|
44
|
+
|
|
45
|
+
const requestedUrls = () => get.mock.calls.map(([url]) => url);
|
|
46
|
+
|
|
47
|
+
let AppLoaderBridge;
|
|
48
|
+
|
|
49
|
+
beforeEach(() => {
|
|
50
|
+
storage.clear();
|
|
51
|
+
get.mockReset();
|
|
52
|
+
process.env.ENABLE_REMOTE_DATA_IN_DEV = "true";
|
|
53
|
+
|
|
54
|
+
jest.isolateModules(() => {
|
|
55
|
+
require("../../AppData").setAppData(mockAppData);
|
|
56
|
+
AppLoaderBridge = require("..").AppLoaderBridge;
|
|
57
|
+
});
|
|
58
|
+
});
|
|
59
|
+
|
|
60
|
+
afterAll(() => {
|
|
61
|
+
delete process.env.ENABLE_REMOTE_DATA_IN_DEV;
|
|
62
|
+
});
|
|
63
|
+
|
|
64
|
+
describe("getFile", () => {
|
|
65
|
+
it("resolves the parsed file and caches its text and etag", async () => {
|
|
66
|
+
get.mockReturnValue(ok({ id: "config" }, "e1"));
|
|
67
|
+
|
|
68
|
+
await expect(
|
|
69
|
+
AppLoaderBridge.getFile("remoteConfigurations")
|
|
70
|
+
).resolves.toEqual({ id: "config" });
|
|
71
|
+
|
|
72
|
+
expect(get).toHaveBeenCalledWith(
|
|
73
|
+
`${appUrl}/remote_configurations/remote_configurations.json`,
|
|
74
|
+
expect.objectContaining({ headers: { "If-None-Match": null } })
|
|
75
|
+
);
|
|
76
|
+
|
|
77
|
+
expect(storage.get(cacheKey("remoteConfigurations"))).toBe(
|
|
78
|
+
'{"id":"config"}'
|
|
79
|
+
);
|
|
80
|
+
|
|
81
|
+
expect(storage.get(cacheKey("remoteConfigurationsEtag"))).toBe("e1");
|
|
82
|
+
});
|
|
83
|
+
|
|
84
|
+
it("resolves the cached file when the server answers 304", async () => {
|
|
85
|
+
storage.set(cacheKey("pipesEndpoints"), '{"id":"cached"}');
|
|
86
|
+
storage.set(cacheKey("pipesEndpointsEtag"), "e1");
|
|
87
|
+
get.mockResolvedValue({ status: 304, data: "", headers: {} });
|
|
88
|
+
|
|
89
|
+
await expect(AppLoaderBridge.getFile("pipesEndpoints")).resolves.toEqual({
|
|
90
|
+
id: "cached",
|
|
91
|
+
});
|
|
92
|
+
|
|
93
|
+
expect(get).toHaveBeenCalledWith(
|
|
94
|
+
`${familyUrl}/data_source_providers/endpoints.json`,
|
|
95
|
+
expect.objectContaining({ headers: { "If-None-Match": "e1" } })
|
|
96
|
+
);
|
|
97
|
+
});
|
|
98
|
+
|
|
99
|
+
it("rejects with the configuration error when the body is not JSON", async () => {
|
|
100
|
+
get.mockResolvedValue({ status: 200, data: "<html>", headers: {} });
|
|
101
|
+
|
|
102
|
+
await expect(AppLoaderBridge.getFile("layout")).rejects.toThrow(
|
|
103
|
+
"cannot retrieve configuration data for layout"
|
|
104
|
+
);
|
|
105
|
+
});
|
|
106
|
+
});
|
|
107
|
+
|
|
108
|
+
describe("prefetchFiles", () => {
|
|
109
|
+
it("sends the general requests before returning, and getFile uses them", async () => {
|
|
110
|
+
get.mockImplementation((url) => ok({ url }));
|
|
111
|
+
|
|
112
|
+
AppLoaderBridge.prefetchFiles();
|
|
113
|
+
|
|
114
|
+
expect(requestedUrls()).toEqual([
|
|
115
|
+
`${appUrl}/remote_configurations/remote_configurations.json`,
|
|
116
|
+
`${appUrl}/plugin_configurations/plugin_configurations.json`,
|
|
117
|
+
`${familyUrl}/data_source_providers/endpoints.json`,
|
|
118
|
+
]);
|
|
119
|
+
|
|
120
|
+
await expect(
|
|
121
|
+
AppLoaderBridge.getFile("pluginConfigurations")
|
|
122
|
+
).resolves.toEqual({
|
|
123
|
+
url: `${appUrl}/plugin_configurations/plugin_configurations.json`,
|
|
124
|
+
});
|
|
125
|
+
|
|
126
|
+
expect(get).toHaveBeenCalledTimes(3);
|
|
127
|
+
});
|
|
128
|
+
|
|
129
|
+
it("also requests the layout files for a layout id stored by a previous launch", () => {
|
|
130
|
+
storage.set(cacheKey("currentLayoutId"), "stored-layout");
|
|
131
|
+
get.mockImplementation((url) => ok({ url }));
|
|
132
|
+
|
|
133
|
+
AppLoaderBridge.prefetchFiles();
|
|
134
|
+
|
|
135
|
+
expect(requestedUrls()).toEqual(
|
|
136
|
+
expect.arrayContaining([
|
|
137
|
+
`${appUrl}/layouts/stored-layout.json`,
|
|
138
|
+
`${familyUrl}/layouts/stored-layout/cell_styles.json`,
|
|
139
|
+
`${familyUrl}/layouts/stored-layout/presets_mapping.json`,
|
|
140
|
+
])
|
|
141
|
+
);
|
|
142
|
+
|
|
143
|
+
expect(get).toHaveBeenCalledTimes(6);
|
|
144
|
+
});
|
|
145
|
+
|
|
146
|
+
it("falls back to the built-in layout id and stores it as the default", async () => {
|
|
147
|
+
get.mockImplementation((url) => ok({ url }));
|
|
148
|
+
|
|
149
|
+
await expect(AppLoaderBridge.getFile("layout")).resolves.toEqual({
|
|
150
|
+
url: `${appUrl}/layouts/default-layout.json`,
|
|
151
|
+
});
|
|
152
|
+
|
|
153
|
+
expect(storage.get(cacheKey("defaultLayoutId"))).toBe("default-layout");
|
|
154
|
+
});
|
|
155
|
+
|
|
156
|
+
it("requests a file again when its URL changed after the prefetch", async () => {
|
|
157
|
+
storage.set(cacheKey("currentLayoutId"), "old-layout");
|
|
158
|
+
get.mockImplementation((url) => ok({ url }));
|
|
159
|
+
|
|
160
|
+
AppLoaderBridge.prefetchFiles();
|
|
161
|
+
await AppLoaderBridge.switchLayout("new-layout");
|
|
162
|
+
|
|
163
|
+
await expect(AppLoaderBridge.getFile("layout")).resolves.toEqual({
|
|
164
|
+
url: `${appUrl}/layouts/new-layout.json`,
|
|
165
|
+
});
|
|
166
|
+
|
|
167
|
+
expect(get).toHaveBeenCalledTimes(7);
|
|
168
|
+
});
|
|
169
|
+
|
|
170
|
+
it("uses each prefetched request once", async () => {
|
|
171
|
+
get.mockImplementation((url) => ok({ url }));
|
|
172
|
+
|
|
173
|
+
AppLoaderBridge.prefetchFiles();
|
|
174
|
+
await AppLoaderBridge.getFile("remoteConfigurations");
|
|
175
|
+
await AppLoaderBridge.getFile("remoteConfigurations");
|
|
176
|
+
|
|
177
|
+
expect(get).toHaveBeenCalledTimes(4);
|
|
178
|
+
});
|
|
179
|
+
|
|
180
|
+
it("requests a file again when its prefetch failed", async () => {
|
|
181
|
+
get.mockImplementation(() => Promise.reject(new Error("network")));
|
|
182
|
+
|
|
183
|
+
AppLoaderBridge.prefetchFiles();
|
|
184
|
+
|
|
185
|
+
get.mockImplementation((url) => ok({ url }));
|
|
186
|
+
|
|
187
|
+
await expect(
|
|
188
|
+
AppLoaderBridge.getFile("remoteConfigurations")
|
|
189
|
+
).resolves.toEqual({
|
|
190
|
+
url: `${appUrl}/remote_configurations/remote_configurations.json`,
|
|
191
|
+
});
|
|
192
|
+
|
|
193
|
+
expect(requestedUrls()[3]).toBe(
|
|
194
|
+
`${appUrl}/remote_configurations/remote_configurations.json`
|
|
195
|
+
);
|
|
196
|
+
|
|
197
|
+
expect(get).toHaveBeenCalledTimes(4);
|
|
198
|
+
});
|
|
199
|
+
|
|
200
|
+
it("rejects getFile with the configuration error when the prefetch and its retry failed", async () => {
|
|
201
|
+
get.mockImplementation(() => Promise.reject(new Error("network")));
|
|
202
|
+
|
|
203
|
+
AppLoaderBridge.prefetchFiles();
|
|
204
|
+
|
|
205
|
+
await expect(
|
|
206
|
+
AppLoaderBridge.getFile("remoteConfigurations")
|
|
207
|
+
).rejects.toThrow(
|
|
208
|
+
"cannot retrieve configuration data for remoteConfigurations"
|
|
209
|
+
);
|
|
210
|
+
|
|
211
|
+
expect(get).toHaveBeenCalledTimes(4);
|
|
212
|
+
});
|
|
213
|
+
|
|
214
|
+
it("does nothing in development unless remote data is enabled", () => {
|
|
215
|
+
delete process.env.ENABLE_REMOTE_DATA_IN_DEV;
|
|
216
|
+
|
|
217
|
+
AppLoaderBridge.prefetchFiles();
|
|
218
|
+
|
|
219
|
+
expect(get).not.toHaveBeenCalled();
|
|
220
|
+
});
|
|
221
|
+
});
|
|
@@ -1,67 +1,115 @@
|
|
|
1
|
-
import axios, {
|
|
1
|
+
import axios, { AxiosResponse } from "axios";
|
|
2
|
+
import { getAppData } from "../AppData";
|
|
2
3
|
import { getStorageModule } from "../Storage";
|
|
3
4
|
import { STORAGE } from "./utils/enums";
|
|
4
|
-
import {
|
|
5
|
+
import {
|
|
6
|
+
buildUrl,
|
|
7
|
+
createUrl,
|
|
8
|
+
getLayoutIds,
|
|
9
|
+
readLoaderCache,
|
|
10
|
+
writeLoaderCache,
|
|
11
|
+
} from "./utils/utils";
|
|
5
12
|
|
|
6
13
|
type StorageResponse = string | null;
|
|
7
14
|
|
|
15
|
+
type Prefetched = { url: string; response: Promise<AxiosResponse<string>> };
|
|
16
|
+
|
|
8
17
|
const localStorage: any = getStorageModule("localStorage");
|
|
9
18
|
|
|
19
|
+
const GENERAL_FILES = [
|
|
20
|
+
"remoteConfigurations",
|
|
21
|
+
"pluginConfigurations",
|
|
22
|
+
"pipesEndpoints",
|
|
23
|
+
];
|
|
24
|
+
|
|
25
|
+
const LAYOUT_FILES = ["layout", "cellStyles", "presetsMapping"];
|
|
26
|
+
|
|
27
|
+
function requestFile(type: string, url: string) {
|
|
28
|
+
return axios.get<string>(url, {
|
|
29
|
+
headers: {
|
|
30
|
+
// Sent even without a stored etag (as "null"). A conditional header
|
|
31
|
+
// keeps the browser's HTTP cache out of these requests, so every launch
|
|
32
|
+
// gets the current file.
|
|
33
|
+
"If-None-Match": readLoaderCache(`${type}Etag`),
|
|
34
|
+
},
|
|
35
|
+
validateStatus: (status: number): boolean =>
|
|
36
|
+
status === 200 || status === 304,
|
|
37
|
+
// Keep the body as text: it is stored as-is and parsed once in getFile.
|
|
38
|
+
transformResponse: [(body) => body],
|
|
39
|
+
});
|
|
40
|
+
}
|
|
41
|
+
|
|
10
42
|
export const AppLoaderBridge = (function () {
|
|
11
|
-
|
|
43
|
+
// In-flight requests started by prefetchFiles, each used by the first
|
|
44
|
+
// getFile call for the same file and URL, which requests the file again
|
|
45
|
+
// if the prefetch failed.
|
|
46
|
+
const prefetched = new Map<string, Prefetched>();
|
|
47
|
+
|
|
48
|
+
/**
|
|
49
|
+
* Resolves the parsed file. Native bridges resolve a JSON string instead;
|
|
50
|
+
* getFile callers accept both, which saves serializing and re-parsing
|
|
51
|
+
* every file on web.
|
|
52
|
+
*/
|
|
53
|
+
async function getFile(type: string): Promise<unknown> {
|
|
12
54
|
const url: string = await createUrl(type);
|
|
55
|
+
const pending = prefetched.get(type);
|
|
56
|
+
prefetched.delete(type);
|
|
13
57
|
|
|
14
58
|
try {
|
|
15
|
-
const
|
|
16
|
-
|
|
17
|
-
|
|
18
|
-
);
|
|
19
|
-
|
|
20
|
-
const config: AxiosRequestConfig = {
|
|
21
|
-
method: "get",
|
|
22
|
-
url,
|
|
23
|
-
headers: {
|
|
24
|
-
"If-None-Match": currentEtag,
|
|
25
|
-
},
|
|
26
|
-
validateStatus: (status: number): boolean =>
|
|
27
|
-
status === 200 || status === 304,
|
|
28
|
-
};
|
|
29
|
-
|
|
30
|
-
const response: AxiosResponse = await axios(config);
|
|
31
|
-
|
|
32
|
-
const {
|
|
33
|
-
data,
|
|
34
|
-
headers: { etag },
|
|
35
|
-
status,
|
|
36
|
-
} = response;
|
|
59
|
+
const { data, headers, status } = await (pending?.url === url
|
|
60
|
+
? pending.response.catch(() => requestFile(type, url))
|
|
61
|
+
: requestFile(type, url));
|
|
37
62
|
|
|
38
63
|
if (status === 304) {
|
|
39
|
-
|
|
40
|
-
type,
|
|
41
|
-
STORAGE.APP_LOADER_BRIDGE_NAME_SPACE
|
|
42
|
-
);
|
|
43
|
-
|
|
44
|
-
return JSON.stringify(res);
|
|
64
|
+
return readLoaderCache(type);
|
|
45
65
|
}
|
|
46
66
|
|
|
47
|
-
|
|
48
|
-
`${type}Etag`,
|
|
49
|
-
etag,
|
|
50
|
-
STORAGE.APP_LOADER_BRIDGE_NAME_SPACE
|
|
51
|
-
);
|
|
67
|
+
const file = JSON.parse(data);
|
|
52
68
|
|
|
53
|
-
|
|
54
|
-
|
|
55
|
-
JSON.stringify(data),
|
|
56
|
-
STORAGE.APP_LOADER_BRIDGE_NAME_SPACE
|
|
57
|
-
);
|
|
69
|
+
writeLoaderCache(`${type}Etag`, headers.etag);
|
|
70
|
+
writeLoaderCache(type, data);
|
|
58
71
|
|
|
59
|
-
return
|
|
72
|
+
return file;
|
|
60
73
|
} catch (error) {
|
|
61
74
|
throw new Error("cannot retrieve configuration data for " + type);
|
|
62
75
|
}
|
|
63
76
|
}
|
|
64
77
|
|
|
78
|
+
/**
|
|
79
|
+
* Starts the configuration requests while the app bundle is still
|
|
80
|
+
* evaluating, so the files download during it instead of after it.
|
|
81
|
+
* Layout files are only requested for a layout id stored by a previous
|
|
82
|
+
* launch; without one, the remote configuration may select another layout.
|
|
83
|
+
* A file whose URL changes by the time getFile asks for it is requested again.
|
|
84
|
+
*/
|
|
85
|
+
function prefetchFiles(): void {
|
|
86
|
+
// Mirrors appRemoteDataLoader, which skips remote data in development.
|
|
87
|
+
if (__DEV__ && process.env.ENABLE_REMOTE_DATA_IN_DEV !== "true") {
|
|
88
|
+
return;
|
|
89
|
+
}
|
|
90
|
+
|
|
91
|
+
try {
|
|
92
|
+
const appData = getAppData();
|
|
93
|
+
const { storedLayoutId } = getLayoutIds(appData);
|
|
94
|
+
|
|
95
|
+
const types = storedLayoutId
|
|
96
|
+
? [...GENERAL_FILES, ...LAYOUT_FILES]
|
|
97
|
+
: GENERAL_FILES;
|
|
98
|
+
|
|
99
|
+
types.forEach((type) => {
|
|
100
|
+
const url = buildUrl(type, appData, storedLayoutId);
|
|
101
|
+
const response = requestFile(type, url);
|
|
102
|
+
|
|
103
|
+
// getFile reports failures; this only stops an early or unused
|
|
104
|
+
// failure from surfacing as an unhandled rejection.
|
|
105
|
+
response.catch(() => undefined);
|
|
106
|
+
prefetched.set(type, { url, response });
|
|
107
|
+
});
|
|
108
|
+
} catch (error) {
|
|
109
|
+
// Nothing prefetched; getFile requests the files as before.
|
|
110
|
+
}
|
|
111
|
+
}
|
|
112
|
+
|
|
65
113
|
async function switchLayout(layoutID: string): Promise<boolean> {
|
|
66
114
|
return await localStorage.setItem(
|
|
67
115
|
STORAGE.CURRENT_LAYOUT_ID,
|
|
@@ -86,6 +134,7 @@ export const AppLoaderBridge = (function () {
|
|
|
86
134
|
|
|
87
135
|
return {
|
|
88
136
|
getFile,
|
|
137
|
+
prefetchFiles,
|
|
89
138
|
switchLayout,
|
|
90
139
|
getCurrentLayoutId,
|
|
91
140
|
getDefaultLayoutId,
|
|
@@ -1,57 +1,68 @@
|
|
|
1
1
|
import { appStore } from "@applicaster/zapp-react-native-redux/AppStore";
|
|
2
|
-
import {
|
|
2
|
+
import { parseJsonIfNeeded } from "@applicaster/zapp-react-native-utils/functionUtils";
|
|
3
|
+
import { applyNamespaceToKeyName } from "../../Storage";
|
|
3
4
|
import { STORAGE, STORE_PLATFORM } from "./enums";
|
|
4
5
|
|
|
5
|
-
const
|
|
6
|
+
const cacheKey = (key: string) =>
|
|
7
|
+
applyNamespaceToKeyName(key, STORAGE.APP_LOADER_BRIDGE_NAME_SPACE);
|
|
6
8
|
|
|
7
|
-
|
|
8
|
-
|
|
9
|
+
/**
|
|
10
|
+
* Reads the loader cache with the same keys and parsing as
|
|
11
|
+
* getStorageModule("localStorage"), but synchronously. Web storage is
|
|
12
|
+
* synchronous, and prefetchFiles needs it that way: it runs while the app
|
|
13
|
+
* bundle is still evaluating, and an awaited read would only resume after it.
|
|
14
|
+
*/
|
|
15
|
+
export function readLoaderCache(key: string) {
|
|
16
|
+
return parseJsonIfNeeded(window.localStorage.getItem(cacheKey(key)));
|
|
17
|
+
}
|
|
9
18
|
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
store,
|
|
14
|
-
versionName,
|
|
15
|
-
appFamilyId,
|
|
16
|
-
} = appData;
|
|
19
|
+
export function writeLoaderCache(key: string, value: string) {
|
|
20
|
+
window.localStorage.setItem(cacheKey(key), value);
|
|
21
|
+
}
|
|
17
22
|
|
|
23
|
+
/**
|
|
24
|
+
* The layout id to request, split by where it comes from.
|
|
25
|
+
* `storedLayoutId` is only set when a previous launch stored one.
|
|
26
|
+
*/
|
|
27
|
+
export function getLayoutIds(appData) {
|
|
18
28
|
// appData.json is camelized, so the built-in id is riversConfigurationId.
|
|
19
29
|
const defaultLayoutId =
|
|
20
30
|
appData.riversConfigurationId || appData.rivers_configuration_id;
|
|
21
31
|
|
|
22
|
-
const storedDefault =
|
|
23
|
-
STORAGE.DEFAULT_LAYOUT_ID,
|
|
24
|
-
STORAGE.APP_LOADER_BRIDGE_NAME_SPACE
|
|
25
|
-
);
|
|
32
|
+
const storedDefault = readLoaderCache(STORAGE.DEFAULT_LAYOUT_ID);
|
|
26
33
|
|
|
27
34
|
if ((!storedDefault || storedDefault === "undefined") && defaultLayoutId) {
|
|
28
|
-
|
|
29
|
-
STORAGE.DEFAULT_LAYOUT_ID,
|
|
30
|
-
defaultLayoutId,
|
|
31
|
-
STORAGE.APP_LOADER_BRIDGE_NAME_SPACE
|
|
32
|
-
);
|
|
35
|
+
writeLoaderCache(STORAGE.DEFAULT_LAYOUT_ID, defaultLayoutId);
|
|
33
36
|
}
|
|
34
37
|
|
|
35
|
-
const currentLayoutId =
|
|
36
|
-
STORAGE.CURRENT_LAYOUT_ID,
|
|
37
|
-
STORAGE.APP_LOADER_BRIDGE_NAME_SPACE
|
|
38
|
-
);
|
|
38
|
+
const currentLayoutId = readLoaderCache(STORAGE.CURRENT_LAYOUT_ID);
|
|
39
39
|
|
|
40
|
-
const remoteConfig: RemoteConfigurations =
|
|
41
|
-
STORAGE.REMOTE_CONFIGURATIONS
|
|
42
|
-
STORAGE.APP_LOADER_BRIDGE_NAME_SPACE
|
|
40
|
+
const remoteConfig: RemoteConfigurations = readLoaderCache(
|
|
41
|
+
STORAGE.REMOTE_CONFIGURATIONS
|
|
43
42
|
);
|
|
44
43
|
|
|
45
44
|
const remoteConfigLayoutId = remoteConfig?.general_settings?.layout_id;
|
|
46
45
|
|
|
47
46
|
/**
|
|
48
|
-
* It is very important to note the order used
|
|
47
|
+
* It is very important to note the order used for the layout id (createUrl uses storedLayoutId || defaultLayoutId).
|
|
49
48
|
* First we are attempting ot use the id set by layout plugin, it is an override of the defined layout.
|
|
50
49
|
* Or use layout id from the remote configuration, remote config is loaded first and it has current layoutId from zapp.
|
|
51
50
|
* Or use layout id that was built with the app, this id can be found in appData.json and is loaded into redux store.
|
|
52
51
|
*/
|
|
53
|
-
|
|
54
|
-
currentLayoutId || remoteConfigLayoutId
|
|
52
|
+
return {
|
|
53
|
+
storedLayoutId: currentLayoutId || remoteConfigLayoutId,
|
|
54
|
+
defaultLayoutId,
|
|
55
|
+
};
|
|
56
|
+
}
|
|
57
|
+
|
|
58
|
+
export function buildUrl(type: string, appData, layoutId: string): string {
|
|
59
|
+
const {
|
|
60
|
+
accountsAccountId,
|
|
61
|
+
bundleIdentifier,
|
|
62
|
+
store,
|
|
63
|
+
versionName,
|
|
64
|
+
appFamilyId,
|
|
65
|
+
} = appData;
|
|
55
66
|
|
|
56
67
|
const baseURL = `https://assets-secure.applicaster.com/zapp/accounts/${accountsAccountId}`;
|
|
57
68
|
const urlTemplate = `apps/${bundleIdentifier}/${STORE_PLATFORM[store]}/${versionName}`;
|
|
@@ -62,14 +73,21 @@ export async function createUrl(type: string): Promise<string> {
|
|
|
62
73
|
case "pluginConfigurations":
|
|
63
74
|
return `${baseURL}/${urlTemplate}/plugin_configurations/plugin_configurations.json`;
|
|
64
75
|
case "layout":
|
|
65
|
-
return `${baseURL}/${urlTemplate}/layouts/${
|
|
76
|
+
return `${baseURL}/${urlTemplate}/layouts/${layoutId}.json`;
|
|
66
77
|
case "pipesEndpoints":
|
|
67
78
|
return `${baseURL}/app_families/${appFamilyId}/data_source_providers/endpoints.json`;
|
|
68
79
|
case "cellStyles":
|
|
69
|
-
return `${baseURL}/app_families/${appFamilyId}/layouts/${
|
|
80
|
+
return `${baseURL}/app_families/${appFamilyId}/layouts/${layoutId}/cell_styles.json`;
|
|
70
81
|
case "presetsMapping":
|
|
71
|
-
return `${baseURL}/app_families/${appFamilyId}/layouts/${
|
|
82
|
+
return `${baseURL}/app_families/${appFamilyId}/layouts/${layoutId}/presets_mapping.json`;
|
|
72
83
|
default:
|
|
73
84
|
return null;
|
|
74
85
|
}
|
|
75
86
|
}
|
|
87
|
+
|
|
88
|
+
export async function createUrl(type: string): Promise<string> {
|
|
89
|
+
const appData = appStore.get("appData");
|
|
90
|
+
const { storedLayoutId, defaultLayoutId } = getLayoutIds(appData);
|
|
91
|
+
|
|
92
|
+
return buildUrl(type, appData, storedLayoutId || defaultLayoutId);
|
|
93
|
+
}
|
package/package.json
CHANGED
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
{
|
|
2
2
|
"name": "@applicaster/zapp-react-dom-app",
|
|
3
|
-
"version": "16.0.0-rc.
|
|
3
|
+
"version": "16.0.0-rc.128",
|
|
4
4
|
"description": "Zapp App Component for Applicaster's Quick Brick React Native App",
|
|
5
5
|
"main": "index.js",
|
|
6
6
|
"scripts": {
|
|
@@ -22,11 +22,11 @@
|
|
|
22
22
|
},
|
|
23
23
|
"homepage": "https://github.com/applicaster/zapp-react-dom-app#readme",
|
|
24
24
|
"dependencies": {
|
|
25
|
-
"@applicaster/zapp-react-dom-ui-components": "16.0.0-rc.
|
|
26
|
-
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.
|
|
27
|
-
"@applicaster/zapp-react-native-redux": "16.0.0-rc.
|
|
28
|
-
"@applicaster/zapp-react-native-ui-components": "16.0.0-rc.
|
|
29
|
-
"@applicaster/zapp-react-native-utils": "16.0.0-rc.
|
|
25
|
+
"@applicaster/zapp-react-dom-ui-components": "16.0.0-rc.128",
|
|
26
|
+
"@applicaster/zapp-react-native-bridge": "16.0.0-rc.128",
|
|
27
|
+
"@applicaster/zapp-react-native-redux": "16.0.0-rc.128",
|
|
28
|
+
"@applicaster/zapp-react-native-ui-components": "16.0.0-rc.128",
|
|
29
|
+
"@applicaster/zapp-react-native-utils": "16.0.0-rc.128",
|
|
30
30
|
"abortcontroller-polyfill": "^1.7.5",
|
|
31
31
|
"typeface-montserrat": "^0.0.54",
|
|
32
32
|
"video.js": "7.14.3",
|