Skip to content

Commit 454a8be

Browse files
committed
fixup! feat: add support for lens social network integration
1 parent a578527 commit 454a8be

File tree

18 files changed

+246
-75
lines changed

18 files changed

+246
-75
lines changed

packages/mask/package.json

Lines changed: 1 addition & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -36,6 +36,7 @@
3636
"@dimensiondev/mask-wallet-core": "0.1.0-20211013082857-eb62e5f",
3737
"@ethereumjs/util": "^9.0.3",
3838
"@hookform/resolvers": "^3.6.0",
39+
"@lens-protocol/client": "0.0.0-canary-20250408064617",
3940
"@masknet/backup-format": "workspace:^",
4041
"@masknet/encryption": "workspace:^",
4142
"@masknet/flags": "workspace:^",

packages/mask/popups/Popup.tsx

Lines changed: 28 additions & 19 deletions
Original file line numberDiff line numberDiff line change
@@ -1,10 +1,10 @@
11
import { PageUIProvider, PersonaContext } from '@masknet/shared'
2-
import { jsxCompose, MaskMessages, PopupRoutes } from '@masknet/shared-base'
2+
import { MaskMessages, PopupRoutes } from '@masknet/shared-base'
33
import { PopupSnackbarProvider } from '@masknet/theme'
44
import { EVMWeb3ContextProvider } from '@masknet/web3-hooks-base'
55
import { ProviderType } from '@masknet/web3-shared-evm'
66
import { Box } from '@mui/material'
7-
import { Suspense, cloneElement, lazy, memo, useEffect, useMemo, useState, type ReactNode } from 'react'
7+
import { Suspense, lazy, memo, useEffect, useMemo, useState, type ReactNode } from 'react'
88
import { useIdleTimer } from 'react-idle-timer'
99
import {
1010
createHashRouter,
@@ -30,6 +30,7 @@ import { WalletFrame, walletRoutes } from './pages/Wallet/index.js'
3030
import { ContactsFrame, contactsRoutes } from './pages/Friends/index.js'
3131
import { ErrorBoundaryUIOfError } from '../../shared-base-ui/src/components/ErrorBoundary/ErrorBoundary.js'
3232
import { TraderFrame, traderRoutes } from './pages/Trader/index.js'
33+
import { InteractionWalletContext } from './pages/Wallet/Interaction/InteractionContext.js'
3334

3435
const personaInitialState = {
3536
queryOwnedPersonaInformation: Services.Identity.queryOwnedPersonaInformation,
@@ -108,23 +109,31 @@ export default function Popups() {
108109
throttle: 10000,
109110
})
110111

111-
return jsxCompose(
112-
<PersistQueryClientProvider client={queryClient} persistOptions={queryPersistOptions} />,
113-
// eslint-disable-next-line react-compiler/react-compiler
114-
<PageUIProvider useTheme={usePopupTheme} />,
115-
<PopupSnackbarProvider children={null!} />,
116-
<EVMWeb3ContextProvider providerType={ProviderType.MaskWallet} />,
117-
<PopupContext />,
118-
<PageTitleContext value={titleContext} />,
119-
)(
120-
cloneElement,
121-
<>
122-
{/* https://github.com/TanStack/query/issues/5417 */}
123-
{process.env.NODE_ENV === 'development' ?
124-
<ReactQueryDevtools buttonPosition="bottom-right" />
125-
: null}
126-
<RouterProvider router={router} fallbackElement={pending} future={{ v7_startTransition: true }} />
127-
</>,
112+
return (
113+
<PersistQueryClientProvider client={queryClient} persistOptions={queryPersistOptions}>
114+
{/* eslint-disable-next-line react-compiler/react-compiler */}
115+
<PageUIProvider useTheme={usePopupTheme}>
116+
<PopupSnackbarProvider>
117+
<EVMWeb3ContextProvider providerType={ProviderType.MaskWallet}>
118+
<InteractionWalletContext>
119+
<PopupContext>
120+
<PageTitleContext value={titleContext}>
121+
{/* https://github.com/TanStack/query/issues/5417 */}
122+
{process.env.NODE_ENV === 'development' ?
123+
<ReactQueryDevtools buttonPosition="bottom-right" />
124+
: null}
125+
<RouterProvider
126+
router={router}
127+
fallbackElement={pending}
128+
future={{ v7_startTransition: true }}
129+
/>
130+
</PageTitleContext>
131+
</PopupContext>
132+
</InteractionWalletContext>
133+
</EVMWeb3ContextProvider>
134+
</PopupSnackbarProvider>
135+
</PageUIProvider>
136+
</PersistQueryClientProvider>
128137
)
129138
}
130139

packages/mask/popups/modals/ConnectSocialAccountModal/index.tsx

Lines changed: 3 additions & 2 deletions
Original file line numberDiff line numberDiff line change
@@ -21,8 +21,9 @@ export const ConnectSocialAccountModal = memo<ActionModalBaseProps>(function Con
2121
const handleConnect = useCallback(
2222
async (networkIdentifier: EnhanceableSite) => {
2323
if (networkIdentifier === EnhanceableSite.Farcaster) {
24-
navigate(PopupRoutes.ConnectFirefly)
25-
return
24+
return navigate(PopupRoutes.ConnectFirefly)
25+
} else if (networkIdentifier === EnhanceableSite.Lens) {
26+
return navigate(PopupRoutes.ConnectLens)
2627
}
2728
if (!currentPersona) return
2829
if (!(await requestPermissionFromExtensionPage(networkIdentifier))) return

packages/mask/popups/pages/Personas/ConnectFirefly/index.tsx

Lines changed: 12 additions & 20 deletions
Original file line numberDiff line numberDiff line change
@@ -48,7 +48,6 @@ function useLogin() {
4848
const account = await createAccount()
4949

5050
const done = await addAccount(account, options)
51-
console.log('created account', account)
5251
if (done) showSnackbar(<Trans>Your {Social.Source.Farcaster} account is now connected.</Trans>)
5352
} catch (error) {
5453
// skip if the error is abort error
@@ -92,26 +91,19 @@ export const Component = memo(function ConnectFireflyPage() {
9291
const { currentPersona } = PersonaContext.useContainer()
9392

9493
useMount(async () => {
95-
login(async () => {
96-
try {
97-
const account = await createAccountByRelayService((url) => {
98-
setUrl(url)
99-
})
100-
console.log('account', account)
101-
if (currentPersona) {
102-
await Services.Identity.attachProfile(
103-
ProfileIdentifier.of(EnhanceableSite.Farcaster, account.session.profileId).unwrap(),
104-
currentPersona.identifier,
105-
{ connectionConfirmState: 'pending' },
106-
{ token: account.session.token },
107-
)
108-
}
109-
console.log('account', account)
110-
return account
111-
} catch (err) {
112-
console.log('error', err)
113-
throw err
94+
await login(async () => {
95+
const account = await createAccountByRelayService((url) => {
96+
setUrl(url)
97+
})
98+
if (currentPersona) {
99+
await Services.Identity.attachProfile(
100+
ProfileIdentifier.of(EnhanceableSite.Farcaster, account.session.profileId).unwrap(),
101+
currentPersona.identifier,
102+
{ connectionConfirmState: 'pending' },
103+
{ token: account.session.token },
104+
)
114105
}
106+
return account
115107
})
116108
})
117109

Lines changed: 155 additions & 0 deletions
Original file line numberDiff line numberDiff line change
@@ -0,0 +1,155 @@
1+
import type { Account, EvmAddress } from '@lens-protocol/client'
2+
import { Image, useAvailableLensAccounts } from '@masknet/shared'
3+
import { EMPTY_LIST } from '@masknet/shared-base'
4+
import { makeStyles } from '@masknet/theme'
5+
import { LensV3 } from '@masknet/web3-providers'
6+
import { isSameAddress } from '@masknet/web3-shared-base'
7+
import {
8+
List,
9+
ListItemButton,
10+
ListItemIcon,
11+
ListItemSecondaryAction,
12+
ListItemText,
13+
Radio,
14+
Typography,
15+
} from '@mui/material'
16+
import { first } from 'lodash-es'
17+
import { memo, useState } from 'react'
18+
import { Icons } from '@masknet/icons'
19+
import { formatEthereumAddress } from '@masknet/web3-shared-evm'
20+
21+
const useStyles = makeStyles()((theme) => ({
22+
container: {
23+
minHeight: 0,
24+
display: 'flex',
25+
flexDirection: 'column',
26+
},
27+
list: {
28+
minHeight: 0,
29+
overflow: 'auto',
30+
marginBottom: theme.spacing(1.5),
31+
scrollbarWidth: 'none',
32+
'::-webkit-scrollbar': {
33+
backgroundColor: 'transparent',
34+
width: 18,
35+
},
36+
'::-webkit-scrollbar-thumb': {
37+
borderRadius: '20px',
38+
width: 5,
39+
border: '7px solid rgba(0, 0, 0, 0)',
40+
backgroundColor: theme.palette.maskColor.secondaryLine,
41+
backgroundClip: 'padding-box',
42+
},
43+
},
44+
avatar: {
45+
borderRadius: 99,
46+
overflow: 'hidden',
47+
},
48+
primary: {
49+
color: theme.palette.maskColor.main,
50+
fontWeight: 700,
51+
lineHeight: '18px',
52+
overflow: 'hidden',
53+
whiteSpace: 'nowrap',
54+
textOverflow: 'ellipsis',
55+
paddingRight: 50,
56+
},
57+
second: {
58+
display: 'flex',
59+
columnGap: 4,
60+
alignItems: 'center',
61+
},
62+
address: {
63+
fontWeight: 700,
64+
fontSize: 14,
65+
lineHeight: '18px',
66+
color: theme.palette.maskColor.second,
67+
},
68+
managedTag: {
69+
background: theme.palette.maskColor.third,
70+
color: theme.palette.maskColor.bottom,
71+
fontSize: 12,
72+
padding: theme.spacing(0.5),
73+
borderRadius: 4,
74+
lineHeight: '12px',
75+
},
76+
item: {
77+
padding: theme.spacing(1.5),
78+
borderRadius: 8,
79+
},
80+
disabled: {
81+
opacity: 0.5,
82+
cursor: 'not-allowed',
83+
},
84+
listItemText: {
85+
margin: 0,
86+
},
87+
}))
88+
89+
export const Component = memo(function ConnectLensView() {
90+
const { classes, cx } = useStyles()
91+
const { data: accounts = EMPTY_LIST } = useAvailableLensAccounts()
92+
const [selectedLensAddress, setSelectedLensAddress] = useState<string>()
93+
const currentAccount =
94+
accounts?.find((p) => isSameAddress(p.account.address, selectedLensAddress)) || first(accounts)
95+
const currentAccountId = currentAccount?.account.username?.id
96+
97+
const onChange = (account: Account) => {
98+
setSelectedLensAddress(account.address)
99+
}
100+
101+
return (
102+
<div className={classes.container}>
103+
<List disablePadding className={classes.list}>
104+
{accounts?.map(({ account, __typename: accountType }) => {
105+
const avatar = LensV3.getAccountAvatar(account)
106+
const name = account.metadata?.name || account.username?.localName
107+
const ownerAddress: EvmAddress = account.username?.ownedBy
108+
const accountId = account.username?.id
109+
const disabled = accountId === currentAccountId
110+
return (
111+
<ListItemButton
112+
className={cx(classes.item, { [classes.disabled]: disabled })}
113+
key={accountId}
114+
disabled={disabled}
115+
onClick={() => {
116+
if (disabled) return
117+
onChange(account)
118+
}}>
119+
<ListItemIcon>
120+
{avatar ?
121+
<Image
122+
rounded
123+
size={36}
124+
src={avatar}
125+
fallback={<Icons.DarkLens size={36} className={classes.avatar} />}
126+
/>
127+
: <Icons.DarkLens size={36} className={classes.avatar} />}
128+
</ListItemIcon>
129+
<ListItemText
130+
classes={{ primary: classes.primary, root: classes.listItemText }}
131+
primary={name}
132+
secondaryTypographyProps={{ component: 'div' }}
133+
secondary={
134+
<div className={classes.second}>
135+
<Typography component="div" className={classes.address}>
136+
{formatEthereumAddress(ownerAddress, 4)}
137+
</Typography>
138+
{accountType === 'AccountManaged' ?
139+
<Typography component="span" className={classes.managedTag}>
140+
Managed
141+
</Typography>
142+
: null}
143+
</div>
144+
}
145+
/>
146+
<ListItemSecondaryAction>
147+
<Radio checked={currentAccountId === accountId} />
148+
</ListItemSecondaryAction>
149+
</ListItemButton>
150+
)
151+
})}
152+
</List>
153+
</div>
154+
)
155+
})

packages/mask/popups/pages/Personas/index.tsx

Lines changed: 15 additions & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -1,11 +1,12 @@
1-
import { memo, useEffect } from 'react'
2-
import { useMount, useAsync } from 'react-use'
3-
import { Navigate, Outlet, useNavigate, useSearchParams, type RouteObject } from 'react-router-dom'
1+
import Services from '#services'
42
import { CrossIsolationMessages, PopupModalRoutes, PopupRoutes, relativeRouteOf } from '@masknet/shared-base'
5-
import { PersonaHeader } from './components/PersonaHeader/index.js'
63
import { EVMWeb3ContextProvider } from '@masknet/web3-hooks-base'
4+
import { memo, useEffect } from 'react'
5+
import { Navigate, Outlet, useNavigate, useSearchParams, type RouteObject } from 'react-router-dom'
6+
import { useAsync, useMount } from 'react-use'
77
import { useModalNavigate } from '../../components/index.js'
8-
import Services from '#services'
8+
import { WalletGuard } from '../Wallet/WalletGuard/index.js'
9+
import { PersonaHeader } from './components/PersonaHeader/index.js'
910

1011
const r = relativeRouteOf(PopupRoutes.Personas)
1112
export const personaRoute: RouteObject[] = [
@@ -18,6 +19,15 @@ export const personaRoute: RouteObject[] = [
1819
{ path: r(PopupRoutes.ExportPrivateKey), lazy: () => import('./ExportPrivateKey/index.js') },
1920
{ path: r(PopupRoutes.PersonaAvatarSetting), lazy: () => import('./PersonaAvatarSetting/index.js') },
2021
{ path: r(PopupRoutes.ConnectFirefly), lazy: () => import('./ConnectFirefly/index.js') },
22+
{
23+
element: <WalletGuard />,
24+
children: [
25+
{
26+
path: r(PopupRoutes.ConnectLens),
27+
lazy: () => import('./ConnectLens/index.js'),
28+
},
29+
],
30+
},
2131
{ path: '*', element: <Navigate replace to={PopupRoutes.Personas} /> },
2232
]
2333

packages/mask/popups/pages/Wallet/Interaction/InteractionContext.ts

Lines changed: 1 addition & 1 deletion
Original file line numberDiff line numberDiff line change
@@ -10,7 +10,7 @@ import { createContainer, useRenderPhraseCallbackOnDepsChange } from '@masknet/s
1010
export const { Provider: InteractionWalletContext, useContainer: useInteractionWalletContext } = createContainer(
1111
function () {
1212
const wallet = useWallet()
13-
const [interactionWallet, setInteractionWallet] = useState<string | undefined>()
13+
const [interactionWallet, setInteractionWallet] = useState<string>()
1414

1515
function useInteractionWallet(currentInteractingWallet: string | undefined) {
1616
useRenderPhraseCallbackOnDepsChange(() => {

packages/mask/popups/pages/Wallet/WalletGuard/index.tsx

Lines changed: 12 additions & 14 deletions
Original file line numberDiff line numberDiff line change
@@ -9,16 +9,21 @@ import { WalletHeader } from '../components/WalletHeader/index.js'
99
import { useWalletLockStatus } from '../hooks/index.js'
1010
import { useMessageGuard } from './useMessageGuard.js'
1111
import { usePaymentPasswordGuard } from './usePaymentPasswordGuard.js'
12-
import { InteractionWalletContext, useInteractionWalletContext } from '../Interaction/InteractionContext.js'
12+
import { useInteractionWalletContext } from '../Interaction/InteractionContext.js'
1313

14-
export const WalletGuard = memo(function WalletGuard() {
14+
interface Props {
15+
disableHeader?: boolean
16+
}
17+
18+
export const WalletGuard = memo(function WalletGuard({ disableHeader }: Props) {
1519
const wallets = useWallets()
1620
const location = useLocation()
1721
const [params] = useSearchParams()
1822
const { isLocked, isPending } = useWalletLockStatus()
1923

2024
const hitPaymentPasswordGuard = usePaymentPasswordGuard()
2125
const hitMessageGuard = useMessageGuard()
26+
const { interactionWallet } = useInteractionWalletContext()
2227

2328
if (!wallets.length) {
2429
return (
@@ -44,18 +49,11 @@ export const WalletGuard = memo(function WalletGuard() {
4449
if (hitMessageGuard) return <Navigate to={PopupRoutes.ContractInteraction} />
4550

4651
return (
47-
<InteractionWalletContext>
48-
<WalletGuardContent />
49-
</InteractionWalletContext>
50-
)
51-
})
52-
53-
function WalletGuardContent() {
54-
const { interactionWallet } = useInteractionWalletContext()
55-
return (
56-
<ChainContextProvider account={interactionWallet}>
57-
<WalletHeader />
52+
<ChainContextProvider account={interactionWallet} isPopupWallet>
53+
{disableHeader ?
54+
<WalletHeader />
55+
: null}
5856
<Outlet />
5957
</ChainContextProvider>
6058
)
61-
}
59+
})

packages/mask/popups/pages/Wallet/components/WalletHeader/UI.tsx

Lines changed: 1 addition & 5 deletions
Original file line numberDiff line numberDiff line change
@@ -169,11 +169,7 @@ export const WalletHeaderUI = memo<WalletHeaderUIProps>(function WalletHeaderUI(
169169
</Typography>
170170
</TextOverflowTooltip>
171171
{!disabled && !wallet.owner ?
172-
<Icons.ArrowDrop
173-
size={20}
174-
className={classes.arrow}
175-
style={{ transform: status ? 'rotate(-180deg)' : undefined }}
176-
/>
172+
<Icons.ArrowDrop size={20} className={classes.arrow} />
177173
: null}
178174
</Box>
179175
{isPending ? null : (

0 commit comments

Comments
 (0)