@controleonline/ui-common 1.2.100 → 1.2.101
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/.release/rc-manifest.json +7 -7
- package/docs/homepage-1056.md +5 -1
- package/package.json +1 -1
- package/src/api/index.js +54 -40
- package/src/react/components/AnimatedModal.js +1 -6
- package/src/react/components/BottomNavigationBar.js +5 -6
- package/src/react/components/BottomNavigationBar.soft.styles.js +4 -5
- package/src/react/components/ProductCatalogCacheService.js +45 -53
- package/src/react/components/RuntimeBottomNavigationBar.js +4 -11
- package/src/react/components/UserAvatar.js +13 -2
- package/src/react/pages/Devices/deviceTypes/DeviceGroupCard.js +4 -6
- package/src/react/pages/Devices/deviceTypes/shared.js +4 -9
- package/src/react/pages/Devices.js +183 -184
- package/src/react/pages/IntegrationConfigPage.js +13 -59
- package/src/react/pages/IntegrationConfigPage.utils.js +0 -17
- package/src/react/pages/integrationsCatalog.js +0 -71
- package/src/tests/react/utils/runtimeFooter.test.js +189 -189
- package/src/utils/integrationConfigs.js +0 -23
- package/src/react/hooks/useBottomNavigationPreference.js +0 -49
- package/src/react/utils/productCatalogCacheRevision.js +0 -13
- package/src/tests/react/utils/storedSessionToken.test.js +0 -22
- package/src/utils/storedSessionToken.js +0 -18
|
@@ -1,10 +1,10 @@
|
|
|
1
1
|
{
|
|
2
|
-
"version": "1.2.
|
|
3
|
-
"rc":
|
|
4
|
-
"branch": "rc/1.2.
|
|
5
|
-
"baseMaster": "
|
|
6
|
-
"tasks": [
|
|
7
|
-
"repositories": { "ControleOnline/ui-common": "
|
|
8
|
-
"compositionNotes": ["
|
|
2
|
+
"version": "1.2.93",
|
|
3
|
+
"rc": 1,
|
|
4
|
+
"branch": "rc/1.2.93-rc.1",
|
|
5
|
+
"baseMaster": "b4db94eb1ccd3bf878dc96e3d32c0249aad07402",
|
|
6
|
+
"tasks": [77],
|
|
7
|
+
"repositories": { "ControleOnline/ui-common": "4dfdb2d7ccb160f265115bdad3125d6ecd26bdfd" },
|
|
8
|
+
"compositionNotes": ["Verified on Android emulator: Text width was still 0 with flexBasis auto because Yoga prioritises flex over flexGrow/flexBasis. flex:0 fixes it."],
|
|
9
9
|
"frozen": true
|
|
10
10
|
}
|
package/docs/homepage-1056.md
CHANGED
|
@@ -9,4 +9,8 @@ Local evidence: 40 homepage tests and 34 runtime footer/zoom tests passed. No pr
|
|
|
9
9
|
|
|
10
10
|
Keep the navigation tree mounted while translations initialize. Hide the shell and disable pointer/accessibility interaction until the existing translation readiness contract succeeds. Unmounting navigation during login resets its route and repeatedly mounts/registers the bottom bar, causing React maximum update depth. No route, permission, translation source, theme or zoom changes. Native behavior is unchanged.
|
|
11
11
|
|
|
12
|
-
Regression evidence: app-community homepage bootstrapNavigation.test.js reproduced maximum update depth before the fix, then passed after it. 44 tests across 9 homepage suites passed. Runtime Dev reproduced the unfixed bug; new runtime verification is required after consuming ui-common 1.2.
|
|
12
|
+
Regression evidence: app-community homepage bootstrapNavigation.test.js reproduced maximum update depth before the fix, then passed after it. 44 tests across 9 homepage suites passed. Runtime Dev reproduced the unfixed bug; new runtime verification is required after consuming ui-common 1.2.96.
|
|
13
|
+
|
|
14
|
+
Release 1.2.96 uses the task-only c91510d source plus version metadata. The integration merge into dev preserves independent team work, but those changes are excluded from this homepage package. Publication uses the existing tag-triggered trusted publishing workflow.
|
|
15
|
+
|
|
16
|
+
Release 1.2.97 completes the web bootstrap gate with visibility:hidden and aria-hidden: pending descendants cannot receive keyboard focus while navigation stays mounted. Native behavior remains unchanged.
|
package/package.json
CHANGED
package/src/api/index.js
CHANGED
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {readStoredSessionToken} from '@controleonline/ui-common/src/utils/storedSessionToken';
|
|
2
1
|
import myFetch from '@controleonline/ui-common/src/api/fetch';
|
|
3
2
|
import axios from 'axios';
|
|
4
3
|
import {env as APP_ENV} from '@env';
|
|
@@ -216,8 +215,8 @@ async function requestSmokeUrl(url, options) {
|
|
|
216
215
|
export const api = {
|
|
217
216
|
device: JSON.parse(localStorage.getItem('device') || '{}'),
|
|
218
217
|
masterDevice: JSON.parse(localStorage.getItem('master-device') || '{}'),
|
|
219
|
-
|
|
220
|
-
|
|
218
|
+
|
|
219
|
+
|
|
221
220
|
upload: async function (uri, formData) {
|
|
222
221
|
const token = await this.getToken();
|
|
223
222
|
const appDomain = resolveAppDomain(APP_ENV.DOMAIN);
|
|
@@ -243,8 +242,8 @@ export const api = {
|
|
|
243
242
|
fetch: async function (uri, options = {}) {
|
|
244
243
|
if (typeof options.headers === 'undefined')
|
|
245
244
|
Object.assign(options, { headers: new Headers() });
|
|
246
|
-
|
|
247
|
-
// Always refresh device from localStorage in case it was set after module load
|
|
245
|
+
|
|
246
|
+
// Always refresh device from localStorage in case it was set after module load
|
|
248
247
|
try {
|
|
249
248
|
this.device = JSON.parse(localStorage.getItem('device') || '{}');
|
|
250
249
|
} catch (e) {
|
|
@@ -271,16 +270,16 @@ export const api = {
|
|
|
271
270
|
if (options.body) {
|
|
272
271
|
if (typeof options.body === 'object') {
|
|
273
272
|
bodyDeviceId = options.body.device || null;
|
|
274
|
-
} else if (typeof options.body === 'string') {
|
|
275
|
-
try {
|
|
276
|
-
const parsedBody = JSON.parse(options.body);
|
|
277
|
-
bodyDeviceId = parsedBody?.device || null;
|
|
278
|
-
} catch (e) {
|
|
279
|
-
bodyDeviceId = null;
|
|
280
|
-
}
|
|
281
|
-
}
|
|
282
|
-
}
|
|
283
|
-
|
|
273
|
+
} else if (typeof options.body === 'string') {
|
|
274
|
+
try {
|
|
275
|
+
const parsedBody = JSON.parse(options.body);
|
|
276
|
+
bodyDeviceId = parsedBody?.device || null;
|
|
277
|
+
} catch (e) {
|
|
278
|
+
bodyDeviceId = null;
|
|
279
|
+
}
|
|
280
|
+
}
|
|
281
|
+
}
|
|
282
|
+
|
|
284
283
|
const requestDevice = resolveRequestDevice(
|
|
285
284
|
this.masterDevice,
|
|
286
285
|
this.device,
|
|
@@ -297,10 +296,10 @@ export const api = {
|
|
|
297
296
|
options.headers.set('DEVICE-TYPE', requestDeviceType);
|
|
298
297
|
}
|
|
299
298
|
}
|
|
300
|
-
if (options.responseType != 'text') {
|
|
301
|
-
options.headers.set('Content-Type', MIME_TYPE);
|
|
302
|
-
options.headers.set('Accept', MIME_TYPE);
|
|
303
|
-
}
|
|
299
|
+
if (options.responseType != 'text') {
|
|
300
|
+
options.headers.set('Content-Type', MIME_TYPE);
|
|
301
|
+
options.headers.set('Accept', MIME_TYPE);
|
|
302
|
+
}
|
|
304
303
|
options.headers.set('App-Domain', resolveAppDomain(APP_ENV.DOMAIN));
|
|
305
304
|
|
|
306
305
|
if (options.body && typeof options.body != 'string') {
|
|
@@ -311,13 +310,28 @@ export const api = {
|
|
|
311
310
|
if (options.params) {
|
|
312
311
|
uri = this.buildQueryString(uri, options);
|
|
313
312
|
}
|
|
314
|
-
return myFetch(uri, options).catch(e => {
|
|
315
|
-
throw e;
|
|
316
|
-
});
|
|
317
|
-
},
|
|
318
|
-
async getToken() {
|
|
319
|
-
|
|
320
|
-
|
|
313
|
+
return myFetch(uri, options).catch(e => {
|
|
314
|
+
throw e;
|
|
315
|
+
});
|
|
316
|
+
},
|
|
317
|
+
async getToken() {
|
|
318
|
+
const sessionString = localStorage.getItem('session');
|
|
319
|
+
let session = null;
|
|
320
|
+
if (sessionString) {
|
|
321
|
+
try {
|
|
322
|
+
const cleanString =
|
|
323
|
+
typeof sessionString == 'string' &&
|
|
324
|
+
sessionString.startsWith('__q_objt|')
|
|
325
|
+
? sessionString.substring('__q_objt|'.length)
|
|
326
|
+
: sessionString;
|
|
327
|
+
session = JSON.parse(cleanString); // Transforma a string em objeto
|
|
328
|
+
} catch (e) {
|
|
329
|
+
console.error('Failed to parse session from localStorage', e);
|
|
330
|
+
}
|
|
331
|
+
}
|
|
332
|
+
|
|
333
|
+
return session?.token || session?.api_key;
|
|
334
|
+
},
|
|
321
335
|
serialize(obj, prefix = '') {
|
|
322
336
|
const pairs = [];
|
|
323
337
|
|
|
@@ -330,12 +344,12 @@ export const api = {
|
|
|
330
344
|
|
|
331
345
|
const value = obj[key];
|
|
332
346
|
let fullKey = prefix ? `${prefix}.${key}` : key; // Usa ponto para objetos
|
|
333
|
-
|
|
334
|
-
if (value === null || value === undefined) {
|
|
335
|
-
pairs.push(`${fullKey}=`);
|
|
336
|
-
} else if (typeof value === 'object' && !Array.isArray(value)) {
|
|
337
|
-
// Objeto aninhado: chama serialize recursivamente com ponto
|
|
338
|
-
pairs.push(...this.serialize(value, fullKey));
|
|
347
|
+
|
|
348
|
+
if (value === null || value === undefined) {
|
|
349
|
+
pairs.push(`${fullKey}=`);
|
|
350
|
+
} else if (typeof value === 'object' && !Array.isArray(value)) {
|
|
351
|
+
// Objeto aninhado: chama serialize recursivamente com ponto
|
|
352
|
+
pairs.push(...this.serialize(value, fullKey));
|
|
339
353
|
} else if (Array.isArray(value)) {
|
|
340
354
|
// Array: adiciona [] e itera sobre os valores
|
|
341
355
|
if (value.length === 0) {
|
|
@@ -350,14 +364,14 @@ export const api = {
|
|
|
350
364
|
});
|
|
351
365
|
}
|
|
352
366
|
} else {
|
|
353
|
-
// Valor simples
|
|
354
|
-
pairs.push(`${fullKey}=${encodeURIComponent(value)}`);
|
|
355
|
-
}
|
|
356
|
-
}
|
|
357
|
-
}
|
|
358
|
-
return pairs;
|
|
359
|
-
},
|
|
360
|
-
|
|
367
|
+
// Valor simples
|
|
368
|
+
pairs.push(`${fullKey}=${encodeURIComponent(value)}`);
|
|
369
|
+
}
|
|
370
|
+
}
|
|
371
|
+
}
|
|
372
|
+
return pairs;
|
|
373
|
+
},
|
|
374
|
+
|
|
361
375
|
buildQueryString(uri, options) {
|
|
362
376
|
const safeUri = stripDomainQueryParams(uri);
|
|
363
377
|
|
|
@@ -38,8 +38,6 @@ export default function AnimatedModal({
|
|
|
38
38
|
onRequestClose,
|
|
39
39
|
children,
|
|
40
40
|
style,
|
|
41
|
-
overlayColor,
|
|
42
|
-
contentStyle,
|
|
43
41
|
}) {
|
|
44
42
|
const [showing, setShowing] = useState(visible);
|
|
45
43
|
const [keyboardHeight, setKeyboardHeight] = useState(0);
|
|
@@ -127,8 +125,6 @@ export default function AnimatedModal({
|
|
|
127
125
|
const overlayStyle = [
|
|
128
126
|
styles.overlay,
|
|
129
127
|
style,
|
|
130
|
-
overlayColor,
|
|
131
|
-
contentStyle,
|
|
132
128
|
Platform.OS === 'android' ? {paddingBottom: keyboardHeight} : null,
|
|
133
129
|
];
|
|
134
130
|
|
|
@@ -156,12 +152,11 @@ export default function AnimatedModal({
|
|
|
156
152
|
style={[
|
|
157
153
|
StyleSheet.absoluteFillObject,
|
|
158
154
|
styles.overlayBg,
|
|
159
|
-
overlayColor ? {backgroundColor: overlayColor} : null,
|
|
160
155
|
{opacity: overlayOpacity},
|
|
161
156
|
]}
|
|
162
157
|
/>
|
|
163
158
|
<Animated.View
|
|
164
|
-
style={[styles.content,
|
|
159
|
+
style={[styles.content, {transform: [{translateY: slideY}]}]}>
|
|
165
160
|
{normalizedChildren}
|
|
166
161
|
</Animated.View>
|
|
167
162
|
<Toasts
|
|
@@ -17,8 +17,7 @@ const BottomNavigationBar = ({
|
|
|
17
17
|
colors = {},
|
|
18
18
|
testID = 'bottom-navigation',
|
|
19
19
|
useModernWebChromeProps = false,
|
|
20
|
-
appearance = '
|
|
21
|
-
placement = 'overlay',
|
|
20
|
+
appearance = 'legacy',
|
|
22
21
|
}) => {
|
|
23
22
|
const Icon = appearance === 'soft' ? HomeIcon : LegacyIcon;
|
|
24
23
|
colors = appearance === 'soft' ? resolveHomepagePalette(colors) : colors;
|
|
@@ -105,7 +104,9 @@ const BottomNavigationBar = ({
|
|
|
105
104
|
|
|
106
105
|
const routeItems = Array.isArray(items) ? items : [];
|
|
107
106
|
const knownRoute = routeItems.some(item => item?.route === activeRouteName);
|
|
108
|
-
const effectiveActiveRoute = knownRoute
|
|
107
|
+
const effectiveActiveRoute = knownRoute
|
|
108
|
+
? activeRouteName
|
|
109
|
+
: routeItems[0]?.route || '';
|
|
109
110
|
|
|
110
111
|
const navigateTo = item => {
|
|
111
112
|
try {
|
|
@@ -133,7 +134,7 @@ const BottomNavigationBar = ({
|
|
|
133
134
|
};
|
|
134
135
|
|
|
135
136
|
return (
|
|
136
|
-
<View {...hostProps} style={
|
|
137
|
+
<View {...hostProps} style={hostStyle}>
|
|
137
138
|
<View style={styles.stack}>
|
|
138
139
|
<View style={styles.dock} testID={testID}>
|
|
139
140
|
<View style={styles.itemsRow}>
|
|
@@ -150,8 +151,6 @@ const BottomNavigationBar = ({
|
|
|
150
151
|
<Pressable
|
|
151
152
|
key={item.route}
|
|
152
153
|
accessibilityRole="button"
|
|
153
|
-
accessibilityLabel={item.label}
|
|
154
|
-
accessibilityState={{selected: isActive, disabled: Boolean(isDisabled)}}
|
|
155
154
|
disabled={isDisabled}
|
|
156
155
|
onPress={() => navigateTo(item)}
|
|
157
156
|
style={({pressed}) => [
|
|
@@ -1,6 +1,6 @@
|
|
|
1
1
|
import {Platform} from 'react-native';
|
|
2
2
|
|
|
3
|
-
//
|
|
3
|
+
// Only the homepage opts into this chrome; preserve all default navigation styles.
|
|
4
4
|
export default (styles, colors, appearance) => appearance !== 'soft' ? styles : {
|
|
5
5
|
...styles,
|
|
6
6
|
dock: [styles.dock, {
|
|
@@ -16,10 +16,9 @@ export default (styles, colors, appearance) => appearance !== 'soft' ? styles :
|
|
|
16
16
|
shadowOpacity: 0,
|
|
17
17
|
...(Platform.OS === 'web' ? {boxShadow: 'none'} : {}),
|
|
18
18
|
}],
|
|
19
|
-
itemsRow: [styles.itemsRow, {minHeight:
|
|
20
|
-
item: [styles.item, {minHeight:
|
|
21
|
-
|
|
22
|
-
itemLabel: [styles.itemLabel, {fontSize: 11, lineHeight: 14, fontWeight: '400'}],
|
|
19
|
+
itemsRow: [styles.itemsRow, {minHeight: 64, paddingHorizontal: 12, paddingVertical: 5}],
|
|
20
|
+
item: [styles.item, {minHeight: 54, borderRadius: 12}],
|
|
21
|
+
itemLabel: [styles.itemLabel, {fontSize: 11, fontWeight: '400'}],
|
|
23
22
|
footerSlot: [styles.footerSlot, {
|
|
24
23
|
borderTopWidth: 1,
|
|
25
24
|
borderColor: colors.navigationBorder,
|
|
@@ -1,53 +1,45 @@
|
|
|
1
|
-
import React, {useEffect} from 'react';
|
|
2
|
-
import {useStore} from '@store';
|
|
3
|
-
|
|
4
|
-
|
|
5
|
-
const
|
|
6
|
-
const
|
|
7
|
-
|
|
8
|
-
|
|
9
|
-
const
|
|
10
|
-
|
|
11
|
-
|
|
12
|
-
|
|
13
|
-
|
|
14
|
-
|
|
15
|
-
|
|
16
|
-
messages &&
|
|
17
|
-
|
|
18
|
-
|
|
19
|
-
|
|
20
|
-
|
|
21
|
-
categoryActions.
|
|
22
|
-
|
|
23
|
-
|
|
24
|
-
|
|
25
|
-
|
|
26
|
-
|
|
27
|
-
|
|
28
|
-
|
|
29
|
-
|
|
30
|
-
|
|
31
|
-
|
|
32
|
-
|
|
33
|
-
|
|
34
|
-
|
|
35
|
-
|
|
36
|
-
|
|
37
|
-
|
|
38
|
-
|
|
39
|
-
|
|
40
|
-
|
|
41
|
-
|
|
42
|
-
|
|
43
|
-
|
|
44
|
-
|
|
45
|
-
|
|
46
|
-
productActions.setReload?.(true);
|
|
47
|
-
categoryActions.setMessage(null);
|
|
48
|
-
}, [categoryActions, message, productActions]);
|
|
49
|
-
|
|
50
|
-
return null;
|
|
51
|
-
};
|
|
52
|
-
|
|
53
|
-
export default ProductCatalogCacheService;
|
|
1
|
+
import React, {useEffect} from 'react';
|
|
2
|
+
import {useStore} from '@store';
|
|
3
|
+
|
|
4
|
+
const ProductCatalogCacheService = () => {
|
|
5
|
+
const categoriesStore = useStore('categories');
|
|
6
|
+
const productsStore = useStore('products');
|
|
7
|
+
|
|
8
|
+
const categoryActions = categoriesStore.actions;
|
|
9
|
+
const productActions = productsStore.actions;
|
|
10
|
+
|
|
11
|
+
const {messages, message} = categoriesStore.getters;
|
|
12
|
+
|
|
13
|
+
useEffect(() => {
|
|
14
|
+
if (
|
|
15
|
+
messages &&
|
|
16
|
+
messages.length > 0 &&
|
|
17
|
+
(!message || Object.keys(message).length === 0)
|
|
18
|
+
) {
|
|
19
|
+
const nextMessages = [...messages];
|
|
20
|
+
categoryActions.setMessage(nextMessages.pop());
|
|
21
|
+
categoryActions.setMessages(nextMessages);
|
|
22
|
+
}
|
|
23
|
+
}, [categoryActions, message, messages]);
|
|
24
|
+
|
|
25
|
+
useEffect(() => {
|
|
26
|
+
if (!message || Object.keys(message).length === 0) {
|
|
27
|
+
return;
|
|
28
|
+
}
|
|
29
|
+
|
|
30
|
+
if (message?.command !== 'clear-product-cache') {
|
|
31
|
+
categoryActions.setMessage(null);
|
|
32
|
+
return;
|
|
33
|
+
}
|
|
34
|
+
|
|
35
|
+
localStorage.setItem('categories', JSON.stringify([]));
|
|
36
|
+
localStorage.removeItem('categories-company');
|
|
37
|
+
categoryActions.setItems([]);
|
|
38
|
+
productActions.setItems([]);
|
|
39
|
+
categoryActions.setMessage(null);
|
|
40
|
+
}, [categoryActions, message, productActions]);
|
|
41
|
+
|
|
42
|
+
return null;
|
|
43
|
+
};
|
|
44
|
+
|
|
45
|
+
export default ProductCatalogCacheService;
|
|
@@ -19,8 +19,7 @@ const {
|
|
|
19
19
|
} = require('@controleonline/ui-common/src/react/utils/bottomNavigationToolbar');
|
|
20
20
|
|
|
21
21
|
const RuntimeBottomNavigationBar = ({
|
|
22
|
-
appearance = '
|
|
23
|
-
placement = 'overlay',
|
|
22
|
+
appearance = 'legacy',
|
|
24
23
|
activeRouteName: activeRouteNameProp,
|
|
25
24
|
colors: colorsOverride,
|
|
26
25
|
disabled: disabledOverride,
|
|
@@ -43,16 +42,13 @@ const RuntimeBottomNavigationBar = ({
|
|
|
43
42
|
const peopleGetters = peopleStore?.getters || {};
|
|
44
43
|
const currentCompany = peopleGetters.currentCompany || {};
|
|
45
44
|
const themeColors = themeGetters.colors || {};
|
|
46
|
-
const [
|
|
45
|
+
const [runtimeMenus, setRuntimeMenus] = useState([]);
|
|
47
46
|
const preset = presetKey ? getBottomNavigationPreset(presetKey) : null;
|
|
48
47
|
const routeAliases = routeAliasesProp || preset?.routeAliases || {};
|
|
49
48
|
const appType = normalizeAppType(app_type);
|
|
50
|
-
const menuScope = JSON.stringify([appType, currentCompany?.id, menuType, presetKey]);
|
|
51
|
-
const runtimeMenus = menuState.scope === menuScope ? menuState.menus : [];
|
|
52
49
|
|
|
53
50
|
// Always consult menus-people for role-aware toolbar; preset is fallback only.
|
|
54
51
|
useEffect(() => {
|
|
55
|
-
const setRuntimeMenus = menus => setMenuState({scope: menuScope, menus});
|
|
56
52
|
if (!currentCompany?.id || !menuType) {
|
|
57
53
|
setRuntimeMenus(
|
|
58
54
|
resolveToolbarRuntimeMenus({
|
|
@@ -109,7 +105,7 @@ const RuntimeBottomNavigationBar = ({
|
|
|
109
105
|
return () => {
|
|
110
106
|
cancelled = true;
|
|
111
107
|
};
|
|
112
|
-
}, [appType, currentCompany?.id, menuType, preset, presetKey
|
|
108
|
+
}, [appType, currentCompany?.id, menuType, preset, presetKey]);
|
|
113
109
|
|
|
114
110
|
const colors = useMemo(() => {
|
|
115
111
|
if (colorsOverride) {
|
|
@@ -155,19 +151,16 @@ const RuntimeBottomNavigationBar = ({
|
|
|
155
151
|
const knownRoute = navItems.some(item => item.route === resolvedActiveRoute);
|
|
156
152
|
const effectiveActiveRoute = knownRoute
|
|
157
153
|
? resolvedActiveRoute
|
|
158
|
-
: activeRouteName;
|
|
154
|
+
: navItems[0]?.route || activeRouteName;
|
|
159
155
|
|
|
160
156
|
const isDisabled =
|
|
161
157
|
typeof disabledOverride === 'boolean'
|
|
162
158
|
? disabledOverride
|
|
163
159
|
: !currentCompany || Object.keys(currentCompany).length === 0;
|
|
164
160
|
|
|
165
|
-
if (!navItems.length) return null;
|
|
166
|
-
|
|
167
161
|
return (
|
|
168
162
|
<BottomNavigationBar
|
|
169
163
|
appearance={appearance}
|
|
170
|
-
placement={placement}
|
|
171
164
|
activeRouteName={effectiveActiveRoute}
|
|
172
165
|
colors={colors}
|
|
173
166
|
disabled={isDisabled}
|
|
@@ -1,4 +1,3 @@
|
|
|
1
|
-
import {readStoredSessionToken} from '../../utils/storedSessionToken';
|
|
2
1
|
import React, {useEffect, useMemo, useState} from 'react';
|
|
3
2
|
import {Image, Platform, StyleSheet, Text, View} from 'react-native';
|
|
4
3
|
import {env as APP_ENV} from '@env';
|
|
@@ -15,6 +14,18 @@ const isBackendDownloadUrl = uri =>
|
|
|
15
14
|
/** Session cache: gravatar URL → true (ok) | false (missing). Avoids repeat probes. */
|
|
16
15
|
const gravatarProbeCache = new Map();
|
|
17
16
|
|
|
17
|
+
const readSessionToken = () => {
|
|
18
|
+
if (typeof localStorage === 'undefined' || !localStorage?.getItem) {
|
|
19
|
+
return '';
|
|
20
|
+
}
|
|
21
|
+
|
|
22
|
+
try {
|
|
23
|
+
const session = JSON.parse(localStorage.getItem('session') || '{}');
|
|
24
|
+
return normalizeUrl(session?.api_key || session?.token);
|
|
25
|
+
} catch {
|
|
26
|
+
return '';
|
|
27
|
+
}
|
|
28
|
+
};
|
|
18
29
|
|
|
19
30
|
/**
|
|
20
31
|
* Probe Gravatar (d=404) via fetch BEFORE exposing URL to <Image>.
|
|
@@ -75,7 +86,7 @@ const useDisplayUri = uri => {
|
|
|
75
86
|
return;
|
|
76
87
|
}
|
|
77
88
|
|
|
78
|
-
const token =
|
|
89
|
+
const token = readSessionToken();
|
|
79
90
|
|
|
80
91
|
// Authenticated backend download on web
|
|
81
92
|
if (isBackendDownloadUrl(next) && Platform.OS === 'web' && token) {
|
|
@@ -24,7 +24,7 @@ import {
|
|
|
24
24
|
parseConfigsObject,
|
|
25
25
|
tt,
|
|
26
26
|
} from './deviceListHelpers';
|
|
27
|
-
import
|
|
27
|
+
import styles from '../../Devices.styles';
|
|
28
28
|
|
|
29
29
|
/**
|
|
30
30
|
* Normalize DefaultTable card `item` into a device group shape.
|
|
@@ -65,7 +65,6 @@ const normalizeDeviceGroup = item => {
|
|
|
65
65
|
export default function DeviceGroupCard({
|
|
66
66
|
item,
|
|
67
67
|
brandColors,
|
|
68
|
-
themedStyles = null,
|
|
69
68
|
creatingPdv,
|
|
70
69
|
goToDetail,
|
|
71
70
|
handleCreateCurrentPdv,
|
|
@@ -75,7 +74,6 @@ export default function DeviceGroupCard({
|
|
|
75
74
|
runtimeDeviceType,
|
|
76
75
|
showCurrentPdvSetup,
|
|
77
76
|
}) {
|
|
78
|
-
const styles = themedStyles || legacyStyles;
|
|
79
77
|
const deviceGroup = normalizeDeviceGroup(item);
|
|
80
78
|
if (!deviceGroup) {
|
|
81
79
|
return null;
|
|
@@ -118,7 +116,7 @@ export default function DeviceGroupCard({
|
|
|
118
116
|
deviceGroup.device?.id ||
|
|
119
117
|
deviceGroup.key,
|
|
120
118
|
);
|
|
121
|
-
const accent =
|
|
119
|
+
const accent = getDeviceTypeAccent(normalizedType);
|
|
122
120
|
const metaChips = [];
|
|
123
121
|
|
|
124
122
|
if (isPdv && primaryConfig) {
|
|
@@ -198,7 +196,7 @@ export default function DeviceGroupCard({
|
|
|
198
196
|
<View style={styles.deviceConfigRow}>
|
|
199
197
|
{deviceConfigs.map(deviceConfig => {
|
|
200
198
|
const configType = getDeviceConfigType(deviceConfig);
|
|
201
|
-
const configAccent =
|
|
199
|
+
const configAccent = getDeviceTypeAccent(configType);
|
|
202
200
|
const isSessionConfig =
|
|
203
201
|
isCurrentDevice &&
|
|
204
202
|
runtimeDeviceType &&
|
|
@@ -299,5 +297,5 @@ export default function DeviceGroupCard({
|
|
|
299
297
|
</View>
|
|
300
298
|
</View>
|
|
301
299
|
);
|
|
302
|
-
|
|
300
|
+
|
|
303
301
|
}
|
|
@@ -36,7 +36,7 @@ import {
|
|
|
36
36
|
prioritizeCurrentDeviceGroups,
|
|
37
37
|
readStoredRuntimeDevice,
|
|
38
38
|
} from '../currentDevice';
|
|
39
|
-
import
|
|
39
|
+
import styles from '../../Devices.styles';
|
|
40
40
|
import DeviceGroupCard from './DeviceGroupCard';
|
|
41
41
|
import {useDeviceNetworkConnectivity} from './useDeviceNetworkConnectivity';
|
|
42
42
|
import {
|
|
@@ -89,7 +89,7 @@ export const createDeviceTypeTab = ({
|
|
|
89
89
|
clientFilter = null,
|
|
90
90
|
offerCurrentPdvSetup = false,
|
|
91
91
|
}) => {
|
|
92
|
-
const DeviceTypeTab = (
|
|
92
|
+
const DeviceTypeTab = () => {
|
|
93
93
|
const navigation = useNavigation();
|
|
94
94
|
const peopleStore = useStore('people');
|
|
95
95
|
const deviceStore = useStore('device');
|
|
@@ -101,15 +101,13 @@ export const createDeviceTypeTab = ({
|
|
|
101
101
|
|
|
102
102
|
const brandColors = useMemo(
|
|
103
103
|
() =>
|
|
104
|
-
|
|
104
|
+
resolveThemePalette(
|
|
105
105
|
{...themeColors, ...(currentCompany?.theme?.colors || {})},
|
|
106
106
|
colors,
|
|
107
107
|
),
|
|
108
|
-
[
|
|
108
|
+
[themeColors, currentCompany?.id],
|
|
109
109
|
);
|
|
110
110
|
|
|
111
|
-
const styles = useMemo(() => themePalette ? createStyles(brandColors) : legacyStyles, [themePalette, brandColors]);
|
|
112
|
-
|
|
113
111
|
const [deviceConfigs, setDeviceConfigs] = useState([]);
|
|
114
112
|
const [loading, setLoading] = useState(false);
|
|
115
113
|
const [refreshing, setRefreshing] = useState(false);
|
|
@@ -374,7 +372,6 @@ export const createDeviceTypeTab = ({
|
|
|
374
372
|
<DeviceGroupCard
|
|
375
373
|
{...cardProps}
|
|
376
374
|
brandColors={brandColors}
|
|
377
|
-
themedStyles={themePalette ? styles : null}
|
|
378
375
|
creatingPdv={creatingPdv}
|
|
379
376
|
goToDetail={goToDetail}
|
|
380
377
|
handleCreateCurrentPdv={handleCreateCurrentPdv}
|
|
@@ -387,8 +384,6 @@ export const createDeviceTypeTab = ({
|
|
|
387
384
|
),
|
|
388
385
|
[
|
|
389
386
|
brandColors,
|
|
390
|
-
styles,
|
|
391
|
-
themePalette,
|
|
392
387
|
creatingPdv,
|
|
393
388
|
goToDetail,
|
|
394
389
|
handleCreateCurrentPdv,
|