varminer-app-header 2.2.2 → 2.2.4
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/dist/AppHeader.d.ts.map +1 -1
- package/dist/index.d.ts +7 -19
- package/dist/index.d.ts.map +1 -1
- package/dist/index.esm.js +200 -169
- package/dist/index.esm.js.map +1 -1
- package/dist/index.js +200 -168
- package/dist/index.js.map +1 -1
- package/dist/types.d.ts +2 -0
- package/dist/types.d.ts.map +1 -1
- package/dist/utils/localStorage.d.ts +4 -18
- package/dist/utils/localStorage.d.ts.map +1 -1
- package/package.json +1 -1
package/dist/index.js
CHANGED
|
@@ -248,8 +248,8 @@ const getAllDataFromStorage = () => {
|
|
|
248
248
|
try {
|
|
249
249
|
const parsed = JSON.parse(headerStr);
|
|
250
250
|
const auth = parsed?.auth;
|
|
251
|
-
const accessToken = auth?.accessToken;
|
|
252
|
-
if (accessToken) {
|
|
251
|
+
const accessToken = auth?.accessToken ?? auth?.access_token ?? auth?.token;
|
|
252
|
+
if (accessToken && typeof accessToken === "string") {
|
|
253
253
|
const decodedToken = decodeJWT(accessToken);
|
|
254
254
|
return {
|
|
255
255
|
...emptyStorageResult(),
|
|
@@ -263,6 +263,48 @@ const getAllDataFromStorage = () => {
|
|
|
263
263
|
console.error("Error parsing header persist:", err);
|
|
264
264
|
}
|
|
265
265
|
}
|
|
266
|
+
// Fallback: try IAM persist key (persist:linn-i-am) then persist:userdb
|
|
267
|
+
const tryParsePersistForAuth = (raw) => {
|
|
268
|
+
if (!raw)
|
|
269
|
+
return null;
|
|
270
|
+
try {
|
|
271
|
+
const outer = JSON.parse(raw);
|
|
272
|
+
const parseNested = (v) => typeof v === "string" ? (() => { try {
|
|
273
|
+
return JSON.parse(v);
|
|
274
|
+
}
|
|
275
|
+
catch {
|
|
276
|
+
return v;
|
|
277
|
+
} })() : v;
|
|
278
|
+
for (const key of Object.keys(outer)) {
|
|
279
|
+
const parsed = parseNested(outer[key]);
|
|
280
|
+
if (parsed?.accessToken || parsed?.access_token || parsed?.token || parsed?.refreshToken)
|
|
281
|
+
return parsed;
|
|
282
|
+
if (parsed?.auth && typeof parsed.auth === "object") {
|
|
283
|
+
const a = parsed.auth;
|
|
284
|
+
if (a.accessToken || a.access_token || a.token)
|
|
285
|
+
return a;
|
|
286
|
+
}
|
|
287
|
+
}
|
|
288
|
+
}
|
|
289
|
+
catch {
|
|
290
|
+
/* ignore */
|
|
291
|
+
}
|
|
292
|
+
return null;
|
|
293
|
+
};
|
|
294
|
+
const iamPersist = localStorage.getItem("persist:linn-i-am");
|
|
295
|
+
const iamAuth = tryParsePersistForAuth(iamPersist);
|
|
296
|
+
if (iamAuth) {
|
|
297
|
+
const token = (iamAuth.accessToken ?? iamAuth.access_token ?? iamAuth.token);
|
|
298
|
+
if (token) {
|
|
299
|
+
const decodedToken = decodeJWT(token);
|
|
300
|
+
return {
|
|
301
|
+
...emptyStorageResult(),
|
|
302
|
+
auth: iamAuth,
|
|
303
|
+
decodedToken: decodedToken ? Object.fromEntries(Object.entries(decodedToken).map(([k, v]) => [k, v ?? null])) : null,
|
|
304
|
+
rawData: iamAuth,
|
|
305
|
+
};
|
|
306
|
+
}
|
|
307
|
+
}
|
|
266
308
|
// Fallback: read access token from persist:userdb (e.g. legacy or shared auth)
|
|
267
309
|
const userDbString = localStorage.getItem("persist:userdb");
|
|
268
310
|
if (!userDbString) {
|
|
@@ -285,17 +327,14 @@ const getAllDataFromStorage = () => {
|
|
|
285
327
|
// Parse all top-level keys dynamically (no hardcoding)
|
|
286
328
|
const parsedData = {};
|
|
287
329
|
let authData = null;
|
|
288
|
-
// Helper to recursively find auth data (contains accessToken or refreshToken)
|
|
330
|
+
// Helper to recursively find auth data (contains accessToken, access_token, token, or refreshToken)
|
|
289
331
|
const findAuthData = (obj) => {
|
|
290
332
|
if (!obj || typeof obj !== 'object')
|
|
291
333
|
return null;
|
|
292
|
-
|
|
293
|
-
if (obj.accessToken || obj.refreshToken) {
|
|
334
|
+
if (obj.accessToken || obj.access_token || obj.token || obj.refreshToken)
|
|
294
335
|
return obj;
|
|
295
|
-
}
|
|
296
|
-
// Recursively search in nested objects
|
|
297
336
|
for (const key in obj) {
|
|
298
|
-
if (
|
|
337
|
+
if (Object.prototype.hasOwnProperty.call(obj, key)) {
|
|
299
338
|
const nested = findAuthData(obj[key]);
|
|
300
339
|
if (nested)
|
|
301
340
|
return nested;
|
|
@@ -313,9 +352,11 @@ const getAllDataFromStorage = () => {
|
|
|
313
352
|
parsedData.auth = authData;
|
|
314
353
|
}
|
|
315
354
|
});
|
|
316
|
-
// Get accessToken and decode it
|
|
355
|
+
// Get accessToken (support accessToken, access_token, token) and decode it
|
|
317
356
|
let decodedToken = null;
|
|
318
|
-
const accessToken = authData?.accessToken
|
|
357
|
+
const accessToken = authData?.accessToken ?? authData?.access_token ?? authData?.token ??
|
|
358
|
+
parsedData.auth?.accessToken ?? parsedData.auth?.access_token ?? parsedData.auth?.token ??
|
|
359
|
+
parsedData.authDetails?.accessToken ?? parsedData.authDetails?.access_token ?? parsedData.authDetails?.token;
|
|
319
360
|
if (accessToken) {
|
|
320
361
|
decodedToken = decodeJWT(accessToken);
|
|
321
362
|
}
|
|
@@ -366,175 +407,159 @@ const getProfilePictureUrl = (baseUrl = "http://objectstore.impact0mics.local:90
|
|
|
366
407
|
}
|
|
367
408
|
};
|
|
368
409
|
/**
|
|
369
|
-
*
|
|
370
|
-
*
|
|
371
|
-
* @param
|
|
372
|
-
* @
|
|
373
|
-
* @param region - AWS Region (default: ap-south-2)
|
|
374
|
-
* @param expiresIn - Expiration time in seconds (default: 3600 = 1 hour)
|
|
375
|
-
* @returns Presigned URL string
|
|
410
|
+
* Fetch profile picture from object store API (plain GET with Authorization header).
|
|
411
|
+
* API returns the image directly; response is converted to a blob URL for use in img src.
|
|
412
|
+
* @param baseUrl - Base URL for the object store API (default: http://objectstore.impact0mics.local:9012)
|
|
413
|
+
* @returns Promise that resolves to blob URL string or null if fetch fails
|
|
376
414
|
*/
|
|
377
|
-
|
|
415
|
+
/** Resolve access token from auth object (supports accessToken, access_token, token). */
|
|
416
|
+
function getAccessTokenFromAuth(auth) {
|
|
417
|
+
if (!auth || typeof auth !== "object")
|
|
418
|
+
return undefined;
|
|
419
|
+
const token = auth.accessToken ??
|
|
420
|
+
auth.access_token ??
|
|
421
|
+
auth.token;
|
|
422
|
+
return typeof token === "string" && token.length > 0 ? token : undefined;
|
|
423
|
+
}
|
|
424
|
+
/** True if string looks like a JWT (three base64 parts) or a bearer token, not JSON. */
|
|
425
|
+
function looksLikeToken(s) {
|
|
426
|
+
return s.length > 0 && !s.trimStart().startsWith("{") && !s.trimStart().startsWith("[");
|
|
427
|
+
}
|
|
428
|
+
/** Recursively find first string value at keys accessToken, access_token, or token. */
|
|
429
|
+
function findTokenInObject(obj) {
|
|
430
|
+
if (obj === null || obj === undefined)
|
|
431
|
+
return undefined;
|
|
432
|
+
if (typeof obj === "string")
|
|
433
|
+
return looksLikeToken(obj) ? obj : undefined;
|
|
434
|
+
if (typeof obj !== "object")
|
|
435
|
+
return undefined;
|
|
436
|
+
const rec = obj;
|
|
437
|
+
const direct = rec.accessToken ?? rec.access_token ?? rec.token;
|
|
438
|
+
if (typeof direct === "string" && looksLikeToken(direct))
|
|
439
|
+
return direct;
|
|
440
|
+
for (const key of Object.keys(rec)) {
|
|
441
|
+
const found = findTokenInObject(rec[key]);
|
|
442
|
+
if (found)
|
|
443
|
+
return found;
|
|
444
|
+
}
|
|
445
|
+
return undefined;
|
|
446
|
+
}
|
|
447
|
+
/** Parse redux-persist style value (may be double stringified). */
|
|
448
|
+
function parsePersistValue(raw) {
|
|
449
|
+
if (!raw)
|
|
450
|
+
return null;
|
|
378
451
|
try {
|
|
379
|
-
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
383
|
-
|
|
384
|
-
|
|
385
|
-
|
|
386
|
-
|
|
387
|
-
extractedRegion = hostnameParts[2] || region;
|
|
388
|
-
}
|
|
389
|
-
else if (hostnameParts.length >= 2 && hostnameParts[1].startsWith('s3-')) {
|
|
390
|
-
// Format: bucket.s3-region.amazonaws.com
|
|
391
|
-
extractedRegion = hostnameParts[1].substring(3) || region;
|
|
452
|
+
const first = JSON.parse(raw);
|
|
453
|
+
if (typeof first === "string") {
|
|
454
|
+
try {
|
|
455
|
+
return JSON.parse(first);
|
|
456
|
+
}
|
|
457
|
+
catch {
|
|
458
|
+
return first;
|
|
459
|
+
}
|
|
392
460
|
}
|
|
393
|
-
|
|
394
|
-
// AWS credentials
|
|
395
|
-
const awsAccessKeyId = accessKeyId;
|
|
396
|
-
const awsSecretAccessKey = secretAccessKey;
|
|
397
|
-
const awsRegion = extractedRegion;
|
|
398
|
-
// Current timestamp
|
|
399
|
-
const now = new Date();
|
|
400
|
-
const dateStamp = now.toISOString().slice(0, 10).replace(/-/g, '');
|
|
401
|
-
const amzDate = dateStamp + 'T' + now.toISOString().slice(11, 19).replace(/[:-]/g, '') + 'Z';
|
|
402
|
-
// Step 1: Create canonical request
|
|
403
|
-
const canonicalUri = '/' + encodeURIComponent(key).replace(/%2F/g, '/');
|
|
404
|
-
const canonicalQuerystring = `X-Amz-Algorithm=AWS4-HMAC-SHA256&X-Amz-Credential=${encodeURIComponent(awsAccessKeyId + '/' + dateStamp + '/' + awsRegion + '/s3/aws4_request')}&X-Amz-Date=${amzDate}&X-Amz-Expires=${expiresIn}&X-Amz-SignedHeaders=host`;
|
|
405
|
-
const canonicalHeaders = `host:${bucket}.s3.${awsRegion}.amazonaws.com\n`;
|
|
406
|
-
const signedHeaders = 'host';
|
|
407
|
-
const payloadHash = 'UNSIGNED-PAYLOAD';
|
|
408
|
-
const canonicalRequest = `GET\n${canonicalUri}\n${canonicalQuerystring}\n${canonicalHeaders}\n${signedHeaders}\n${payloadHash}`;
|
|
409
|
-
// Step 2: Create string to sign
|
|
410
|
-
const algorithm = 'AWS4-HMAC-SHA256';
|
|
411
|
-
const credentialScope = `${dateStamp}/${awsRegion}/s3/aws4_request`;
|
|
412
|
-
const stringToSign = `${algorithm}\n${amzDate}\n${credentialScope}\n${await sha256(canonicalRequest)}`;
|
|
413
|
-
// Step 3: Calculate signature
|
|
414
|
-
const kDate = await hmacSha256('AWS4' + awsSecretAccessKey, dateStamp);
|
|
415
|
-
const kRegion = await hmacSha256(kDate, awsRegion);
|
|
416
|
-
const kService = await hmacSha256(kRegion, 's3');
|
|
417
|
-
const kSigning = await hmacSha256(kService, 'aws4_request');
|
|
418
|
-
const signature = await hmacSha256(kSigning, stringToSign);
|
|
419
|
-
// Convert signature to hex
|
|
420
|
-
const signatureHex = arrayBufferToHex(signature);
|
|
421
|
-
// Step 4: Construct presigned URL
|
|
422
|
-
const presignedUrl = `https://${bucket}.s3.${awsRegion}.amazonaws.com${canonicalUri}?${canonicalQuerystring}&X-Amz-Signature=${signatureHex}`;
|
|
423
|
-
return presignedUrl;
|
|
424
|
-
}
|
|
425
|
-
catch (err) {
|
|
426
|
-
console.error("Error generating S3 presigned URL:", err);
|
|
427
|
-
throw err;
|
|
428
|
-
}
|
|
429
|
-
};
|
|
430
|
-
// Helper function for SHA-256 hashing
|
|
431
|
-
const sha256 = async (message) => {
|
|
432
|
-
const msgBuffer = new TextEncoder().encode(message);
|
|
433
|
-
const hashBuffer = await crypto.subtle.digest('SHA-256', msgBuffer);
|
|
434
|
-
return Array.from(new Uint8Array(hashBuffer))
|
|
435
|
-
.map(b => b.toString(16).padStart(2, '0'))
|
|
436
|
-
.join('');
|
|
437
|
-
};
|
|
438
|
-
// Helper function for HMAC-SHA256
|
|
439
|
-
const hmacSha256 = async (key, message) => {
|
|
440
|
-
const encoder = new TextEncoder();
|
|
441
|
-
let keyBuffer;
|
|
442
|
-
if (typeof key === 'string') {
|
|
443
|
-
keyBuffer = encoder.encode(key);
|
|
461
|
+
return first;
|
|
444
462
|
}
|
|
445
|
-
|
|
446
|
-
|
|
463
|
+
catch {
|
|
464
|
+
return null;
|
|
447
465
|
}
|
|
448
|
-
|
|
449
|
-
|
|
450
|
-
|
|
451
|
-
|
|
452
|
-
|
|
453
|
-
|
|
454
|
-
|
|
455
|
-
|
|
456
|
-
|
|
457
|
-
};
|
|
466
|
+
}
|
|
467
|
+
/** Get a nested value that may be a string (need parse) or already an object. */
|
|
468
|
+
function parseNestedValue(val) {
|
|
469
|
+
if (val == null)
|
|
470
|
+
return null;
|
|
471
|
+
if (typeof val === "string")
|
|
472
|
+
return parsePersistValue(val);
|
|
473
|
+
return val;
|
|
474
|
+
}
|
|
458
475
|
/**
|
|
459
|
-
*
|
|
460
|
-
*
|
|
461
|
-
|
|
462
|
-
|
|
463
|
-
|
|
464
|
-
|
|
465
|
-
|
|
466
|
-
|
|
467
|
-
|
|
468
|
-
|
|
469
|
-
|
|
470
|
-
|
|
476
|
+
* Extract access token from persist:userdb shape: authDetails (string) → parse → auth.accessToken.
|
|
477
|
+
* Structure: { authDetails: "{\"auth\":{\"accessToken\":\"...\"}}", profileInformation: "...", _persist: "..." }
|
|
478
|
+
*/
|
|
479
|
+
function getTokenFromUserDbPersist(parsed) {
|
|
480
|
+
const authDetails = parsed.authDetails;
|
|
481
|
+
const inner = parseNestedValue(authDetails);
|
|
482
|
+
const obj = typeof inner === "object" && inner !== null ? inner : null;
|
|
483
|
+
if (!obj)
|
|
484
|
+
return undefined;
|
|
485
|
+
const auth = obj.auth;
|
|
486
|
+
const authObj = typeof auth === "object" && auth !== null ? auth : null;
|
|
487
|
+
if (!authObj)
|
|
488
|
+
return undefined;
|
|
489
|
+
const token = authObj.accessToken ??
|
|
490
|
+
authObj.access_token ??
|
|
491
|
+
authObj.token;
|
|
492
|
+
return typeof token === "string" && looksLikeToken(token) ? token : undefined;
|
|
493
|
+
}
|
|
494
|
+
/**
|
|
495
|
+
* Get access token from all known storage keys (header, IAM, userdb, and plain keys).
|
|
496
|
+
* persist:userdb shape: authDetails (stringified) contains auth.accessToken.
|
|
471
497
|
*/
|
|
472
|
-
|
|
498
|
+
function getAccessTokenForRequest() {
|
|
499
|
+
const keysToTry = [
|
|
500
|
+
PERSIST_HEADER_KEY,
|
|
501
|
+
"persist:linn-i-am",
|
|
502
|
+
"persist:userdb",
|
|
503
|
+
"token",
|
|
504
|
+
"accessToken",
|
|
505
|
+
"auth",
|
|
506
|
+
];
|
|
507
|
+
for (const key of keysToTry) {
|
|
508
|
+
const raw = localStorage.getItem(key);
|
|
509
|
+
if (!raw)
|
|
510
|
+
continue;
|
|
511
|
+
const parsed = key.startsWith("persist:") ? parsePersistValue(raw) : (() => { try {
|
|
512
|
+
return JSON.parse(raw);
|
|
513
|
+
}
|
|
514
|
+
catch {
|
|
515
|
+
return raw;
|
|
516
|
+
} })();
|
|
517
|
+
const token = findTokenInObject(parsed);
|
|
518
|
+
if (token)
|
|
519
|
+
return token;
|
|
520
|
+
if (key.startsWith("persist:")) {
|
|
521
|
+
const outer = typeof parsed === "object" && parsed !== null ? parsed : null;
|
|
522
|
+
if (outer) {
|
|
523
|
+
if (key === "persist:userdb") {
|
|
524
|
+
const fromUserDb = getTokenFromUserDbPersist(outer);
|
|
525
|
+
if (fromUserDb)
|
|
526
|
+
return fromUserDb;
|
|
527
|
+
}
|
|
528
|
+
for (const k of Object.keys(outer)) {
|
|
529
|
+
const inner = parseNestedValue(outer[k]);
|
|
530
|
+
const t = findTokenInObject(inner);
|
|
531
|
+
if (t)
|
|
532
|
+
return t;
|
|
533
|
+
}
|
|
534
|
+
}
|
|
535
|
+
}
|
|
536
|
+
}
|
|
537
|
+
return getAccessTokenFromAuth(getAllDataFromStorage().auth);
|
|
538
|
+
}
|
|
539
|
+
const fetchProfilePictureAsBlobUrl = async (baseUrl = "http://objectstore.impact0mics.local:9012", accessTokenOverride) => {
|
|
473
540
|
try {
|
|
474
541
|
const profilePictureUrl = getProfilePictureUrl(baseUrl);
|
|
475
|
-
if (!profilePictureUrl)
|
|
542
|
+
if (!profilePictureUrl)
|
|
476
543
|
return null;
|
|
477
|
-
|
|
478
|
-
|
|
479
|
-
|
|
480
|
-
|
|
481
|
-
secretAccessKey: "kbMVqmx6s29njcS5P48qAqpXlb1oir6+b7zu1Qxi",
|
|
482
|
-
region: "ap-south-2",
|
|
483
|
-
bucket: "development-varminer-test"
|
|
484
|
-
};
|
|
485
|
-
const finalAwsConfig = awsConfig || defaultAwsConfig;
|
|
486
|
-
// Generate message ID and correlation ID if not provided
|
|
487
|
-
const msgId = messageId || `msg-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
488
|
-
const corrId = correlationId || `corr-${Date.now()}-${Math.random().toString(36).substr(2, 9)}`;
|
|
489
|
-
const allData = getAllDataFromStorage();
|
|
490
|
-
const accessToken = allData.auth?.accessToken;
|
|
491
|
-
const headers = {
|
|
492
|
-
'X-Message-Id': msgId,
|
|
493
|
-
'X-Correlation-Id': corrId,
|
|
494
|
-
};
|
|
544
|
+
const accessToken = (typeof accessTokenOverride === "string" && accessTokenOverride.length > 0
|
|
545
|
+
? accessTokenOverride
|
|
546
|
+
: null) ?? getAccessTokenForRequest();
|
|
547
|
+
const headers = {};
|
|
495
548
|
if (accessToken) {
|
|
496
|
-
headers[
|
|
497
|
-
}
|
|
498
|
-
// Step 1: Fetch the profile picture path from API (returns JSON with filePath)
|
|
499
|
-
const apiResponse = await fetch(profilePictureUrl, {
|
|
500
|
-
method: 'GET',
|
|
501
|
-
headers,
|
|
502
|
-
});
|
|
503
|
-
// Check if the API response is successful
|
|
504
|
-
if (!apiResponse.ok) {
|
|
505
|
-
console.warn(`Failed to fetch profile picture path: ${apiResponse.status} ${apiResponse.statusText}`);
|
|
506
|
-
return null;
|
|
549
|
+
headers["Authorization"] = `Bearer ${accessToken}`;
|
|
507
550
|
}
|
|
508
|
-
|
|
509
|
-
|
|
510
|
-
|
|
511
|
-
const s3Url = apiData?.filePath;
|
|
512
|
-
if (!s3Url || typeof s3Url !== 'string') {
|
|
513
|
-
console.warn('Profile picture API response does not contain valid filePath');
|
|
514
|
-
return null;
|
|
515
|
-
}
|
|
516
|
-
// Step 2: Generate presigned URL for S3 object
|
|
517
|
-
const presignedUrl = await generateS3PresignedUrl(s3Url, finalAwsConfig.accessKeyId, finalAwsConfig.secretAccessKey, finalAwsConfig.region);
|
|
518
|
-
// Step 3: Fetch the image using presigned URL
|
|
519
|
-
const imageResponse = await fetch(presignedUrl, {
|
|
520
|
-
method: 'GET',
|
|
521
|
-
});
|
|
522
|
-
// Check if the image response is successful
|
|
523
|
-
if (!imageResponse.ok) {
|
|
524
|
-
console.warn(`Failed to fetch profile picture image: ${imageResponse.status} ${imageResponse.statusText}`);
|
|
551
|
+
const response = await fetch(profilePictureUrl, { method: "GET", headers });
|
|
552
|
+
if (!response.ok) {
|
|
553
|
+
console.warn(`Failed to fetch profile picture: ${response.status} ${response.statusText}`);
|
|
525
554
|
return null;
|
|
526
555
|
}
|
|
527
|
-
|
|
528
|
-
|
|
529
|
-
if (!contentType || !contentType.startsWith('image/')) {
|
|
556
|
+
const contentType = response.headers.get("content-type");
|
|
557
|
+
if (!contentType || !contentType.startsWith("image/")) {
|
|
530
558
|
console.warn(`Profile picture response is not an image: ${contentType}`);
|
|
531
559
|
return null;
|
|
532
560
|
}
|
|
533
|
-
|
|
534
|
-
|
|
535
|
-
// Step 5: Create blob URL
|
|
536
|
-
const blobUrl = URL.createObjectURL(blob);
|
|
537
|
-
return blobUrl;
|
|
561
|
+
const blob = await response.blob();
|
|
562
|
+
return URL.createObjectURL(blob);
|
|
538
563
|
}
|
|
539
564
|
catch (err) {
|
|
540
565
|
console.error("Error fetching profile picture:", err);
|
|
@@ -654,7 +679,7 @@ const DEFAULT_ROUTES = {
|
|
|
654
679
|
profile: "/user/profile",
|
|
655
680
|
logout: "/user/login",
|
|
656
681
|
};
|
|
657
|
-
const AppHeader = ({ language: languageProp }) => {
|
|
682
|
+
const AppHeader = ({ language: languageProp, accessToken: accessTokenProp }) => {
|
|
658
683
|
// Get initial language from props, URL, localStorage, or default to 'en'
|
|
659
684
|
const getInitialLanguage = () => {
|
|
660
685
|
// Priority 1: Props
|
|
@@ -751,12 +776,18 @@ const AppHeader = ({ language: languageProp }) => {
|
|
|
751
776
|
userRole = userRole || profileData.role || profileData.userRole || "";
|
|
752
777
|
}
|
|
753
778
|
}
|
|
779
|
+
const avatarSrc = storedUser?.avatar ??
|
|
780
|
+
allData.auth?.avatar ??
|
|
781
|
+
allData.profile?.avatar;
|
|
782
|
+
const initialsVal = storedUser?.initials ??
|
|
783
|
+
allData.auth?.initials ??
|
|
784
|
+
allData.profile?.initials;
|
|
754
785
|
return {
|
|
755
786
|
name: userName || "",
|
|
756
787
|
email: userEmail || "",
|
|
757
788
|
role: userRole || "",
|
|
758
|
-
avatar:
|
|
759
|
-
initials:
|
|
789
|
+
avatar: typeof avatarSrc === "string" ? avatarSrc : undefined,
|
|
790
|
+
initials: typeof initialsVal === "string" ? initialsVal : undefined,
|
|
760
791
|
};
|
|
761
792
|
});
|
|
762
793
|
const [notificationCount, setNotificationCount] = React.useState(() => {
|
|
@@ -771,8 +802,8 @@ const AppHeader = ({ language: languageProp }) => {
|
|
|
771
802
|
// Fetch profile picture from API when component mounts or user data changes
|
|
772
803
|
React.useEffect(() => {
|
|
773
804
|
const fetchProfilePicture = async () => {
|
|
774
|
-
|
|
775
|
-
const blobUrl = await fetchProfilePictureAsBlobUrl();
|
|
805
|
+
const token = accessTokenProp ?? getAccessTokenForRequest();
|
|
806
|
+
const blobUrl = await fetchProfilePictureAsBlobUrl(undefined, token ?? undefined);
|
|
776
807
|
if (blobUrl) {
|
|
777
808
|
// Clean up previous blob URL if it exists
|
|
778
809
|
setProfilePictureBlobUrl((prevUrl) => {
|
|
@@ -793,7 +824,7 @@ const AppHeader = ({ language: languageProp }) => {
|
|
|
793
824
|
return null;
|
|
794
825
|
});
|
|
795
826
|
};
|
|
796
|
-
}, []); //
|
|
827
|
+
}, [accessTokenProp]); // Refetch when accessToken prop changes (e.g. after login)
|
|
797
828
|
React.useEffect(() => {
|
|
798
829
|
const allData = getAllDataFromStorage();
|
|
799
830
|
let userName = "";
|
|
@@ -1130,6 +1161,7 @@ exports.DrawerProvider = DrawerProvider;
|
|
|
1130
1161
|
exports.PERSIST_HEADER_KEY = PERSIST_HEADER_KEY;
|
|
1131
1162
|
exports.USER_DETAILS_STORAGE_KEY = USER_DETAILS_STORAGE_KEY;
|
|
1132
1163
|
exports.fetchProfilePictureAsBlobUrl = fetchProfilePictureAsBlobUrl;
|
|
1164
|
+
exports.getAccessTokenForRequest = getAccessTokenForRequest;
|
|
1133
1165
|
exports.getAllDataFromStorage = getAllDataFromStorage;
|
|
1134
1166
|
exports.getI18nLocaleFromStorage = getI18nLocaleFromStorage;
|
|
1135
1167
|
exports.getMessageCountFromStorage = getMessageCountFromStorage;
|