@stokr/components-library 3.0.68 → 3.0.69
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.
|
@@ -14,7 +14,10 @@ import YtLogo from "../../static/images/social/youtube_social_circle_red.png.js"
|
|
|
14
14
|
import TelegramLogo from "../../static/images/social/Telegram-Logo.png.js";
|
|
15
15
|
import IsoLogo from "../../static/images/iso27001-black.svg.js";
|
|
16
16
|
import { SocialLinksContainer } from "../Header/Header.styles.js";
|
|
17
|
-
const CollapseWrapper = ({ children, collapse = false, isOpened = false }) => collapse ?
|
|
17
|
+
const CollapseWrapper = ({ children, collapse = false, isOpened = false }) => collapse ? (
|
|
18
|
+
// inert while collapsed so the hidden links leave the tab order / a11y tree.
|
|
19
|
+
/* @__PURE__ */ jsx(Collapse, { isOpened, children: /* @__PURE__ */ jsx("div", { ...isOpened ? {} : { inert: true }, children }) })
|
|
20
|
+
) : /* @__PURE__ */ jsx(Fragment$1, { children });
|
|
18
21
|
CollapseWrapper.propTypes = {
|
|
19
22
|
children: PropTypes.node.isRequired,
|
|
20
23
|
collapse: PropTypes.bool,
|
|
@@ -25,31 +25,7 @@ import { sizes } from "../../styles/rwd.js";
|
|
|
25
25
|
import { track } from "../../analytics/index.js";
|
|
26
26
|
import { navigateToHref } from "../../routing/navigate-app.js";
|
|
27
27
|
import { Breakdown } from "../breakdown/Breakdown.js";
|
|
28
|
-
const UserAvatarComponent = ({ avatar }) => {
|
|
29
|
-
const [photo, setphoto] = useState(avatarPlaceholder);
|
|
30
|
-
const prevAvatar = usePrevious(avatar);
|
|
31
|
-
const checkUserPhoto = (_avatar) => {
|
|
32
|
-
try {
|
|
33
|
-
const http = new XMLHttpRequest();
|
|
34
|
-
http.open("HEAD", _avatar, true);
|
|
35
|
-
http.send();
|
|
36
|
-
if (http.status !== 404) {
|
|
37
|
-
setphoto(_avatar);
|
|
38
|
-
}
|
|
39
|
-
} catch (error) {
|
|
40
|
-
console.log("error: ", error);
|
|
41
|
-
}
|
|
42
|
-
};
|
|
43
|
-
useEffect(() => {
|
|
44
|
-
checkUserPhoto(avatar);
|
|
45
|
-
}, [avatar]);
|
|
46
|
-
useEffect(() => {
|
|
47
|
-
if (avatar != prevAvatar) {
|
|
48
|
-
checkUserPhoto(avatar);
|
|
49
|
-
}
|
|
50
|
-
}, [avatar]);
|
|
51
|
-
return /* @__PURE__ */ jsx(UserAvatar, { src: photo });
|
|
52
|
-
};
|
|
28
|
+
const UserAvatarComponent = ({ avatar }) => /* @__PURE__ */ jsx(UserAvatar, { src: avatar || avatarPlaceholder });
|
|
53
29
|
const RenderSubMenu = ({
|
|
54
30
|
title,
|
|
55
31
|
isActive,
|
|
@@ -189,7 +165,7 @@ function HeaderView({
|
|
|
189
165
|
withSidebar && /* @__PURE__ */ jsx(SidebarToggle, { isSidebarExpanded, onClick: sidebarHandler, children: /* @__PURE__ */ jsx(HamburgerIcon, {}) }),
|
|
190
166
|
/* @__PURE__ */ jsxs(HeaderInner, { withSidebar, children: [
|
|
191
167
|
/* @__PURE__ */ jsxs(MainNavWrap, { hasProgress: progress, children: [
|
|
192
|
-
/* @__PURE__ */ jsx(Logo, { isHighlight: currentActiveMenu === "main", children: /* @__PURE__ */ jsx(stdin_default$1, { to: useRelativePathForMenu ? "/" : platformURL, "data-cy": "logo-nav-link", children: /* @__PURE__ */ jsx(stdin_default$2, {}) }) }),
|
|
168
|
+
/* @__PURE__ */ jsx(Logo, { isHighlight: currentActiveMenu === "main", children: /* @__PURE__ */ jsx(stdin_default$1, { to: useRelativePathForMenu ? "/" : platformURL, "data-cy": "logo-nav-link", "aria-label": "STOKR home", children: /* @__PURE__ */ jsx(stdin_default$2, {}) }) }),
|
|
193
169
|
!progress && /* @__PURE__ */ jsx(HeaderMainNav, { children: /* @__PURE__ */ jsx(MenuNav, { children: /* @__PURE__ */ jsxs("ul", { children: [
|
|
194
170
|
/* @__PURE__ */ jsx("li", { children: /* @__PURE__ */ jsx(
|
|
195
171
|
stdin_default$1,
|
|
@@ -248,6 +224,9 @@ function HeaderView({
|
|
|
248
224
|
isActive: currentActiveMenu === "main",
|
|
249
225
|
onClick: () => toggleMenu("main"),
|
|
250
226
|
withSidebar,
|
|
227
|
+
type: "button",
|
|
228
|
+
"aria-label": "Toggle navigation menu",
|
|
229
|
+
"aria-expanded": currentActiveMenu === "main",
|
|
251
230
|
children: [
|
|
252
231
|
/* @__PURE__ */ jsx("span", {}),
|
|
253
232
|
/* @__PURE__ */ jsx("span", {}),
|
package/dist/context/Auth.js
CHANGED
|
@@ -1,12 +1,10 @@
|
|
|
1
1
|
import Cookies from "js-cookie";
|
|
2
|
-
import axios from "axios";
|
|
3
2
|
import axiosInstance from "../model/axios.js";
|
|
4
3
|
import axiosInstance$1 from "../model/axiosPublic.js";
|
|
5
4
|
import getCookieDomain from "../utils/get-cookie-domain.js";
|
|
6
5
|
import { getLastFirebaseConfig } from "../config.js";
|
|
7
6
|
import { initFirebase, getFirebaseAuth } from "../firebase-config.js";
|
|
8
7
|
import { updatePassword, updateProfile, signInWithCustomToken, signInWithEmailAndPassword, createUserWithEmailAndPassword, applyActionCode, verifyPasswordResetCode, confirmPasswordReset, onAuthStateChanged } from "firebase/auth";
|
|
9
|
-
import { getConfig } from "../runtime-config.js";
|
|
10
8
|
function normalizeCustomToken(token) {
|
|
11
9
|
if (token == null) return "";
|
|
12
10
|
if (typeof token === "object" && token !== null) {
|
|
@@ -372,16 +370,30 @@ class Auth {
|
|
|
372
370
|
});
|
|
373
371
|
}
|
|
374
372
|
/**
|
|
375
|
-
*
|
|
376
|
-
*
|
|
373
|
+
* Get the authenticated user's avatar from service-media.
|
|
374
|
+
* Returns the image Blob, or null when the user has no picture (404).
|
|
377
375
|
*/
|
|
378
|
-
static
|
|
376
|
+
static getAvatar() {
|
|
379
377
|
return new Promise((resolve, reject) => {
|
|
380
|
-
|
|
381
|
-
|
|
382
|
-
|
|
378
|
+
axiosInstance.post(`media/user/avatar/get`, null, { responseType: "blob" }).then((response) => {
|
|
379
|
+
resolve(response.data);
|
|
380
|
+
}).catch((err) => {
|
|
381
|
+
if (err?.response?.status === 404) {
|
|
382
|
+
resolve(null);
|
|
383
|
+
return;
|
|
383
384
|
}
|
|
384
|
-
|
|
385
|
+
reject(err);
|
|
386
|
+
});
|
|
387
|
+
});
|
|
388
|
+
}
|
|
389
|
+
/**
|
|
390
|
+
* Upload the authenticated user's avatar to service-media.
|
|
391
|
+
* Responds 201 with the processed (resized) image, so we read it back as a Blob.
|
|
392
|
+
* @param data FormData with a `file` field (jpeg/png, ≤10mb)
|
|
393
|
+
*/
|
|
394
|
+
static uploadPhoto(data) {
|
|
395
|
+
return new Promise((resolve, reject) => {
|
|
396
|
+
axiosInstance.post(`media/user/avatar/upload`, data, { responseType: "blob" }).then((response) => {
|
|
385
397
|
resolve(response.data);
|
|
386
398
|
}).catch((err) => {
|
|
387
399
|
reject(err);
|
|
@@ -389,16 +401,11 @@ class Auth {
|
|
|
389
401
|
});
|
|
390
402
|
}
|
|
391
403
|
/**
|
|
392
|
-
* Delete
|
|
393
|
-
* @param data
|
|
404
|
+
* Delete the authenticated user's avatar from service-media.
|
|
394
405
|
*/
|
|
395
|
-
static deletePhoto(
|
|
406
|
+
static deletePhoto() {
|
|
396
407
|
return new Promise((resolve, reject) => {
|
|
397
|
-
|
|
398
|
-
headers: {
|
|
399
|
-
"Content-Type": "multipart/form-data"
|
|
400
|
-
}
|
|
401
|
-
}).then((response) => {
|
|
408
|
+
axiosInstance.post(`media/user/avatar/delete`).then((response) => {
|
|
402
409
|
resolve(response.data);
|
|
403
410
|
}).catch((err) => {
|
|
404
411
|
reject(err);
|
|
@@ -102,6 +102,7 @@ class AuthProviderClass extends Component {
|
|
|
102
102
|
INACTIVITY_EVENTS.forEach((event) => window.removeEventListener(event, this.resetInactivityTimer));
|
|
103
103
|
this.clearInactivityTimer();
|
|
104
104
|
this.clearCookieExpiryTimer();
|
|
105
|
+
if (this.avatarObjectUrl) URL.revokeObjectURL(this.avatarObjectUrl);
|
|
105
106
|
}
|
|
106
107
|
checkTokenIsValid = (redirect = false) => {
|
|
107
108
|
const accessToken = Auth.getAccessToken();
|
|
@@ -398,6 +399,10 @@ class AuthProviderClass extends Component {
|
|
|
398
399
|
delete axiosInstance.defaults.headers.common.Authorization;
|
|
399
400
|
reset();
|
|
400
401
|
this.userRef.current = null;
|
|
402
|
+
if (this.avatarObjectUrl) {
|
|
403
|
+
URL.revokeObjectURL(this.avatarObjectUrl);
|
|
404
|
+
this.avatarObjectUrl = void 0;
|
|
405
|
+
}
|
|
401
406
|
this.setState({
|
|
402
407
|
user: null,
|
|
403
408
|
firebaseUser: null,
|
|
@@ -446,20 +451,27 @@ class AuthProviderClass extends Component {
|
|
|
446
451
|
merged.active = merged.emailVerified;
|
|
447
452
|
return merged;
|
|
448
453
|
};
|
|
449
|
-
|
|
454
|
+
// Fetch the authenticated user's avatar from service-media as a Blob and expose it as an
|
|
455
|
+
// object URL. Falls back to the placeholder on 404/error. The endpoint requires the Bearer
|
|
456
|
+
// token, so it can't be used directly as an <img src> — hence the blob round-trip.
|
|
457
|
+
loadAvatar = async () => {
|
|
450
458
|
try {
|
|
451
|
-
const
|
|
452
|
-
|
|
453
|
-
http.onload = () => {
|
|
454
|
-
if (http.status === 200) {
|
|
455
|
-
this.setState({ avatar });
|
|
456
|
-
}
|
|
457
|
-
};
|
|
458
|
-
http.send();
|
|
459
|
+
const blob = await Auth.getAvatar();
|
|
460
|
+
this.setAvatarObjectUrl(blob ? URL.createObjectURL(blob) : avatarPlaceholder);
|
|
459
461
|
} catch (error) {
|
|
460
|
-
console.log("error: ", error);
|
|
462
|
+
console.log("error loading avatar: ", error);
|
|
463
|
+
this.setAvatarObjectUrl(avatarPlaceholder);
|
|
464
|
+
}
|
|
465
|
+
};
|
|
466
|
+
setAvatarObjectUrl = (nextAvatar) => {
|
|
467
|
+
if (this.avatarObjectUrl && this.avatarObjectUrl !== nextAvatar) {
|
|
468
|
+
URL.revokeObjectURL(this.avatarObjectUrl);
|
|
461
469
|
}
|
|
470
|
+
this.avatarObjectUrl = nextAvatar?.startsWith("blob:") ? nextAvatar : void 0;
|
|
471
|
+
this.setState({ avatar: nextAvatar });
|
|
462
472
|
};
|
|
473
|
+
// Kept for backward compatibility; refetches the current user's avatar.
|
|
474
|
+
checkUserPhoto = () => this.loadAvatar();
|
|
463
475
|
getUser = async (accessToken = Auth.getAccessToken(), firebaseUserOverride) => {
|
|
464
476
|
if (!accessToken) {
|
|
465
477
|
this.setState({
|
|
@@ -533,11 +545,7 @@ class AuthProviderClass extends Component {
|
|
|
533
545
|
const user = this.patchUserObject(result.user, firebaseUser);
|
|
534
546
|
this.checkUserIsValid(user);
|
|
535
547
|
customValidateGetUser?.(user);
|
|
536
|
-
|
|
537
|
-
if (userIdForMedia) {
|
|
538
|
-
const userAvatar = `${getConfig("photoApiUrl")}/media/picture/view/${userIdForMedia}`;
|
|
539
|
-
this.checkUserPhoto(userAvatar);
|
|
540
|
-
}
|
|
548
|
+
this.loadAvatar();
|
|
541
549
|
this.setUser(user);
|
|
542
550
|
this.setState({
|
|
543
551
|
isFetchingUser: false
|
|
@@ -617,13 +625,10 @@ class AuthProviderClass extends Component {
|
|
|
617
625
|
return;
|
|
618
626
|
}
|
|
619
627
|
const formData = new FormData();
|
|
620
|
-
formData.append("userId", user._id);
|
|
621
628
|
formData.append("file", file);
|
|
622
|
-
const
|
|
623
|
-
this.
|
|
624
|
-
|
|
625
|
-
});
|
|
626
|
-
return result;
|
|
629
|
+
const blob = await Auth.uploadPhoto(formData);
|
|
630
|
+
this.setAvatarObjectUrl(blob ? URL.createObjectURL(blob) : avatarPlaceholder);
|
|
631
|
+
return blob;
|
|
627
632
|
} catch (error) {
|
|
628
633
|
console.log(`Error: ${error}`);
|
|
629
634
|
throw error;
|
|
@@ -634,12 +639,8 @@ class AuthProviderClass extends Component {
|
|
|
634
639
|
if (!user) {
|
|
635
640
|
return;
|
|
636
641
|
}
|
|
637
|
-
|
|
638
|
-
|
|
639
|
-
await Auth.deletePhoto(formData);
|
|
640
|
-
this.setState({
|
|
641
|
-
avatar: void 0
|
|
642
|
-
});
|
|
642
|
+
await Auth.deletePhoto();
|
|
643
|
+
this.setAvatarObjectUrl(avatarPlaceholder);
|
|
643
644
|
};
|
|
644
645
|
replaceLocationPathName = () => {
|
|
645
646
|
const pathname = this.props.location?.pathname ?? "";
|