@@ -42,13 +42,24 @@ export interface UserProfileConnectedAccount {
4242 canRemove ?: boolean ;
4343}
4444
45+ export interface UserProfileWeb3Wallet {
46+ id : string ;
47+ address : string ;
48+ provider ?: string ;
49+ iconUrl ?: string ;
50+ isPrimary ?: boolean ;
51+ isVerified ?: boolean ;
52+ canRemove ?: boolean ;
53+ }
54+
4555export interface UserProfileProfilePanelViewProps {
4656 imageUrl ?: string ;
4757 name : string ;
4858 username : string ;
4959 emails : UserProfileEmail [ ] ;
5060 phones : UserProfilePhone [ ] ;
5161 connectedAccounts ?: UserProfileConnectedAccount [ ] ;
62+ web3Wallets ?: UserProfileWeb3Wallet [ ] ;
5263 onEditProfilePicture ?: ( ) => void ;
5364 onNameChange ?: ( value : string ) => void ;
5465 onUsernameChange ?: ( value : string ) => void ;
@@ -65,6 +76,10 @@ export interface UserProfileProfilePanelViewProps {
6576 onConnectAccount ?: ( id : string ) => void ;
6677 onManageConnectedAccount ?: ( id : string ) => void ;
6778 onRemoveConnectedAccount ?: ( id : string ) => void ;
79+ onAddWeb3Wallet ?: ( ) => void ;
80+ onManageWeb3Wallet ?: ( id : string ) => void ;
81+ onSetPrimaryWeb3Wallet ?: ( id : string ) => void ;
82+ onRemoveWeb3Wallet ?: ( id : string ) => void ;
6883 onDeleteAccount ?: ( ) => void ;
6984}
7085
@@ -290,7 +305,7 @@ function ConnectedAccountsSection({
290305 </ Heading >
291306 < Card . Root elevation = 'outlined' >
292307 < Card . Content style = { { paddingBlock : space [ '4' ] , paddingInline : space [ '4' ] } } >
293- < Item . Group { ...stylex . props ( styles . connectedAccountsList ) } >
308+ < Item . Group { ...stylex . props ( styles . resourceList ) } >
294309 { accounts . map ( ( account , index ) => {
295310 const connected = account . connected ?? Boolean ( account . identifier ) ;
296311 const actions : ProfileMenuAction [ ] = [ ] ;
@@ -375,6 +390,140 @@ function ConnectedAccountsSection({
375390 ) ;
376391}
377392
393+ const shortenWeb3Address = ( address : string ) => {
394+ if ( address . length <= 10 ) {
395+ return address ;
396+ }
397+
398+ return `${ address . slice ( 0 , 6 ) } ...${ address . slice ( - 4 ) } ` ;
399+ } ;
400+
401+ function Web3WalletsSection ( {
402+ wallets,
403+ onAdd,
404+ onManage,
405+ onSetPrimary,
406+ onRemove,
407+ } : {
408+ wallets : UserProfileWeb3Wallet [ ] ;
409+ onAdd ?: ( ) => void ;
410+ onManage ?: ( id : string ) => void ;
411+ onSetPrimary ?: ( id : string ) => void ;
412+ onRemove ?: ( id : string ) => void ;
413+ } ) {
414+ return (
415+ < section
416+ aria-labelledby = 'user-profile-profile-panel-web3-wallets'
417+ { ...stylex . props ( styles . section ) }
418+ >
419+ < div { ...stylex . props ( styles . sectionHeader ) } >
420+ < Heading
421+ id = 'user-profile-profile-panel-web3-wallets'
422+ render = { props => < h4 { ...props } /> }
423+ size = 'xs'
424+ >
425+ Web3 wallets
426+ </ Heading >
427+ { onAdd ? (
428+ < Button
429+ color = 'neutral'
430+ size = 'sm'
431+ variant = 'outline'
432+ onClick = { onAdd }
433+ >
434+ Add
435+ </ Button >
436+ ) : null }
437+ </ div >
438+ < Card . Root elevation = 'outlined' >
439+ < Card . Content style = { { paddingBlock : space [ '4' ] , paddingInline : space [ '4' ] } } >
440+ < Item . Group { ...stylex . props ( styles . resourceList ) } >
441+ { wallets . map ( ( wallet , index ) => {
442+ const actions : ProfileMenuAction [ ] = [ ] ;
443+ const hasExplicitActions = Boolean ( onSetPrimary || onRemove ) ;
444+
445+ if ( ! wallet . isPrimary && wallet . isVerified !== false && onSetPrimary ) {
446+ actions . push ( { label : 'Set as primary' , onClick : ( ) => onSetPrimary ( wallet . id ) } ) ;
447+ }
448+
449+ if ( onRemove && wallet . canRemove !== false ) {
450+ actions . push ( { label : 'Remove wallet' , color : 'negative' , onClick : ( ) => onRemove ( wallet . id ) } ) ;
451+ }
452+
453+ if ( ! hasExplicitActions && onManage ) {
454+ actions . push ( { label : 'Manage' , onClick : ( ) => onManage ( wallet . id ) } ) ;
455+ }
456+
457+ const address = shortenWeb3Address ( wallet . address ) ;
458+
459+ return (
460+ < Fragment key = { wallet . id } >
461+ { index > 0 ? (
462+ < Item . Separator
463+ style = { {
464+ backgroundColor : colorVars [ '--cl-color-border' ] ,
465+ marginBlock : space [ '4' ] ,
466+ marginInline : space [ '4' ] ,
467+ width : 'auto' ,
468+ } }
469+ />
470+ ) : null }
471+ < Item . Root
472+ size = 'md'
473+ style = { { height : 'auto' , paddingBlock : 0 , paddingInline : 0 } }
474+ >
475+ { wallet . iconUrl ? (
476+ < Item . Media style = { { width : space [ '6' ] } } >
477+ < img
478+ alt = ''
479+ src = { wallet . iconUrl }
480+ { ...stylex . props ( styles . providerIcon ) }
481+ />
482+ </ Item . Media >
483+ ) : null }
484+ < Item . Content style = { { gap : space [ '0.5' ] } } >
485+ < div { ...stylex . props ( styles . contactValue ) } >
486+ < Item . Title > { wallet . provider ?? address } </ Item . Title >
487+ { wallet . isPrimary ? (
488+ < Badge
489+ color = 'neutral'
490+ style = { { backgroundColor : 'rgba(23, 23, 23, 0.06)' , color : 'inherit' } }
491+ >
492+ Primary
493+ </ Badge >
494+ ) : null }
495+ { wallet . isVerified === false ? < Badge color = 'warning' > Unverified</ Badge > : null }
496+ </ div >
497+ { wallet . provider ? (
498+ < Item . Description
499+ style = { {
500+ fontSize : typeScaleVars [ '--cl-text-sm-size' ] ,
501+ lineHeight : typeScaleVars [ '--cl-text-sm-leading' ] ,
502+ } }
503+ >
504+ { address }
505+ </ Item . Description >
506+ ) : null }
507+ </ Item . Content >
508+ { actions . length > 0 ? (
509+ < Item . Actions >
510+ < ProfileActionMenu
511+ actions = { actions }
512+ label = { `Manage ${ wallet . provider ?? address } ` }
513+ />
514+ </ Item . Actions >
515+ ) : null }
516+ </ Item . Root >
517+ </ Fragment >
518+ ) ;
519+ } ) }
520+ </ Item . Group >
521+ </ Card . Content >
522+ </ Card . Root >
523+ </ section >
524+ ) ;
525+ }
526+
378527function DangerZoneSection ( { onDelete } : { onDelete : ( ) => void } ) {
379528 return (
380529 < section
@@ -536,6 +685,7 @@ export function UserProfileProfilePanelView({
536685 emails = [ ] ,
537686 phones = [ ] ,
538687 connectedAccounts = [ ] ,
688+ web3Wallets = [ ] ,
539689 onEditProfilePicture,
540690 onNameChange,
541691 onUsernameChange,
@@ -552,6 +702,10 @@ export function UserProfileProfilePanelView({
552702 onConnectAccount,
553703 onManageConnectedAccount,
554704 onRemoveConnectedAccount,
705+ onAddWeb3Wallet,
706+ onManageWeb3Wallet,
707+ onSetPrimaryWeb3Wallet,
708+ onRemoveWeb3Wallet,
555709 onDeleteAccount,
556710} : UserProfileProfilePanelViewProps ) : ReactElement {
557711 return (
@@ -590,6 +744,15 @@ export function UserProfileProfilePanelView({
590744 onRemove = { onRemoveConnectedAccount }
591745 />
592746 ) : null }
747+ { web3Wallets . length > 0 || onAddWeb3Wallet ? (
748+ < Web3WalletsSection
749+ wallets = { web3Wallets }
750+ onAdd = { onAddWeb3Wallet }
751+ onManage = { onManageWeb3Wallet }
752+ onRemove = { onRemoveWeb3Wallet }
753+ onSetPrimary = { onSetPrimaryWeb3Wallet }
754+ />
755+ ) : null }
593756 { onDeleteAccount ? < DangerZoneSection onDelete = { onDeleteAccount } /> : null }
594757 </ div >
595758 ) ;
0 commit comments