@b3dotfun/sdk 0.0.63 → 0.0.64-alpha.1

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.
Files changed (32) hide show
  1. package/dist/cjs/global-account/react/components/B3DynamicModal.js +7 -3
  2. package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.js +3 -3
  3. package/dist/cjs/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
  4. package/dist/cjs/global-account/react/components/ProfileEditor/ProfileEditor.js +141 -0
  5. package/dist/cjs/global-account/react/components/index.d.ts +2 -0
  6. package/dist/cjs/global-account/react/components/index.js +7 -2
  7. package/dist/cjs/global-account/react/hooks/useAuthentication.js +0 -11
  8. package/dist/cjs/global-account/react/stores/useModalStore.d.ts +7 -1
  9. package/dist/cjs/global-account/react/utils/profileDisplay.d.ts +6 -0
  10. package/dist/cjs/global-account/react/utils/profileDisplay.js +60 -4
  11. package/dist/esm/global-account/react/components/B3DynamicModal.js +7 -3
  12. package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.js +3 -3
  13. package/dist/esm/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
  14. package/dist/esm/global-account/react/components/ProfileEditor/ProfileEditor.js +135 -0
  15. package/dist/esm/global-account/react/components/index.d.ts +2 -0
  16. package/dist/esm/global-account/react/components/index.js +3 -0
  17. package/dist/esm/global-account/react/hooks/useAuthentication.js +0 -11
  18. package/dist/esm/global-account/react/stores/useModalStore.d.ts +7 -1
  19. package/dist/esm/global-account/react/utils/profileDisplay.d.ts +6 -0
  20. package/dist/esm/global-account/react/utils/profileDisplay.js +59 -4
  21. package/dist/types/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +6 -0
  22. package/dist/types/global-account/react/components/index.d.ts +2 -0
  23. package/dist/types/global-account/react/stores/useModalStore.d.ts +7 -1
  24. package/dist/types/global-account/react/utils/profileDisplay.d.ts +6 -0
  25. package/package.json +2 -2
  26. package/src/global-account/react/components/B3DynamicModal.tsx +7 -3
  27. package/src/global-account/react/components/ManageAccount/BalanceContent.tsx +4 -4
  28. package/src/global-account/react/components/ProfileEditor/ProfileEditor.tsx +265 -0
  29. package/src/global-account/react/components/index.ts +4 -0
  30. package/src/global-account/react/hooks/useAuthentication.ts +0 -12
  31. package/src/global-account/react/stores/useModalStore.ts +9 -1
  32. package/src/global-account/react/utils/profileDisplay.ts +67 -4
@@ -15,6 +15,7 @@ const react_4 = require("thirdweb/react");
15
15
  const AvatarEditor_1 = require("./AvatarEditor/AvatarEditor");
16
16
  const useB3_1 = require("./B3Provider/useB3");
17
17
  const LinkAccount_1 = require("./LinkAccount/LinkAccount");
18
+ const ProfileEditor_1 = require("./ProfileEditor/ProfileEditor");
18
19
  const ManageAccount_1 = require("./ManageAccount/ManageAccount");
19
20
  const RequestPermissions_1 = require("./RequestPermissions/RequestPermissions");
20
21
  const SignInWithB3Flow_1 = require("./SignInWithB3/SignInWithB3Flow");
@@ -56,6 +57,7 @@ function B3DynamicModal() {
56
57
  "anySpendBondKit",
57
58
  "linkAccount",
58
59
  "avatarEditor",
60
+ "profileEditor",
59
61
  ];
60
62
  const freestyleTypes = [
61
63
  "anySpendNft",
@@ -118,6 +120,8 @@ function B3DynamicModal() {
118
120
  return (0, jsx_runtime_1.jsx)(AnyspendDepositHype_1.AnySpendDepositHype, { ...contentType, mode: "modal" });
119
121
  case "avatarEditor":
120
122
  return (0, jsx_runtime_1.jsx)(AvatarEditor_1.AvatarEditor, { onSetAvatar: contentType.onSuccess });
123
+ case "profileEditor":
124
+ return (0, jsx_runtime_1.jsx)(ProfileEditor_1.ProfileEditor, { onSuccess: contentType.onSuccess });
121
125
  // Add other modal types here
122
126
  default:
123
127
  return null;
@@ -128,8 +132,8 @@ function B3DynamicModal() {
128
132
  const ModalTitle = isMobile ? drawer_1.DrawerTitle : dialog_1.DialogTitle;
129
133
  const ModalDescription = isMobile ? drawer_1.DrawerDescription : dialog_1.DialogDescription;
130
134
  return ((0, jsx_runtime_1.jsxs)(ModalComponent, { open: isOpen, onOpenChange: setB3ModalOpen, children: [(0, jsx_runtime_1.jsxs)(ModalContent, { className: (0, cn_1.cn)(contentClass, "rounded-2xl bg-white shadow-xl dark:bg-gray-900", "border border-gray-200 dark:border-gray-800",
131
- // Remove default width classes for avatar editor
132
- contentType?.type === "avatarEditor"
135
+ // Remove default width classes for avatar editor and profile editor
136
+ contentType?.type === "avatarEditor" || contentType?.type === "profileEditor"
133
137
  ? "!w-[90vw] !max-w-none" // Use !important to override default styles
134
- : "mx-auto w-full max-w-md sm:max-w-lg"), hideCloseButton: hideCloseButton, children: [(0, jsx_runtime_1.jsx)(ModalTitle, { className: "sr-only hidden", children: contentType?.type || "Modal" }), (0, jsx_runtime_1.jsx)(ModalDescription, { className: "sr-only hidden", children: contentType?.type || "Modal Body" }), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("no-scrollbar max-h-[90dvh] overflow-auto sm:max-h-[80dvh]"), children: [history.length > 0 && contentType?.showBackButton && ((0, jsx_runtime_1.jsxs)("button", { onClick: navigateBack, className: "flex items-center gap-2 px-6 py-4 text-gray-600 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: [(0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [(0, jsx_runtime_1.jsx)("path", { d: "M15.8337 10H4.16699", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), (0, jsx_runtime_1.jsx)("path", { d: "M10.0003 15.8334L4.16699 10L10.0003 4.16669", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium", children: "Back" })] })), renderContent()] })] }), contentType?.type === "avatarEditor" && ((0, jsx_runtime_1.jsx)("button", { onClick: () => setB3ModalOpen(false), className: "fixed right-5 top-5 z-[100] cursor-pointer text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: (0, jsx_runtime_1.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: (0, jsx_runtime_1.jsx)("path", { d: "M18 6L6 18M6 6L18 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] }));
138
+ : "mx-auto w-full max-w-md sm:max-w-lg"), hideCloseButton: hideCloseButton, children: [(0, jsx_runtime_1.jsx)(ModalTitle, { className: "sr-only hidden", children: contentType?.type || "Modal" }), (0, jsx_runtime_1.jsx)(ModalDescription, { className: "sr-only hidden", children: contentType?.type || "Modal Body" }), (0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("no-scrollbar max-h-[90dvh] overflow-auto sm:max-h-[80dvh]"), children: [history.length > 0 && contentType?.showBackButton && ((0, jsx_runtime_1.jsxs)("button", { onClick: navigateBack, className: "flex items-center gap-2 px-6 py-4 text-gray-600 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: [(0, jsx_runtime_1.jsxs)("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [(0, jsx_runtime_1.jsx)("path", { d: "M15.8337 10H4.16699", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), (0, jsx_runtime_1.jsx)("path", { d: "M10.0003 15.8334L4.16699 10L10.0003 4.16669", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }), (0, jsx_runtime_1.jsx)("span", { className: "text-sm font-medium", children: "Back" })] })), renderContent()] })] }), (contentType?.type === "avatarEditor" || contentType?.type === "profileEditor") && ((0, jsx_runtime_1.jsx)("button", { onClick: () => setB3ModalOpen(false), className: "fixed right-5 top-5 z-[100] cursor-pointer text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: (0, jsx_runtime_1.jsx)("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: (0, jsx_runtime_1.jsx)("path", { d: "M18 6L6 18M6 6L18 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] }));
135
139
  }
@@ -35,10 +35,10 @@ function BalanceContent({ onLogout, showDeposit = true, showSwap = true }) {
35
35
  const [openAccordions, setOpenAccordions] = (0, react_2.useState)([]);
36
36
  const hasExpandedRef = (0, react_2.useRef)(false);
37
37
  const avatarUrl = user?.avatar ? (0, ipfs_1.getIpfsUrl)(user?.avatar) : profile?.avatar;
38
- const handleEditAvatar = () => {
38
+ const handleEditProfile = () => {
39
39
  setB3ModalOpen(true);
40
40
  setB3ModalContentType({
41
- type: "avatarEditor",
41
+ type: "profileEditor",
42
42
  showBackButton: true,
43
43
  onSuccess: () => {
44
44
  // navigate back on success
@@ -90,7 +90,7 @@ function BalanceContent({ onLogout, showDeposit = true, showSwap = true }) {
90
90
  setB3ModalOpen(false);
91
91
  setLogoutLoading(false);
92
92
  };
93
- return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-between", children: (0, jsx_runtime_1.jsxs)("div", { className: "global-account-profile flex items-center gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "global-account-profile-avatar relative", children: [avatarUrl ? ((0, jsx_runtime_1.jsx)("img", { src: avatarUrl, alt: "Profile", className: "size-24 rounded-full" })) : ((0, jsx_runtime_1.jsx)("div", { className: "bg-b3-primary-wash size-24 rounded-full" })), (0, jsx_runtime_1.jsx)("button", { onClick: handleEditAvatar, className: "bg-b3-grey border-b3-background hover:bg-b3-grey/80 absolute -bottom-1 -right-1 flex size-8 items-center justify-center rounded-full border-4 transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 16, className: "text-b3-background" }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "global-account-profile-info", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-b3-grey text-xl font-semibold", children: profile?.displayName || (0, utils_1.formatUsername)(profile?.name || "") }), (0, jsx_runtime_1.jsxs)("div", { className: "address-button border-b3-line bg-b3-line/20 hover:bg-b3-line/40 flex w-fit items-center gap-2 rounded-full border px-3 py-1 transition-colors", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-b3-foreground-muted font-mono text-xs", children: centerTruncate(account?.address || "", 6) }), (0, jsx_runtime_1.jsx)(react_1.CopyToClipboard, { text: account?.address || "" })] })] })] }) }), (showDeposit || showSwap) && ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-3", children: [showDeposit && ((0, jsx_runtime_1.jsxs)(react_1.Button, { className: "manage-account-deposit bg-b3-primary-wash hover:bg-b3-primary-wash/70 h-[84px] w-full flex-col items-start gap-2 rounded-2xl", onClick: () => {
93
+ return ((0, jsx_runtime_1.jsxs)("div", { className: "flex flex-col gap-6", children: [(0, jsx_runtime_1.jsx)("div", { className: "flex items-center justify-between", children: (0, jsx_runtime_1.jsxs)("div", { className: "global-account-profile flex items-center gap-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "global-account-profile-avatar relative", children: [avatarUrl ? ((0, jsx_runtime_1.jsx)("img", { src: avatarUrl, alt: "Profile", className: "size-24 rounded-full" })) : ((0, jsx_runtime_1.jsx)("div", { className: "bg-b3-primary-wash size-24 rounded-full" })), (0, jsx_runtime_1.jsx)("button", { onClick: handleEditProfile, className: "bg-b3-grey border-b3-background hover:bg-b3-grey/80 absolute -bottom-1 -right-1 flex size-8 items-center justify-center rounded-full border-4 transition-colors", children: (0, jsx_runtime_1.jsx)(lucide_react_1.Pencil, { size: 16, className: "text-b3-background" }) })] }), (0, jsx_runtime_1.jsxs)("div", { className: "global-account-profile-info", children: [(0, jsx_runtime_1.jsx)("h2", { className: "text-b3-grey text-xl font-semibold", children: user?.username || profile?.displayName || (0, utils_1.formatUsername)(profile?.name || "") }), (0, jsx_runtime_1.jsxs)("div", { className: "address-button border-b3-line bg-b3-line/20 hover:bg-b3-line/40 flex w-fit items-center gap-2 rounded-full border px-3 py-1 transition-colors", children: [(0, jsx_runtime_1.jsx)("span", { className: "text-b3-foreground-muted font-mono text-xs", children: centerTruncate(account?.address || "", 6) }), (0, jsx_runtime_1.jsx)(react_1.CopyToClipboard, { text: account?.address || "" })] })] })] }) }), (showDeposit || showSwap) && ((0, jsx_runtime_1.jsxs)("div", { className: "grid grid-cols-2 gap-3", children: [showDeposit && ((0, jsx_runtime_1.jsxs)(react_1.Button, { className: "manage-account-deposit bg-b3-primary-wash hover:bg-b3-primary-wash/70 h-[84px] w-full flex-col items-start gap-2 rounded-2xl", onClick: () => {
94
94
  setB3ModalOpen(true);
95
95
  setB3ModalContentType({
96
96
  type: "anySpend",
@@ -0,0 +1,6 @@
1
+ interface ProfileEditorProps {
2
+ onSuccess?: () => void;
3
+ className?: string;
4
+ }
5
+ export declare function ProfileEditor({ onSuccess, className }: ProfileEditorProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};
@@ -0,0 +1,141 @@
1
+ "use strict";
2
+ "use client";
3
+ var __importDefault = (this && this.__importDefault) || function (mod) {
4
+ return (mod && mod.__esModule) ? mod : { "default": mod };
5
+ };
6
+ Object.defineProperty(exports, "__esModule", { value: true });
7
+ exports.ProfileEditor = ProfileEditor;
8
+ const jsx_runtime_1 = require("react/jsx-runtime");
9
+ const app_1 = __importDefault(require("../../../../global-account/app"));
10
+ const react_1 = require("../../../../global-account/react");
11
+ const profileDisplay_1 = require("../../../../global-account/react/utils/profileDisplay");
12
+ const cn_1 = require("../../../../shared/utils/cn");
13
+ const debug_1 = require("../../../../shared/utils/debug");
14
+ const ipfs_1 = require("../../../../shared/utils/ipfs");
15
+ const thirdweb_1 = require("../../../../shared/utils/thirdweb");
16
+ const lucide_react_1 = require("lucide-react");
17
+ const react_2 = require("react");
18
+ const sonner_1 = require("sonner");
19
+ const react_3 = require("thirdweb/react");
20
+ const storage_1 = require("thirdweb/storage");
21
+ const debug = (0, debug_1.debugB3React)("ProfileEditor");
22
+ function ProfileEditor({ onSuccess, className }) {
23
+ const [selectedFile, setSelectedFile] = (0, react_2.useState)(null);
24
+ const [previewUrl, setPreviewUrl] = (0, react_2.useState)(null);
25
+ const [username, setUsername] = (0, react_2.useState)("");
26
+ const [isUploading, setIsUploading] = (0, react_2.useState)(false);
27
+ const [isSaving, setIsSaving] = (0, react_2.useState)(false);
28
+ const fileInputRef = (0, react_2.useRef)(null);
29
+ const { user, setUser } = (0, react_1.useB3)();
30
+ const account = (0, react_3.useActiveAccount)();
31
+ const { data: profile, refetch: refreshProfile } = (0, react_1.useProfile)({
32
+ address: account?.address,
33
+ fresh: true,
34
+ });
35
+ const rawAvatarUrl = user?.avatar ? (0, ipfs_1.getIpfsUrl)(user?.avatar) : profile?.avatar;
36
+ const avatarUrl = (0, profileDisplay_1.validateImageUrl)(rawAvatarUrl);
37
+ const safePreviewUrl = (0, profileDisplay_1.validateImageUrl)(previewUrl);
38
+ const hasAvatar = !!avatarUrl;
39
+ const currentUsername = user?.username || "";
40
+ const handleFileSelect = (event) => {
41
+ const file = event.target.files?.[0];
42
+ if (file) {
43
+ // Validate file type
44
+ if (!file.type.startsWith("image/")) {
45
+ sonner_1.toast.error("Please select an image file");
46
+ return;
47
+ }
48
+ // Validate file size (max 5MB)
49
+ if (file.size > 5 * 1024 * 1024) {
50
+ sonner_1.toast.error("File size must be less than 5MB");
51
+ return;
52
+ }
53
+ setSelectedFile(file);
54
+ // Create preview URL
55
+ const url = URL.createObjectURL(file);
56
+ setPreviewUrl(url);
57
+ }
58
+ };
59
+ const handleRemoveFile = () => {
60
+ setSelectedFile(null);
61
+ if (previewUrl) {
62
+ URL.revokeObjectURL(previewUrl);
63
+ setPreviewUrl(null);
64
+ }
65
+ if (fileInputRef.current) {
66
+ fileInputRef.current.value = "";
67
+ }
68
+ };
69
+ const handleSave = async () => {
70
+ // Check if there are any changes
71
+ const hasAvatarChange = selectedFile !== null;
72
+ const hasUsernameChange = username.trim() !== "" && username !== currentUsername;
73
+ if (!hasAvatarChange && !hasUsernameChange) {
74
+ sonner_1.toast.error("Please make at least one change");
75
+ return;
76
+ }
77
+ setIsSaving(true);
78
+ try {
79
+ let ipfsUrl;
80
+ // Upload avatar if selected
81
+ if (hasAvatarChange && selectedFile) {
82
+ debug("Starting upload to IPFS", selectedFile);
83
+ setIsUploading(true);
84
+ ipfsUrl = await (0, storage_1.upload)({
85
+ client: thirdweb_1.client,
86
+ files: [selectedFile],
87
+ });
88
+ debug("Upload successful", ipfsUrl);
89
+ setIsUploading(false);
90
+ }
91
+ // Update user profile
92
+ let updatedUser = user;
93
+ // If both avatar and username need updating, do them sequentially
94
+ // Update avatar first if uploaded
95
+ if (ipfsUrl) {
96
+ // @ts-expect-error this resolved fine, look into why expect-error needed
97
+ updatedUser = await app_1.default.service("users").setAvatar({
98
+ avatar: ipfsUrl,
99
+ },
100
+ // @ts-expect-error - our typed client is expecting context even though it's set elsewhere
101
+ {});
102
+ }
103
+ // Update username if changed (this will use the updated user from avatar change if both were updated)
104
+ if (hasUsernameChange && user?._id) {
105
+ // @ts-expect-error this resolved fine, look into why expect-error needed
106
+ updatedUser = await app_1.default.service("users").registerUsername({ username: username },
107
+ // @ts-expect-error - our typed client is expecting context even though it's set elsewhere
108
+ {});
109
+ }
110
+ // Update user state
111
+ setUser(updatedUser);
112
+ // Refresh profile to get updated data
113
+ await refreshProfile();
114
+ // Show success message
115
+ const changes = [];
116
+ if (hasAvatarChange)
117
+ changes.push("avatar");
118
+ if (hasUsernameChange)
119
+ changes.push("username");
120
+ sonner_1.toast.success(`Successfully updated ${changes.join(" and ")}!`);
121
+ onSuccess?.();
122
+ // Clean up
123
+ handleRemoveFile();
124
+ setUsername("");
125
+ }
126
+ catch (error) {
127
+ debug("Error updating profile:", error);
128
+ sonner_1.toast.error("Failed to update profile. Please try again.");
129
+ }
130
+ finally {
131
+ setIsUploading(false);
132
+ setIsSaving(false);
133
+ }
134
+ };
135
+ const handleFileInputClick = () => {
136
+ fileInputRef.current?.click();
137
+ };
138
+ const isLoading = isUploading || isSaving;
139
+ const hasChanges = selectedFile !== null || (username.trim() !== "" && username !== currentUsername);
140
+ return ((0, jsx_runtime_1.jsxs)("div", { className: (0, cn_1.cn)("flex flex-col items-center justify-center space-y-6 p-8", className), children: [(0, jsx_runtime_1.jsxs)("div", { className: "space-y-2 text-center", children: [(0, jsx_runtime_1.jsx)("h2", { className: "font-neue-montreal-semibold text-b3-grey text-2xl", children: "Edit Your Profile" }), (0, jsx_runtime_1.jsx)("p", { className: "text-b3-foreground-muted font-neue-montreal-medium", children: "Update your avatar and username" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "w-full max-w-md space-y-4", children: [(0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { className: "text-b3-grey font-neue-montreal-semibold text-sm", children: "Avatar" }), (0, jsx_runtime_1.jsx)("div", { className: "flex justify-center", children: safePreviewUrl || avatarUrl ? ((0, jsx_runtime_1.jsxs)("div", { className: "relative", children: [(0, jsx_runtime_1.jsx)("div", { className: "border-b3-primary-blue h-32 w-32 overflow-hidden rounded-full border-4", children: (0, jsx_runtime_1.jsx)("img", { src: safePreviewUrl || avatarUrl || "", alt: safePreviewUrl ? "Preview" : "Current avatar", className: "h-full w-full object-cover" }) }), safePreviewUrl && ((0, jsx_runtime_1.jsx)("button", { onClick: handleRemoveFile, className: "bg-b3-negative absolute -right-2 -top-2 flex h-8 w-8 items-center justify-center rounded-full text-white transition-colors hover:bg-red-600", disabled: isLoading, children: (0, jsx_runtime_1.jsx)(lucide_react_1.X, { size: 16 }) }))] })) : ((0, jsx_runtime_1.jsx)("div", { className: "bg-b3-primary-wash h-32 w-32 rounded-full" })) }), !selectedFile && ((0, jsx_runtime_1.jsxs)(react_1.Button, { variant: "outline", onClick: handleFileInputClick, disabled: isLoading, className: "w-full", children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Upload, { className: "mr-2 h-4 w-4" }), hasAvatar ? "Change Avatar" : "Upload Avatar"] })), (0, jsx_runtime_1.jsx)("input", { ref: fileInputRef, type: "file", accept: "image/*", onChange: handleFileSelect, className: "hidden" })] }), (0, jsx_runtime_1.jsxs)("div", { className: "space-y-2", children: [(0, jsx_runtime_1.jsx)("label", { htmlFor: "username", className: "text-b3-grey font-neue-montreal-semibold text-sm", children: "Username" }), (0, jsx_runtime_1.jsx)("input", { id: "username", type: "text", value: username, onChange: e => setUsername(e.target.value), placeholder: currentUsername || "Enter username", className: "border-b3-line bg-b3-background text-b3-grey placeholder:text-b3-foreground-muted font-neue-montreal-medium focus:border-b3-primary-blue w-full rounded-lg border px-4 py-3 transition-colors focus:outline-none", disabled: isLoading }), currentUsername && ((0, jsx_runtime_1.jsxs)("p", { className: "text-b3-foreground-muted font-neue-montreal-medium text-xs", children: ["Current: ", currentUsername] }))] })] }), (0, jsx_runtime_1.jsx)("div", { className: "flex w-full max-w-md gap-3", children: (0, jsx_runtime_1.jsx)(react_1.Button, { onClick: handleSave, disabled: isLoading || !hasChanges, className: "bg-b3-primary-blue hover:bg-b3-primary-blue/90 flex-1 text-white disabled:opacity-50", children: isLoading ? ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Loader2, { className: "mr-2 h-4 w-4 animate-spin" }), isUploading ? "Uploading..." : "Saving..."] })) : ((0, jsx_runtime_1.jsxs)(jsx_runtime_1.Fragment, { children: [(0, jsx_runtime_1.jsx)(lucide_react_1.Check, { className: "mr-2 h-4 w-4" }), "Save Changes"] })) }) }), (0, jsx_runtime_1.jsx)("div", { className: "text-b3-foreground-muted font-neue-montreal-medium max-w-md text-center text-xs", children: (0, jsx_runtime_1.jsx)("p", { children: "Your avatar will be uploaded to IPFS and stored securely. Make sure you have the rights to use this image." }) })] }));
141
+ }
@@ -13,6 +13,8 @@ export { SignInWithB3Privy } from "./SignInWithB3/SignInWithB3Privy";
13
13
  export { LoginStepContainer } from "./SignInWithB3/steps/LoginStep";
14
14
  export { getConnectOptionsFromStrategy, isWalletType, type AllowedStrategy } from "./SignInWithB3/utils/signInUtils";
15
15
  export { ManageAccount } from "./ManageAccount/ManageAccount";
16
+ export { AvatarEditor } from "./AvatarEditor/AvatarEditor";
17
+ export { ProfileEditor } from "./ProfileEditor/ProfileEditor";
16
18
  export { RequestPermissions } from "./RequestPermissions/RequestPermissions";
17
19
  export { RequestPermissionsButton } from "./RequestPermissions/RequestPermissionsButton";
18
20
  export { AccountAssets } from "./AccountAssets/AccountAssets";
@@ -1,7 +1,7 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
- exports.DialogPortal = exports.DialogOverlay = exports.DialogHeader = exports.DialogFooter = exports.DialogDescription = exports.DialogContent = exports.DialogClose = exports.Dialog = exports.CommandShortcut = exports.CommandSeparator = exports.CommandList = exports.CommandItem = exports.CommandInput = exports.CommandGroup = exports.CommandEmpty = exports.CommandDialog = exports.Command = exports.buttonVariants = exports.Button = exports.badgeVariants = exports.Badge = exports.WalletConnectorIcon = exports.StaggeredFadeLoader = exports.CopyToClipboard = exports.ClientOnly = exports.customButtonVariants = exports.CustomButton = exports.SendERC20Button = exports.SendETHButton = exports.MintButton = exports.AccountAssets = exports.RequestPermissionsButton = exports.RequestPermissions = exports.ManageAccount = exports.isWalletType = exports.getConnectOptionsFromStrategy = exports.LoginStepContainer = exports.SignInWithB3Privy = exports.SignInWithB3Flow = exports.SignInWithB3 = exports.WalletRow = exports.PermissionItem = exports.AuthButton = exports.StyleRoot = exports.useB3 = exports.B3Context = exports.RelayKitProviderWrapper = exports.InnerProvider = exports.B3Provider = exports.B3DynamicModal = void 0;
4
- exports.AnimatedLottie = exports.TransitionPanel = exports.TooltipTrigger = exports.TooltipProvider = exports.TooltipContent = exports.Tooltip = exports.TextShimmer = exports.TextLoop = exports.TabsTransitionWrapper = exports.TabsList = exports.TabsContent = exports.Tabs = exports.TabTrigger = exports.TabsPrimitive = exports.TabsListPrimitive = exports.TabsContentPrimitive = exports.TabTriggerPrimitive = exports.Skeleton = exports.ShinyButton = exports.ScrollBar = exports.ScrollArea = exports.PopoverTrigger = exports.PopoverContent = exports.Popover = exports.Loading = exports.Input = exports.GlareCardRounded = exports.GlareCard = exports.DropdownMenuTrigger = exports.DropdownMenuSeparator = exports.DropdownMenuItem = exports.DropdownMenuContent = exports.DropdownMenu = exports.DrawerTrigger = exports.DrawerTitle = exports.DrawerPortal = exports.DrawerOverlay = exports.DrawerHeader = exports.DrawerFooter = exports.DrawerDescription = exports.DrawerContent = exports.DrawerClose = exports.Drawer = exports.DialogTrigger = exports.DialogTitle = void 0;
3
+ exports.DialogHeader = exports.DialogFooter = exports.DialogDescription = exports.DialogContent = exports.DialogClose = exports.Dialog = exports.CommandShortcut = exports.CommandSeparator = exports.CommandList = exports.CommandItem = exports.CommandInput = exports.CommandGroup = exports.CommandEmpty = exports.CommandDialog = exports.Command = exports.buttonVariants = exports.Button = exports.badgeVariants = exports.Badge = exports.WalletConnectorIcon = exports.StaggeredFadeLoader = exports.CopyToClipboard = exports.ClientOnly = exports.customButtonVariants = exports.CustomButton = exports.SendERC20Button = exports.SendETHButton = exports.MintButton = exports.AccountAssets = exports.RequestPermissionsButton = exports.RequestPermissions = exports.ProfileEditor = exports.AvatarEditor = exports.ManageAccount = exports.isWalletType = exports.getConnectOptionsFromStrategy = exports.LoginStepContainer = exports.SignInWithB3Privy = exports.SignInWithB3Flow = exports.SignInWithB3 = exports.WalletRow = exports.PermissionItem = exports.AuthButton = exports.StyleRoot = exports.useB3 = exports.B3Context = exports.RelayKitProviderWrapper = exports.InnerProvider = exports.B3Provider = exports.B3DynamicModal = void 0;
4
+ exports.AnimatedLottie = exports.TransitionPanel = exports.TooltipTrigger = exports.TooltipProvider = exports.TooltipContent = exports.Tooltip = exports.TextShimmer = exports.TextLoop = exports.TabsTransitionWrapper = exports.TabsList = exports.TabsContent = exports.Tabs = exports.TabTrigger = exports.TabsPrimitive = exports.TabsListPrimitive = exports.TabsContentPrimitive = exports.TabTriggerPrimitive = exports.Skeleton = exports.ShinyButton = exports.ScrollBar = exports.ScrollArea = exports.PopoverTrigger = exports.PopoverContent = exports.Popover = exports.Loading = exports.Input = exports.GlareCardRounded = exports.GlareCard = exports.DropdownMenuTrigger = exports.DropdownMenuSeparator = exports.DropdownMenuItem = exports.DropdownMenuContent = exports.DropdownMenu = exports.DrawerTrigger = exports.DrawerTitle = exports.DrawerPortal = exports.DrawerOverlay = exports.DrawerHeader = exports.DrawerFooter = exports.DrawerDescription = exports.DrawerContent = exports.DrawerClose = exports.Drawer = exports.DialogTrigger = exports.DialogTitle = exports.DialogPortal = exports.DialogOverlay = void 0;
5
5
  // Core Components
6
6
  var B3DynamicModal_1 = require("./B3DynamicModal");
7
7
  Object.defineProperty(exports, "B3DynamicModal", { enumerable: true, get: function () { return B3DynamicModal_1.B3DynamicModal; } });
@@ -37,6 +37,11 @@ Object.defineProperty(exports, "isWalletType", { enumerable: true, get: function
37
37
  // ManageAccount Components
38
38
  var ManageAccount_1 = require("./ManageAccount/ManageAccount");
39
39
  Object.defineProperty(exports, "ManageAccount", { enumerable: true, get: function () { return ManageAccount_1.ManageAccount; } });
40
+ // Profile Components
41
+ var AvatarEditor_1 = require("./AvatarEditor/AvatarEditor");
42
+ Object.defineProperty(exports, "AvatarEditor", { enumerable: true, get: function () { return AvatarEditor_1.AvatarEditor; } });
43
+ var ProfileEditor_1 = require("./ProfileEditor/ProfileEditor");
44
+ Object.defineProperty(exports, "ProfileEditor", { enumerable: true, get: function () { return ProfileEditor_1.ProfileEditor; } });
40
45
  // RequestPermissions Components
41
46
  var RequestPermissions_1 = require("./RequestPermissions/RequestPermissions");
42
47
  Object.defineProperty(exports, "RequestPermissions", { enumerable: true, get: function () { return RequestPermissions_1.RequestPermissions; } });
@@ -44,17 +44,6 @@ function useAuthentication(partnerId) {
44
44
  const activeWagmiAccount = (0, wagmi_1.useAccount)();
45
45
  const { switchAccount } = (0, wagmi_1.useSwitchAccount)();
46
46
  debug("@@activeWagmiAccount", activeWagmiAccount);
47
- // Check localStorage version and clear if not found or mismatched
48
- (0, react_2.useEffect)(() => {
49
- if (typeof localStorage !== "undefined") {
50
- const version = localStorage.getItem("version");
51
- if (version !== "1") {
52
- debug("@@localStorage:clearing due to version mismatch", { version });
53
- localStorage.clear();
54
- localStorage.setItem("version", "1");
55
- }
56
- }
57
- }, []);
58
47
  const wallet = (0, wallets_1.ecosystemWallet)(constants_1.ecosystemWalletId, {
59
48
  partnerId: partnerId,
60
49
  });
@@ -351,10 +351,16 @@ export interface AvatarEditorModalProps extends BaseModalProps {
351
351
  /** Callback function called when avatar is successfully set */
352
352
  onSuccess?: () => void;
353
353
  }
354
+ export interface ProfileEditorModalProps extends BaseModalProps {
355
+ /** Modal type identifier */
356
+ type: "profileEditor";
357
+ /** Callback function called when profile is successfully updated */
358
+ onSuccess?: () => void;
359
+ }
354
360
  /**
355
361
  * Union type of all possible modal content types
356
362
  */
357
- export type ModalContentType = SignInWithB3ModalProps | RequestPermissionsModalProps | ManageAccountModalProps | AnySpendModalProps | AnyspendOrderDetailsProps | AnySpendNftProps | AnySpendJoinTournamentProps | AnySpendFundTournamentProps | AnySpendOrderHistoryProps | AnySpendStakeB3Props | AnySpendStakeB3ExactInProps | AnySpendStakeUpsideProps | AnySpendStakeUpsideExactInProps | AnySpendBuySpinProps | AnySpendSignatureMintProps | AnySpendBondKitProps | LinkAccountModalProps | AnySpendDepositHypeProps | AvatarEditorModalProps;
363
+ export type ModalContentType = SignInWithB3ModalProps | RequestPermissionsModalProps | ManageAccountModalProps | AnySpendModalProps | AnyspendOrderDetailsProps | AnySpendNftProps | AnySpendJoinTournamentProps | AnySpendFundTournamentProps | AnySpendOrderHistoryProps | AnySpendStakeB3Props | AnySpendStakeB3ExactInProps | AnySpendStakeUpsideProps | AnySpendStakeUpsideExactInProps | AnySpendBuySpinProps | AnySpendSignatureMintProps | AnySpendBondKitProps | LinkAccountModalProps | AnySpendDepositHypeProps | AvatarEditorModalProps | ProfileEditorModalProps;
358
364
  /**
359
365
  * State interface for the modal store
360
366
  */
@@ -1,4 +1,10 @@
1
1
  import { type Profile } from "thirdweb/wallets";
2
+ /**
3
+ * Validates that an image URL uses an allowed schema
4
+ * @param url - The URL to validate
5
+ * @returns The URL if valid, null otherwise
6
+ */
7
+ export declare function validateImageUrl(url: string | null | undefined): string | null;
2
8
  export interface ExtendedProfileDetails {
3
9
  id?: string;
4
10
  email?: string;
@@ -1,6 +1,62 @@
1
1
  "use strict";
2
2
  Object.defineProperty(exports, "__esModule", { value: true });
3
+ exports.validateImageUrl = validateImageUrl;
3
4
  exports.getProfileDisplayInfo = getProfileDisplayInfo;
5
+ const debug_1 = require("../../../shared/utils/debug");
6
+ const debug = (0, debug_1.debugB3React)("profileDisplay");
7
+ /**
8
+ * Validates that an image URL uses an allowed schema
9
+ * @param url - The URL to validate
10
+ * @returns The URL if valid, null otherwise
11
+ */
12
+ function validateImageUrl(url) {
13
+ if (!url)
14
+ return null;
15
+ try {
16
+ // For blob URLs (from createObjectURL)
17
+ if (url.startsWith("blob:")) {
18
+ return url;
19
+ }
20
+ // For IPFS protocol URLs
21
+ if (url.startsWith("ipfs://")) {
22
+ return url;
23
+ }
24
+ // Parse URL to validate protocol and hostname
25
+ const parsedUrl = new URL(url);
26
+ // Only allow http and https protocols
27
+ if (parsedUrl.protocol !== "http:" && parsedUrl.protocol !== "https:") {
28
+ debug("Rejected unsafe protocol:", parsedUrl.protocol, url);
29
+ return null;
30
+ }
31
+ // Whitelist of allowed IPFS gateway hostnames
32
+ const allowedIpfsGateways = [
33
+ "ipfs.io",
34
+ "gateway.pinata.cloud",
35
+ "cloudflare-ipfs.com",
36
+ "dweb.link",
37
+ "nftstorage.link",
38
+ "w3s.link",
39
+ ];
40
+ // Check if hostname matches allowed IPFS gateways
41
+ const hostname = parsedUrl.hostname.toLowerCase();
42
+ const isAllowedIpfsGateway = allowedIpfsGateways.some(gateway => {
43
+ // Exact match or subdomain of the gateway
44
+ return hostname === gateway || hostname.endsWith(`.${gateway}`);
45
+ });
46
+ if (isAllowedIpfsGateway) {
47
+ return url;
48
+ }
49
+ // For standard HTTP(S) URLs from trusted sources
50
+ // Add additional hostname validation here if needed
51
+ // For now, allow all HTTP(S) URLs (can be restricted further if needed)
52
+ return url;
53
+ }
54
+ catch (error) {
55
+ // Invalid URL format
56
+ debug("Invalid image URL format:", url, error);
57
+ return null;
58
+ }
59
+ }
4
60
  function getProfileDisplayInfo(profile) {
5
61
  const { type, details } = profile;
6
62
  // Default display info
@@ -17,7 +73,7 @@ function getProfileDisplayInfo(profile) {
17
73
  displayInfo = {
18
74
  title: details.name || details.username || "Unknown",
19
75
  subtitle: details.username ? `@${details.username}` : "X Account",
20
- imageUrl: details.profileImageUrl || null,
76
+ imageUrl: validateImageUrl(details.profileImageUrl),
21
77
  initial: "X",
22
78
  type,
23
79
  };
@@ -26,7 +82,7 @@ function getProfileDisplayInfo(profile) {
26
82
  displayInfo = {
27
83
  title: details.name || details.username || "Unknown",
28
84
  subtitle: details.username ? `@${details.username}` : "Farcaster Account",
29
- imageUrl: details.profileImageUrl || null,
85
+ imageUrl: validateImageUrl(details.profileImageUrl),
30
86
  initial: "F",
31
87
  type,
32
88
  };
@@ -35,7 +91,7 @@ function getProfileDisplayInfo(profile) {
35
91
  displayInfo = {
36
92
  title: details.name || details.email || "Unknown",
37
93
  subtitle: details.email || "Google Account",
38
- imageUrl: details.profileImageUrl || null,
94
+ imageUrl: validateImageUrl(details.profileImageUrl),
39
95
  initial: "G",
40
96
  type,
41
97
  };
@@ -44,7 +100,7 @@ function getProfileDisplayInfo(profile) {
44
100
  displayInfo = {
45
101
  title: details.username || details.name || "Unknown",
46
102
  subtitle: "Discord Account",
47
- imageUrl: details.profileImageUrl || null,
103
+ imageUrl: validateImageUrl(details.profileImageUrl),
48
104
  initial: "D",
49
105
  type,
50
106
  };
@@ -12,6 +12,7 @@ import { useSetActiveWallet } from "thirdweb/react";
12
12
  import { AvatarEditor } from "./AvatarEditor/AvatarEditor.js";
13
13
  import { useB3 } from "./B3Provider/useB3.js";
14
14
  import { LinkAccount } from "./LinkAccount/LinkAccount.js";
15
+ import { ProfileEditor } from "./ProfileEditor/ProfileEditor.js";
15
16
  import { ManageAccount } from "./ManageAccount/ManageAccount.js";
16
17
  import { RequestPermissions } from "./RequestPermissions/RequestPermissions.js";
17
18
  import { SignInWithB3Flow } from "./SignInWithB3/SignInWithB3Flow.js";
@@ -53,6 +54,7 @@ export function B3DynamicModal() {
53
54
  "anySpendBondKit",
54
55
  "linkAccount",
55
56
  "avatarEditor",
57
+ "profileEditor",
56
58
  ];
57
59
  const freestyleTypes = [
58
60
  "anySpendNft",
@@ -115,6 +117,8 @@ export function B3DynamicModal() {
115
117
  return _jsx(AnySpendDepositHype, { ...contentType, mode: "modal" });
116
118
  case "avatarEditor":
117
119
  return _jsx(AvatarEditor, { onSetAvatar: contentType.onSuccess });
120
+ case "profileEditor":
121
+ return _jsx(ProfileEditor, { onSuccess: contentType.onSuccess });
118
122
  // Add other modal types here
119
123
  default:
120
124
  return null;
@@ -125,8 +129,8 @@ export function B3DynamicModal() {
125
129
  const ModalTitle = isMobile ? DrawerTitle : DialogTitle;
126
130
  const ModalDescription = isMobile ? DrawerDescription : DialogDescription;
127
131
  return (_jsxs(ModalComponent, { open: isOpen, onOpenChange: setB3ModalOpen, children: [_jsxs(ModalContent, { className: cn(contentClass, "rounded-2xl bg-white shadow-xl dark:bg-gray-900", "border border-gray-200 dark:border-gray-800",
128
- // Remove default width classes for avatar editor
129
- contentType?.type === "avatarEditor"
132
+ // Remove default width classes for avatar editor and profile editor
133
+ contentType?.type === "avatarEditor" || contentType?.type === "profileEditor"
130
134
  ? "!w-[90vw] !max-w-none" // Use !important to override default styles
131
- : "mx-auto w-full max-w-md sm:max-w-lg"), hideCloseButton: hideCloseButton, children: [_jsx(ModalTitle, { className: "sr-only hidden", children: contentType?.type || "Modal" }), _jsx(ModalDescription, { className: "sr-only hidden", children: contentType?.type || "Modal Body" }), _jsxs("div", { className: cn("no-scrollbar max-h-[90dvh] overflow-auto sm:max-h-[80dvh]"), children: [history.length > 0 && contentType?.showBackButton && (_jsxs("button", { onClick: navigateBack, className: "flex items-center gap-2 px-6 py-4 text-gray-600 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M15.8337 10H4.16699", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M10.0003 15.8334L4.16699 10L10.0003 4.16669", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }), _jsx("span", { className: "text-sm font-medium", children: "Back" })] })), renderContent()] })] }), contentType?.type === "avatarEditor" && (_jsx("button", { onClick: () => setB3ModalOpen(false), className: "fixed right-5 top-5 z-[100] cursor-pointer text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M18 6L6 18M6 6L18 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] }));
135
+ : "mx-auto w-full max-w-md sm:max-w-lg"), hideCloseButton: hideCloseButton, children: [_jsx(ModalTitle, { className: "sr-only hidden", children: contentType?.type || "Modal" }), _jsx(ModalDescription, { className: "sr-only hidden", children: contentType?.type || "Modal Body" }), _jsxs("div", { className: cn("no-scrollbar max-h-[90dvh] overflow-auto sm:max-h-[80dvh]"), children: [history.length > 0 && contentType?.showBackButton && (_jsxs("button", { onClick: navigateBack, className: "flex items-center gap-2 px-6 py-4 text-gray-600 transition-colors hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: [_jsxs("svg", { width: "20", height: "20", viewBox: "0 0 20 20", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: [_jsx("path", { d: "M15.8337 10H4.16699", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" }), _jsx("path", { d: "M10.0003 15.8334L4.16699 10L10.0003 4.16669", stroke: "currentColor", strokeWidth: "1.5", strokeLinecap: "round", strokeLinejoin: "round" })] }), _jsx("span", { className: "text-sm font-medium", children: "Back" })] })), renderContent()] })] }), (contentType?.type === "avatarEditor" || contentType?.type === "profileEditor") && (_jsx("button", { onClick: () => setB3ModalOpen(false), className: "fixed right-5 top-5 z-[100] cursor-pointer text-gray-600 hover:text-gray-900 dark:text-gray-400 dark:hover:text-white", children: _jsx("svg", { width: "24", height: "24", viewBox: "0 0 24 24", fill: "none", xmlns: "http://www.w3.org/2000/svg", children: _jsx("path", { d: "M18 6L6 18M6 6L18 18", stroke: "currentColor", strokeWidth: "2", strokeLinecap: "round", strokeLinejoin: "round" }) }) }))] }));
132
136
  }
@@ -32,10 +32,10 @@ export function BalanceContent({ onLogout, showDeposit = true, showSwap = true }
32
32
  const [openAccordions, setOpenAccordions] = useState([]);
33
33
  const hasExpandedRef = useRef(false);
34
34
  const avatarUrl = user?.avatar ? getIpfsUrl(user?.avatar) : profile?.avatar;
35
- const handleEditAvatar = () => {
35
+ const handleEditProfile = () => {
36
36
  setB3ModalOpen(true);
37
37
  setB3ModalContentType({
38
- type: "avatarEditor",
38
+ type: "profileEditor",
39
39
  showBackButton: true,
40
40
  onSuccess: () => {
41
41
  // navigate back on success
@@ -87,7 +87,7 @@ export function BalanceContent({ onLogout, showDeposit = true, showSwap = true }
87
87
  setB3ModalOpen(false);
88
88
  setLogoutLoading(false);
89
89
  };
90
- return (_jsxs("div", { className: "flex flex-col gap-6", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "global-account-profile flex items-center gap-4", children: [_jsxs("div", { className: "global-account-profile-avatar relative", children: [avatarUrl ? (_jsx("img", { src: avatarUrl, alt: "Profile", className: "size-24 rounded-full" })) : (_jsx("div", { className: "bg-b3-primary-wash size-24 rounded-full" })), _jsx("button", { onClick: handleEditAvatar, className: "bg-b3-grey border-b3-background hover:bg-b3-grey/80 absolute -bottom-1 -right-1 flex size-8 items-center justify-center rounded-full border-4 transition-colors", children: _jsx(Pencil, { size: 16, className: "text-b3-background" }) })] }), _jsxs("div", { className: "global-account-profile-info", children: [_jsx("h2", { className: "text-b3-grey text-xl font-semibold", children: profile?.displayName || formatUsername(profile?.name || "") }), _jsxs("div", { className: "address-button border-b3-line bg-b3-line/20 hover:bg-b3-line/40 flex w-fit items-center gap-2 rounded-full border px-3 py-1 transition-colors", children: [_jsx("span", { className: "text-b3-foreground-muted font-mono text-xs", children: centerTruncate(account?.address || "", 6) }), _jsx(CopyToClipboard, { text: account?.address || "" })] })] })] }) }), (showDeposit || showSwap) && (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [showDeposit && (_jsxs(Button, { className: "manage-account-deposit bg-b3-primary-wash hover:bg-b3-primary-wash/70 h-[84px] w-full flex-col items-start gap-2 rounded-2xl", onClick: () => {
90
+ return (_jsxs("div", { className: "flex flex-col gap-6", children: [_jsx("div", { className: "flex items-center justify-between", children: _jsxs("div", { className: "global-account-profile flex items-center gap-4", children: [_jsxs("div", { className: "global-account-profile-avatar relative", children: [avatarUrl ? (_jsx("img", { src: avatarUrl, alt: "Profile", className: "size-24 rounded-full" })) : (_jsx("div", { className: "bg-b3-primary-wash size-24 rounded-full" })), _jsx("button", { onClick: handleEditProfile, className: "bg-b3-grey border-b3-background hover:bg-b3-grey/80 absolute -bottom-1 -right-1 flex size-8 items-center justify-center rounded-full border-4 transition-colors", children: _jsx(Pencil, { size: 16, className: "text-b3-background" }) })] }), _jsxs("div", { className: "global-account-profile-info", children: [_jsx("h2", { className: "text-b3-grey text-xl font-semibold", children: user?.username || profile?.displayName || formatUsername(profile?.name || "") }), _jsxs("div", { className: "address-button border-b3-line bg-b3-line/20 hover:bg-b3-line/40 flex w-fit items-center gap-2 rounded-full border px-3 py-1 transition-colors", children: [_jsx("span", { className: "text-b3-foreground-muted font-mono text-xs", children: centerTruncate(account?.address || "", 6) }), _jsx(CopyToClipboard, { text: account?.address || "" })] })] })] }) }), (showDeposit || showSwap) && (_jsxs("div", { className: "grid grid-cols-2 gap-3", children: [showDeposit && (_jsxs(Button, { className: "manage-account-deposit bg-b3-primary-wash hover:bg-b3-primary-wash/70 h-[84px] w-full flex-col items-start gap-2 rounded-2xl", onClick: () => {
91
91
  setB3ModalOpen(true);
92
92
  setB3ModalContentType({
93
93
  type: "anySpend",
@@ -0,0 +1,6 @@
1
+ interface ProfileEditorProps {
2
+ onSuccess?: () => void;
3
+ className?: string;
4
+ }
5
+ export declare function ProfileEditor({ onSuccess, className }: ProfileEditorProps): import("react/jsx-runtime").JSX.Element;
6
+ export {};