diff --git a/cspell.json b/cspell.json
index 802ff9b768d2..2206d4c57230 100644
--- a/cspell.json
+++ b/cspell.json
@@ -236,5 +236,6 @@
],
"ignoreRegExpList": ["/[A-Za-z0-9]{44}/", "/[A-Za-z0-9]{46}/", "/[A-Za-z0-9]{59}/"],
"overrides": [],
- "words": []
+ "words": ["endregion"]
}
+
diff --git a/packages/mask/content-script/site-adaptor-infra/ui.ts b/packages/mask/content-script/site-adaptor-infra/ui.ts
index f30c49dbc5db..e898b735edc8 100644
--- a/packages/mask/content-script/site-adaptor-infra/ui.ts
+++ b/packages/mask/content-script/site-adaptor-infra/ui.ts
@@ -96,9 +96,7 @@ export async function activateSiteAdaptorUIInner(ui_deferred: SiteAdaptorUI.Defe
ui.injection.userAvatar?.(signal)
ui.injection.profileAvatar?.(signal)
ui.injection.tips?.(signal)
- ui.injection.nameWidget?.(signal)
- ui.injection.farcaster?.(signal)
- ui.injection.lens?.(signal)
+ ui.injection.badges?.(signal)
ui.injection.enhancedProfileNFTAvatar?.(signal)
ui.injection.openNFTAvatar?.(signal)
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/index.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/index.tsx
new file mode 100644
index 000000000000..8b0acb51ac85
--- /dev/null
+++ b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/index.tsx
@@ -0,0 +1,13 @@
+import { injectBadgesOnConversation } from './injectBadgesOnConversation.js'
+import { injectBadgesOnPost } from './injectBadgesOnPost.js'
+import { injectBadgesOnProfile } from './injectBadgesOnProfile.js'
+import { injectBadgesOnSpaceDock } from './injectBadgesOnSpaceDock.js'
+import { injectBadgesOnUserCell } from './injectBadgesOnUserCell.js'
+
+export function injectBadges(signal: AbortSignal) {
+ injectBadgesOnProfile(signal)
+ injectBadgesOnPost(signal)
+ injectBadgesOnUserCell(signal)
+ injectBadgesOnConversation(signal)
+ injectBadgesOnSpaceDock(signal)
+}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnConversation.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnConversation.tsx
similarity index 89%
rename from packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnConversation.tsx
rename to packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnConversation.tsx
index 4417aa7a2ca2..341679c691ee 100644
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnConversation.tsx
+++ b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnConversation.tsx
@@ -42,14 +42,14 @@ function createRootElement() {
return span
}
-const ConversationFarcasterSlot = memo(function ConversationFarcasterSlot({ userId }: Props) {
+const ConversationBadgesSlot = memo(function ConversationBadgesSlot({ userId }: Props) {
const [disabled, setDisabled] = useState(true)
const { classes, cx } = useStyles()
const component = useMemo(() => {
const Component = createInjectHooksRenderer(
useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Farcaster?.UI?.Content,
+ (plugin) => plugin.Badges?.UI?.Content,
undefined,
createRootElement,
)
@@ -59,7 +59,7 @@ const ConversationFarcasterSlot = memo(function ConversationFarcasterSlot({ user
return (
)
@@ -73,7 +73,7 @@ const ConversationFarcasterSlot = memo(function ConversationFarcasterSlot({ user
/**
* Inject on conversation, including both DM drawer and message page (/messages/xxx)
*/
-export function injectFarcasterOnConversation(signal: AbortSignal) {
+export function injectBadgesOnConversation(signal: AbortSignal) {
const watcher = new MutationObserverWatcher(selector())
startWatch(watcher, signal)
watcher.useForeach((node, _, proxy) => {
@@ -90,7 +90,7 @@ export function injectFarcasterOnConversation(signal: AbortSignal) {
}, '')
if (!userId) return
attachReactTreeWithContainer(proxy.afterShadow, { signal, untilVisible: true }).render(
- ,
+ ,
)
})
}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnPost.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnPost.tsx
similarity index 88%
rename from packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnPost.tsx
rename to packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnPost.tsx
index 4238552ce692..5e4cb58824a3 100644
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnPost.tsx
+++ b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnPost.tsx
@@ -14,7 +14,7 @@ function selector() {
}
// structure:
-export function injectLensOnPost(signal: AbortSignal) {
+export function injectBadgesOnPost(signal: AbortSignal) {
const watcher = new MutationObserverWatcher(selector())
startWatch(watcher, signal)
watcher.useForeach((node, _, proxy) => {
@@ -26,7 +26,7 @@ export function injectLensOnPost(signal: AbortSignal) {
const userId = href?.split('/')[1]
if (!userId) return
attachReactTreeWithContainer(proxy.afterShadow, { signal, untilVisible: true }).render(
- ,
+ ,
)
})
}
@@ -58,21 +58,23 @@ function createRootElement() {
})
return span
}
-function PostLensSlot({ userId }: Props) {
+function PostBadgesSlot({ userId }: Props) {
const [disabled, setDisabled] = useState(true)
const { classes, cx } = useStyles()
const component = useMemo(() => {
const Component = createInjectHooksRenderer(
useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Lens?.UI?.Content,
+ (plugin) => plugin.Badges?.UI?.Content,
undefined,
createRootElement,
)
const identifier = ProfileIdentifier.of(EnhanceableSite.Twitter, userId).unwrap()
if (!identifier) return null
- return
+ return (
+
+ )
}, [userId])
if (!component) return null
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnProfile.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnProfile.tsx
similarity index 88%
rename from packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnProfile.tsx
rename to packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnProfile.tsx
index 308d72966689..2276795fcb1e 100644
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnProfile.tsx
+++ b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnProfile.tsx
@@ -11,10 +11,10 @@ function selector() {
return querySelector('[data-testid=UserName] div[dir]')
}
-export function injectLensOnProfile(signal: AbortSignal) {
+export function injectBadgesOnProfile(signal: AbortSignal) {
const watcher = new MutationObserverWatcher(selector())
startWatch(watcher, signal)
- attachReactTreeWithContainer(watcher.firstDOMProxy.afterShadow, { signal }).render()
+ attachReactTreeWithContainer(watcher.firstDOMProxy.afterShadow, { signal }).render()
}
const useStyles = makeStyles()((theme) => ({
@@ -32,7 +32,7 @@ const useStyles = makeStyles()((theme) => ({
},
}))
-function ProfileLensSlot() {
+function ProfileBadgesSlot() {
const visitingIdentity = useCurrentVisitingIdentity()
const [disabled, setDisabled] = useState(true)
const { classes, cx } = useStyles()
@@ -40,13 +40,13 @@ function ProfileLensSlot() {
const component = useMemo(() => {
const Component = createInjectHooksRenderer(
useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Lens?.UI?.Content,
+ (plugin) => plugin.Badges?.UI?.Content,
)
return (
)
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnSpaceDock.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnSpaceDock.tsx
similarity index 91%
rename from packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnSpaceDock.tsx
rename to packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnSpaceDock.tsx
index 062aa96b02a6..e66841e81081 100644
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnSpaceDock.tsx
+++ b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnSpaceDock.tsx
@@ -19,7 +19,7 @@ function avatarSelector() {
/**
* Inject on space dock
*/
-export function injectFarcasterOnSpaceDock(signal: AbortSignal) {
+export function injectBadgesOnSpaceDock(signal: AbortSignal) {
const watcher = new MutationObserverWatcher(avatarSelector())
startWatch(watcher, signal)
watcher.useForeach((node, _, proxy) => {
@@ -30,7 +30,7 @@ export function injectFarcasterOnSpaceDock(signal: AbortSignal) {
const userId = avatar.dataset.testid?.slice('UserAvatar-Container-'.length)
if (!userId) return
attachReactTreeWithContainer(proxy.afterShadow, { signal, untilVisible: true }).render(
- ,
+ ,
)
})
}
@@ -66,14 +66,14 @@ function createRootElement() {
return span
}
-function SpaceDockFarcasterSlot({ userId }: Props) {
+function SpaceDockBadgesSlot({ userId }: Props) {
const [disabled, setDisabled] = useState(true)
const { classes, cx } = useStyles()
const component = useMemo(() => {
const Component = createInjectHooksRenderer(
useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Farcaster?.UI?.Content,
+ (plugin) => plugin.Badges?.UI?.Content,
undefined,
createRootElement,
)
@@ -83,7 +83,7 @@ function SpaceDockFarcasterSlot({ userId }: Props) {
return (
)
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnUserCell.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnUserCell.tsx
similarity index 90%
rename from packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnUserCell.tsx
rename to packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnUserCell.tsx
index 145dfc4e01ab..1e53280db9fa 100644
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnUserCell.tsx
+++ b/packages/mask/content-script/site-adaptors/twitter.com/injection/Badges/injectBadgesOnUserCell.tsx
@@ -17,14 +17,14 @@ function selector() {
/**
* Inject on sidebar user cell
*/
-export function injectFarcasterOnUserCell(signal: AbortSignal) {
+export function injectBadgesOnUserCell(signal: AbortSignal) {
const watcher = new MutationObserverWatcher(selector())
startWatch(watcher, signal)
watcher.useForeach((node, _, proxy) => {
const userId = node.closest('[role=link]')?.getAttribute('href')?.slice(1)
if (!userId) return
// Intended to set `untilVisible` to true, but mostly user cells are fixed and visible
- attachReactTreeWithContainer(proxy.afterShadow, { signal }).render()
+ attachReactTreeWithContainer(proxy.afterShadow, { signal }).render()
})
}
@@ -59,14 +59,14 @@ function createRootElement() {
return span
}
-function UserCellFarcasterSlot({ userId }: Props) {
+function UserCellBadgesSlot({ userId }: Props) {
const [disabled, setDisabled] = useState(true)
const { classes, cx } = useStyles()
const component = useMemo(() => {
const Component = createInjectHooksRenderer(
useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Farcaster?.UI?.Content,
+ (plugin) => plugin.Badges?.UI?.Content,
undefined,
createRootElement,
)
@@ -77,7 +77,7 @@ function UserCellFarcasterSlot({ userId }: Props) {
return (
)
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/index.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/index.tsx
deleted file mode 100644
index 34e296b395d5..000000000000
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/index.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { injectFarcasterOnConversation } from './injectFarcasterOnConversation.js'
-import { injectFarcasterOnPost } from './injectFarcasterOnPost.js'
-import { injectFarcasterOnProfile } from './injectFarcasterOnProfile.js'
-import { injectFarcasterOnSpaceDock } from './injectFarcasterOnSpaceDock.js'
-import { injectFarcasterOnUserCell } from './injectFarcasterOnUserCell.js'
-
-export function injectFarcaster(signal: AbortSignal) {
- injectFarcasterOnProfile(signal)
- injectFarcasterOnPost(signal)
- injectFarcasterOnUserCell(signal)
- injectFarcasterOnConversation(signal)
- injectFarcasterOnSpaceDock(signal)
-}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnPost.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnPost.tsx
deleted file mode 100644
index bd45efd24e4a..000000000000
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnPost.tsx
+++ /dev/null
@@ -1,87 +0,0 @@
-import { useMemo, useState } from 'react'
-import { EnhanceableSite, ProfileIdentifier } from '@masknet/shared-base'
-import { makeStyles } from '@masknet/theme'
-import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
-import { createInjectHooksRenderer, Plugin, useActivatedPluginsSiteAdaptor } from '@masknet/plugin-infra/content-script'
-import { startWatch } from '../../../../utils/startWatch.js'
-import { attachReactTreeWithContainer } from '../../../../utils/shadow-root/renderInShadowRoot.js'
-import { querySelectorAll } from '../../utils/selector.js'
-
-function selector() {
- return querySelectorAll('[data-testid=User-Name] div').filter((node) => {
- return node.firstElementChild?.matches('a[role=link]:not([tabindex])')
- })
-}
-
-// structure:
-export function injectFarcasterOnPost(signal: AbortSignal) {
- const watcher = new MutationObserverWatcher(selector())
- startWatch(watcher, signal)
- watcher.useForeach((node, _, proxy) => {
- const link = node.querySelector('a[href][role=link]')
- // To simplify the selector above, we do this checking manually
- // has tabindex=-1, has a child time element
- if (link?.hasAttribute('tabindex') || link?.querySelector('time')) return
- const href = link?.getAttribute('href')
- const userId = href?.split('/')[1]
- if (!userId) return
- attachReactTreeWithContainer(proxy.afterShadow, { signal, untilVisible: true }).render(
- ,
- )
- })
-}
-
-const useStyles = makeStyles()((theme) => ({
- hide: {
- display: 'none',
- },
- slot: {
- position: 'relative',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- borderRadius: 999,
- marginLeft: theme.spacing(0.5),
- verticalAlign: 'top',
- },
-}))
-
-interface Props {
- userId: string
-}
-
-function createRootElement() {
- const span = document.createElement('span')
- Object.assign(span.style, {
- alignItems: 'center',
- display: 'flex',
- })
- return span
-}
-function PostFarcasterSlot({ userId }: Props) {
- const [disabled, setDisabled] = useState(true)
- const { classes, cx } = useStyles()
-
- const component = useMemo(() => {
- const Component = createInjectHooksRenderer(
- useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Farcaster?.UI?.Content,
- undefined,
- createRootElement,
- )
- const identifier = ProfileIdentifier.of(EnhanceableSite.Twitter, userId).unwrap()
- if (!identifier) return null
-
- return (
-
- )
- }, [userId])
-
- if (!component) return null
-
- return {component}
-}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnProfile.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnProfile.tsx
deleted file mode 100644
index 0e941017376f..000000000000
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Farcaster/injectFarcasterOnProfile.tsx
+++ /dev/null
@@ -1,58 +0,0 @@
-import { useMemo, useState } from 'react'
-import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
-import { createInjectHooksRenderer, Plugin, useActivatedPluginsSiteAdaptor } from '@masknet/plugin-infra/content-script'
-import { makeStyles } from '@masknet/theme'
-import { startWatch } from '../../../../utils/startWatch.js'
-import { useCurrentVisitingIdentity } from '../../../../components/DataSource/useActivatedUI.js'
-import { attachReactTreeWithContainer } from '../../../../utils/shadow-root/renderInShadowRoot.js'
-import { querySelector } from '../../utils/selector.js'
-
-function selector() {
- return querySelector('[data-testid=UserName] div[dir]')
-}
-
-export function injectFarcasterOnProfile(signal: AbortSignal) {
- const watcher = new MutationObserverWatcher(selector())
- startWatch(watcher, signal)
- attachReactTreeWithContainer(watcher.firstDOMProxy.afterShadow, { signal }).render()
-}
-
-const useStyles = makeStyles()((theme) => ({
- hide: {
- display: 'none',
- },
- slot: {
- position: 'relative',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- borderRadius: 999,
- marginLeft: theme.spacing(0.5),
- verticalAlign: 'top',
- },
-}))
-
-function ProfileFarcasterSlot() {
- const visitingIdentity = useCurrentVisitingIdentity()
- const [disabled, setDisabled] = useState(true)
- const { classes, cx } = useStyles()
-
- const component = useMemo(() => {
- const Component = createInjectHooksRenderer(
- useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Farcaster?.UI?.Content,
- )
-
- return (
-
- )
- }, [visitingIdentity.identifier])
-
- if (!component || !visitingIdentity.identifier) return null
-
- return {component}
-}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/index.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/index.tsx
deleted file mode 100644
index f88a03d46af4..000000000000
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/index.tsx
+++ /dev/null
@@ -1,13 +0,0 @@
-import { injectLensOnConversation } from './injectLensOnConversation.js'
-import { injectLensOnPost } from './injectLensOnPost.js'
-import { injectLensOnProfile } from './injectLensOnProfile.js'
-import { injectLensOnSpaceDock } from './injectLensOnSpaceDock.js'
-import { injectLensOnUserCell } from './injectLensOnUserCell.js'
-
-export function injectLens(signal: AbortSignal) {
- injectLensOnProfile(signal)
- injectLensOnPost(signal)
- injectLensOnUserCell(signal)
- injectLensOnConversation(signal)
- injectLensOnSpaceDock(signal)
-}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnConversation.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnConversation.tsx
deleted file mode 100644
index 5866bb3ad745..000000000000
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnConversation.tsx
+++ /dev/null
@@ -1,92 +0,0 @@
-import { memo, useMemo, useState } from 'react'
-import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
-import { createInjectHooksRenderer, Plugin, useActivatedPluginsSiteAdaptor } from '@masknet/plugin-infra/content-script'
-import { EnhanceableSite, ProfileIdentifier } from '@masknet/shared-base'
-import { makeStyles } from '@masknet/theme'
-import { attachReactTreeWithContainer } from '../../../../utils/shadow-root/renderInShadowRoot.js'
-import { querySelectorAll } from '../../utils/selector.js'
-import { startWatch } from '../../../../utils/startWatch.js'
-
-function selector() {
- return querySelectorAll('[data-testid=conversation] div:not([tabindex]) div[dir] + div[dir]')
-}
-
-const useStyles = makeStyles()((theme) => ({
- hide: {
- display: 'none',
- },
- slot: {
- position: 'relative',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- borderRadius: 999,
- marginLeft: theme.spacing(0.5),
- verticalAlign: 'top',
- },
-}))
-
-interface Props {
- userId: string
-}
-
-function createRootElement() {
- const span = document.createElement('span')
- Object.assign(span.style, {
- verticalAlign: 'bottom',
- height: '21px',
- alignItems: 'center',
- justifyContent: 'center',
- display: 'inline-flex',
- } as CSSStyleDeclaration)
- return span
-}
-
-const ConversationLensSlot = memo(function ConversationLensSlot({ userId }: Props) {
- const [disabled, setDisabled] = useState(true)
- const { classes, cx } = useStyles()
-
- const component = useMemo(() => {
- const Component = createInjectHooksRenderer(
- useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Lens?.UI?.Content,
- undefined,
- createRootElement,
- )
- const identifier = ProfileIdentifier.of(EnhanceableSite.Twitter, userId).unwrapOr(null)
- if (!identifier) return null
-
- return (
-
- )
- }, [userId])
-
- if (!component) return null
-
- return {component}
-})
-
-/**
- * Inject on conversation, including both DM drawer and message page (/messages/xxx)
- */
-export function injectLensOnConversation(signal: AbortSignal) {
- const watcher = new MutationObserverWatcher(selector())
- startWatch(watcher, signal)
- watcher.useForeach((node, _, proxy) => {
- const spans = node
- .closest('[data-testid=conversation]')
- ?.querySelectorAll('[tabindex] [dir] span:not([data-testid=tweetText])')
- if (!spans) return
- const userId = [...spans].reduce((id, node) => {
- if (id) return id
- if (node.textContent?.match(/@\w/)) {
- return node.textContent.trim().slice(1)
- }
- return ''
- }, '')
- if (!userId) return
- attachReactTreeWithContainer(proxy.afterShadow, { signal, untilVisible: true }).render(
- ,
- )
- })
-}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnSpaceDock.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnSpaceDock.tsx
deleted file mode 100644
index 9adf543d77ab..000000000000
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnSpaceDock.tsx
+++ /dev/null
@@ -1,91 +0,0 @@
-import { useMemo, useState } from 'react'
-import { makeStyles } from '@masknet/theme'
-import { EnhanceableSite, ProfileIdentifier } from '@masknet/shared-base'
-import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
-import { createInjectHooksRenderer, Plugin, useActivatedPluginsSiteAdaptor } from '@masknet/plugin-infra/content-script'
-import { startWatch } from '../../../../utils/startWatch.js'
-import { attachReactTreeWithContainer } from '../../../../utils/shadow-root/renderInShadowRoot.js'
-import { querySelectorAll } from '../../utils/selector.js'
-
-function avatarSelector() {
- return querySelectorAll(
- '[data-testid=SpaceDockExpanded] [data-testid^=UserAvatar-Container-],[data-testid=sheetDialog] [data-testid^=UserAvatar-Container-]',
- ).map((node) => {
- const span = node.parentElement?.parentElement?.nextElementSibling?.querySelector('div > span + span > span')
- return span
- })
-}
-
-/**
- * Inject on space dock
- */
-export function injectLensOnSpaceDock(signal: AbortSignal) {
- const watcher = new MutationObserverWatcher(avatarSelector())
- startWatch(watcher, signal)
- watcher.useForeach((node, _, proxy) => {
- const avatar = node
- .closest('div[dir]')
- ?.previousElementSibling?.querySelector('[data-testid^=UserAvatar-Container-]')
- if (!avatar) return
- const userId = avatar.dataset.testid?.slice('UserAvatar-Container-'.length)
- if (!userId) return
- attachReactTreeWithContainer(proxy.afterShadow, { signal, untilVisible: true }).render(
- ,
- )
- })
-}
-
-const useStyles = makeStyles()((theme) => ({
- hide: {
- display: 'none',
- },
- slot: {
- position: 'relative',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- borderRadius: 999,
- marginLeft: theme.spacing(0.5),
- verticalAlign: 'top',
- },
-}))
-
-interface Props {
- userId: string
-}
-
-function createRootElement() {
- const span = document.createElement('span')
- Object.assign(span.style, {
- verticalAlign: 'bottom',
- height: '21px',
- alignItems: 'center',
- justifyContent: 'center',
- display: 'inline-flex',
- } as CSSStyleDeclaration)
- return span
-}
-
-function SpaceDockLensSlot({ userId }: Props) {
- const [disabled, setDisabled] = useState(true)
- const { classes, cx } = useStyles()
-
- const component = useMemo(() => {
- const Component = createInjectHooksRenderer(
- useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Lens?.UI?.Content,
- undefined,
- createRootElement,
- )
- const identifier = ProfileIdentifier.of(EnhanceableSite.Twitter, userId).unwrap()
- if (!identifier) return null
-
- return (
-
- )
- }, [userId])
-
- if (!component) return null
-
- return {component}
-}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnUserCell.tsx b/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnUserCell.tsx
deleted file mode 100644
index 81efeaf3f762..000000000000
--- a/packages/mask/content-script/site-adaptors/twitter.com/injection/Lens/injectLensOnUserCell.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { useMemo, useState } from 'react'
-import { MutationObserverWatcher } from '@dimensiondev/holoflows-kit'
-import { createInjectHooksRenderer, Plugin, useActivatedPluginsSiteAdaptor } from '@masknet/plugin-infra/content-script'
-import { EnhanceableSite, ProfileIdentifier } from '@masknet/shared-base'
-import { makeStyles } from '@masknet/theme'
-import { querySelectorAll } from '../../utils/selector.js'
-import { attachReactTreeWithContainer } from '../../../../utils/shadow-root/renderInShadowRoot.js'
-import { startWatch } from '../../../../utils/startWatch.js'
-
-function selector() {
- // [href^="/search"] is a hash tag
- return querySelectorAll(
- '[data-testid=UserCell] div > a[role=link]:not([tabindex]):not([href^="/search"]) [dir]:last-of-type',
- )
-}
-
-/**
- * Inject on sidebar user cell
- */
-export function injectLensOnUserCell(signal: AbortSignal) {
- const watcher = new MutationObserverWatcher(selector())
- startWatch(watcher, signal)
- watcher.useForeach((node, _, proxy) => {
- const userId = node.closest('[role=link]')?.getAttribute('href')?.slice(1)
- if (!userId) return
- // Intended to set `untilVisible` to true, but mostly user cells are fixed and visible
- attachReactTreeWithContainer(proxy.afterShadow, { signal }).render()
- })
-}
-
-const useStyles = makeStyles()((theme) => ({
- hide: {
- display: 'none',
- },
- slot: {
- position: 'relative',
- display: 'flex',
- justifyContent: 'center',
- alignItems: 'center',
- borderRadius: 999,
- marginLeft: theme.spacing(0.5),
- verticalAlign: 'top',
- },
-}))
-
-interface Props {
- userId: string
-}
-
-function createRootElement() {
- const span = document.createElement('span')
- Object.assign(span.style, {
- verticalAlign: 'bottom',
- height: '21px',
- alignItems: 'center',
- justifyContent: 'center',
- display: 'flex',
- } as CSSStyleDeclaration)
- return span
-}
-
-function UserCellLensSlot({ userId }: Props) {
- const [disabled, setDisabled] = useState(true)
- const { classes, cx } = useStyles()
-
- const component = useMemo(() => {
- const Component = createInjectHooksRenderer(
- useActivatedPluginsSiteAdaptor.visibility.useNotMinimalMode,
- (plugin) => plugin.Lens?.UI?.Content,
- undefined,
- createRootElement,
- )
- if (userId.includes('/')) return null
- const identifier = ProfileIdentifier.of(EnhanceableSite.Twitter, userId).unwrap()
- if (!identifier) return null
-
- return (
-
- )
- }, [userId])
-
- if (!component) return null
-
- return {component}
-}
diff --git a/packages/mask/content-script/site-adaptors/twitter.com/ui-provider.ts b/packages/mask/content-script/site-adaptors/twitter.com/ui-provider.ts
index 02d53696a046..7f7e22678188 100644
--- a/packages/mask/content-script/site-adaptors/twitter.com/ui-provider.ts
+++ b/packages/mask/content-script/site-adaptors/twitter.com/ui-provider.ts
@@ -40,12 +40,10 @@ import { TwitterRenderFragments } from './customization/render-fragments.js'
import { injectProfileCover } from './injection/ProfileCover.js'
import { injectProfileCardHolder } from './injection/ProfileCard/index.js'
import { injectAvatar } from './injection/Avatar/index.js'
-import { injectLens } from './injection/Lens/index.js'
import { injectNFTAvatarInTwitter } from './injection/NFT/index.js'
import { injectSwitchLogoButton } from './injection/SwitchLogo.js'
import { injectCalendar } from './injection/Calendar.js'
-import { injectNameWidget } from './injection/NameWidget/index.js'
-import { injectFarcaster } from './injection/Farcaster/index.js'
+import { injectBadges } from './injection/Badges/index.js'
const useInjectedDialogClassesOverwriteTwitter = makeStyles()((theme) => {
const smallQuery = `@media (max-width: ${theme.breakpoints.values.sm}px)`
@@ -205,9 +203,7 @@ const twitterUI: SiteAdaptorUI.Definition = {
openNFTAvatarSettingDialog,
avatar: injectAvatar,
tips: injectTips,
- lens: injectLens,
- farcaster: injectFarcaster,
- nameWidget: injectNameWidget,
+ badges: injectBadges,
profileCard: injectProfileCardHolder,
switchLogo: injectSwitchLogoButton,
calendar: injectCalendar,
diff --git a/packages/plugin-infra/src/types.ts b/packages/plugin-infra/src/types.ts
index 3717f3eedb95..fdc1aa4927b7 100644
--- a/packages/plugin-infra/src/types.ts
+++ b/packages/plugin-infra/src/types.ts
@@ -260,6 +260,7 @@ export namespace Plugin.SiteAdaptor {
Lens?: LensWidget
/** This UI will be rendered components on the Lens realm */
Farcaster?: FarcasterWidget
+ Badges?: BadgesWidget
NameWidget?: NameWidget
/** This UI will be rendered as plugin wrapper page */
wrapperProps?: PluginWrapperProps
@@ -540,6 +541,13 @@ export namespace Plugin.SiteAdaptor {
Sidebar = 'sidebar',
}
+ export enum BadgesSlot {
+ ProfileName = 'profile-name',
+ Post = 'post',
+ Sidebar = 'sidebar',
+ }
+
+ /** @deprecated */
export interface LensOptions {
identity?: ProfileIdentifier
slot: LensSlot
@@ -547,6 +555,7 @@ export namespace Plugin.SiteAdaptor {
/** To update enabled/disabled status */
onStatusUpdate?(disabled: boolean): void
}
+ /** @deprecated */
export interface FarcasterOptions {
identity?: ProfileIdentifier
slot: FarcasterSlot
@@ -555,6 +564,14 @@ export namespace Plugin.SiteAdaptor {
onStatusUpdate?(disabled: boolean): void
}
+ export interface SocialBadgeOptions {
+ identity?: ProfileIdentifier
+ slot: BadgesSlot
+ /** To update enabled/disabled status */
+ onStatusUpdate?(disabled: boolean): void
+ }
+
+ /** @deprecated */
export interface LensWidget {
ID: string
UI?: {
@@ -564,6 +581,7 @@ export namespace Plugin.SiteAdaptor {
Content: InjectUI
}
}
+ /** @deprecated */
export interface FarcasterWidget {
ID: string
UI?: {
@@ -573,6 +591,16 @@ export namespace Plugin.SiteAdaptor {
Content: InjectUI
}
}
+ export interface BadgesWidget {
+ ID: string
+ UI?: {
+ /**
+ * The injected Social Badge Content component
+ */
+ Content: InjectUI
+ }
+ }
+
export enum NameWidgetSlot {
ProfileName = 'profile-name',
Post = 'post',
diff --git a/packages/plugins/RSS3/src/SiteAdaptor/SocialFeeds/SocialFeed.tsx b/packages/plugins/RSS3/src/SiteAdaptor/SocialFeeds/SocialFeed.tsx
index 864b5d1f34de..7b0e77d000ef 100644
--- a/packages/plugins/RSS3/src/SiteAdaptor/SocialFeeds/SocialFeed.tsx
+++ b/packages/plugins/RSS3/src/SiteAdaptor/SocialFeeds/SocialFeed.tsx
@@ -178,7 +178,7 @@ const useStyles = makeStyles {
diff --git a/packages/plugins/RSS3/src/SiteAdaptor/components/share.ts b/packages/plugins/RSS3/src/SiteAdaptor/components/share.ts
index 78360bdb1690..066ea0859a21 100644
--- a/packages/plugins/RSS3/src/SiteAdaptor/components/share.ts
+++ b/packages/plugins/RSS3/src/SiteAdaptor/components/share.ts
@@ -139,7 +139,7 @@ export const hostIconMap: Record = {
'polygonscan.com': Icons.PolygonScan,
'crossbell.io': Icons.Crossbell,
'scan.crossbell.io': Icons.Crossbell,
- 'lenster.xyz': Icons.Lens,
+ 'lenster.xyz': Icons.DarkLens,
'looksrare.org': Icons.LooksRare,
'gitcoin.co': Icons.Gitcoin,
'bscscan.com': Icons.BSC,
diff --git a/packages/plugins/RedPacket/src/SiteAdaptor/FireflyRedPacketDetailsItem.tsx b/packages/plugins/RedPacket/src/SiteAdaptor/FireflyRedPacketDetailsItem.tsx
index a2d5df872a87..0bb95a6cae65 100644
--- a/packages/plugins/RedPacket/src/SiteAdaptor/FireflyRedPacketDetailsItem.tsx
+++ b/packages/plugins/RedPacket/src/SiteAdaptor/FireflyRedPacketDetailsItem.tsx
@@ -171,7 +171,7 @@ const useStyles = makeStyles<{ listItemBackground?: string; listItemBackgroundIc
const platformIconMap = {
[FireflyRedPacketAPI.PlatformType.twitter]: ,
- [FireflyRedPacketAPI.PlatformType.lens]: ,
+ [FireflyRedPacketAPI.PlatformType.lens]: ,
[FireflyRedPacketAPI.PlatformType.farcaster]: ,
}
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/Web3ProfileGlobalInjection.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/Web3ProfileGlobalInjection.tsx
index fbf3ecd40d92..b0e9c517ee58 100644
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/Web3ProfileGlobalInjection.tsx
+++ b/packages/plugins/Web3Profile/src/SiteAdaptor/Web3ProfileGlobalInjection.tsx
@@ -1,12 +1,11 @@
-import { memo, useEffect, useState } from 'react'
-import { ChainId } from '@masknet/web3-shared-evm'
-import { useRemoteControlledDialog } from '@masknet/shared-base-ui'
import { CrossIsolationMessages } from '@masknet/shared-base'
+import { useRemoteControlledDialog } from '@masknet/shared-base-ui'
import { EVMWeb3ContextProvider } from '@masknet/web3-hooks-base'
-import { LensPopup } from './components/Lens/LensPopup.js'
+import { ChainId } from '@masknet/web3-shared-evm'
+import { memo, useEffect, useState } from 'react'
import { FollowLensDialog } from './components/Lens/FollowLensDialog.js'
+import { SocialPopup } from './components/SocialBadges/SocialPopup.js'
import { Web3ProfileDialog } from './components/Web3ProfileDialog.js'
-import { FarcasterPopup } from './components/Farcaster/FarcasterPopup.js'
export const Web3ProfileGlobalInjection = memo(function Web3ProfileGlobalInjection() {
const [profileOpen, setProfileOpen] = useState(false)
@@ -39,8 +38,7 @@ export const Web3ProfileGlobalInjection = memo(function Web3ProfileGlobalInjecti
: null}
-
-
+
>
)
})
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterBadge.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterBadge.tsx
deleted file mode 100644
index 503a5cc18338..000000000000
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterBadge.tsx
+++ /dev/null
@@ -1,85 +0,0 @@
-import { Icons } from '@masknet/icons'
-import { Plugin } from '@masknet/plugin-infra'
-import { makeStyles } from '@masknet/theme'
-import type { FireflyConfigAPI } from '@masknet/web3-providers/types'
-import { IconButton } from '@mui/material'
-import { memo, useEffect, useRef } from 'react'
-import { closeFarcasterPopup, openFarcasterPopup } from '../../emitter.js'
-
-const FarcasterIconSizeMap: Record = {
- [Plugin.SiteAdaptor.FarcasterSlot.Post]: 18,
- [Plugin.SiteAdaptor.FarcasterSlot.ProfileName]: 18,
- [Plugin.SiteAdaptor.FarcasterSlot.Sidebar]: 16,
-}
-
-const useStyles = makeStyles()({
- badge: {
- padding: 0,
- verticalAlign: 'baseline',
- },
-})
-interface Props {
- slot: Plugin.SiteAdaptor.FarcasterSlot
- accounts: FireflyConfigAPI.FarcasterProfile[]
- userId: string
-}
-
-export const FarcasterBadge = memo(({ slot, accounts, userId }: Props) => {
- const buttonRef = useRef(null)
- const { classes } = useStyles()
-
- useEffect(() => {
- const button = buttonRef.current
- if (!button) return
- let openTimer: ReturnType
- const enter = () => {
- clearTimeout(openTimer)
-
- openTimer = setTimeout(() => {
- openFarcasterPopup({
- accounts,
- userId,
- popupAnchorEl: buttonRef.current,
- })
- }, 200)
- }
- const leave = () => {
- clearTimeout(openTimer)
- }
- button.addEventListener('mouseenter', enter)
- button.addEventListener('mouseleave', leave)
- return () => {
- clearTimeout(openTimer)
- button.removeEventListener('mouseenter', enter)
- button.removeEventListener('mouseleave', leave)
- }
- }, [accounts, userId])
-
- useEffect(() => {
- function hide() {
- closeFarcasterPopup({
- popupAnchorEl: buttonRef.current,
- })
- }
- const ob = new IntersectionObserver((entries) => {
- if (entries[0].intersectionRatio !== 0) return
- hide()
- })
- if (buttonRef.current) {
- ob.observe(buttonRef.current)
- }
- return () => {
- hide()
- ob.disconnect()
- }
- // eslint-disable-next-line react-compiler/react-compiler
- }, [buttonRef.current])
-
- return (
-
-
-
- )
-})
-
-FarcasterBadge.displayName = 'FarcasterBadge'
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterPopup.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterPopup.tsx
deleted file mode 100644
index 37ae7c236963..000000000000
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterPopup.tsx
+++ /dev/null
@@ -1,62 +0,0 @@
-import { isEqual } from 'lodash-es'
-import { memo, useEffect, useRef, useState } from 'react'
-import { ShadowRootPopper, makeStyles } from '@masknet/theme'
-import type { FireflyConfigAPI } from '@masknet/web3-providers/types'
-import { Fade } from '@mui/material'
-import { emitter } from '../../emitter.js'
-import { useControlFarcasterPopup } from '../../hooks/Farcaster/useControlFarcasterPopup.js'
-import { FarcasterList } from './FarcasterList.js'
-
-const useStyles = makeStyles()((theme) => ({
- popup: {
- position: 'absolute',
- zIndex: 99,
- borderRadius: 16,
- boxShadow:
- theme.palette.mode === 'light' ?
- '0px 4px 30px rgba(0, 0, 0, 0.1)'
- : '0px 4px 30px rgba(255, 255, 255, 0.15)',
- },
-}))
-
-export const FarcasterPopup = memo(() => {
- const { classes } = useStyles()
- const holderRef = useRef(null)
- const [accounts, setAccounts] = useState([])
- const active = useControlFarcasterPopup(holderRef)
- const [anchorEl, setAnchorEl] = useState()
- const anchorElRef = useRef(undefined)
-
- useEffect(() => {
- const unsubscribeOpen = emitter.on('open-farcaster', async ({ accounts, popupAnchorEl }) => {
- setAccounts((oldAccounts) => {
- return isEqual(oldAccounts, accounts) ? oldAccounts : accounts
- })
- setAnchorEl(popupAnchorEl)
- anchorElRef.current = popupAnchorEl
- })
- const unsubscribeClose = emitter.on('close-farcaster', ({ popupAnchorEl }) => {
- if (popupAnchorEl !== anchorElRef.current) return
- setAnchorEl(null)
- })
- return () => {
- unsubscribeOpen()
- unsubscribeClose()
- }
- }, [])
-
- return (
-
-
-
-
-
- )
-})
-
-FarcasterPopup.displayName = 'FarcasterPopup'
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/FollowLensDialog.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/FollowLensDialog.tsx
index ce34c95522a0..724a62e887cd 100644
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/FollowLensDialog.tsx
+++ b/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/FollowLensDialog.tsx
@@ -1,3 +1,4 @@
+import { Trans } from '@lingui/macro'
import { Icons } from '@masknet/icons'
import {
ChainBoundary,
@@ -21,9 +22,8 @@ import { getFireflyLensProfileLink, getProfileAvatar } from '../../../utils.js'
import { useConfettiExplosion } from '../../hooks/ConfettiExplosion/index.js'
import { useFollow } from '../../hooks/Lens/useFollow.js'
import { useUnfollow } from '../../hooks/Lens/useUnfollow.js'
-import { HandlerDescription } from './HandlerDescription.js'
import { useUpdateFollowingStatus } from '../../hooks/Lens/useUpdateFollowingStatus.js'
-import { Trans } from '@lingui/macro'
+import { HandlerDescription } from './HandlerDescription.js'
const useStyles = makeStyles<{ account: boolean }>()((theme, { account }) => ({
container: {
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/LensPopup.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/LensPopup.tsx
deleted file mode 100644
index aeb6891745b8..000000000000
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/LensPopup.tsx
+++ /dev/null
@@ -1,82 +0,0 @@
-import { isEqual, sortBy, uniqBy } from 'lodash-es'
-import { memo, useEffect, useRef, useState } from 'react'
-import { ShadowRootPopper, makeStyles } from '@masknet/theme'
-import { NextIDProof } from '@masknet/web3-providers'
-import type { FireflyConfigAPI, NextIDBaseAPI } from '@masknet/web3-providers/types'
-import { Fade } from '@mui/material'
-import { emitter } from '../../emitter.js'
-import { useControlLensPopup } from '../../hooks/Lens/useControlLensPopup.js'
-import { LensList } from './LensList.js'
-
-const useStyles = makeStyles()((theme) => ({
- popup: {
- position: 'absolute',
- zIndex: 99,
- borderRadius: 16,
- boxShadow:
- theme.palette.mode === 'light' ?
- '0px 4px 30px rgba(0, 0, 0, 0.1)'
- : '0px 4px 30px rgba(255, 255, 255, 0.15)',
- },
-}))
-
-export const NextIdLensToFireflyLens = (account: NextIDBaseAPI.LensAccount): FireflyConfigAPI.LensAccount => {
- return {
- address: account.address,
- name: account.displayName,
- handle: account.handle,
- bio: '',
- url: '',
- profileUri: [],
- }
-}
-
-export const LensPopup = memo(() => {
- const { classes } = useStyles()
- const holderRef = useRef(null)
- const [lens, setLens] = useState([])
- const active = useControlLensPopup(holderRef)
- const [anchorEl, setAnchorEl] = useState()
- const anchorElRef = useRef(undefined)
-
- useEffect(() => {
- const unsubscribeOpen = emitter.on('open', async ({ lensAccounts, popupAnchorEl }) => {
- setLens((oldAccounts) => {
- return isEqual(oldAccounts, lensAccounts) ? oldAccounts : lensAccounts
- })
- setAnchorEl(popupAnchorEl)
- anchorElRef.current = popupAnchorEl
- if (!lens[0]?.handle) return
- const accounts = await NextIDProof.queryAllLens(lens[0].handle)
- if (!accounts.length) return
- setLens((oldAccounts) => {
- if (accounts.length <= oldAccounts.length) return oldAccounts
- const merged = uniqBy([...oldAccounts, ...accounts.map(NextIdLensToFireflyLens)], (x) => x.handle)
- return sortBy(merged, [(x) => -accounts.findIndex((y) => x.handle === y.handle)])
- })
- })
- const unsubscribeClose = emitter.on('close', ({ popupAnchorEl }) => {
- if (popupAnchorEl !== anchorElRef.current) return
- setAnchorEl(null)
- })
- return () => {
- unsubscribeOpen()
- unsubscribeClose()
- }
- }, [])
-
- return (
-
-
-
-
-
- )
-})
-
-LensPopup.displayName = 'LensPopup'
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/ProfilePopup.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/ProfilePopup.tsx
index e0e18336eb26..0ad1717ca371 100644
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/ProfilePopup.tsx
+++ b/packages/plugins/Web3Profile/src/SiteAdaptor/components/ProfilePopup.tsx
@@ -1,6 +1,12 @@
+import { Trans } from '@lingui/macro'
import { Icons } from '@masknet/icons'
+import { Image, SelectProviderModal, WalletIcon } from '@masknet/shared'
+import { NetworkPluginID } from '@masknet/shared-base'
import { makeStyles, usePortalShadowRoot } from '@masknet/theme'
+import { useChainContext, useProviderDescriptor, useWeb3Utils } from '@masknet/web3-hooks-base'
import type { LensBaseAPI } from '@masknet/web3-providers/types'
+import { isSameAddress } from '@masknet/web3-shared-base'
+import { ChainId, formatEthereumAddress } from '@masknet/web3-shared-evm'
import {
Box,
Button,
@@ -14,13 +20,7 @@ import {
Typography,
} from '@mui/material'
import { memo } from 'react'
-import { Image, SelectProviderModal, WalletIcon } from '@masknet/shared'
-import { ChainId, formatEthereumAddress } from '@masknet/web3-shared-evm'
-import { NetworkPluginID } from '@masknet/shared-base'
-import { useChainContext, useProviderDescriptor, useWeb3Utils } from '@masknet/web3-hooks-base'
-import { isSameAddress } from '@masknet/web3-shared-base'
import { getProfileAvatar } from '../../utils.js'
-import { Trans } from '@lingui/macro'
const useStyles = makeStyles()((theme) => ({
paper: {
@@ -171,9 +171,9 @@ export const ProfilePopup = memo(function ProfilePopup({
className={classes.avatar}
size={36}
src={avatar}
- fallback={}
+ fallback={}
/>
- : }
+ : }
= {
- [Plugin.SiteAdaptor.LensSlot.Post]: 18,
- [Plugin.SiteAdaptor.LensSlot.ProfileName]: 18,
- [Plugin.SiteAdaptor.LensSlot.Sidebar]: 16,
+const BadgesIconSizeMap: Record = {
+ [Plugin.SiteAdaptor.BadgesSlot.Post]: 18,
+ [Plugin.SiteAdaptor.BadgesSlot.ProfileName]: 18,
+ [Plugin.SiteAdaptor.BadgesSlot.Sidebar]: 16,
}
const useStyles = makeStyles()({
@@ -17,14 +17,23 @@ const useStyles = makeStyles()({
padding: 0,
verticalAlign: 'baseline',
},
+ farcaster: {
+ marginLeft: -5,
+ },
})
interface Props {
- slot: Plugin.SiteAdaptor.LensSlot
- accounts: FireflyConfigAPI.LensAccount[]
+ slot: Plugin.SiteAdaptor.BadgesSlot
+ lensAccounts: FireflyConfigAPI.LensAccount[]
+ farcasterAccounts: FireflyConfigAPI.FarcasterProfile[]
userId: string
}
-export const LensBadge = memo(({ slot, accounts, userId }: Props) => {
+export const SocialBadges = memo(function SocialBadges({
+ slot,
+ lensAccounts,
+ farcasterAccounts,
+ userId,
+}: Props) {
const buttonRef = useRef(null)
const { classes } = useStyles()
@@ -37,7 +46,8 @@ export const LensBadge = memo(({ slot, accounts, userId }: Props) => {
openTimer = setTimeout(() => {
openPopup({
- lensAccounts: accounts,
+ lensAccounts,
+ farcasterAccounts,
userId,
popupAnchorEl: buttonRef.current,
})
@@ -53,7 +63,7 @@ export const LensBadge = memo(({ slot, accounts, userId }: Props) => {
button.removeEventListener('mouseenter', enter)
button.removeEventListener('mouseleave', leave)
}
- }, [accounts, userId])
+ }, [lensAccounts, userId, farcasterAccounts])
useEffect(() => {
function hide() {
@@ -75,11 +85,15 @@ export const LensBadge = memo(({ slot, accounts, userId }: Props) => {
// eslint-disable-next-line react-compiler/react-compiler
}, [buttonRef.current])
+ const size = BadgesIconSizeMap[slot]
return (
-
+ {lensAccounts.length ?
+
+ : null}
+ {farcasterAccounts.length ?
+
+ : null}
)
})
-
-LensBadge.displayName = 'LensBadge'
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterList.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/FarcasterList.tsx
similarity index 87%
rename from packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterList.tsx
rename to packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/FarcasterList.tsx
index 35d1cf2a5303..ccd99bc37779 100644
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Farcaster/FarcasterList.tsx
+++ b/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/FarcasterList.tsx
@@ -2,7 +2,7 @@ import { Icons } from '@masknet/icons'
import { Image } from '@masknet/shared'
import { makeStyles } from '@masknet/theme'
import type { FireflyConfigAPI } from '@masknet/web3-providers/types'
-import { List, ListItem, Typography, type ListProps, Link } from '@mui/material'
+import { ListItem, Typography, Link } from '@mui/material'
import { memo } from 'react'
const useStyles = makeStyles()((theme) => {
@@ -69,29 +69,25 @@ const useStyles = makeStyles()((theme) => {
},
}
})
-interface Props extends ListProps {
+interface Props {
accounts: FireflyConfigAPI.FarcasterProfile[]
}
-export const FarcasterList = memo(({ className, accounts, ...rest }: Props) => {
- const { classes, cx } = useStyles()
-
+export const FarcasterList = memo(function FarcasterList({ accounts }: Props) {
return (
-
+ <>
{accounts.map((account, key) => {
return
})}
-
+ >
)
})
-FarcasterList.displayName = 'FarcasterList'
-
interface FarcasterListItemProps {
account: FireflyConfigAPI.FarcasterProfile
}
-const FarcasterListItem = memo(({ account }) => {
+const FarcasterListItem = memo(function FarcasterListItem({ account }) {
const { classes } = useStyles()
const profileUri = `https://firefly.mask.social/profile/farcaster/${account.fid}`
const farcasterIcon =
@@ -117,5 +113,3 @@ const FarcasterListItem = memo(({ account }) => {
)
})
-
-FarcasterListItem.displayName = 'FarcasterListItem'
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/LensList.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/LensList.tsx
similarity index 87%
rename from packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/LensList.tsx
rename to packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/LensList.tsx
index 3a6503ee404c..ad09dad2ff58 100644
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/components/Lens/LensList.tsx
+++ b/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/LensList.tsx
@@ -1,35 +1,20 @@
+import { Trans } from '@lingui/macro'
import { Icons } from '@masknet/icons'
import { Image } from '@masknet/shared'
import { CrossIsolationMessages, EMPTY_LIST, PersistentStorages } from '@masknet/shared-base'
import { ActionButton, makeStyles } from '@masknet/theme'
-import { memo } from 'react'
import { useChainContext } from '@masknet/web3-hooks-base'
import { Lens } from '@masknet/web3-providers'
import type { FireflyConfigAPI } from '@masknet/web3-providers/types'
import { isSameAddress } from '@masknet/web3-shared-base'
-import { List, ListItem, Typography, type ListProps } from '@mui/material'
+import { ListItem, Typography } from '@mui/material'
import { useQuery } from '@tanstack/react-query'
import { compact, first } from 'lodash-es'
+import { memo } from 'react'
import { useSubscription } from 'use-subscription'
-import { Trans } from '@lingui/macro'
const useStyles = makeStyles()((theme) => {
- const isDark = theme.palette.mode === 'dark'
return {
- list: {
- backgroundColor: isDark ? '#030303' : theme.palette.common.white,
- maxWidth: 320,
- // Show up to 6 item
- maxHeight: 244,
- overflow: 'auto',
- minWidth: 240,
- padding: theme.spacing(1.5),
- boxSizing: 'border-box',
- borderRadius: 16,
- '&::-webkit-scrollbar': {
- display: 'none',
- },
- },
listItem: {
cursor: 'default',
display: 'flex',
@@ -90,12 +75,11 @@ const useStyles = makeStyles()((theme) => {
},
}
})
-interface Props extends ListProps {
+interface Props {
accounts: FireflyConfigAPI.LensAccount[]
}
-export const LensList = memo(({ className, accounts, ...rest }: Props) => {
- const { classes, cx } = useStyles()
+export const LensList = memo(function LensList({ accounts }: Props) {
const { account: wallet } = useChainContext()
const latestProfile = useSubscription(PersistentStorages.Settings.storage.latestLensProfile.subscription)
@@ -152,22 +136,20 @@ export const LensList = memo(({ className, accounts, ...rest }: Props) => {
})
return (
-
+ <>
{data.map((account, key) => {
return
})}
-
+ >
)
})
-LensList.displayName = 'LensList'
-
interface LensListItemProps {
account: FireflyConfigAPI.LensAccount
loading: boolean
}
-const LensListItem = memo(({ account, loading }) => {
+const LensListItem = memo(function LensListItem({ account, loading }) {
const { classes } = useStyles()
const { account: wallet } = useChainContext()
const profileUri = account.profileUri.filter(Boolean)
@@ -207,5 +189,3 @@ const LensListItem = memo(({ account, loading }) => {
)
})
-
-LensListItem.displayName = 'LensListItem'
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/SocialPopup.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/SocialPopup.tsx
new file mode 100644
index 000000000000..77745582cd04
--- /dev/null
+++ b/packages/plugins/Web3Profile/src/SiteAdaptor/components/SocialBadges/SocialPopup.tsx
@@ -0,0 +1,96 @@
+import { isEqual, sortBy, uniqBy } from 'lodash-es'
+import { memo, useEffect, useRef, useState } from 'react'
+import { ShadowRootPopper, makeStyles } from '@masknet/theme'
+import { NextIDProof } from '@masknet/web3-providers'
+import type { FireflyConfigAPI } from '@masknet/web3-providers/types'
+import { Fade, List } from '@mui/material'
+import { emitter } from '../../emitter.js'
+import { useControlLensPopup } from '../../hooks/Lens/useControlLensPopup.js'
+import { LensList } from './LensList.js'
+import { FarcasterList } from './FarcasterList.js'
+import { NextIdLensToFireflyLens } from '../../../utils.js'
+
+const useStyles = makeStyles()((theme) => {
+ const isDark = theme.palette.mode === 'dark'
+ return {
+ popup: {
+ position: 'absolute',
+ zIndex: 99,
+ borderRadius: 16,
+ boxShadow:
+ theme.palette.mode === 'light' ?
+ '0px 4px 30px rgba(0, 0, 0, 0.1)'
+ : '0px 4px 30px rgba(255, 255, 255, 0.15)',
+ },
+ list: {
+ backgroundColor: isDark ? '#030303' : theme.palette.common.white,
+ maxWidth: 320,
+ // Show up to 6 item
+ maxHeight: 244,
+ overflow: 'auto',
+ minWidth: 240,
+ padding: theme.spacing(1.5),
+ boxSizing: 'border-box',
+ borderRadius: 16,
+ '&::-webkit-scrollbar': {
+ display: 'none',
+ },
+ },
+ }
+})
+
+export const SocialPopup = memo(function SocialPopup() {
+ const { classes } = useStyles()
+ const holderRef = useRef(null)
+ const [lensAccounts, setLensAccounts] = useState([])
+ const [farcasterAccounts, setFarcasterAccounts] = useState([])
+ const active = useControlLensPopup(holderRef)
+ const [anchorEl, setAnchorEl] = useState()
+ const anchorElRef = useRef(undefined)
+
+ useEffect(() => {
+ const unsubscribeOpen = emitter.on('open', async ({ lensAccounts, farcasterAccounts, popupAnchorEl }) => {
+ setLensAccounts((old) => (isEqual(old, lensAccounts) ? old : lensAccounts))
+ setFarcasterAccounts((old) => (isEqual(old, farcasterAccounts) ? old : farcasterAccounts))
+ setAnchorEl(popupAnchorEl)
+ anchorElRef.current = popupAnchorEl
+ if (lensAccounts[0]?.handle) {
+ const accounts = await NextIDProof.queryAllLens(lensAccounts[0].handle)
+ if (!accounts.length) return
+ setLensAccounts((oldAccounts) => {
+ if (accounts.length <= oldAccounts.length) return oldAccounts
+ const merged = uniqBy([...oldAccounts, ...accounts.map(NextIdLensToFireflyLens)], (x) => x.handle)
+ return sortBy(merged, [(x) => -accounts.findIndex((y) => x.handle === y.handle)])
+ })
+ }
+ })
+ const unsubscribeClose = emitter.on('close', ({ popupAnchorEl }) => {
+ if (popupAnchorEl !== anchorElRef.current) return
+ setAnchorEl(null)
+ })
+ return () => {
+ unsubscribeOpen()
+ unsubscribeClose()
+ }
+ }, [])
+
+ return (
+
+
+
+ {lensAccounts.length ?
+
+ : null}
+ {farcasterAccounts.length ?
+
+ : null}
+
+
+
+ )
+})
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/emitter.ts b/packages/plugins/Web3Profile/src/SiteAdaptor/emitter.ts
index 3b859562dd3b..3c00a3ea49eb 100644
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/emitter.ts
+++ b/packages/plugins/Web3Profile/src/SiteAdaptor/emitter.ts
@@ -3,6 +3,7 @@ import { Emitter } from '@servie/events'
interface OpenPopupOptions {
lensAccounts: FireflyConfigAPI.LensAccount[]
+ farcasterAccounts: FireflyConfigAPI.FarcasterProfile[]
/** For lazy load lens accounts from NextID */
userId: string
popupAnchorEl: HTMLElement | null
diff --git a/packages/plugins/Web3Profile/src/SiteAdaptor/index.tsx b/packages/plugins/Web3Profile/src/SiteAdaptor/index.tsx
index 1c09c9e09f06..cf32ff11e753 100644
--- a/packages/plugins/Web3Profile/src/SiteAdaptor/index.tsx
+++ b/packages/plugins/Web3Profile/src/SiteAdaptor/index.tsx
@@ -11,10 +11,9 @@ import { useEffect, useMemo } from 'react'
import { Trans } from '@lingui/macro'
import { base } from '../base.js'
import { Web3ProfileGlobalInjection } from './Web3ProfileGlobalInjection.js'
-import { FarcasterBadge } from './components/Farcaster/FarcasterBadge.js'
-import { LensBadge } from './components/Lens/LensBadge.js'
-import { NextIdLensToFireflyLens } from './components/Lens/LensPopup.js'
import { setupStorage } from './context.js'
+import { SocialBadges } from './components/SocialBadges/Badges.js'
+import { NextIdLensToFireflyLens } from '../utils.js'
const site: Plugin.SiteAdaptor.Definition = {
...base,
@@ -62,13 +61,15 @@ const site: Plugin.SiteAdaptor.Definition = {
}
})(),
],
- Lens: {
- ID: `${base.ID}_lens`,
+ Badges: {
+ ID: `${base.ID}_badges`,
UI: {
Content({ identity, slot, onStatusUpdate }) {
const userId = identity?.userId
+
+ // #region lens
const { data: accounts = EMPTY_LIST } = useFireflyLensAccounts(userId, true)
- const isProfile = slot === Plugin.SiteAdaptor.LensSlot.ProfileName
+ const isProfile = slot === Plugin.SiteAdaptor.BadgesSlot.ProfileName
const handle = accounts[0]?.handle
const { data: nextIdLens = EMPTY_LIST } = useQuery({
@@ -84,32 +85,27 @@ const site: Plugin.SiteAdaptor.Definition = {
() => (isProfile ? uniqBy([...accounts, ...nextIdLens], (x) => x.handle) : accounts),
[isProfile, accounts, nextIdLens],
)
+ // #endregion
+
+ // #region farcaster
+ const { data: farcasterAccounts = EMPTY_LIST } = useFireflyFarcasterAccounts(userId)
+ // #endregion
- const disabled = !lensAccounts.length
+ const disabled = !lensAccounts.length && !farcasterAccounts.length
useEffect(() => {
onStatusUpdate?.(disabled)
}, [onStatusUpdate, disabled])
if (!accounts.length || !userId) return null
- return
- },
- },
- },
- Farcaster: {
- ID: `${base.ID}_farcaster`,
- UI: {
- Content({ identity, slot, onStatusUpdate }) {
- const userId = identity?.userId
-
- const { data: profiles = EMPTY_LIST } = useFireflyFarcasterAccounts(userId)
- const disabled = !profiles.length
-
- useEffect(() => {
- onStatusUpdate?.(disabled)
- }, [onStatusUpdate, disabled])
- if (!userId) return null
- return
+ return (
+
+ )
},
},
},
diff --git a/packages/plugins/Web3Profile/src/utils.ts b/packages/plugins/Web3Profile/src/utils.ts
index 9a2bd42f30ef..ec12e7281f1d 100644
--- a/packages/plugins/Web3Profile/src/utils.ts
+++ b/packages/plugins/Web3Profile/src/utils.ts
@@ -1,4 +1,4 @@
-import type { LensBaseAPI } from '@masknet/web3-providers/types'
+import type { FireflyConfigAPI, LensBaseAPI, NextIDBaseAPI } from '@masknet/web3-providers/types'
import urlcat from 'urlcat'
export function getFireflyLensProfileLink(handle: string) {
@@ -11,3 +11,14 @@ export function getProfileAvatar(profile: LensBaseAPI.Profile | undefined) {
if ('optimized' in picture) return picture.optimized?.uri || picture.raw.uri
return picture.image.optimized?.uri || picture.image.raw.uri
}
+
+export const NextIdLensToFireflyLens = (account: NextIDBaseAPI.LensAccount): FireflyConfigAPI.LensAccount => {
+ return {
+ address: account.address,
+ name: account.displayName,
+ handle: account.handle,
+ bio: '',
+ url: '',
+ profileUri: [],
+ }
+}
diff --git a/packages/shared/src/UI/components/AccountIcons/index.tsx b/packages/shared/src/UI/components/AccountIcons/index.tsx
index 44311128fafd..e96f621bf06a 100644
--- a/packages/shared/src/UI/components/AccountIcons/index.tsx
+++ b/packages/shared/src/UI/components/AccountIcons/index.tsx
@@ -1,12 +1,12 @@
+import { Select, Trans } from '@lingui/macro'
import { Icons } from '@masknet/icons'
import { SocialAddressType, type SocialAccount } from '@masknet/shared-base'
import { makeStyles, ShadowRootTooltip } from '@masknet/theme'
import type { Web3Helper } from '@masknet/web3-helpers'
import { resolveSocialAddressLink } from '@masknet/web3-shared-base'
-import { Typography, type TooltipProps, Link } from '@mui/material'
+import { Link, Typography, type TooltipProps } from '@mui/material'
import { compact } from 'lodash-es'
import { Linking } from '../../../index.js'
-import { Select, Trans } from '@lingui/macro'
const useStyles = makeStyles()((theme) => {
return {
@@ -191,7 +191,7 @@ export function AccountIcons({ socialAccount, classes: externalClasses }: Accoun
supportedAddressTypes.includes(SocialAddressType.Lens) ?
{
type: SocialAddressType.Lens,
- icon: ,
+ icon: ,
}
: null,
])
diff --git a/packages/shared/src/UI/components/SocialAccountList/utils.tsx b/packages/shared/src/UI/components/SocialAccountList/utils.tsx
index 1c150f64cf2a..ab2e50672483 100644
--- a/packages/shared/src/UI/components/SocialAccountList/utils.tsx
+++ b/packages/shared/src/UI/components/SocialAccountList/utils.tsx
@@ -10,7 +10,7 @@ export const resolveNextIDPlatformIcon = createLookupTableResolver