@b3dotfun/sdk 0.0.65-alpha.0 → 0.0.65-test.3
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/cjs/anyspend/react/components/AnySpend.js +77 -35
- package/dist/cjs/anyspend/react/components/AnySpendCustom.js +2 -2
- package/dist/cjs/anyspend/react/components/AnySpendCustomExactIn.js +1 -1
- package/dist/cjs/anyspend/react/components/AnyspendDepositHype.js +1 -1
- package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.d.ts +0 -6
- package/dist/cjs/anyspend/react/components/common/CryptoPaymentMethod.js +5 -3
- package/dist/cjs/anyspend/react/components/common/FeeDetailPanel.js +1 -1
- package/dist/cjs/anyspend/react/components/common/FiatPaymentMethod.js +2 -2
- package/dist/cjs/anyspend/react/components/common/OrderHistory.d.ts +1 -1
- package/dist/cjs/anyspend/react/components/common/OrderHistory.js +7 -3
- package/dist/cjs/anyspend/react/components/common/PanelOnrampPayment.js +1 -1
- package/dist/cjs/anyspend/react/components/common/PointsDetailPanel.js +1 -1
- package/dist/cjs/anyspend/react/components/common/RecipientSelection.js +1 -1
- package/dist/cjs/anyspend/react/hooks/useSigMint.d.ts +1 -1
- package/dist/cjs/global-account/react/components/AccountAssets/AccountAssets.js +38 -2
- package/dist/cjs/global-account/react/components/AvatarEditor/AvatarEditor.js +184 -35
- package/dist/cjs/global-account/react/components/B3DynamicModal.js +23 -12
- package/dist/cjs/global-account/react/components/Deposit/Deposit.d.ts +1 -0
- package/dist/cjs/global-account/react/components/Deposit/Deposit.js +65 -0
- package/dist/cjs/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.d.ts +39 -0
- package/dist/cjs/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.js +37 -0
- package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.d.ts +6 -4
- package/dist/cjs/global-account/react/components/LinkAccount/LinkAccount.js +113 -279
- package/dist/cjs/global-account/react/components/LinkAccount/LinkNewAccount.d.ts +4 -0
- package/dist/cjs/global-account/react/components/LinkAccount/LinkNewAccount.js +331 -0
- package/dist/cjs/global-account/react/components/ManageAccount/AppsContent.d.ts +6 -0
- package/dist/cjs/global-account/react/components/ManageAccount/AppsContent.js +34 -0
- package/dist/cjs/global-account/react/components/ManageAccount/BalanceContent.js +6 -5
- package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.d.ts +2 -0
- package/dist/cjs/global-account/react/components/ManageAccount/BottomNavigation.js +23 -0
- package/dist/cjs/global-account/react/components/ManageAccount/Header.d.ts +3 -0
- package/dist/cjs/global-account/react/components/ManageAccount/Header.js +120 -0
- package/dist/cjs/global-account/react/components/ManageAccount/HomeActions.d.ts +5 -0
- package/dist/cjs/global-account/react/components/ManageAccount/HomeActions.js +43 -0
- package/dist/cjs/global-account/react/components/ManageAccount/HomeContent.d.ts +6 -0
- package/dist/cjs/global-account/react/components/ManageAccount/HomeContent.js +16 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ManageAccount.js +24 -193
- package/dist/cjs/global-account/react/components/ManageAccount/NFTContent.d.ts +2 -0
- package/dist/cjs/global-account/react/components/ManageAccount/NFTContent.js +15 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ProfileSection.d.ts +2 -0
- package/dist/cjs/global-account/react/components/ManageAccount/ProfileSection.js +47 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsContent.d.ts +7 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsContent.js +50 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsMenuItem.d.ts +9 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsMenuItem.js +8 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsProfileCard.d.ts +2 -0
- package/dist/cjs/global-account/react/components/ManageAccount/SettingsProfileCard.js +106 -0
- package/dist/cjs/global-account/react/components/ManageAccount/TokenContent.d.ts +2 -0
- package/dist/cjs/global-account/react/components/ManageAccount/TokenContent.js +22 -0
- package/dist/cjs/global-account/react/components/ModalHeader/ModalHeader.d.ts +10 -0
- package/dist/cjs/global-account/react/components/ModalHeader/ModalHeader.js +12 -0
- package/dist/cjs/global-account/react/components/Send/Send.d.ts +5 -0
- package/dist/cjs/global-account/react/components/Send/Send.js +187 -0
- package/dist/cjs/global-account/react/components/SignInWithB3/SignIn.js +3 -1
- package/dist/cjs/global-account/react/components/icons/BellIcon.d.ts +3 -0
- package/dist/cjs/global-account/react/components/icons/BellIcon.js +5 -0
- package/dist/cjs/global-account/react/components/icons/ChevronDownIcon.d.ts +2 -0
- package/dist/cjs/global-account/react/components/icons/ChevronDownIcon.js +7 -0
- package/dist/cjs/global-account/react/components/icons/CopyIcon.d.ts +2 -0
- package/dist/cjs/global-account/react/components/icons/CopyIcon.js +7 -0
- package/dist/cjs/global-account/react/components/icons/LinkIcon.d.ts +3 -0
- package/dist/cjs/global-account/react/components/icons/LinkIcon.js +5 -0
- package/dist/cjs/global-account/react/components/icons/LockIcon.d.ts +3 -0
- package/dist/cjs/global-account/react/components/icons/LockIcon.js +5 -0
- package/dist/cjs/global-account/react/components/icons/WalletIcon.d.ts +2 -0
- package/dist/cjs/global-account/react/components/icons/WalletIcon.js +7 -0
- package/dist/cjs/global-account/react/components/index.d.ts +5 -4
- package/dist/cjs/global-account/react/components/index.js +14 -9
- package/dist/cjs/global-account/react/components/ui/Tabs.js +2 -2
- package/dist/cjs/global-account/react/components/ui/dialog.js +2 -2
- package/dist/cjs/global-account/react/hooks/index.d.ts +1 -1
- package/dist/cjs/global-account/react/hooks/index.js +3 -1
- package/dist/cjs/global-account/react/hooks/useAccountWallet.d.ts +1 -0
- package/dist/cjs/global-account/react/hooks/useAccountWallet.js +18 -0
- package/dist/cjs/global-account/react/hooks/useAuthentication.d.ts +2 -2
- package/dist/cjs/global-account/react/hooks/useB3BalanceFromAddresses.js +1 -0
- package/dist/cjs/global-account/react/hooks/useUserQuery.d.ts +2 -2
- package/dist/cjs/global-account/react/stores/index.d.ts +1 -0
- package/dist/cjs/global-account/react/stores/index.js +3 -1
- package/dist/cjs/global-account/react/stores/useModalStore.d.ts +31 -6
- package/dist/cjs/global-account/react/stores/useRecentAddressesStore.d.ts +25 -0
- package/dist/cjs/global-account/react/stores/useRecentAddressesStore.js +36 -0
- package/dist/cjs/global-account/react/utils/profileDisplay.d.ts +2 -0
- package/dist/cjs/global-account/react/utils/profileDisplay.js +2 -2
- package/dist/cjs/shared/constants/chains/supported.d.ts +3 -3
- package/dist/cjs/shared/utils/ipfs.js +10 -3
- package/dist/esm/anyspend/react/components/AnySpend.js +78 -36
- package/dist/esm/anyspend/react/components/AnySpendCustom.js +2 -2
- package/dist/esm/anyspend/react/components/AnySpendCustomExactIn.js +1 -1
- package/dist/esm/anyspend/react/components/AnyspendDepositHype.js +1 -1
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.d.ts +0 -6
- package/dist/esm/anyspend/react/components/common/CryptoPaymentMethod.js +5 -3
- package/dist/esm/anyspend/react/components/common/FeeDetailPanel.js +1 -1
- package/dist/esm/anyspend/react/components/common/FiatPaymentMethod.js +2 -2
- package/dist/esm/anyspend/react/components/common/OrderHistory.d.ts +1 -1
- package/dist/esm/anyspend/react/components/common/OrderHistory.js +6 -5
- package/dist/esm/anyspend/react/components/common/PanelOnrampPayment.js +1 -1
- package/dist/esm/anyspend/react/components/common/PointsDetailPanel.js +1 -1
- package/dist/esm/anyspend/react/components/common/RecipientSelection.js +1 -1
- package/dist/esm/anyspend/react/hooks/useSigMint.d.ts +1 -1
- package/dist/esm/global-account/react/components/AccountAssets/AccountAssets.js +38 -2
- package/dist/esm/global-account/react/components/AvatarEditor/AvatarEditor.js +186 -37
- package/dist/esm/global-account/react/components/B3DynamicModal.js +23 -12
- package/dist/esm/global-account/react/components/Deposit/Deposit.d.ts +1 -0
- package/dist/esm/global-account/react/components/Deposit/Deposit.js +59 -0
- package/dist/esm/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.d.ts +39 -0
- package/dist/esm/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.js +34 -0
- package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.d.ts +6 -4
- package/dist/esm/global-account/react/components/LinkAccount/LinkAccount.js +113 -280
- package/dist/esm/global-account/react/components/LinkAccount/LinkNewAccount.d.ts +4 -0
- package/dist/esm/global-account/react/components/LinkAccount/LinkNewAccount.js +325 -0
- package/dist/esm/global-account/react/components/ManageAccount/AppsContent.d.ts +6 -0
- package/dist/esm/global-account/react/components/ManageAccount/AppsContent.js +32 -0
- package/dist/esm/global-account/react/components/ManageAccount/BalanceContent.js +6 -5
- package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.d.ts +2 -0
- package/dist/esm/global-account/react/components/ManageAccount/BottomNavigation.js +21 -0
- package/dist/esm/global-account/react/components/ManageAccount/Header.d.ts +3 -0
- package/dist/esm/global-account/react/components/ManageAccount/Header.js +81 -0
- package/dist/esm/global-account/react/components/ManageAccount/HomeActions.d.ts +5 -0
- package/dist/esm/global-account/react/components/ManageAccount/HomeActions.js +41 -0
- package/dist/esm/global-account/react/components/ManageAccount/HomeContent.d.ts +6 -0
- package/dist/esm/global-account/react/components/ManageAccount/HomeContent.js +10 -0
- package/dist/esm/global-account/react/components/ManageAccount/ManageAccount.js +26 -195
- package/dist/esm/global-account/react/components/ManageAccount/NFTContent.d.ts +2 -0
- package/dist/esm/global-account/react/components/ManageAccount/NFTContent.js +13 -0
- package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.d.ts +2 -0
- package/dist/esm/global-account/react/components/ManageAccount/ProfileSection.js +45 -0
- package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.d.ts +7 -0
- package/dist/esm/global-account/react/components/ManageAccount/SettingsContent.js +45 -0
- package/dist/esm/global-account/react/components/ManageAccount/SettingsMenuItem.d.ts +9 -0
- package/dist/esm/global-account/react/components/ManageAccount/SettingsMenuItem.js +6 -0
- package/dist/esm/global-account/react/components/ManageAccount/SettingsProfileCard.d.ts +2 -0
- package/dist/esm/global-account/react/components/ManageAccount/SettingsProfileCard.js +101 -0
- package/dist/esm/global-account/react/components/ManageAccount/TokenContent.d.ts +2 -0
- package/dist/esm/global-account/react/components/ManageAccount/TokenContent.js +20 -0
- package/dist/esm/global-account/react/components/ModalHeader/ModalHeader.d.ts +10 -0
- package/dist/esm/global-account/react/components/ModalHeader/ModalHeader.js +10 -0
- package/dist/esm/global-account/react/components/Send/Send.d.ts +5 -0
- package/dist/esm/global-account/react/components/Send/Send.js +181 -0
- package/dist/esm/global-account/react/components/SignInWithB3/SignIn.js +4 -2
- package/dist/esm/global-account/react/components/icons/BellIcon.d.ts +3 -0
- package/dist/esm/global-account/react/components/icons/BellIcon.js +3 -0
- package/dist/esm/global-account/react/components/icons/ChevronDownIcon.d.ts +2 -0
- package/dist/esm/global-account/react/components/icons/ChevronDownIcon.js +4 -0
- package/dist/esm/global-account/react/components/icons/CopyIcon.d.ts +2 -0
- package/dist/esm/global-account/react/components/icons/CopyIcon.js +4 -0
- package/dist/esm/global-account/react/components/icons/LinkIcon.d.ts +3 -0
- package/dist/esm/global-account/react/components/icons/LinkIcon.js +3 -0
- package/dist/esm/global-account/react/components/icons/LockIcon.d.ts +3 -0
- package/dist/esm/global-account/react/components/icons/LockIcon.js +3 -0
- package/dist/esm/global-account/react/components/icons/WalletIcon.d.ts +2 -0
- package/dist/esm/global-account/react/components/icons/WalletIcon.js +4 -0
- package/dist/esm/global-account/react/components/index.d.ts +5 -4
- package/dist/esm/global-account/react/components/index.js +9 -5
- package/dist/esm/global-account/react/components/ui/Tabs.js +2 -2
- package/dist/esm/global-account/react/components/ui/dialog.js +2 -2
- package/dist/esm/global-account/react/hooks/index.d.ts +1 -1
- package/dist/esm/global-account/react/hooks/index.js +1 -1
- package/dist/esm/global-account/react/hooks/useAccountWallet.d.ts +1 -0
- package/dist/esm/global-account/react/hooks/useAccountWallet.js +17 -0
- package/dist/esm/global-account/react/hooks/useAuthentication.d.ts +2 -2
- package/dist/esm/global-account/react/hooks/useB3BalanceFromAddresses.js +1 -0
- package/dist/esm/global-account/react/hooks/useUserQuery.d.ts +2 -2
- package/dist/esm/global-account/react/stores/index.d.ts +1 -0
- package/dist/esm/global-account/react/stores/index.js +1 -0
- package/dist/esm/global-account/react/stores/useModalStore.d.ts +31 -6
- package/dist/esm/global-account/react/stores/useRecentAddressesStore.d.ts +25 -0
- package/dist/esm/global-account/react/stores/useRecentAddressesStore.js +33 -0
- package/dist/esm/global-account/react/utils/profileDisplay.d.ts +2 -0
- package/dist/esm/global-account/react/utils/profileDisplay.js +2 -2
- package/dist/esm/shared/constants/chains/supported.d.ts +3 -3
- package/dist/esm/shared/utils/ipfs.js +10 -3
- package/dist/styles/index.css +1 -1
- package/dist/types/anyspend/react/components/common/CryptoPaymentMethod.d.ts +0 -6
- package/dist/types/anyspend/react/components/common/OrderHistory.d.ts +1 -1
- package/dist/types/anyspend/react/hooks/useSigMint.d.ts +1 -1
- package/dist/types/global-account/react/components/Deposit/Deposit.d.ts +1 -0
- package/dist/types/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.d.ts +39 -0
- package/dist/types/global-account/react/components/LinkAccount/LinkAccount.d.ts +6 -4
- package/dist/types/global-account/react/components/LinkAccount/LinkNewAccount.d.ts +4 -0
- package/dist/types/global-account/react/components/ManageAccount/AppsContent.d.ts +6 -0
- package/dist/types/global-account/react/components/ManageAccount/BottomNavigation.d.ts +2 -0
- package/dist/types/global-account/react/components/ManageAccount/Header.d.ts +3 -0
- package/dist/types/global-account/react/components/ManageAccount/HomeActions.d.ts +5 -0
- package/dist/types/global-account/react/components/ManageAccount/HomeContent.d.ts +6 -0
- package/dist/types/global-account/react/components/ManageAccount/NFTContent.d.ts +2 -0
- package/dist/types/global-account/react/components/ManageAccount/ProfileSection.d.ts +2 -0
- package/dist/types/global-account/react/components/ManageAccount/SettingsContent.d.ts +7 -0
- package/dist/types/global-account/react/components/ManageAccount/SettingsMenuItem.d.ts +9 -0
- package/dist/types/global-account/react/components/ManageAccount/SettingsProfileCard.d.ts +2 -0
- package/dist/types/global-account/react/components/ManageAccount/TokenContent.d.ts +2 -0
- package/dist/types/global-account/react/components/ModalHeader/ModalHeader.d.ts +10 -0
- package/dist/types/global-account/react/components/Send/Send.d.ts +5 -0
- package/dist/types/global-account/react/components/icons/BellIcon.d.ts +3 -0
- package/dist/types/global-account/react/components/icons/ChevronDownIcon.d.ts +2 -0
- package/dist/types/global-account/react/components/icons/CopyIcon.d.ts +2 -0
- package/dist/types/global-account/react/components/icons/LinkIcon.d.ts +3 -0
- package/dist/types/global-account/react/components/icons/LockIcon.d.ts +3 -0
- package/dist/types/global-account/react/components/icons/WalletIcon.d.ts +2 -0
- package/dist/types/global-account/react/components/index.d.ts +5 -4
- package/dist/types/global-account/react/hooks/index.d.ts +1 -1
- package/dist/types/global-account/react/hooks/useAccountWallet.d.ts +1 -0
- package/dist/types/global-account/react/hooks/useAuthentication.d.ts +2 -2
- package/dist/types/global-account/react/hooks/useUserQuery.d.ts +2 -2
- package/dist/types/global-account/react/stores/index.d.ts +1 -0
- package/dist/types/global-account/react/stores/useModalStore.d.ts +31 -6
- package/dist/types/global-account/react/stores/useRecentAddressesStore.d.ts +25 -0
- package/dist/types/global-account/react/utils/profileDisplay.d.ts +2 -0
- package/dist/types/shared/constants/chains/supported.d.ts +3 -3
- package/package.json +1 -1
- package/src/anyspend/react/components/AnySpend.tsx +229 -170
- package/src/anyspend/react/components/AnySpendCustom.tsx +1 -3
- package/src/anyspend/react/components/AnySpendCustomExactIn.tsx +0 -2
- package/src/anyspend/react/components/AnyspendDepositHype.tsx +0 -2
- package/src/anyspend/react/components/common/CryptoPaymentMethod.tsx +7 -14
- package/src/anyspend/react/components/common/FeeDetailPanel.tsx +1 -1
- package/src/anyspend/react/components/common/FiatPaymentMethod.tsx +2 -2
- package/src/anyspend/react/components/common/OrderHistory.tsx +8 -13
- package/src/anyspend/react/components/common/PanelOnrampPayment.tsx +1 -1
- package/src/anyspend/react/components/common/PointsDetailPanel.tsx +1 -1
- package/src/anyspend/react/components/common/RecipientSelection.tsx +1 -1
- package/src/global-account/react/components/AccountAssets/AccountAssets.tsx +115 -25
- package/src/global-account/react/components/AvatarEditor/AvatarEditor.tsx +360 -128
- package/src/global-account/react/components/B3DynamicModal.tsx +28 -14
- package/src/global-account/react/components/Deposit/Deposit.tsx +211 -0
- package/src/global-account/react/components/IPFSMediaRenderer/IPFSMediaRenderer.tsx +84 -0
- package/src/global-account/react/components/LinkAccount/LinkAccount.tsx +332 -433
- package/src/global-account/react/components/LinkAccount/LinkNewAccount.tsx +490 -0
- package/src/global-account/react/components/ManageAccount/AppsContent.tsx +79 -0
- package/src/global-account/react/components/ManageAccount/BalanceContent.tsx +6 -10
- package/src/global-account/react/components/ManageAccount/BottomNavigation.tsx +83 -0
- package/src/global-account/react/components/ManageAccount/Header.tsx +230 -0
- package/src/global-account/react/components/ManageAccount/HomeActions.tsx +118 -0
- package/src/global-account/react/components/ManageAccount/HomeContent.tsx +42 -0
- package/src/global-account/react/components/ManageAccount/ManageAccount.tsx +73 -589
- package/src/global-account/react/components/ManageAccount/NFTContent.tsx +24 -0
- package/src/global-account/react/components/ManageAccount/ProfileSection.tsx +72 -0
- package/src/global-account/react/components/ManageAccount/SettingsContent.tsx +87 -0
- package/src/global-account/react/components/ManageAccount/SettingsMenuItem.tsx +31 -0
- package/src/global-account/react/components/ManageAccount/SettingsProfileCard.tsx +180 -0
- package/src/global-account/react/components/ManageAccount/TokenContent.tsx +41 -0
- package/src/global-account/react/components/ModalHeader/ModalHeader.tsx +50 -0
- package/src/global-account/react/components/Send/Send.tsx +585 -0
- package/src/global-account/react/components/SignInWithB3/SignIn.tsx +11 -7
- package/src/global-account/react/components/icons/BellIcon.tsx +15 -0
- package/src/global-account/react/components/icons/ChevronDownIcon.tsx +17 -0
- package/src/global-account/react/components/icons/CopyIcon.tsx +22 -0
- package/src/global-account/react/components/icons/LinkIcon.tsx +15 -0
- package/src/global-account/react/components/icons/LockIcon.tsx +15 -0
- package/src/global-account/react/components/icons/WalletIcon.tsx +21 -0
- package/src/global-account/react/components/index.ts +11 -5
- package/src/global-account/react/components/ui/Tabs.tsx +5 -13
- package/src/global-account/react/components/ui/dialog.tsx +32 -14
- package/src/global-account/react/hooks/index.ts +3 -0
- package/src/global-account/react/hooks/useAccountWallet.tsx +26 -0
- package/src/global-account/react/hooks/useB3BalanceFromAddresses.ts +1 -0
- package/src/global-account/react/stores/index.ts +1 -0
- package/src/global-account/react/stores/useModalStore.ts +35 -6
- package/src/global-account/react/stores/useRecentAddressesStore.ts +55 -0
- package/src/global-account/react/utils/profileDisplay.ts +4 -2
- package/src/shared/utils/ipfs.ts +10 -3
- package/src/styles/index.css +6 -1
- package/dist/cjs/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +0 -6
- package/dist/cjs/global-account/react/components/ProfileEditor/ProfileEditor.js +0 -141
- package/dist/esm/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +0 -6
- package/dist/esm/global-account/react/components/ProfileEditor/ProfileEditor.js +0 -135
- package/dist/types/global-account/react/components/ProfileEditor/ProfileEditor.d.ts +0 -6
- package/src/global-account/react/components/ProfileEditor/ProfileEditor.tsx +0 -265
|
@@ -1,15 +1,19 @@
|
|
|
1
1
|
"use client";
|
|
2
2
|
|
|
3
3
|
import app from "@b3dotfun/sdk/global-account/app";
|
|
4
|
-
import { Button, useB3, useProfile } from "@b3dotfun/sdk/global-account/react";
|
|
4
|
+
import { Button, IPFSMediaRenderer, useB3, useProfile } from "@b3dotfun/sdk/global-account/react";
|
|
5
|
+
import { validateImageUrl } from "@b3dotfun/sdk/global-account/react/utils/profileDisplay";
|
|
5
6
|
import { cn } from "@b3dotfun/sdk/shared/utils/cn";
|
|
6
7
|
import { debugB3React } from "@b3dotfun/sdk/shared/utils/debug";
|
|
7
8
|
import { client } from "@b3dotfun/sdk/shared/utils/thirdweb";
|
|
8
|
-
import {
|
|
9
|
+
import { Loader2, Upload, X } from "lucide-react";
|
|
9
10
|
import { useRef, useState } from "react";
|
|
10
11
|
import { toast } from "sonner";
|
|
11
12
|
import { useActiveAccount } from "thirdweb/react";
|
|
12
13
|
import { upload } from "thirdweb/storage";
|
|
14
|
+
import { useProfileSettings } from "../../hooks/useProfile";
|
|
15
|
+
import { useModalStore } from "../../stores";
|
|
16
|
+
import ModalHeader from "../ModalHeader/ModalHeader";
|
|
13
17
|
|
|
14
18
|
const debug = debugB3React("AvatarEditor");
|
|
15
19
|
|
|
@@ -18,13 +22,22 @@ interface AvatarEditorProps {
|
|
|
18
22
|
className?: string;
|
|
19
23
|
}
|
|
20
24
|
|
|
25
|
+
type ViewStep = "select" | "upload";
|
|
26
|
+
|
|
21
27
|
export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
|
|
28
|
+
const [viewStep, setViewStep] = useState<ViewStep>("select");
|
|
29
|
+
const [selectedAvatar, setSelectedAvatar] = useState<string | null>(null);
|
|
30
|
+
const [selectedProfileType, setSelectedProfileType] = useState<string | null>(null); // Track which profile was selected
|
|
31
|
+
const [hoveredProfile, setHoveredProfile] = useState<string | null>(null);
|
|
22
32
|
const [selectedFile, setSelectedFile] = useState<File | null>(null);
|
|
23
33
|
const [previewUrl, setPreviewUrl] = useState<string | null>(null);
|
|
24
|
-
const [isUploading, setIsUploading] = useState(false);
|
|
25
34
|
const [isSaving, setIsSaving] = useState(false);
|
|
35
|
+
const [isDragging, setIsDragging] = useState(false);
|
|
26
36
|
const fileInputRef = useRef<HTMLInputElement>(null);
|
|
27
|
-
const { setUser } = useB3();
|
|
37
|
+
const { setUser, user, partnerId } = useB3();
|
|
38
|
+
const setB3ModalContentType = useModalStore(state => state.setB3ModalContentType);
|
|
39
|
+
const contentType = useModalStore(state => state.contentType);
|
|
40
|
+
const { setPreference } = useProfileSettings();
|
|
28
41
|
|
|
29
42
|
const account = useActiveAccount();
|
|
30
43
|
const { data: profile, refetch: refreshProfile } = useProfile({
|
|
@@ -32,9 +45,10 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
|
|
|
32
45
|
fresh: true,
|
|
33
46
|
});
|
|
34
47
|
|
|
35
|
-
//
|
|
36
|
-
|
|
37
|
-
const
|
|
48
|
+
// Get raw avatar URLs, convert IPFS URLs, and validate them
|
|
49
|
+
const rawCurrentAvatar = user?.avatar || profile?.avatar;
|
|
50
|
+
const currentAvatar = validateImageUrl(rawCurrentAvatar);
|
|
51
|
+
const safePreviewUrl = validateImageUrl(previewUrl);
|
|
38
52
|
|
|
39
53
|
const handleFileSelect = (event: React.ChangeEvent<HTMLInputElement>) => {
|
|
40
54
|
const file = event.target.files?.[0];
|
|
@@ -52,14 +66,19 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
|
|
|
52
66
|
}
|
|
53
67
|
|
|
54
68
|
setSelectedFile(file);
|
|
69
|
+
// Clear profile type selection when uploading a new file
|
|
70
|
+
setSelectedProfileType(null);
|
|
55
71
|
|
|
56
72
|
// Create preview URL
|
|
57
73
|
const url = URL.createObjectURL(file);
|
|
58
74
|
setPreviewUrl(url);
|
|
75
|
+
setSelectedAvatar(url);
|
|
59
76
|
}
|
|
60
77
|
};
|
|
61
78
|
|
|
62
|
-
const
|
|
79
|
+
const handleRemovePreview = () => {
|
|
80
|
+
setSelectedAvatar(currentAvatar || null);
|
|
81
|
+
setSelectedProfileType(null);
|
|
63
82
|
setSelectedFile(null);
|
|
64
83
|
if (previewUrl) {
|
|
65
84
|
URL.revokeObjectURL(previewUrl);
|
|
@@ -70,163 +89,376 @@ export function AvatarEditor({ onSetAvatar, className }: AvatarEditorProps) {
|
|
|
70
89
|
}
|
|
71
90
|
};
|
|
72
91
|
|
|
73
|
-
const
|
|
74
|
-
if (!
|
|
75
|
-
toast.error("
|
|
92
|
+
const handleSaveChanges = async () => {
|
|
93
|
+
if (!account?.address) {
|
|
94
|
+
toast.error("No account connected");
|
|
76
95
|
return;
|
|
77
96
|
}
|
|
78
97
|
|
|
79
|
-
|
|
98
|
+
setIsSaving(true);
|
|
80
99
|
try {
|
|
81
|
-
|
|
100
|
+
let fileToUpload: File | null = null;
|
|
82
101
|
|
|
83
|
-
//
|
|
84
|
-
|
|
85
|
-
|
|
86
|
-
|
|
87
|
-
|
|
102
|
+
// If user uploaded a new file
|
|
103
|
+
if (selectedFile) {
|
|
104
|
+
fileToUpload = selectedFile;
|
|
105
|
+
} else if (selectedProfileType && selectedAvatar) {
|
|
106
|
+
// User selected from existing profile avatars
|
|
107
|
+
// Fetch the image from the URL and convert to blob
|
|
108
|
+
debug("Fetching image from social profile:", selectedAvatar);
|
|
109
|
+
|
|
110
|
+
try {
|
|
111
|
+
const response = await fetch(selectedAvatar);
|
|
112
|
+
if (!response.ok) {
|
|
113
|
+
throw new Error("Failed to fetch image");
|
|
114
|
+
}
|
|
115
|
+
|
|
116
|
+
const blob = await response.blob();
|
|
117
|
+
debug("Fetched blob with type:", blob.type);
|
|
118
|
+
|
|
119
|
+
// Determine the correct extension from the blob's MIME type
|
|
120
|
+
// This handles URLs without extensions (like Farcaster images)
|
|
121
|
+
const mimeToExtension: Record<string, string> = {
|
|
122
|
+
"image/jpeg": "jpg",
|
|
123
|
+
"image/jpg": "jpg",
|
|
124
|
+
"image/png": "png",
|
|
125
|
+
"image/gif": "gif",
|
|
126
|
+
"image/webp": "webp",
|
|
127
|
+
"image/svg+xml": "svg",
|
|
128
|
+
};
|
|
129
|
+
|
|
130
|
+
const extension = blob.type ? mimeToExtension[blob.type.toLowerCase()] || "jpg" : "jpg";
|
|
131
|
+
const mimeType = blob.type || `image/${extension}`;
|
|
132
|
+
|
|
133
|
+
fileToUpload = new File([blob], `avatar-${selectedProfileType}.${extension}`, { type: mimeType });
|
|
134
|
+
|
|
135
|
+
debug("Successfully converted social profile image to file with extension:", extension);
|
|
136
|
+
} catch (fetchError) {
|
|
137
|
+
debug("Error fetching social profile image:", fetchError);
|
|
138
|
+
toast.error("Failed to fetch profile image. Please try uploading manually.");
|
|
139
|
+
setIsSaving(false);
|
|
140
|
+
return;
|
|
141
|
+
}
|
|
142
|
+
}
|
|
143
|
+
|
|
144
|
+
// Upload to IPFS if we have a file
|
|
145
|
+
if (fileToUpload) {
|
|
146
|
+
debug("Starting upload to IPFS", fileToUpload);
|
|
147
|
+
|
|
148
|
+
// Upload to IPFS using Thirdweb
|
|
149
|
+
const ipfsUrl = await upload({
|
|
150
|
+
client,
|
|
151
|
+
files: [fileToUpload],
|
|
152
|
+
});
|
|
88
153
|
|
|
89
|
-
|
|
90
|
-
|
|
91
|
-
|
|
92
|
-
|
|
93
|
-
|
|
94
|
-
|
|
95
|
-
|
|
96
|
-
|
|
97
|
-
|
|
98
|
-
|
|
99
|
-
|
|
100
|
-
|
|
101
|
-
|
|
102
|
-
|
|
154
|
+
debug("Upload successful", ipfsUrl);
|
|
155
|
+
|
|
156
|
+
// Save avatar URL using profiles service
|
|
157
|
+
const user = await app.service("users").setAvatar(
|
|
158
|
+
{
|
|
159
|
+
avatar: ipfsUrl,
|
|
160
|
+
},
|
|
161
|
+
// @ts-expect-error - our typed client is expecting context even though it's set elsewhere
|
|
162
|
+
{},
|
|
163
|
+
);
|
|
164
|
+
// update user
|
|
165
|
+
// @ts-expect-error this resolved fine, look into why expect-error needed
|
|
166
|
+
setUser(user);
|
|
167
|
+
|
|
168
|
+
toast.success("Looks great! Your avatar has been saved!");
|
|
169
|
+
}
|
|
103
170
|
|
|
104
171
|
// Refresh profile to get updated avatar
|
|
105
172
|
await refreshProfile();
|
|
106
173
|
|
|
107
|
-
toast.success(
|
|
108
|
-
hasAvatar ? "Nice look! Your avatar has been updated!" : "Looks great! Your avatar has been saved!",
|
|
109
|
-
);
|
|
110
|
-
|
|
111
174
|
onSetAvatar?.();
|
|
112
|
-
|
|
113
|
-
// Clean up
|
|
114
|
-
handleRemoveFile();
|
|
115
175
|
} catch (error) {
|
|
116
|
-
debug("Error
|
|
117
|
-
toast.error("Failed to
|
|
176
|
+
debug("Error saving avatar:", error);
|
|
177
|
+
toast.error("Failed to save avatar. Please try again.");
|
|
118
178
|
} finally {
|
|
119
|
-
setIsUploading(false);
|
|
120
179
|
setIsSaving(false);
|
|
121
180
|
}
|
|
122
181
|
};
|
|
123
182
|
|
|
124
|
-
const
|
|
183
|
+
const handleCancel = () => {
|
|
184
|
+
if (viewStep === "upload") {
|
|
185
|
+
setViewStep("select");
|
|
186
|
+
handleRemovePreview();
|
|
187
|
+
} else {
|
|
188
|
+
setB3ModalContentType({
|
|
189
|
+
type: "manageAccount",
|
|
190
|
+
chain: (contentType as any)?.chain,
|
|
191
|
+
partnerId: partnerId,
|
|
192
|
+
});
|
|
193
|
+
}
|
|
194
|
+
};
|
|
195
|
+
|
|
196
|
+
const handleProfileAvatarSelect = (avatarUrl: string, profileType: string) => {
|
|
197
|
+
setSelectedAvatar(avatarUrl);
|
|
198
|
+
setSelectedProfileType(profileType);
|
|
199
|
+
// Clear any selected file since we're selecting from profile
|
|
200
|
+
setSelectedFile(null);
|
|
201
|
+
if (previewUrl) {
|
|
202
|
+
URL.revokeObjectURL(previewUrl);
|
|
203
|
+
setPreviewUrl(null);
|
|
204
|
+
}
|
|
205
|
+
};
|
|
206
|
+
|
|
207
|
+
const handleUploadImageClick = () => {
|
|
208
|
+
setViewStep("upload");
|
|
209
|
+
};
|
|
210
|
+
|
|
211
|
+
const handleOpenFilePicker = () => {
|
|
125
212
|
fileInputRef.current?.click();
|
|
126
213
|
};
|
|
127
214
|
|
|
128
|
-
const
|
|
215
|
+
const handleDragEnter = (e: React.DragEvent<HTMLDivElement>) => {
|
|
216
|
+
e.preventDefault();
|
|
217
|
+
e.stopPropagation();
|
|
218
|
+
setIsDragging(true);
|
|
219
|
+
};
|
|
220
|
+
|
|
221
|
+
const handleDragLeave = (e: React.DragEvent<HTMLDivElement>) => {
|
|
222
|
+
e.preventDefault();
|
|
223
|
+
e.stopPropagation();
|
|
224
|
+
setIsDragging(false);
|
|
225
|
+
};
|
|
226
|
+
|
|
227
|
+
const handleDragOver = (e: React.DragEvent<HTMLDivElement>) => {
|
|
228
|
+
e.preventDefault();
|
|
229
|
+
e.stopPropagation();
|
|
230
|
+
};
|
|
231
|
+
|
|
232
|
+
const handleDrop = (e: React.DragEvent<HTMLDivElement>) => {
|
|
233
|
+
e.preventDefault();
|
|
234
|
+
e.stopPropagation();
|
|
235
|
+
setIsDragging(false);
|
|
236
|
+
|
|
237
|
+
const file = e.dataTransfer.files?.[0];
|
|
238
|
+
if (file) {
|
|
239
|
+
// Validate file type
|
|
240
|
+
if (!file.type.startsWith("image/")) {
|
|
241
|
+
toast.error("Please select an image file");
|
|
242
|
+
return;
|
|
243
|
+
}
|
|
244
|
+
|
|
245
|
+
// Validate file size (max 5MB)
|
|
246
|
+
if (file.size > 5 * 1024 * 1024) {
|
|
247
|
+
toast.error("File size must be less than 5MB");
|
|
248
|
+
return;
|
|
249
|
+
}
|
|
250
|
+
|
|
251
|
+
setSelectedFile(file);
|
|
252
|
+
// Clear profile type selection when uploading a new file
|
|
253
|
+
setSelectedProfileType(null);
|
|
254
|
+
|
|
255
|
+
// Create preview URL
|
|
256
|
+
const url = URL.createObjectURL(file);
|
|
257
|
+
setPreviewUrl(url);
|
|
258
|
+
setSelectedAvatar(url);
|
|
259
|
+
}
|
|
260
|
+
};
|
|
261
|
+
|
|
262
|
+
const handleLinkMoreAccount = () => {
|
|
263
|
+
setB3ModalContentType({
|
|
264
|
+
type: "linkAccount",
|
|
265
|
+
chain: (contentType as any)?.chain,
|
|
266
|
+
partnerId: partnerId,
|
|
267
|
+
});
|
|
268
|
+
};
|
|
269
|
+
|
|
270
|
+
const isLoading = isSaving;
|
|
271
|
+
|
|
272
|
+
// Get profile avatars with validated URLs
|
|
273
|
+
const profileAvatars =
|
|
274
|
+
profile?.profiles
|
|
275
|
+
?.filter(p => p.avatar)
|
|
276
|
+
.map(p => {
|
|
277
|
+
const rawAvatarUrl = p?.avatar || "";
|
|
278
|
+
const validatedUrl = validateImageUrl(rawAvatarUrl);
|
|
279
|
+
return {
|
|
280
|
+
type: p.type,
|
|
281
|
+
avatar: validatedUrl,
|
|
282
|
+
name: p.name || p.type,
|
|
283
|
+
};
|
|
284
|
+
})
|
|
285
|
+
.filter(p => p.avatar !== null) || []; // Filter out profiles with invalid avatars
|
|
129
286
|
|
|
130
287
|
return (
|
|
131
|
-
<div className={cn("flex
|
|
132
|
-
|
|
133
|
-
|
|
134
|
-
{hasAvatar ? "Update Your Avatar" : "Set Your Avatar"}
|
|
135
|
-
</h2>
|
|
136
|
-
<p className="text-b3-foreground-muted font-neue-montreal-medium">
|
|
137
|
-
Upload an image to personalize your profile
|
|
138
|
-
</p>
|
|
139
|
-
</div>
|
|
288
|
+
<div className={cn("flex w-full max-w-md flex-col bg-white", className)}>
|
|
289
|
+
{/* Header */}
|
|
290
|
+
{viewStep === "upload" && <ModalHeader title="Upload Image" />}
|
|
140
291
|
|
|
141
|
-
{/*
|
|
142
|
-
|
|
143
|
-
|
|
144
|
-
|
|
145
|
-
|
|
146
|
-
|
|
147
|
-
|
|
148
|
-
|
|
149
|
-
|
|
150
|
-
|
|
151
|
-
|
|
152
|
-
|
|
153
|
-
|
|
154
|
-
onClick={handleFileInputClick}
|
|
155
|
-
className="border-b3-line hover:border-b3-primary-blue hover:bg-b3-primary-wash/20 cursor-pointer rounded-xl border-2 border-dashed p-8 text-center transition-colors"
|
|
156
|
-
>
|
|
157
|
-
<Upload className="text-b3-grey mx-auto mb-4 h-12 w-12" />
|
|
158
|
-
<p className="text-b3-grey font-neue-montreal-semibold mb-2">Click to select an image</p>
|
|
159
|
-
<p className="text-b3-foreground-muted font-neue-montreal-medium text-sm">PNG, JPG, or GIF up to 5MB</p>
|
|
160
|
-
</div>
|
|
161
|
-
) : (
|
|
162
|
-
<div className="space-y-4">
|
|
163
|
-
{/* Preview */}
|
|
164
|
-
<div className="relative">
|
|
165
|
-
<div className="border-b3-primary-blue mx-auto h-32 w-32 overflow-hidden rounded-full border-4">
|
|
166
|
-
{previewUrl ? (
|
|
167
|
-
<img src={previewUrl} alt="Preview" className="h-full w-full object-cover" />
|
|
292
|
+
{/* Content */}
|
|
293
|
+
<div className="flex flex-col items-center p-6">
|
|
294
|
+
{viewStep === "select" ? (
|
|
295
|
+
<>
|
|
296
|
+
{/* Avatar Preview */}
|
|
297
|
+
<div className="relative mb-6">
|
|
298
|
+
<div className="h-32 w-32 overflow-hidden rounded-full">
|
|
299
|
+
{safePreviewUrl || selectedAvatar || currentAvatar ? (
|
|
300
|
+
<IPFSMediaRenderer
|
|
301
|
+
src={safePreviewUrl || selectedAvatar || currentAvatar || ""}
|
|
302
|
+
alt="Avatar preview"
|
|
303
|
+
className="h-full w-full object-cover"
|
|
304
|
+
/>
|
|
168
305
|
) : (
|
|
169
|
-
<div className="bg-b3-primary-wash
|
|
170
|
-
<p className="text-b3-grey font-neue-montreal-semibold text-sm">No file selected</p>
|
|
171
|
-
</div>
|
|
306
|
+
<div className="bg-b3-primary-wash h-full w-full" />
|
|
172
307
|
)}
|
|
173
308
|
</div>
|
|
174
|
-
|
|
175
|
-
|
|
176
|
-
|
|
177
|
-
|
|
178
|
-
|
|
179
|
-
|
|
180
|
-
|
|
309
|
+
{(selectedAvatar !== currentAvatar || selectedFile) && (
|
|
310
|
+
<button
|
|
311
|
+
onClick={handleRemovePreview}
|
|
312
|
+
className="absolute -right-1 -top-1 flex h-8 w-8 items-center justify-center rounded-full bg-[#51525c] text-white transition-colors hover:bg-[#71717a]"
|
|
313
|
+
>
|
|
314
|
+
<X className="h-4 w-4" />
|
|
315
|
+
</button>
|
|
316
|
+
)}
|
|
181
317
|
</div>
|
|
182
318
|
|
|
183
|
-
{/*
|
|
184
|
-
<
|
|
185
|
-
|
|
186
|
-
|
|
187
|
-
|
|
319
|
+
{/* Upload Image Button */}
|
|
320
|
+
<button
|
|
321
|
+
onClick={handleUploadImageClick}
|
|
322
|
+
className="font-inter shadow-xs mb-6 flex w-full items-center justify-center gap-2 rounded-xl border border-[#e4e4e7] bg-white px-4 py-3 text-sm font-semibold text-[#18181b] transition-colors hover:bg-[#f4f4f5]"
|
|
323
|
+
>
|
|
324
|
+
<Upload className="h-4 w-4" />
|
|
325
|
+
Upload image
|
|
326
|
+
</button>
|
|
327
|
+
|
|
328
|
+
{/* Select Profile Image Section */}
|
|
329
|
+
<div className="w-full">
|
|
330
|
+
<h3 className="mb-2 text-base font-semibold text-[#18181b]">Select your profile image</h3>
|
|
331
|
+
<p className="mb-4 text-sm font-semibold text-[#475467]">
|
|
332
|
+
Pick an avatar from your linked profiles, ENS or upload a new one.
|
|
188
333
|
</p>
|
|
189
|
-
</div>
|
|
190
|
-
</div>
|
|
191
|
-
)}
|
|
192
334
|
|
|
193
|
-
|
|
194
|
-
|
|
195
|
-
|
|
335
|
+
{/* Profile Avatars */}
|
|
336
|
+
<div className="mb-4 flex gap-3">
|
|
337
|
+
{profileAvatars.map((profileAvatar, index) => {
|
|
338
|
+
// Skip if avatar is null (should not happen due to filter, but TypeScript doesn't know that)
|
|
339
|
+
if (!profileAvatar.avatar) return null;
|
|
340
|
+
|
|
341
|
+
return (
|
|
342
|
+
<div
|
|
343
|
+
key={index}
|
|
344
|
+
className="relative"
|
|
345
|
+
onMouseEnter={() => setHoveredProfile(profileAvatar.type)}
|
|
346
|
+
onMouseLeave={() => setHoveredProfile(null)}
|
|
347
|
+
>
|
|
348
|
+
<button
|
|
349
|
+
onClick={() => handleProfileAvatarSelect(profileAvatar.avatar || "", profileAvatar.type || "")}
|
|
350
|
+
className={cn(
|
|
351
|
+
"h-16 w-16 overflow-hidden rounded-full border-2 transition-all",
|
|
352
|
+
selectedProfileType === profileAvatar.type
|
|
353
|
+
? "border-[#3368ef] ring-2 ring-[#3368ef]/20"
|
|
354
|
+
: "border-transparent hover:border-[#e4e4e7]",
|
|
355
|
+
)}
|
|
356
|
+
>
|
|
357
|
+
<img
|
|
358
|
+
src={profileAvatar.avatar}
|
|
359
|
+
alt={`${profileAvatar.type} avatar`}
|
|
360
|
+
className="h-full w-full object-cover"
|
|
361
|
+
/>
|
|
362
|
+
</button>
|
|
196
363
|
|
|
197
|
-
|
|
198
|
-
|
|
199
|
-
|
|
200
|
-
|
|
201
|
-
|
|
202
|
-
|
|
203
|
-
|
|
204
|
-
|
|
205
|
-
|
|
206
|
-
|
|
207
|
-
|
|
208
|
-
|
|
209
|
-
|
|
364
|
+
{/* Tooltip */}
|
|
365
|
+
{hoveredProfile === profileAvatar.type && (
|
|
366
|
+
<div className="absolute -top-10 left-1/2 -translate-x-1/2 whitespace-nowrap rounded-md bg-[#18181b] px-3 py-1.5 text-xs text-white">
|
|
367
|
+
{profileAvatar.name}
|
|
368
|
+
</div>
|
|
369
|
+
)}
|
|
370
|
+
</div>
|
|
371
|
+
);
|
|
372
|
+
})}
|
|
373
|
+
</div>
|
|
374
|
+
|
|
375
|
+
{/* Link More Account */}
|
|
376
|
+
<button
|
|
377
|
+
onClick={handleLinkMoreAccount}
|
|
378
|
+
className="font-inter flex items-center gap-2 text-sm font-semibold text-[#3368ef] hover:underline"
|
|
379
|
+
>
|
|
380
|
+
<svg
|
|
381
|
+
width="16"
|
|
382
|
+
height="16"
|
|
383
|
+
viewBox="0 0 16 16"
|
|
384
|
+
fill="none"
|
|
385
|
+
xmlns="http://www.w3.org/2000/svg"
|
|
386
|
+
className="h-4 w-4"
|
|
387
|
+
>
|
|
388
|
+
<path
|
|
389
|
+
d="M8.75 2.75C8.75 2.33579 8.41421 2 8 2C7.58579 2 7.25 2.33579 7.25 2.75V7.25H2.75C2.33579 7.25 2 7.58579 2 8C2 8.41421 2.33579 8.75 2.75 8.75H7.25V13.25C7.25 13.6642 7.58579 14 8 14C8.41421 14 8.75 13.6642 8.75 13.25V8.75H13.25C13.6642 8.75 14 8.41421 14 8C14 7.58579 13.6642 7.25 13.25 7.25H8.75V2.75Z"
|
|
390
|
+
fill="currentColor"
|
|
391
|
+
/>
|
|
392
|
+
</svg>
|
|
393
|
+
Link more account
|
|
394
|
+
</button>
|
|
395
|
+
</div>
|
|
396
|
+
</>
|
|
397
|
+
) : (
|
|
398
|
+
<>
|
|
399
|
+
{/* Upload View */}
|
|
400
|
+
{!selectedFile ? (
|
|
401
|
+
<div
|
|
402
|
+
onClick={handleOpenFilePicker}
|
|
403
|
+
onDragEnter={handleDragEnter}
|
|
404
|
+
onDragLeave={handleDragLeave}
|
|
405
|
+
onDragOver={handleDragOver}
|
|
406
|
+
onDrop={handleDrop}
|
|
407
|
+
className={cn(
|
|
408
|
+
"mb-6 flex w-full cursor-pointer flex-col items-center justify-center rounded-xl border-2 border-dashed p-16 transition-colors",
|
|
409
|
+
isDragging
|
|
410
|
+
? "border-[#3368ef] bg-[#f0f5ff]"
|
|
411
|
+
: "border-[#e4e4e7] hover:border-[#3368ef] hover:bg-[#f0f5ff]",
|
|
412
|
+
)}
|
|
413
|
+
>
|
|
414
|
+
<p className="font-inter mb-1 text-sm">
|
|
415
|
+
<span className="font-semibold text-[#3368ef]">Click to upload</span>
|
|
416
|
+
<span className="text-[#71717a]"> or drag and drop</span>
|
|
417
|
+
</p>
|
|
418
|
+
<p className="text-xs text-[#71717a]">PNG, JPG or GIF (up to 5MB)</p>
|
|
419
|
+
</div>
|
|
210
420
|
) : (
|
|
211
|
-
|
|
212
|
-
<
|
|
213
|
-
|
|
214
|
-
|
|
421
|
+
<div className="mb-6 w-full">
|
|
422
|
+
<div className="aspect-square w-full overflow-hidden rounded-xl bg-[#f4f4f5]">
|
|
423
|
+
{safePreviewUrl ? (
|
|
424
|
+
<img src={safePreviewUrl} alt="Preview" className="h-full w-full object-cover" />
|
|
425
|
+
) : (
|
|
426
|
+
<div className="bg-b3-primary-wash h-full w-full" />
|
|
427
|
+
)}
|
|
428
|
+
</div>
|
|
429
|
+
</div>
|
|
215
430
|
)}
|
|
216
|
-
|
|
431
|
+
</>
|
|
217
432
|
)}
|
|
218
433
|
|
|
219
|
-
|
|
220
|
-
|
|
221
|
-
{selectedFile ? "Change Image" : "Select Image"}
|
|
222
|
-
</Button>
|
|
434
|
+
{/* Hidden file input */}
|
|
435
|
+
<input ref={fileInputRef} type="file" accept="image/*" onChange={handleFileSelect} className="hidden" />
|
|
223
436
|
</div>
|
|
224
437
|
|
|
225
|
-
{/*
|
|
226
|
-
<div className="
|
|
227
|
-
<
|
|
228
|
-
|
|
229
|
-
|
|
438
|
+
{/* Footer Buttons */}
|
|
439
|
+
<div className="font-inter flex gap-3 border-t border-[#e4e4e7] p-6 font-semibold">
|
|
440
|
+
<Button
|
|
441
|
+
onClick={handleCancel}
|
|
442
|
+
variant="outline"
|
|
443
|
+
disabled={isLoading}
|
|
444
|
+
className="flex-1 rounded-xl border-[#e4e4e7] text-[#18181b] hover:bg-[#f4f4f5]"
|
|
445
|
+
>
|
|
446
|
+
Cancel
|
|
447
|
+
</Button>
|
|
448
|
+
<Button
|
|
449
|
+
onClick={handleSaveChanges}
|
|
450
|
+
disabled={isLoading || (!selectedFile && !selectedProfileType)}
|
|
451
|
+
className="flex-1 rounded-xl bg-[#3368ef] text-white hover:bg-[#2952cc]"
|
|
452
|
+
>
|
|
453
|
+
{isLoading ? (
|
|
454
|
+
<>
|
|
455
|
+
<Loader2 className="mr-2 h-4 w-4 animate-spin" />
|
|
456
|
+
Saving...
|
|
457
|
+
</>
|
|
458
|
+
) : (
|
|
459
|
+
"Save changes"
|
|
460
|
+
)}
|
|
461
|
+
</Button>
|
|
230
462
|
</div>
|
|
231
463
|
</div>
|
|
232
464
|
);
|